[PATCH v2 4/4] shell32/autocomplete: Drop the listbox up if there's not enough space down

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


Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
 dlls/shell32/autocomplete.c | 70 +++++++++++++++++++++++++++++++------
 1 file changed, 59 insertions(+), 11 deletions(-)

diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
index 446642a..cbc5b20 100644
--- a/dlls/shell32/autocomplete.c
+++ b/dlls/shell32/autocomplete.c
@@ -59,6 +59,7 @@ typedef struct
     LONG ref;
     BOOL initialized;
     BOOL enabled;
+    BOOL dropped_up;
     UINT enum_strs_num;
     WCHAR **enum_strs;
     WCHAR **listbox_strs;
@@ -289,9 +290,9 @@ static void free_enum_strs(IAutoCompleteImpl *ac)
     }
 }
 
-static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height)
+static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height, BOOL drop_flipped)
 {
-    UINT item_height, listbox_width, grip_sz, scroll_h, prev_scroll_h;
+    UINT item_height, listbox_width, grip_sz, scroll_top, scroll_h, prev_scroll_h;
     SCROLLINFO info;
     RECT r;
 
@@ -310,26 +311,30 @@ static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height)
     if (info.nMax >= info.nPage && height > grip_sz)
     {
         scroll_h    = height - grip_sz;
+        scroll_top  = ac->dropped_up ? grip_sz : 0;
         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);
+        if (!prev_scroll_h) SetWindowRgn(ac->hwndListBoxGrip, NULL, !drop_flipped);
         listbox_width -= grip_sz;
     }
     else
     {
         scroll_h = 0;
+        scroll_top = 0;
 
-        if (prev_scroll_h)
+        if (prev_scroll_h || drop_flipped)
         {
             /* 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;
+            if (ac->dropped_up)
+                pt[1].y = 0;
 
             hrgn = CreatePolygonRgn(pt, ARRAY_SIZE(pt), WINDING);
             if (hrgn && !SetWindowRgn(ac->hwndListBoxGrip, hrgn, FALSE))
@@ -337,14 +342,19 @@ static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height)
         }
     }
 
-    SetWindowPos(ac->hwndListBoxScroll, NULL, width - grip_sz, 0, grip_sz, scroll_h,
+    SetWindowPos(ac->hwndListBoxScroll, NULL, width - grip_sz, scroll_top, 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->hwndListBoxGrip, NULL, width - grip_sz, ac->dropped_up ? 0 : height - grip_sz, 0, 0,
+                 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE |
+                 (drop_flipped ? SWP_NOCOPYBITS : 0));
 
     SetWindowPos(ac->hwndListBox, NULL, 0, 0, listbox_width, height,
                  SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE);
+
+    /* When it flips without a scrollbar, make sure it's redrawn properly */
+    if (drop_flipped && !scroll_h && !prev_scroll_h)
+        InvalidateRect(ac->hwndListBox, NULL, TRUE);
 }
 
 static void hide_listbox(IAutoCompleteImpl *ac, HWND hwnd, BOOL reset)
@@ -361,6 +371,8 @@ static void show_listbox(IAutoCompleteImpl *ac)
     HMONITOR hmon;
     MONITORINFO info;
     UINT cnt, width, height, grip_sz;
+    BOOL prev_dropped_up = ac->dropped_up;
+    ac->dropped_up = FALSE;
 
     GetWindowRect(ac->hwndEdit, &r);
 
@@ -388,7 +400,19 @@ static void show_listbox(IAutoCompleteImpl *ac)
         LONG max_w = max(info.rcMonitor.right, r.right) - r.left;
         LONG max_h = info.rcMonitor.bottom - top;
         width = min(width, max_w);
-        height = min(height, max_h);
+
+        /* Drop it above the editbox if it goes off-screen and there's more space up */
+        if (height > max_h)
+        {
+            if (r.top - 1 - info.rcMonitor.top > max_h)
+            {
+                max_h = r.top - 1 - info.rcMonitor.top;
+                height = min(height, max_h);
+                top = r.top - 1 - height;
+                ac->dropped_up = TRUE;
+            }
+            else height = max_h;
+        }
     }
     grip_sz = GetSystemMetrics(SM_CXVSCROLL);
     width   = max(width,  grip_sz);
@@ -400,7 +424,7 @@ static void show_listbox(IAutoCompleteImpl *ac)
     /* 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);
+    update_listbox_size(ac, width, height, prev_dropped_up ^ ac->dropped_up);
 }
 
 static void set_listbox_font(IAutoCompleteImpl *ac, HFONT font)
@@ -1044,13 +1068,13 @@ static LRESULT APIENTRY ACLBoxOwnerSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPa
 
                 This->listbox_width  = wp->cx;
                 This->listbox_height = wp->cy;
-                update_listbox_size(This, wp->cx, wp->cy);
+                update_listbox_size(This, wp->cx, wp->cy, FALSE);
             }
             break;
         }
         case WM_NCHITTEST:
             if (hit_test_listbox_grip(This, lParam))
-                return HTBOTTOMRIGHT;
+                return This->dropped_up ? HTTOPRIGHT : HTBOTTOMRIGHT;
             break;
         case WM_NCLBUTTONDBLCLK:
             if (hit_test_listbox_grip(This, lParam))
@@ -1077,6 +1101,30 @@ static LRESULT APIENTRY ACLBoxGripSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPar
             return MA_NOACTIVATE;
         case WM_NCHITTEST:
             return HTTRANSPARENT;
+        case WM_PAINT:
+        {
+            PAINTSTRUCT ps;
+            int old_mode;
+            LRESULT ret;
+            HDC hdc;
+            if (!This->dropped_up) break;
+
+            /* Flip the grip vertically if dropped up */
+            hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
+
+            old_mode = SetMapMode(hdc, MM_ANISOTROPIC);
+            SetViewportOrgEx(hdc, 0, GetSystemMetrics(SM_CXVSCROLL) - 1, NULL);
+            SetViewportExtEx(hdc, 1, -1, NULL);
+
+            ret = CallWindowProcW(This->wpOrigLBoxGripProc, hwnd, uMsg, (WPARAM)hdc, lParam);
+
+            SetViewportOrgEx(hdc, 0, 0, NULL);
+            SetViewportExtEx(hdc, 1, 1, NULL);
+            SetMapMode(hdc, old_mode);
+
+            if (!wParam) EndPaint(hwnd, &ps);
+            return ret;
+        }
     }
     return CallWindowProcW(This->wpOrigLBoxGripProc, hwnd, uMsg, wParam, lParam);
 }
-- 
2.21.0




More information about the wine-devel mailing list