[PATCH 1/4] shell32/autocomplete: Implement the listbox scrollbar manually

Gabriel Ivăncescu gabrielopcode at gmail.com
Fri Apr 5 07:14:24 CDT 2019


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

This is a no-op patch; its purpose is to keep the next patch smaller and
focused only on implementing the resizing grip, because IMO it was a bit
large before I split it. Note that to avoid more pointless changes in the
next patch, names with "grip" have been used for the scrollbar, I hope
that's not a problem.

Some of the things are done purposefully to keep the next patch smaller, while
they may not make sense by themselves. For example, the WM_WINDOWPOSCHANGED
is also useful to that end.

 dlls/shell32/autocomplete.c | 132 ++++++++++++++++++++++++++++++++++--
 1 file changed, 127 insertions(+), 5 deletions(-)

diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
index f0777ff..38ca8ca 100644
--- a/dlls/shell32/autocomplete.c
+++ b/dlls/shell32/autocomplete.c
@@ -65,9 +65,11 @@ typedef struct
     HWND hwndEdit;
     HWND hwndListBox;
     HWND hwndListBoxOwner;
+    HWND hwndListBoxGrip;
     WNDPROC wpOrigEditProc;
     WNDPROC wpOrigLBoxProc;
     WNDPROC wpOrigLBoxOwnerProc;
+    WNDPROC wpOrigLBoxGripProc;
     WCHAR *txtbackup;
     WCHAR *quickComplete;
     IEnumString *enumstr;
@@ -284,6 +286,41 @@ static void free_enum_strs(IAutoCompleteImpl *ac)
     }
 }
 
+static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height)
+{
+    UINT item_height, listbox_width, grip_sz, grip_height;
+    SCROLLINFO info;
+
+    info.nMax   = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0) - 1;
+    item_height = SendMessageW(ac->hwndListBox, LB_GETITEMHEIGHT, 0, 0);
+    grip_sz     = GetSystemMetrics(SM_CXVSCROLL);
+    width      -= GetSystemMetrics(SM_CXBORDER) * 2;
+    height     -= GetSystemMetrics(SM_CYBORDER) * 2;
+    info.nPage  = max(height / item_height, 1);
+
+    /* Set the scrollbar info if it's visible */
+    listbox_width = width;
+    if (info.nMax >= info.nPage)
+    {
+        grip_height   = height;
+        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->hwndListBoxGrip, SB_VERT, &info, FALSE);
+
+        listbox_width -= grip_sz;
+    }
+    else
+        grip_height   = 0;
+
+    SetWindowPos(ac->hwndListBoxGrip, NULL, width - grip_sz, 0, grip_sz, grip_height,
+                 SWP_NOACTIVATE | 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);
+}
+
 static void hide_listbox(IAutoCompleteImpl *ac, HWND hwnd, BOOL reset)
 {
     ShowWindow(ac->hwndListBoxOwner, SW_HIDE);
@@ -304,7 +341,12 @@ static void show_listbox(IAutoCompleteImpl *ac)
     width = r.right - r.left;
 
     SetWindowPos(ac->hwndListBoxOwner, HWND_TOP, r.left, r.bottom + 1, width, height,
-                 SWP_SHOWWINDOW | SWP_NOACTIVATE);
+                 SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_DEFERERASE);
+
+    /* Update the grip here (as we skip it during message processing), due
+       to the fact that the size itself might not always change, while the
+       count does and thus it possibly needs updating of the scrollbar */
+    update_listbox_size(ac, width, height);
 }
 
 static void set_listbox_font(IAutoCompleteImpl *ac, HFONT font)
@@ -863,6 +905,36 @@ static LRESULT APIENTRY ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
             set_text_and_selection(This, This->hwndEdit, msg, 0, strlenW(msg));
             hide_listbox(This, hwnd, TRUE);
             return 0;
+        case WM_VSCROLL:
+            /* Handle thumb tracking manually as the listbox doesn't have WS_VSCROLL */
+            if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)
+            {
+                SCROLLINFO info;
+                info.cbSize = sizeof(info);
+                info.fMask = SIF_TRACKPOS;
+                if (!GetScrollInfo(This->hwndListBoxGrip, SB_VERT, &info))
+                    return 0;
+                uMsg = LB_SETTOPINDEX;
+                wParam = info.nTrackPos;
+            }
+            /* fall through */
+        case WM_MOUSEWHEEL:
+        case LB_SETCURSEL:
+        case LB_SETITEMHEIGHT:
+        case LB_SETTOPINDEX:
+        {
+            LRESULT ret = CallWindowProcW(This->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam);
+            SCROLLINFO info;
+            info.cbSize = sizeof(info);
+            info.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+            info.nMin  = 0;
+            info.nMax  = SendMessageW(hwnd, LB_GETCOUNT, 0, 0) - 1;
+            info.nPage = SendMessageW(hwnd, LB_GETLISTBOXINFO, 0, 0);
+            info.nPos  = SendMessageW(hwnd, LB_GETTOPINDEX, 0, 0);
+
+            SetScrollInfo(This->hwndListBoxGrip, SB_VERT, &info, TRUE);
+            return ret;
+        }
     }
     return CallWindowProcW(This->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam);
 }
@@ -879,16 +951,55 @@ static LRESULT APIENTRY ACLBoxOwnerSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPa
             if (draw_listbox_item(This, (DRAWITEMSTRUCT*)lParam, wParam))
                 return TRUE;
             break;
-        case WM_SIZE:
-            SetWindowPos(This->hwndListBox, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam),
-                         SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_DEFERERASE);
+        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;
+
+                update_listbox_size(This, wp->cx, wp->cy);
+            }
             break;
+        }
     }
     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_VSCROLL:
+            return SendMessageW(This->hwndListBox, uMsg, wParam, lParam);
+        case WM_NCCALCSIZE:
+        {
+            NCCALCSIZE_PARAMS *p = (NCCALCSIZE_PARAMS*)lParam;
+            if (wParam == FALSE) return 0;
+
+            p->rgrc[0].right = p->rgrc[0].left;
+
+            return 0;
+        }
+        case WM_NCHITTEST:
+            return HTVSCROLL;
+        case WM_NCLBUTTONDOWN:
+        case WM_NCLBUTTONDBLCLK:
+            return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+    }
+    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);
@@ -899,8 +1010,15 @@ static void create_listbox(IAutoCompleteImpl *This)
     }
 
     /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
+    This->hwndListBoxGrip = CreateWindowExW(WS_EX_NOACTIVATE, WC_STATICW, NULL,
+                                            WS_CHILD | WS_VISIBLE, 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_VSCROLL | LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT,
+                                        WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
+                                        LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT,
                                         0, 0, 0, 0, This->hwndListBoxOwner, NULL, shell32_hInstance, NULL);
 
     if (This->hwndListBox) {
@@ -912,6 +1030,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)
@@ -919,6 +1040,7 @@ static void create_listbox(IAutoCompleteImpl *This)
         return;
     }
 
+fail:
     DestroyWindow(This->hwndListBoxOwner);
     This->hwndListBoxOwner = NULL;
     This->options &= ~ACO_AUTOSUGGEST;
-- 
2.20.1




More information about the wine-devel mailing list