mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-06-07 09:28:03 +08:00
[KBM Editor] Support WM_DPICHANGED event (#11015)
* [KBM Editor] Support WM_DPICHANGED event * [spell checker] add term * [KBM editor] properly cast new DPI value
This commit is contained in:
parent
fa0cfeccb9
commit
0c3ce81c94
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@ -766,6 +766,7 @@ GDISCALED
|
|||||||
generatesqlfromuserquery
|
generatesqlfromuserquery
|
||||||
GETDISPINFO
|
GETDISPINFO
|
||||||
GETDLGCODE
|
GETDLGCODE
|
||||||
|
GETDPISCALEDSIZE
|
||||||
GETEMPTYMARKUP
|
GETEMPTYMARKUP
|
||||||
GETICON
|
GETICON
|
||||||
getline
|
getline
|
||||||
|
@ -5,34 +5,43 @@
|
|||||||
|
|
||||||
namespace DPIAware
|
namespace DPIAware
|
||||||
{
|
{
|
||||||
HRESULT GetScreenDPIForWindow(HWND hwnd, UINT& dpi_x, UINT& dpi_y)
|
HRESULT GetScreenDPIForMonitor(HMONITOR targetMonitor, UINT& dpi)
|
||||||
{
|
{
|
||||||
auto monitor_handle = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
if (targetMonitor != nullptr)
|
||||||
dpi_x = 0;
|
|
||||||
dpi_y = 0;
|
|
||||||
if (monitor_handle != nullptr)
|
|
||||||
{
|
{
|
||||||
return GetDpiForMonitor(monitor_handle, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y);
|
UINT dummy = 0;
|
||||||
|
return GetDpiForMonitor(targetMonitor, MDT_EFFECTIVE_DPI, &dpi, &dummy);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
dpi = DPIAware::DEFAULT_DPI;
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT GetScreenDPIForPoint(POINT p, UINT& dpi_x, UINT& dpi_y)
|
HRESULT GetScreenDPIForWindow(HWND hwnd, UINT& dpi)
|
||||||
{
|
{
|
||||||
auto monitor_handle = MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
|
auto targetMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
dpi_x = 0;
|
return GetScreenDPIForMonitor(targetMonitor, dpi);
|
||||||
dpi_y = 0;
|
}
|
||||||
if (monitor_handle != nullptr)
|
|
||||||
|
HRESULT GetScreenDPIForPoint(POINT point, UINT& dpi)
|
||||||
|
{
|
||||||
|
auto targetMonitor = MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST);
|
||||||
|
return GetScreenDPIForMonitor(targetMonitor, dpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT GetScreenDPIForCursor(UINT& dpi)
|
||||||
|
{
|
||||||
|
HMONITOR targetMonitor = nullptr;
|
||||||
|
POINT currentCursorPos{ 0 };
|
||||||
|
|
||||||
|
if (GetCursorPos(¤tCursorPos))
|
||||||
{
|
{
|
||||||
return GetDpiForMonitor(monitor_handle, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y);
|
targetMonitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return GetScreenDPIForMonitor(targetMonitor, dpi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Convert(HMONITOR monitor_handle, int& width, int& height)
|
void Convert(HMONITOR monitor_handle, int& width, int& height)
|
||||||
@ -54,7 +63,7 @@ namespace DPIAware
|
|||||||
void ConvertByCursorPosition(int& width, int& height)
|
void ConvertByCursorPosition(int& width, int& height)
|
||||||
{
|
{
|
||||||
HMONITOR targetMonitor = nullptr;
|
HMONITOR targetMonitor = nullptr;
|
||||||
POINT currentCursorPos{};
|
POINT currentCursorPos{ 0 };
|
||||||
|
|
||||||
if (GetCursorPos(¤tCursorPos))
|
if (GetCursorPos(¤tCursorPos))
|
||||||
{
|
{
|
||||||
@ -96,7 +105,7 @@ namespace DPIAware
|
|||||||
{
|
{
|
||||||
if (AreDpiAwarenessContextsEqual(levels[i], system_returned_value))
|
if (AreDpiAwarenessContextsEqual(levels[i], system_returned_value))
|
||||||
{
|
{
|
||||||
return static_cast<AwarenessLevel>(i);
|
return static_cast<DPIAware::AwarenessLevel>(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AwarenessLevel::UNAWARE;
|
return AwarenessLevel::UNAWARE;
|
||||||
|
@ -7,8 +7,10 @@ namespace DPIAware
|
|||||||
{
|
{
|
||||||
constexpr inline int DEFAULT_DPI = 96;
|
constexpr inline int DEFAULT_DPI = 96;
|
||||||
|
|
||||||
HRESULT GetScreenDPIForWindow(HWND hwnd, UINT& dpi_x, UINT& dpi_y);
|
HRESULT GetScreenDPIForMonitor(HMONITOR targetMonitor, UINT& dpi);
|
||||||
HRESULT GetScreenDPIForPoint(POINT p, UINT& dpi_x, UINT& dpi_y);
|
HRESULT GetScreenDPIForWindow(HWND hwnd, UINT& dpi);
|
||||||
|
HRESULT GetScreenDPIForPoint(POINT p, UINT& dpi);
|
||||||
|
HRESULT GetScreenDPIForCursor(UINT& dpi);
|
||||||
void Convert(HMONITOR monitor_handle, int& width, int& height);
|
void Convert(HMONITOR monitor_handle, int& width, int& height);
|
||||||
void ConvertByCursorPosition(int& width, int& height);
|
void ConvertByCursorPosition(int& width, int& height);
|
||||||
void InverseConvert(HMONITOR monitor_handle, int& width, int& height);
|
void InverseConvert(HMONITOR monitor_handle, int& width, int& height);
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
using namespace winrt::Windows::Foundation;
|
using namespace winrt::Windows::Foundation;
|
||||||
|
|
||||||
|
static UINT g_currentDPI = DPIAware::DEFAULT_DPI;
|
||||||
|
|
||||||
LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
|
LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
|
||||||
|
|
||||||
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
||||||
@ -144,6 +146,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
|
|||||||
int windowHeight = KeyboardManagerConstants::DefaultEditKeyboardWindowHeight;
|
int windowHeight = KeyboardManagerConstants::DefaultEditKeyboardWindowHeight;
|
||||||
|
|
||||||
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
|
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
|
||||||
|
DPIAware::GetScreenDPIForCursor(g_currentDPI);
|
||||||
|
|
||||||
// Window Creation
|
// Window Creation
|
||||||
HWND _hWndEditKeyboardWindow = CreateWindow(
|
HWND _hWndEditKeyboardWindow = CreateWindow(
|
||||||
@ -385,47 +388,62 @@ void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||||||
TerminateProcess(GetCurrentProcess(), 0);
|
TerminateProcess(GetCurrentProcess(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::wstring getMessage(UINT messageCode)
|
|
||||||
{
|
|
||||||
switch (messageCode)
|
|
||||||
{
|
|
||||||
case WM_SIZE:
|
|
||||||
return L"WM_SIZE";
|
|
||||||
case WM_NCDESTROY:
|
|
||||||
return L"WM_NCDESTROY";
|
|
||||||
default:
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
RECT rcClient;
|
|
||||||
auto message = getMessage(messageCode);
|
|
||||||
if (message != L"")
|
|
||||||
{
|
|
||||||
Logger::trace(L"EditKeyboardWindowProc() messageCode={}", getMessage(messageCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (messageCode)
|
switch (messageCode)
|
||||||
{
|
{
|
||||||
// Resize the XAML window whenever the parent window is painted or resized
|
// Resize the XAML window whenever the parent window is painted or resized
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
case WM_SIZE:
|
case WM_SIZE:
|
||||||
{
|
{
|
||||||
GetClientRect(hWnd, &rcClient);
|
RECT rect = { 0 };
|
||||||
SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW);
|
GetClientRect(hWnd, &rect);
|
||||||
|
SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// To avoid UI elements overlapping on making the window smaller enforce a minimum window size
|
// To avoid UI elements overlapping on making the window smaller enforce a minimum window size
|
||||||
case WM_GETMINMAXINFO:
|
case WM_GETMINMAXINFO:
|
||||||
{
|
{
|
||||||
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
|
LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
|
||||||
int minWidth = KeyboardManagerConstants::MinimumEditKeyboardWindowWidth;
|
int minWidth = KeyboardManagerConstants::MinimumEditKeyboardWindowWidth;
|
||||||
int minHeight = KeyboardManagerConstants::MinimumEditKeyboardWindowHeight;
|
int minHeight = KeyboardManagerConstants::MinimumEditKeyboardWindowHeight;
|
||||||
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
|
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
|
||||||
lpMMI->ptMinTrackSize.x = minWidth;
|
mmi->ptMinTrackSize.x = minWidth;
|
||||||
lpMMI->ptMinTrackSize.y = minHeight;
|
mmi->ptMinTrackSize.y = minHeight;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_GETDPISCALEDSIZE:
|
||||||
|
{
|
||||||
|
UINT newDPI = static_cast<UINT>(wParam);
|
||||||
|
SIZE* size = reinterpret_cast<SIZE*>(lParam);
|
||||||
|
Logger::trace(L"WM_GETDPISCALEDSIZE: DPI {} size X {} Y {}", newDPI, size->cx, size->cy);
|
||||||
|
|
||||||
|
float scalingFactor = static_cast<float>(newDPI) / g_currentDPI;
|
||||||
|
Logger::trace(L"WM_GETDPISCALEDSIZE: scaling factor {}", scalingFactor);
|
||||||
|
|
||||||
|
size->cx = static_cast<LONG>(size->cx * scalingFactor);
|
||||||
|
size->cy = static_cast<LONG>(size->cy * scalingFactor);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_DPICHANGED:
|
||||||
|
{
|
||||||
|
UINT newDPI = static_cast<UINT>(LOWORD(wParam));
|
||||||
|
g_currentDPI = newDPI;
|
||||||
|
|
||||||
|
RECT* rect = reinterpret_cast<RECT*>(lParam);
|
||||||
|
SetWindowPos(
|
||||||
|
hWnd,
|
||||||
|
nullptr,
|
||||||
|
rect->left,
|
||||||
|
rect->top,
|
||||||
|
rect->right - rect->left,
|
||||||
|
rect->bottom - rect->top,
|
||||||
|
SWP_NOZORDER | SWP_NOACTIVATE
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger::trace(L"WM_DPICHANGED: new dpi {} rect {} {} ", newDPI, rect->right - rect->left, rect->bottom - rect->top);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
using namespace winrt::Windows::Foundation;
|
using namespace winrt::Windows::Foundation;
|
||||||
|
|
||||||
|
static UINT g_currentDPI = DPIAware::DEFAULT_DPI;
|
||||||
|
|
||||||
LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM);
|
LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM);
|
||||||
|
|
||||||
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
||||||
@ -96,6 +98,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
|
|||||||
int windowWidth = KeyboardManagerConstants::DefaultEditShortcutsWindowWidth;
|
int windowWidth = KeyboardManagerConstants::DefaultEditShortcutsWindowWidth;
|
||||||
int windowHeight = KeyboardManagerConstants::DefaultEditShortcutsWindowHeight;
|
int windowHeight = KeyboardManagerConstants::DefaultEditShortcutsWindowHeight;
|
||||||
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
|
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
|
||||||
|
DPIAware::GetScreenDPIForCursor(g_currentDPI);
|
||||||
|
|
||||||
// Window Creation
|
// Window Creation
|
||||||
HWND _hWndEditShortcutsWindow = CreateWindow(
|
HWND _hWndEditShortcutsWindow = CreateWindow(
|
||||||
@ -360,26 +363,60 @@ void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||||||
|
|
||||||
LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
RECT rcClient;
|
|
||||||
switch (messageCode)
|
switch (messageCode)
|
||||||
{
|
{
|
||||||
// Resize the XAML window whenever the parent window is painted or resized
|
// Resize the XAML window whenever the parent window is painted or resized
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
case WM_SIZE:
|
case WM_SIZE:
|
||||||
{
|
{
|
||||||
GetClientRect(hWnd, &rcClient);
|
RECT rect = { 0 };
|
||||||
SetWindowPos(hWndXamlIslandEditShortcutsWindow, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW);
|
GetClientRect(hWnd, &rect);
|
||||||
|
SetWindowPos(hWndXamlIslandEditShortcutsWindow, 0, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// To avoid UI elements overlapping on making the window smaller enforce a minimum window size
|
// To avoid UI elements overlapping on making the window smaller enforce a minimum window size
|
||||||
case WM_GETMINMAXINFO:
|
case WM_GETMINMAXINFO:
|
||||||
{
|
{
|
||||||
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
|
LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
|
||||||
int minWidth = KeyboardManagerConstants::MinimumEditShortcutsWindowWidth;
|
int minWidth = KeyboardManagerConstants::MinimumEditShortcutsWindowWidth;
|
||||||
int minHeight = KeyboardManagerConstants::MinimumEditShortcutsWindowHeight;
|
int minHeight = KeyboardManagerConstants::MinimumEditShortcutsWindowHeight;
|
||||||
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
|
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
|
||||||
lpMMI->ptMinTrackSize.x = minWidth;
|
mmi->ptMinTrackSize.x = minWidth;
|
||||||
lpMMI->ptMinTrackSize.y = minHeight;
|
mmi->ptMinTrackSize.y = minHeight;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_GETDPISCALEDSIZE:
|
||||||
|
{
|
||||||
|
UINT newDPI = static_cast<UINT>(wParam);
|
||||||
|
SIZE* size = reinterpret_cast<SIZE*>(lParam);
|
||||||
|
Logger::trace(L"WM_GETDPISCALEDSIZE: DPI {} size X {} Y {}", newDPI, size->cx, size->cy);
|
||||||
|
|
||||||
|
float scalingFactor = static_cast<float>(newDPI) / g_currentDPI;
|
||||||
|
Logger::trace(L"WM_GETDPISCALEDSIZE: scaling factor {}", scalingFactor);
|
||||||
|
|
||||||
|
size->cx = static_cast<LONG>(size->cx * scalingFactor);
|
||||||
|
size->cy = static_cast<LONG>(size->cy * scalingFactor);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_DPICHANGED:
|
||||||
|
{
|
||||||
|
UINT newDPI = static_cast<UINT>(LOWORD(wParam));
|
||||||
|
g_currentDPI = newDPI;
|
||||||
|
|
||||||
|
RECT* rect = reinterpret_cast<RECT*>(lParam);
|
||||||
|
SetWindowPos(
|
||||||
|
hWnd,
|
||||||
|
nullptr,
|
||||||
|
rect->left,
|
||||||
|
rect->top,
|
||||||
|
rect->right - rect->left,
|
||||||
|
rect->bottom - rect->top,
|
||||||
|
SWP_NOZORDER | SWP_NOACTIVATE
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger::trace(L"WM_DPICHANGED: new dpi {} rect {} {} ", newDPI, rect->right - rect->left, rect->bottom - rect->top);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -50,13 +50,22 @@ namespace KeyboardManagerConstants
|
|||||||
// Default window sizes
|
// Default window sizes
|
||||||
inline const int DefaultEditKeyboardWindowWidth = 800;
|
inline const int DefaultEditKeyboardWindowWidth = 800;
|
||||||
inline const int DefaultEditKeyboardWindowHeight = 600;
|
inline const int DefaultEditKeyboardWindowHeight = 600;
|
||||||
inline const int MinimumEditKeyboardWindowWidth = 500;
|
|
||||||
inline const int MinimumEditKeyboardWindowHeight = 450;
|
// Increasing the min size can cause issues when moving the window between
|
||||||
|
// monitors with different DPI scaling factor
|
||||||
|
inline const int MinimumEditKeyboardWindowWidth = 200;
|
||||||
|
inline const int MinimumEditKeyboardWindowHeight = 200;
|
||||||
|
|
||||||
inline const int EditKeyboardTableMinWidth = 700;
|
inline const int EditKeyboardTableMinWidth = 700;
|
||||||
|
|
||||||
inline const int DefaultEditShortcutsWindowWidth = 1050;
|
inline const int DefaultEditShortcutsWindowWidth = 1050;
|
||||||
inline const int DefaultEditShortcutsWindowHeight = 600;
|
inline const int DefaultEditShortcutsWindowHeight = 600;
|
||||||
inline const int MinimumEditShortcutsWindowWidth = 500;
|
|
||||||
inline const int MinimumEditShortcutsWindowHeight = 500;
|
// Increasing the min size can cause issues when moving the window between
|
||||||
|
// monitors with different DPI scaling factor
|
||||||
|
inline const int MinimumEditShortcutsWindowWidth = 200;
|
||||||
|
inline const int MinimumEditShortcutsWindowHeight = 200;
|
||||||
|
|
||||||
inline const int EditShortcutsTableMinWidth = 1000;
|
inline const int EditShortcutsTableMinWidth = 1000;
|
||||||
|
|
||||||
// Key Remap table constants
|
// Key Remap table constants
|
||||||
|
@ -667,8 +667,7 @@ void CPowerRenameUI::_OnInitDlg()
|
|||||||
m_initialWidth = RECT_WIDTH(rc);
|
m_initialWidth = RECT_WIDTH(rc);
|
||||||
m_initialHeight = RECT_HEIGHT(rc);
|
m_initialHeight = RECT_HEIGHT(rc);
|
||||||
|
|
||||||
UINT dummy = 0;
|
DPIAware::GetScreenDPIForWindow(m_hwnd, m_initialDPI);
|
||||||
DPIAware::GetScreenDPIForWindow(m_hwnd, m_initialDPI, dummy);
|
|
||||||
|
|
||||||
for (UINT u = 0; u < ARRAYSIZE(g_repositionMap); u++)
|
for (UINT u = 0; u < ARRAYSIZE(g_repositionMap); u++)
|
||||||
{
|
{
|
||||||
@ -887,8 +886,8 @@ void CPowerRenameUI::_MoveControl(_In_ DWORD id, _In_ DWORD repositionFlags)
|
|||||||
int width = rcWindow.right - rcWindow.left;
|
int width = rcWindow.right - rcWindow.left;
|
||||||
int height = rcWindow.bottom - rcWindow.top;
|
int height = rcWindow.bottom - rcWindow.top;
|
||||||
|
|
||||||
UINT currentDPI = 0, dummy;
|
UINT currentDPI = 0;
|
||||||
DPIAware::GetScreenDPIForWindow(m_hwnd, currentDPI, dummy);
|
DPIAware::GetScreenDPIForWindow(m_hwnd, currentDPI);
|
||||||
float scale = (float)currentDPI / m_initialDPI;
|
float scale = (float)currentDPI / m_initialDPI;
|
||||||
|
|
||||||
switch (id)
|
switch (id)
|
||||||
|
Loading…
Reference in New Issue
Block a user