Signed-off-by: Gabriel Ivăncescu gabrielopcode@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;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@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)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/shell32/autocomplete.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 24aa895..446642a 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -357,7 +357,10 @@ static void hide_listbox(IAutoCompleteImpl *ac, HWND hwnd, BOOL reset) static void show_listbox(IAutoCompleteImpl *ac) { RECT r; - UINT cnt, width, height; + LONG top; + HMONITOR hmon; + MONITORINFO info; + UINT cnt, width, height, grip_sz;
GetWindowRect(ac->hwndEdit, &r);
@@ -374,8 +377,24 @@ static void show_listbox(IAutoCompleteImpl *ac) height = SendMessageW(ac->hwndListBox, LB_GETITEMHEIGHT, 0, 0) * min(cnt + 1, 7); width = r.right - r.left; } + top = r.bottom + 1;
- SetWindowPos(ac->hwndListBoxOwner, HWND_TOP, r.left, r.bottom + 1, width, height, + /* Don't drop off-screen when showing the listbox */ + info.cbSize = sizeof(info); + hmon = MonitorFromRect(&r, MONITOR_DEFAULTTONEAREST); + if (GetMonitorInfoW(hmon, &info)) + { + /* Permit the width to go off-screen if the editbox also does, though */ + 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); + } + grip_sz = GetSystemMetrics(SM_CXVSCROLL); + width = max(width, grip_sz); + height = max(height, grip_sz); + + SetWindowPos(ac->hwndListBoxOwner, HWND_TOP, r.left, top, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_DEFERERASE);
/* Update the grip here (as we skip it during message processing), due
Signed-off-by: Gabriel Ivăncescu gabrielopcode@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); }