[WIN32SS:NTUSER] Implement "Window Snap" feature (#1226) 1247/head
authorDenis Malikov <Getequ@users.noreply.github.com>
Tue, 15 Jan 2019 13:36:41 +0000 (20:36 +0700)
committerHermès BÉLUSCA - MAÏTO <hermes.belusca-maito@reactos.org>
Tue, 15 Jan 2019 13:36:41 +0000 (14:36 +0100)
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

win32ss/user/ntuser/defwnd.c
win32ss/user/ntuser/hotkey.c
win32ss/user/ntuser/hotkey.h
win32ss/user/ntuser/input.c
win32ss/user/ntuser/nonclient.c

index eb72841..d00d9aa 100644 (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:
index f393906..02b504f 100644 (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)
         {
index b965677..e864522 100644 (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);
index e4dbbab..520ccca 100644 (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();
index 8f45582..7a5fcc4 100644 (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);
   }