Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This is a no-op patch; its purpose is to keep the next patch smaller and focused only on implementing the resizing grip, because IMO it was a bit large before I split it. Note that to avoid more pointless changes in the next patch, names with "grip" have been used for the scrollbar, I hope that's not a problem.
Some of the things are done purposefully to keep the next patch smaller, while they may not make sense by themselves. For example, the WM_WINDOWPOSCHANGED is also useful to that end.
dlls/shell32/autocomplete.c | 132 ++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 5 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index f0777ff..38ca8ca 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -65,9 +65,11 @@ typedef struct HWND hwndEdit; HWND hwndListBox; HWND hwndListBoxOwner; + HWND hwndListBoxGrip; WNDPROC wpOrigEditProc; WNDPROC wpOrigLBoxProc; WNDPROC wpOrigLBoxOwnerProc; + WNDPROC wpOrigLBoxGripProc; WCHAR *txtbackup; WCHAR *quickComplete; IEnumString *enumstr; @@ -284,6 +286,41 @@ 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, grip_height; + SCROLLINFO info; + + 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) + { + grip_height = 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->hwndListBoxGrip, SB_VERT, &info, FALSE); + + listbox_width -= grip_sz; + } + else + grip_height = 0; + + SetWindowPos(ac->hwndListBoxGrip, NULL, width - grip_sz, 0, grip_sz, grip_height, + 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 +341,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 +905,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->hwndListBoxGrip, SB_VERT, &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->hwndListBoxGrip, SB_VERT, &info, TRUE); + return ret; + } } return CallWindowProcW(This->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam); } @@ -879,16 +951,55 @@ 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_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; + + update_listbox_size(This, wp->cx, wp->cy); + } break; + } } 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_VSCROLL: + return SendMessageW(This->hwndListBox, uMsg, wParam, lParam); + case WM_NCCALCSIZE: + { + NCCALCSIZE_PARAMS *p = (NCCALCSIZE_PARAMS*)lParam; + if (wParam == FALSE) return 0; + + p->rgrc[0].right = p->rgrc[0].left; + + return 0; + } + case WM_NCHITTEST: + return HTVSCROLL; + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + 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); @@ -899,8 +1010,15 @@ static void create_listbox(IAutoCompleteImpl *This) }
/* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */ + This->hwndListBoxGrip = CreateWindowExW(WS_EX_NOACTIVATE, WC_STATICW, NULL, + WS_CHILD | WS_VISIBLE, 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_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) { @@ -912,6 +1030,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) @@ -919,6 +1040,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 ---
The scrollbar and grip are implemented as non-client area (just like the listbox's normal vertical scrollbar) which simplifies the code and avoids having to meddle with yet another window (which would also add more flickering when resizing as well).
This means that the grip has to be drawn manually, but that needs to happen anyway for the last patch in the series, which flips the grip vertically if required, so it's needed anyway.
Some changes (e.g. in WM_NCCALCSIZE) are done to keep the last patch also smaller.
dlls/shell32/autocomplete.c | 150 +++++++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 10 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 38ca8ca..136d9e8 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -62,6 +62,8 @@ typedef struct UINT enum_strs_num; WCHAR **enum_strs; WCHAR **listbox_strs; + UINT listbox_width; + UINT listbox_height; HWND hwndEdit; HWND hwndListBox; HWND hwndListBoxOwner; @@ -288,8 +290,14 @@ 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, grip_height; + UINT item_height, listbox_width, grip_sz, grip_height, grip_top = 0; SCROLLINFO info; + BOOL had_scroll; + RECT r; + + /* Scrollbar was visible if the grip's client height is nonzero */ + GetClientRect(ac->hwndListBoxGrip, &r); + had_scroll = r.bottom != r.top;
info.nMax = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0) - 1; item_height = SendMessageW(ac->hwndListBox, LB_GETITEMHEIGHT, 0, 0); @@ -300,7 +308,7 @@ 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) { grip_height = height; info.cbSize = sizeof(info); @@ -309,12 +317,30 @@ static void update_listbox_size(IAutoCompleteImpl *ac, UINT width, UINT height) info.nPos = SendMessageW(ac->hwndListBox, LB_GETTOPINDEX, 0, 0); SetScrollInfo(ac->hwndListBoxGrip, SB_VERT, &info, FALSE);
+ if (!had_scroll) SetWindowRgn(ac->hwndListBoxGrip, NULL, FALSE); listbox_width -= grip_sz; } else - grip_height = 0; + { + /* Scrollbar is not visible so adjust the position and size */ + grip_top += height - grip_sz; + grip_height = grip_sz; + + if (had_scroll) + { + /* 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->hwndListBoxGrip, NULL, width - grip_sz, 0, grip_sz, grip_height, + SetWindowPos(ac->hwndListBoxGrip, NULL, width - grip_sz, grip_top, grip_sz, grip_height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_DEFERERASE);
SetWindowPos(ac->hwndListBox, NULL, 0, 0, listbox_width, height, @@ -335,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); @@ -403,6 +438,54 @@ static BOOL draw_listbox_item(IAutoCompleteImpl *ac, DRAWITEMSTRUCT *info, UINT return TRUE; }
+static void draw_listbox_grip(HWND hwnd, HRGN clip) +{ + LONG orig_top; + int type; + HRGN hrgn; + HDC hdc; + RECT r; + + /* Get the grip's square (without the scrollbar) */ + GetWindowRect(hwnd, &r); + orig_top = r.top; + r.top = r.bottom - GetSystemMetrics(SM_CXVSCROLL); + + /* Draw the scrollbar, if visible */ + if (r.top > orig_top) DefWindowProcW(hwnd, WM_NCPAINT, (WPARAM)clip, 0); + + /* Create a region for the grip */ + if (!(hrgn = CreateRectRgnIndirect(&r))) return; + + /* Exclude the clipping region outside of the grip */ + if ((clip > (HRGN)1 && + ((type = CombineRgn(hrgn, clip, hrgn, RGN_AND)) == ERROR || type == NULLREGION) + ) + || !(hdc = GetDCEx(hwnd, hrgn, DCX_CACHE | DCX_WINDOW | DCX_INTERSECTRGN))) + { + DeleteObject(hrgn); + return; + } + + /* Adjust for DrawFrameControl */ + OffsetRect(&r, -r.left, -orig_top); + + DrawFrameControl(hdc, &r, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); + ReleaseDC(hwnd, hdc); +} + +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) }; + RECT r; + + GetWindowRect(ac->hwndListBoxGrip, &r); + r.top = r.bottom - GetSystemMetrics(SM_CXVSCROLL); + + 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 @@ -935,6 +1018,10 @@ static LRESULT APIENTRY ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, SetScrollInfo(This->hwndListBoxGrip, SB_VERT, &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); } @@ -951,6 +1038,19 @@ static LRESULT APIENTRY ACLBoxOwnerSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPa if (draw_listbox_item(This, (DRAWITEMSTRUCT*)lParam, wParam)) return TRUE; break; + 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; @@ -960,10 +1060,27 @@ static LRESULT APIENTRY ACLBoxOwnerSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPa /* 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); } @@ -978,17 +1095,30 @@ static LRESULT APIENTRY ACLBoxGripSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPar return MA_NOACTIVATE; case WM_VSCROLL: return SendMessageW(This->hwndListBox, uMsg, wParam, lParam); + case WM_WINDOWPOSCHANGING: + /* This shouldn't happen, don't let the user move or resize the grip */ + ((WINDOWPOS*)lParam)->flags |= SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOMOVE | + SWP_NOSIZE | SWP_NOZORDER | SWP_DEFERERASE; + return 0; + case WM_NCPAINT: + draw_listbox_grip(hwnd, (HRGN)wParam); + return 0; case WM_NCCALCSIZE: { + UINT grip_sz; NCCALCSIZE_PARAMS *p = (NCCALCSIZE_PARAMS*)lParam; if (wParam == FALSE) return 0;
+ /* The grip is made up of non-client area only, so client width is zero, + 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);
return 0; } case WM_NCHITTEST: - return HTVSCROLL; + return hit_test_listbox_grip(This, lParam) ? HTTRANSPARENT : HTVSCROLL; case WM_NCLBUTTONDOWN: case WM_NCLBUTTONDBLCLK: return DefWindowProcW(hwnd, uMsg, wParam, lParam); @@ -1009,7 +1139,7 @@ static void create_listbox(IAutoCompleteImpl *This) return; }
- /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */ + /* Create a size grip that will also host a scrollbar using non-client area */ This->hwndListBoxGrip = CreateWindowExW(WS_EX_NOACTIVATE, WC_STATICW, NULL, WS_CHILD | WS_VISIBLE, 0, 0, grip_sz, grip_sz, This->hwndListBoxOwner, NULL, shell32_hInstance, NULL);
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 136d9e8..f18517f 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
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=50562
Your paranoid android.
=== debian9 (32 bit report) ===
shell32: progman_dde.c:249: Test failed: expected DMLERR_NO_ERROR, got 16393 progman_dde.c:250: Test failed: directory should not exist
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- 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; }
On Fri, Apr 05, 2019 at 03:14:24PM +0300, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com
This is a no-op patch; its purpose is to keep the next patch smaller and focused only on implementing the resizing grip, because IMO it was a bit large before I split it. Note that to avoid more pointless changes in the next patch, names with "grip" have been used for the scrollbar, I hope that's not a problem.
Some of the things are done purposefully to keep the next patch smaller, while they may not make sense by themselves. For example, the WM_WINDOWPOSCHANGED is also useful to that end.
dlls/shell32/autocomplete.c | 132 ++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 5 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index f0777ff..38ca8ca 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c
@@ -899,8 +1010,15 @@ static void create_listbox(IAutoCompleteImpl *This) }
/* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
- This->hwndListBoxGrip = CreateWindowExW(WS_EX_NOACTIVATE, WC_STATICW, NULL,
WS_CHILD | WS_VISIBLE, 0, 0, grip_sz, grip_sz,
This->hwndListBoxOwner, NULL, shell32_hInstance, NULL);
- if (!This->hwndListBoxGrip) goto fail;
You should be able to use a SBS_SIZEBOX scrollbar for this, rather than re-invent the wheel.
Huw.
On 4/12/19 12:33 PM, Huw Davies wrote:
On Fri, Apr 05, 2019 at 03:14:24PM +0300, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com
This is a no-op patch; its purpose is to keep the next patch smaller and focused only on implementing the resizing grip, because IMO it was a bit large before I split it. Note that to avoid more pointless changes in the next patch, names with "grip" have been used for the scrollbar, I hope that's not a problem.
Some of the things are done purposefully to keep the next patch smaller, while they may not make sense by themselves. For example, the WM_WINDOWPOSCHANGED is also useful to that end.
dlls/shell32/autocomplete.c | 132 ++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 5 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index f0777ff..38ca8ca 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c
@@ -899,8 +1010,15 @@ static void create_listbox(IAutoCompleteImpl *This) }
/* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
- This->hwndListBoxGrip = CreateWindowExW(WS_EX_NOACTIVATE, WC_STATICW, NULL,
WS_CHILD | WS_VISIBLE, 0, 0, grip_sz, grip_sz,
This->hwndListBoxOwner, NULL, shell32_hInstance, NULL);
- if (!This->hwndListBoxGrip) goto fail;
You should be able to use a SBS_SIZEBOX scrollbar for this, rather than re-invent the wheel.
Huw.
Hi Huw,
You mean for the grip? This scrollbar patch will have to be done, regardless, since we can't use the listbox's WS_VSCROLL at all.
I did try with a separate grip window initially, but it actually resulted in more code (and more flickering when moved), so it's not really a gain in simplicity, because it would be yet another window to keep track of and move around, and we'd have to resort to draw the grip anyway (because of the last patch which flips it up).
Right now, the only parts that are added due to using the grip in same window as scrollbar is just the WM_NCCALCSIZE portion and drawing of the grip/scrollbar. Drawing the scrollbar is one DefWindowProc call so it's trivial. And drawing the grip has to be done anyway due to flipping it up and to show properly (if I'm not wrong, I remember SBS_SIZEBOX being different here, visually).
I don't remember exactly why I gave up on the SBS_SIZEBOX idea initially after trying it, but it was for something like this definitely.
On 4/12/19 2:33 PM, Huw Davies wrote:
On Fri, Apr 12, 2019 at 02:16:10PM +0300, Gabriel Ivăncescu wrote:
On 4/12/19 12:33 PM, Huw Davies wrote:
You should be able to use a SBS_SIZEBOX scrollbar for this, rather than re-invent the wheel.
You mean for the grip?
Yes, sorry I meant to reply to [2/4].
Huw.
Ok, that makes more sense, but IMO it's not any better. Most of the code will have to remain or be moved around.
The only difference I can see is in WM_NCCALCSIZE, which would have to be "moved" to update_listbox_size, so it's not really a gain (if I use SBS_SIZEBOX) since the flipping logic would need to be moved there.
WM_NCHITTEST will have to remain for two reasons: when it's flipped up, the arrows and behavior needs to change. Also, when the grip is a triangle (scrollbar is not visible), on Windows moving the mouse to the *square* of the grip enables it just like if it were a square. If I rely on SBS_SIZEBOX's native functionality it wouldn't work like on Windows. Bottom line is, I'd still have to add a WM_NCHITTEST manually, so it's not really a gain here, either.
The painting of the grip will still have to be done due to the flipping (I don't think drawing the scrollbar with a DefWindowProc is a problem).
And if I use a SBS_SIZEBOX it means it will have to be another window, separate from the scrollbar, which I think will complicate the code more.
Looking at the patch, I can't really identify other areas where it would result in less code. Do you have something specific in mind?
I realize the patch, by itself, seems slightly redundant for now, but it's needed for the last patch which flips it up. :-) (I *did* try with separate windows initially and gave up on it due to this)