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;