[PATCH v2 2/4] shell32/autocomplete: Implement the listbox resizing grip and make it resize-able

Gabriel Ivăncescu gabrielopcode at gmail.com
Tue May 28 07:21:41 CDT 2019


Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

Note that we can't rely on the sizing grip's resize because of the triangle
shape which must still act as a square when clicking.

 dlls/shell32/autocomplete.c | 113 +++++++++++++++++++++++++++++++++---
 1 file changed, 106 insertions(+), 7 deletions(-)

diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
index b9029eb..24aa895 100644
--- a/dlls/shell32/autocomplete.c
+++ b/dlls/shell32/autocomplete.c
@@ -62,13 +62,17 @@ typedef struct
     UINT enum_strs_num;
     WCHAR **enum_strs;
     WCHAR **listbox_strs;
+    UINT listbox_width;
+    UINT listbox_height;
     HWND hwndEdit;
     HWND hwndListBox;
     HWND hwndListBoxOwner;
+    HWND hwndListBoxGrip;
     HWND hwndListBoxScroll;
     WNDPROC wpOrigEditProc;
     WNDPROC wpOrigLBoxProc;
     WNDPROC wpOrigLBoxOwnerProc;
+    WNDPROC wpOrigLBoxGripProc;
     WCHAR *txtbackup;
     WCHAR *quickComplete;
     IEnumString *enumstr;
@@ -303,23 +307,42 @@ static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height)
 
     /* Set the scrollbar info if it's visible */
     listbox_width = width;
-    if (info.nMax >= info.nPage)
+    if (info.nMax >= info.nPage && height > grip_sz)
     {
-        scroll_h    = height;
+        scroll_h    = height - grip_sz;
         info.cbSize = sizeof(info);
         info.fMask  = SIF_PAGE | SIF_POS | SIF_RANGE;
         info.nMin   = 0;
         info.nPos   = SendMessageW(ac->hwndListBox, LB_GETTOPINDEX, 0, 0);
         SetScrollInfo(ac->hwndListBoxScroll, SB_CTL, &info, scroll_h == prev_scroll_h);
 
+        if (!prev_scroll_h) SetWindowRgn(ac->hwndListBoxGrip, NULL, TRUE);
         listbox_width -= grip_sz;
     }
     else
+    {
         scroll_h = 0;
 
+        if (prev_scroll_h)
+        {
+            /* The grip is a triangle when the scrollbar is not visible */
+            HRGN hrgn;
+            POINT pt[3];
+            pt[0].x = pt[1].y = pt[2].x = pt[2].y = grip_sz;
+            pt[0].y = pt[1].x = 0;
+
+            hrgn = CreatePolygonRgn(pt, ARRAY_SIZE(pt), WINDING);
+            if (hrgn && !SetWindowRgn(ac->hwndListBoxGrip, hrgn, FALSE))
+                DeleteObject(hrgn);
+        }
+    }
+
     SetWindowPos(ac->hwndListBoxScroll, NULL, width - grip_sz, 0, grip_sz, scroll_h,
                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE);
 
+    SetWindowPos(ac->hwndListBoxGrip, NULL, width - grip_sz, height - grip_sz, 0, 0,
+                 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE);
+
     SetWindowPos(ac->hwndListBox, NULL, 0, 0, listbox_width, height,
                  SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE);
 }
@@ -338,10 +361,19 @@ static void show_listbox(IAutoCompleteImpl *ac)
 
     GetWindowRect(ac->hwndEdit, &r);
 
-    /* Windows XP displays 7 lines at most, then it uses a scroll bar */
-    cnt    = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0);
-    height = SendMessageW(ac->hwndListBox, LB_GETITEMHEIGHT, 0, 0) * min(cnt + 1, 7);
-    width = r.right - r.left;
+    /* See if the listbox has been resized by the user */
+    if (ac->listbox_width)
+    {
+        width  = ac->listbox_width;
+        height = ac->listbox_height;
+    }
+    else
+    {
+        /* Windows XP displays 7 lines at most, then it uses a scroll bar */
+        cnt    = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0);
+        height = SendMessageW(ac->hwndListBox, LB_GETITEMHEIGHT, 0, 0) * min(cnt + 1, 7);
+        width  = r.right - r.left;
+    }
 
     SetWindowPos(ac->hwndListBoxOwner, HWND_TOP, r.left, r.bottom + 1, width, height,
                  SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_DEFERERASE);
@@ -406,6 +438,15 @@ static BOOL draw_listbox_item(IAutoCompleteImpl *ac, DRAWITEMSTRUCT *info, UINT
     return TRUE;
 }
 
+static inline BOOL hit_test_listbox_grip(IAutoCompleteImpl *ac, LPARAM lParam)
+{
+    POINT pt = { (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam) };
+    RECT r;
+
+    GetWindowRect(ac->hwndListBoxGrip, &r);
+    return PtInRect(&r, pt);
+}
+
 static size_t format_quick_complete(WCHAR *dst, const WCHAR *qc, const WCHAR *str, size_t str_len)
 {
     /* Replace the first %s directly without using snprintf, to avoid
@@ -938,6 +979,10 @@ static LRESULT APIENTRY ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
             SetScrollInfo(This->hwndListBoxScroll, SB_CTL, &info, TRUE);
             return ret;
         }
+        case WM_NCHITTEST:
+            if (hit_test_listbox_grip(This, lParam))
+                return HTTRANSPARENT;
+            break;
     }
     return CallWindowProcW(This->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam);
 }
@@ -956,24 +1001,71 @@ static LRESULT APIENTRY ACLBoxOwnerSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPa
             break;
         case WM_VSCROLL:
             return SendMessageW(This->hwndListBox, uMsg, wParam, lParam);
+        case WM_GETMINMAXINFO:
+        {
+            /* Prevent shrinking the dropdown below the grip's size */
+            UINT grip_sz = GetSystemMetrics(SM_CXVSCROLL);
+            ((MINMAXINFO*)lParam)->ptMinTrackSize.x = grip_sz + GetSystemMetrics(SM_CXBORDER) * 2;
+            ((MINMAXINFO*)lParam)->ptMinTrackSize.y = grip_sz + GetSystemMetrics(SM_CYBORDER) * 2;
+            return 0;
+        }
+        case WM_WINDOWPOSCHANGING:
+            /* Fix a bug when resizing against the taskbar */
+            ((WINDOWPOS*)lParam)->flags |= SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_DEFERERASE;
+            ((WINDOWPOS*)lParam)->hwndInsertAfter = HWND_TOP;
+            break;
         case WM_WINDOWPOSCHANGED:
         {
             const WINDOWPOS *wp = (const WINDOWPOS*)lParam;
 
             if (!(wp->flags & SWP_NOSIZE))
             {
+                /* Only proceed with the update if the user resized it */
                 if (wp->flags & SWP_NOSENDCHANGING) break;
 
+                This->listbox_width  = wp->cx;
+                This->listbox_height = wp->cy;
                 update_listbox_size(This, wp->cx, wp->cy);
             }
             break;
         }
+        case WM_NCHITTEST:
+            if (hit_test_listbox_grip(This, lParam))
+                return HTBOTTOMRIGHT;
+            break;
+        case WM_NCLBUTTONDBLCLK:
+            if (hit_test_listbox_grip(This, lParam))
+            {
+                /* For convenience, double-click on the grip reverts to auto sizing */
+                This->listbox_width = 0;
+                show_listbox(This);
+                return 0;
+            }
+            /* fall through */
+        case WM_NCLBUTTONDOWN:
+            return DefWindowProcW(hwnd, uMsg, wParam, lParam);
     }
     return CallWindowProcW(This->wpOrigLBoxOwnerProc, hwnd, uMsg, wParam, lParam);
 }
 
+static LRESULT APIENTRY ACLBoxGripSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    IAutoCompleteImpl *This = (IAutoCompleteImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+
+    switch (uMsg)
+    {
+        case WM_MOUSEACTIVATE:
+            return MA_NOACTIVATE;
+        case WM_NCHITTEST:
+            return HTTRANSPARENT;
+    }
+    return CallWindowProcW(This->wpOrigLBoxGripProc, hwnd, uMsg, wParam, lParam);
+}
+
 static void create_listbox(IAutoCompleteImpl *This)
 {
+    UINT grip_sz = GetSystemMetrics(SM_CXVSCROLL);
+
     This->hwndListBoxOwner = CreateWindowExW(WS_EX_NOACTIVATE, WC_STATICW, NULL,
                                              WS_BORDER | WS_POPUP | WS_CLIPCHILDREN,
                                              0, 0, 0, 0, NULL, NULL, shell32_hInstance, NULL);
@@ -983,12 +1075,16 @@ static void create_listbox(IAutoCompleteImpl *This)
         return;
     }
 
-    /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
     This->hwndListBoxScroll = CreateWindowExW(WS_EX_NOACTIVATE, WC_SCROLLBARW, NULL,
                                               WS_CHILD | WS_VISIBLE | SBS_VERT, 0, 0, 0, 1,
                                               This->hwndListBoxOwner, NULL, shell32_hInstance, NULL);
     if (!This->hwndListBoxScroll) goto fail;
 
+    This->hwndListBoxGrip = CreateWindowExW(WS_EX_NOACTIVATE, WC_SCROLLBARW, NULL,
+                                            WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP, 0, 0, grip_sz, grip_sz,
+                                            This->hwndListBoxOwner, NULL, shell32_hInstance, NULL);
+    if (!This->hwndListBoxGrip) goto fail;
+
     /* Create the listbox */
     This->hwndListBox = CreateWindowExW(WS_EX_NOACTIVATE, WC_LISTBOXW, NULL,
                                         WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
@@ -1004,6 +1100,9 @@ static void create_listbox(IAutoCompleteImpl *This)
         This->wpOrigLBoxOwnerProc = (WNDPROC)SetWindowLongPtrW(This->hwndListBoxOwner, GWLP_WNDPROC, (LONG_PTR)ACLBoxOwnerSubclassProc);
         SetWindowLongPtrW(This->hwndListBoxOwner, GWLP_USERDATA, (LONG_PTR)This);
 
+        This->wpOrigLBoxGripProc = (WNDPROC)SetWindowLongPtrW(This->hwndListBoxGrip, GWLP_WNDPROC, (LONG_PTR)ACLBoxGripSubclassProc);
+        SetWindowLongPtrW(This->hwndListBoxGrip, GWLP_USERDATA, (LONG_PTR)This);
+
         /* Use the same font as the edit control, as it gets destroyed before it anyway */
         edit_font = (HFONT)SendMessageW(This->hwndEdit, WM_GETFONT, 0, 0);
         if (edit_font)
-- 
2.21.0




More information about the wine-devel mailing list