From: Gabriel Ivăncescu Subject: [PATCH 4/4] shell32/autocomplete: Drop the listbox up if there's not enough space down Message-Id: <985da369a874359e9129de980394383687ece1dc.1554465958.git.gabrielopcode@gmail.com> Date: Fri, 5 Apr 2019 15:14:27 +0300 In-Reply-To: <61b38176f8a7619309066752ebdaa687468dec0a.1554465958.git.gabrielopcode@gmail.com> References: <61b38176f8a7619309066752ebdaa687468dec0a.1554465958.git.gabrielopcode@gmail.com> Signed-off-by: Gabriel Ivăncescu --- dlls/shell32/autocomplete.c | 82 +++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index f18517f..7a92f04 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; @@ -288,7 +289,7 @@ 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, grip_height, grip_top = 0; SCROLLINFO info; @@ -323,16 +324,19 @@ static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height) else { /* Scrollbar is not visible so adjust the position and size */ - grip_top += height - grip_sz; + if (!ac->dropped_up) + grip_top += height - grip_sz; grip_height = grip_sz; - if (had_scroll) + if (had_scroll || 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)) @@ -341,10 +345,21 @@ static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height) } SetWindowPos(ac->hwndListBoxGrip, NULL, width - grip_sz, grip_top, grip_sz, grip_height, - SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE); + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE | + (drop_flipped ? SWP_FRAMECHANGED : 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 at the old grip */ + if (drop_flipped && !had_scroll && listbox_width == width) + { + r.left = width - grip_sz; + r.top = ac->dropped_up ? height - grip_sz : 0; + r.right = r.left + grip_sz; + r.bottom = r.top + grip_sz; + InvalidateRect(ac->hwndListBox, &r, FALSE); + } } static void hide_listbox(IAutoCompleteImpl *ac, HWND hwnd, BOOL reset) @@ -361,6 +376,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 +405,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 +429,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) @@ -457,9 +486,10 @@ static BOOL draw_listbox_item(IAutoCompleteImpl *ac, DRAWITEMSTRUCT *info, UINT return TRUE; } -static void draw_listbox_grip(HWND hwnd, HRGN clip) +static void draw_listbox_grip(IAutoCompleteImpl *ac, HWND hwnd, HRGN clip) { LONG orig_top; + UINT grip_sz; int type; HRGN hrgn; HDC hdc; @@ -467,12 +497,20 @@ static void draw_listbox_grip(HWND hwnd, HRGN clip) /* Get the grip's square (without the scrollbar) */ GetWindowRect(hwnd, &r); + grip_sz = GetSystemMetrics(SM_CXVSCROLL); orig_top = r.top; - r.top = r.bottom - GetSystemMetrics(SM_CXVSCROLL); + r.top = r.bottom - grip_sz; /* Draw the scrollbar, if visible */ if (r.top > orig_top) DefWindowProcW(hwnd, WM_NCPAINT, (WPARAM)clip, 0); + /* Adjust the grip's position, if listbox is dropped up */ + if (ac->dropped_up) + { + r.top = orig_top; + r.bottom = orig_top + grip_sz; + } + /* Create a region for the grip */ if (!(hrgn = CreateRectRgnIndirect(&r))) return; @@ -489,6 +527,16 @@ static void draw_listbox_grip(HWND hwnd, HRGN clip) /* Adjust for DrawFrameControl */ OffsetRect(&r, -r.left, -orig_top); + /* Flip the grip vertically if dropped up */ + if (ac->dropped_up) + { + r.top = 1 - r.bottom; + r.bottom = 1; + SetMapMode(hdc, MM_ANISOTROPIC); + SetWindowExtEx(hdc, 1, 1, NULL); + SetViewportExtEx(hdc, 1, -1, NULL); + } + DrawFrameControl(hdc, &r, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); ReleaseDC(hwnd, hdc); } @@ -497,10 +545,15 @@ static BOOL hit_test_listbox_grip(IAutoCompleteImpl *ac, LPARAM lParam) { /* The grip's width is always the same as its height */ POINT pt = { (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam) }; + UINT grip_sz; RECT r; GetWindowRect(ac->hwndListBoxGrip, &r); - r.top = r.bottom - GetSystemMetrics(SM_CXVSCROLL); + grip_sz = GetSystemMetrics(SM_CXVSCROLL); + if (ac->dropped_up) + r.bottom = r.top + grip_sz; + else + r.top = r.bottom - grip_sz; return PtInRect(&r, pt); } @@ -1081,13 +1134,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)) @@ -1120,7 +1173,7 @@ static LRESULT APIENTRY ACLBoxGripSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPar SWP_NOSIZE | SWP_NOZORDER | SWP_DEFERERASE; return 0; case WM_NCPAINT: - draw_listbox_grip(hwnd, (HRGN)wParam); + draw_listbox_grip(This, hwnd, (HRGN)wParam); return 0; case WM_NCCALCSIZE: { @@ -1132,7 +1185,10 @@ static LRESULT APIENTRY ACLBoxGripSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPar and the client height is the height of the scrollbar (if visible) */ grip_sz = GetSystemMetrics(SM_CXVSCROLL); p->rgrc[0].right = p->rgrc[0].left; - p->rgrc[0].bottom = max(p->rgrc[0].bottom - grip_sz, p->rgrc[0].top); + if (This->dropped_up) + p->rgrc[0].top = min(p->rgrc[0].top + grip_sz, p->rgrc[0].bottom); + else + p->rgrc[0].bottom = max(p->rgrc[0].bottom - grip_sz, p->rgrc[0].top); return 0; } -- 2.20.1