[PATCH v2 2/4] shell32/autocomplete: Implement the listbox resizing grip and make it resize-able
Gabriel Ivăncescu
gabrielopcode at gmail.com
Tue May 28 07:21:41 CDT 2019
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at 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)
--
2.21.0
More information about the wine-devel
mailing list