[WIN32SS:NTUSER] Implement "Window Snap" feature (#1226)

Implemented the following actions: stick the window on the left/right or maximize it,
with the following shortcuts:
 - Win key + Arrows;
 - drag to left/right screen border;
 - double-click on top/bottom.

CORE-12845
This commit is contained in:
Denis Malikov 2019-01-15 20:36:41 +07:00 committed by Hermès BÉLUSCA - MAÏTO
parent c95f335893
commit 7e396787ed
5 changed files with 178 additions and 4 deletions

View file

@ -781,6 +781,92 @@ IntDefWindowProc(
co_IntSendMessage(UserHMGetHandle(Wnd), WM_CONTEXTMENU, (WPARAM)UserHMGetHandle(Wnd), MAKELPARAM(-1, -1));
}
}
if (IS_KEY_DOWN(gafAsyncKeyState, VK_LWIN) || IS_KEY_DOWN(gafAsyncKeyState, VK_RWIN))
{
PWND topWnd = UserGetWindowObject(UserGetForegroundWindow());
if (topWnd)
{
if (wParam == VK_DOWN)
{
co_IntSendMessage(UserHMGetHandle(topWnd), WM_SYSCOMMAND, (topWnd->style & WS_MAXIMIZE) ? SC_RESTORE : SC_MINIMIZE, lParam);
}
else
if (wParam == VK_UP)
{
RECT currentRect = (topWnd->InternalPos.NormalRect.right == topWnd->InternalPos.NormalRect.left) ||
(topWnd->InternalPos.NormalRect.top == topWnd->InternalPos.NormalRect.bottom)
? topWnd->rcWindow
: topWnd->InternalPos.NormalRect;
co_IntSendMessage(UserHMGetHandle(topWnd), WM_SYSCOMMAND, SC_MAXIMIZE, 0);
// save normal rect if maximazing snapped window
topWnd->InternalPos.NormalRect = currentRect;
}
else
if (wParam == VK_LEFT || wParam == VK_RIGHT)
{
RECT snapRect;
RECT normalRect;
RECT windowRect;
BOOL snapped = FALSE;
normalRect = topWnd->InternalPos.NormalRect;
snapped = (normalRect.left != 0
&& normalRect.right != 0
&& normalRect.top != 0
&& normalRect.bottom != 0);
if (topWnd->style & WS_MAXIMIZE)
{
co_IntSendMessage(UserHMGetHandle(topWnd), WM_SYSCOMMAND, SC_RESTORE, lParam);
snapped = FALSE;
}
windowRect = topWnd->rcWindow;
UserSystemParametersInfo(SPI_GETWORKAREA, 0, &snapRect, 0);
if (wParam == VK_LEFT)
{
snapRect.right = (snapRect.right - snapRect.left) / 2 + snapRect.left;
}
else // VK_RIGHT
{
snapRect.left = (snapRect.right - snapRect.left) / 2 + snapRect.left;
}
if (snapped)
{
// if window was snapped but moved to other location - restore normal size
if (snapRect.left != windowRect.left ||
snapRect.right != windowRect.right ||
snapRect.top != windowRect.top ||
snapRect.bottom != windowRect.bottom)
{
RECT empty = {0, 0, 0, 0};
co_WinPosSetWindowPos(topWnd,
0,
normalRect.left,
normalRect.top,
normalRect.right - normalRect.left,
normalRect.bottom - normalRect.top,
0);
topWnd->InternalPos.NormalRect = empty;
}
}
else
{
co_WinPosSetWindowPos(topWnd,
0,
snapRect.left,
snapRect.top,
snapRect.right - snapRect.left,
snapRect.bottom - snapRect.top,
0);
topWnd->InternalPos.NormalRect = windowRect;
}
}
}
}
break;
case WM_SYSKEYDOWN:

View file

@ -250,6 +250,19 @@ co_UserProcessHotKeys(WORD wVk, BOOL bIsDown)
return FALSE;
}
}
if (pHotKey->id == IDHK_SNAP_LEFT ||
pHotKey->id == IDHK_SNAP_RIGHT ||
pHotKey->id == IDHK_SNAP_UP ||
pHotKey->id == IDHK_SNAP_DOWN)
{
HWND topWnd = UserGetForegroundWindow();
if (topWnd)
{
UserPostMessage(topWnd, WM_KEYDOWN, wVk, 0);
}
return TRUE;
}
if (!pHotKey->pWnd)
{

View file

@ -16,6 +16,12 @@ typedef struct _HOT_KEY
#define IDHK_WINKEY -7
#define IDHK_REACTOS -8
/* Window Snap Hot Keys */
#define IDHK_SNAP_LEFT -10
#define IDHK_SNAP_RIGHT -11
#define IDHK_SNAP_UP -12
#define IDHK_SNAP_DOWN -13
VOID FASTCALL UnregisterWindowHotKeys(PWND Window);
VOID FASTCALL UnregisterThreadHotKeys(PTHREADINFO pti);
BOOL NTAPI co_UserProcessHotKeys(WORD wVk, BOOL bIsDown);

View file

@ -202,6 +202,11 @@ RawInputThreadMain(VOID)
UserEnterExclusive();
// Register the Window hotkey.
UserRegisterHotKey(PWND_BOTTOM, IDHK_WINKEY, MOD_WIN, 0);
// Register the Window Snap hotkey.
UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_LEFT, MOD_WIN, VK_LEFT);
UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_RIGHT, MOD_WIN, VK_RIGHT);
UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_UP, MOD_WIN, VK_UP);
UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_DOWN, MOD_WIN, VK_DOWN);
// Register the debug hotkeys.
StartDebugHotKeys();
UserLeave();

View file

@ -391,10 +391,59 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
if (!co_IntGetPeekMessage(&msg, 0, 0, 0, PM_REMOVE, TRUE)) break;
if (IntCallMsgFilter( &msg, MSGF_SIZE )) continue;
/* Exit on button-up, Return, or Esc */
if ((msg.message == WM_LBUTTONUP) ||
((msg.message == WM_KEYDOWN) &&
((msg.wParam == VK_RETURN) || (msg.wParam == VK_ESCAPE)))) break;
/* Exit on button-up */
if (msg.message == WM_LBUTTONUP)
{
// check for snapping if was moved by caption
if (hittest == HTCAPTION)
{
RECT snapRect;
BOOL doSideSnap = FALSE;
UserSystemParametersInfo(SPI_GETWORKAREA, 0, &snapRect, 0);
// snap to left
if (pt.x <= snapRect.left)
{
snapRect.right = (snapRect.right - snapRect.left) / 2 + snapRect.left;
doSideSnap = TRUE;
}
// snap to right
if (pt.x >= snapRect.right-1)
{
snapRect.left = (snapRect.right - snapRect.left) / 2 + snapRect.left;
doSideSnap = TRUE;
}
if (doSideSnap)
{
co_WinPosSetWindowPos(pwnd,
0,
snapRect.left,
snapRect.top,
snapRect.right - snapRect.left,
snapRect.bottom - snapRect.top,
0);
pwnd->InternalPos.NormalRect = origRect;
}
else
{
// maximize if on dragged to top
if (pt.y <= snapRect.top)
{
co_IntSendMessage(UserHMGetHandle(pwnd), WM_SYSCOMMAND, SC_MAXIMIZE, 0);
pwnd->InternalPos.NormalRect = origRect;
}
}
}
break;
}
/* Exit on Return or Esc */
if (msg.message == WM_KEYDOWN &&
(msg.wParam == VK_RETURN || msg.wParam == VK_ESCAPE))
{
break;
}
if ((msg.message != WM_KEYDOWN) && (msg.message != WM_MOUSEMOVE))
{
@ -1565,6 +1614,21 @@ NC_HandleNCLButtonDblClk(PWND pWnd, WPARAM wParam, LPARAM lParam)
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_CLOSE, lParam);
break;
}
case HTTOP:
case HTBOTTOM:
{
RECT sizingRect = pWnd->rcWindow, mouseRect;
UserSystemParametersInfo(SPI_GETWORKAREA, 0, &mouseRect, 0);
co_WinPosSetWindowPos(pWnd,
0,
sizingRect.left,
mouseRect.top,
sizingRect.right - sizingRect.left,
mouseRect.bottom - mouseRect.top,
0);
break;
}
default:
return NC_HandleNCLButtonDown(pWnd, wParam, lParam);
}