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

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


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

v2: Use a separate scrollbar control and sizing grip.

This is a no-op patch; its purpose is to keep the next patch smaller and
focused only on implementing the resizing grip. Some of the things are done
that way purposefully, for example, the WM_WINDOWPOSCHANGED is also useful
to that end.

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

diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
index f0777ff..b9029eb 100644
--- a/dlls/shell32/autocomplete.c
+++ b/dlls/shell32/autocomplete.c
@@ -65,6 +65,7 @@ typedef struct
     HWND hwndEdit;
     HWND hwndListBox;
     HWND hwndListBoxOwner;
+    HWND hwndListBoxScroll;
     WNDPROC wpOrigEditProc;
     WNDPROC wpOrigLBoxProc;
     WNDPROC wpOrigLBoxOwnerProc;
@@ -284,6 +285,45 @@ 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, scroll_h, prev_scroll_h;
+    SCROLLINFO info;
+    RECT r;
+
+    GetClientRect(ac->hwndListBoxScroll, &r);
+    prev_scroll_h = r.bottom - r.top;
+
+    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)
+    {
+        scroll_h    = 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->hwndListBoxScroll, SB_CTL, &info, scroll_h == prev_scroll_h);
+
+        listbox_width -= grip_sz;
+    }
+    else
+        scroll_h = 0;
+
+    SetWindowPos(ac->hwndListBoxScroll, NULL, width - grip_sz, 0, grip_sz, scroll_h,
+                 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 +344,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 +908,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->hwndListBoxScroll, SB_CTL, &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->hwndListBoxScroll, SB_CTL, &info, TRUE);
+            return ret;
+        }
     }
     return CallWindowProcW(This->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam);
 }
@@ -879,10 +954,20 @@ 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_VSCROLL:
+            return SendMessageW(This->hwndListBox, uMsg, wParam, lParam);
+        case WM_WINDOWPOSCHANGED:
+        {
+            const WINDOWPOS *wp = (const WINDOWPOS*)lParam;
+
+            if (!(wp->flags & SWP_NOSIZE))
+            {
+                if (wp->flags & SWP_NOSENDCHANGING) break;
+
+                update_listbox_size(This, wp->cx, wp->cy);
+            }
             break;
+        }
     }
     return CallWindowProcW(This->wpOrigLBoxOwnerProc, hwnd, uMsg, wParam, lParam);
 }
@@ -899,8 +984,15 @@ static void create_listbox(IAutoCompleteImpl *This)
     }
 
     /* 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;
+
+    /* 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) {
@@ -919,6 +1011,7 @@ static void create_listbox(IAutoCompleteImpl *This)
         return;
     }
 
+fail:
     DestroyWindow(This->hwndListBoxOwner);
     This->hwndListBoxOwner = NULL;
     This->options &= ~ACO_AUTOSUGGEST;
-- 
2.21.0




More information about the wine-devel mailing list