2 * Theming - Scrollbar control
4 * Copyright (c) 2015 Mark Harmstone
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #include "wine/debug.h"
35 /* Minimum size of the thumb in pixels */
36 #define SCROLL_MIN_THUMB 6
38 /* Minimum size of the rectangle between the arrows */
39 #define SCROLL_MIN_RECT 4
43 SCROLL_NOWHERE
, /* Outside the scroll bar */
44 SCROLL_TOP_ARROW
, /* Top or left arrow */
45 SCROLL_TOP_RECT
, /* Rectangle between the top arrow and the thumb */
46 SCROLL_THUMB
, /* Thumb rectangle */
47 SCROLL_BOTTOM_RECT
, /* Rectangle between the thumb and the bottom arrow */
48 SCROLL_BOTTOM_ARROW
/* Bottom or right arrow */
51 static HWND tracking_win
= 0;
52 static enum SCROLL_HITTEST tracking_hot_part
= SCROLL_NOWHERE
;
54 WINE_DEFAULT_DEBUG_CHANNEL(theme_scroll
);
56 static void calc_thumb_dimensions(unsigned int size
, SCROLLINFO
*si
, unsigned int *thumbpos
, unsigned int *thumbsize
)
58 if (size
<= SCROLL_MIN_RECT
)
59 *thumbpos
= *thumbsize
= 0;
60 else if (si
->nPage
> si
->nMax
- si
->nMin
)
61 *thumbpos
= *thumbsize
= 0;
64 *thumbsize
= MulDiv(size
, si
->nPage
, si
->nMax
- si
->nMin
+ 1);
65 if (*thumbsize
< SCROLL_MIN_THUMB
) *thumbsize
= SCROLL_MIN_THUMB
;
67 else *thumbsize
= GetSystemMetrics(SM_CXVSCROLL
);
69 if (size
< *thumbsize
)
70 *thumbpos
= *thumbsize
= 0;
72 int max
= si
->nMax
- max(si
->nPage
- 1, 0);
77 *thumbpos
= MulDiv(size
, si
->nTrackPos
- si
->nMin
, max
- si
->nMin
);
82 static enum SCROLL_HITTEST
hit_test(HWND hwnd
, HTHEME theme
, POINT pt
)
85 DWORD style
= GetWindowLongW(hwnd
, GWL_STYLE
);
86 BOOL vertical
= style
& SBS_VERT
;
89 unsigned int offset
, size
, upsize
, downsize
, thumbpos
, thumbsize
;
91 GetWindowRect(hwnd
, &r
);
92 OffsetRect(&r
, -r
.left
, -r
.top
);
98 if (FAILED(GetThemePartSize(theme
, NULL
, SBP_ARROWBTN
, ABS_UPNORMAL
, NULL
, TS_DRAW
, &sz
))) {
99 WARN("Could not get up arrow size.\n");
104 if (FAILED(GetThemePartSize(theme
, NULL
, SBP_ARROWBTN
, ABS_DOWNNORMAL
, NULL
, TS_DRAW
, &sz
))) {
105 WARN("Could not get down arrow size.\n");
113 if (FAILED(GetThemePartSize(theme
, NULL
, SBP_ARROWBTN
, ABS_LEFTNORMAL
, NULL
, TS_DRAW
, &sz
))) {
114 WARN("Could not get left arrow size.\n");
119 if (FAILED(GetThemePartSize(theme
, NULL
, SBP_ARROWBTN
, ABS_RIGHTNORMAL
, NULL
, TS_DRAW
, &sz
))) {
120 WARN("Could not get right arrow size.\n");
126 if (pt
.x
< 0 || pt
.x
> r
.right
|| pt
.y
< 0 || pt
.y
> r
.bottom
)
127 return SCROLL_NOWHERE
;
129 if (size
< SCROLL_MIN_RECT
+ upsize
+ downsize
)
130 upsize
= downsize
= (size
- SCROLL_MIN_RECT
)/2;
133 return SCROLL_TOP_ARROW
;
135 if (offset
> size
- downsize
)
136 return SCROLL_BOTTOM_ARROW
;
138 si
.cbSize
= sizeof(si
);
140 if (!GetScrollInfo(hwnd
, SB_CTL
, &si
)) {
141 WARN("GetScrollInfo failed.\n");
142 return SCROLL_NOWHERE
;
145 calc_thumb_dimensions(size
- upsize
- downsize
, &si
, &thumbpos
, &thumbsize
);
147 if (offset
< upsize
+ thumbpos
)
148 return SCROLL_TOP_RECT
;
149 else if (offset
< upsize
+ thumbpos
+ thumbsize
)
152 return SCROLL_BOTTOM_RECT
;
155 static void redraw_part(HWND hwnd
, HTHEME theme
, enum SCROLL_HITTEST part
)
157 DWORD style
= GetWindowLongW(hwnd
, GWL_STYLE
);
158 BOOL vertical
= style
& SBS_VERT
;
161 unsigned int size
, upsize
, downsize
;
163 if (part
== SCROLL_NOWHERE
) { /* redraw everything */
164 InvalidateRect(hwnd
, NULL
, TRUE
);
168 GetWindowRect(hwnd
, &r
);
169 OffsetRect(&r
, -r
.left
, -r
.top
);
174 if (FAILED(GetThemePartSize(theme
, NULL
, SBP_ARROWBTN
, ABS_UPNORMAL
, NULL
, TS_DRAW
, &sz
))) {
175 WARN("Could not get up arrow size.\n");
180 if (FAILED(GetThemePartSize(theme
, NULL
, SBP_ARROWBTN
, ABS_DOWNNORMAL
, NULL
, TS_DRAW
, &sz
))) {
181 WARN("Could not get down arrow size.\n");
188 if (FAILED(GetThemePartSize(theme
, NULL
, SBP_ARROWBTN
, ABS_LEFTNORMAL
, NULL
, TS_DRAW
, &sz
))) {
189 WARN("Could not get left arrow size.\n");
194 if (FAILED(GetThemePartSize(theme
, NULL
, SBP_ARROWBTN
, ABS_RIGHTNORMAL
, NULL
, TS_DRAW
, &sz
))) {
195 WARN("Could not get right arrow size.\n");
201 if (size
< SCROLL_MIN_RECT
+ upsize
+ downsize
)
202 upsize
= downsize
= (size
- SCROLL_MIN_RECT
)/2;
206 if (part
== SCROLL_TOP_ARROW
) {
208 partrect
.bottom
= partrect
.top
+ upsize
;
210 partrect
.right
= partrect
.left
+ upsize
;
211 } else if (part
== SCROLL_BOTTOM_ARROW
) {
213 partrect
.top
= partrect
.bottom
- downsize
;
215 partrect
.left
= partrect
.right
- downsize
;
217 unsigned int thumbpos
, thumbsize
;
220 si
.cbSize
= sizeof(si
);
222 if (!GetScrollInfo(hwnd
, SB_CTL
, &si
)) {
223 WARN("GetScrollInfo failed.\n");
227 calc_thumb_dimensions(size
- upsize
- downsize
, &si
, &thumbpos
, &thumbsize
);
229 if (part
== SCROLL_TOP_RECT
) {
231 partrect
.top
= r
.top
+ upsize
;
232 partrect
.bottom
= partrect
.top
+ thumbpos
;
234 partrect
.left
= r
.left
+ upsize
;
235 partrect
.right
= partrect
.left
+ thumbpos
;
237 } else if (part
== SCROLL_THUMB
) {
239 partrect
.top
= r
.top
+ upsize
+ thumbpos
;
240 partrect
.bottom
= partrect
.top
+ thumbsize
;
242 partrect
.left
= r
.left
+ upsize
+ thumbpos
;
243 partrect
.right
= partrect
.left
+ thumbsize
;
245 } else if (part
== SCROLL_BOTTOM_RECT
) {
247 partrect
.top
= r
.top
+ upsize
+ thumbpos
+ thumbsize
;
248 partrect
.bottom
= r
.bottom
- downsize
;
250 partrect
.left
= r
.left
+ upsize
+ thumbpos
+ thumbsize
;
251 partrect
.right
= r
.right
- downsize
;
256 InvalidateRect(hwnd
, &partrect
, TRUE
);
259 static void scroll_event(HWND hwnd
, HTHEME theme
, UINT msg
, POINT pt
)
261 enum SCROLL_HITTEST hittest
;
264 if (GetWindowLongW(hwnd
, GWL_STYLE
) & (SBS_SIZEGRIP
| SBS_SIZEBOX
))
267 hittest
= hit_test(hwnd
, theme
, pt
);
272 hittest
= hit_test(hwnd
, theme
, pt
);
277 if (tracking_win
== hwnd
) {
278 hittest
= SCROLL_NOWHERE
;
283 tme
.cbSize
= sizeof(tme
);
284 tme
.dwFlags
= TME_QUERY
;
285 TrackMouseEvent(&tme
);
287 if (!(tme
.dwFlags
& TME_LEAVE
) || tme
.hwndTrack
!= hwnd
) {
288 tme
.dwFlags
= TME_LEAVE
;
289 tme
.hwndTrack
= hwnd
;
290 TrackMouseEvent(&tme
);
293 if (tracking_win
!= hwnd
&& msg
== WM_MOUSELEAVE
) {
294 redraw_part(hwnd
, theme
, SCROLL_NOWHERE
);
298 if (tracking_win
== hwnd
&& hittest
!= tracking_hot_part
) {
299 enum SCROLL_HITTEST oldhotpart
= tracking_hot_part
;
301 tracking_hot_part
= hittest
;
303 if (hittest
!= SCROLL_NOWHERE
)
304 redraw_part(hwnd
, theme
, hittest
);
308 if (oldhotpart
!= SCROLL_NOWHERE
)
309 redraw_part(hwnd
, theme
, oldhotpart
);
313 static void paint_scrollbar(HWND hwnd
, HTHEME theme
)
318 DWORD style
= GetWindowLongW(hwnd
, GWL_STYLE
);
319 BOOL vertical
= style
& SBS_VERT
;
320 BOOL disabled
= !IsWindowEnabled(hwnd
);
322 GetWindowRect(hwnd
, &r
);
323 OffsetRect(&r
, -r
.left
, -r
.top
);
325 dc
= BeginPaint(hwnd
, &ps
);
327 if (style
& SBS_SIZEBOX
|| style
& SBS_SIZEGRIP
) {
330 if (style
& SBS_SIZEBOXTOPLEFTALIGN
)
331 state
= SZB_TOPLEFTALIGN
;
333 state
= SZB_RIGHTALIGN
;
335 DrawThemeBackground(theme
, dc
, SBP_SIZEBOX
, state
, &r
, NULL
);
339 unsigned int thumbpos
, thumbsize
;
340 int uppertrackstate
, lowertrackstate
, thumbstate
;
341 RECT partrect
, trackrect
;
344 sbi
.cbSize
= sizeof(sbi
);
345 GetScrollBarInfo(hwnd
, OBJID_CLIENT
, &sbi
);
347 si
.cbSize
= sizeof(si
);
349 GetScrollInfo(hwnd
, SB_CTL
, &si
);
354 uppertrackstate
= SCRBS_DISABLED
;
355 lowertrackstate
= SCRBS_DISABLED
;
356 thumbstate
= SCRBS_DISABLED
;
358 uppertrackstate
= SCRBS_NORMAL
;
359 lowertrackstate
= SCRBS_NORMAL
;
360 thumbstate
= SCRBS_NORMAL
;
362 if (tracking_win
== hwnd
) {
363 if (tracking_hot_part
== SCROLL_TOP_RECT
)
364 uppertrackstate
= SCRBS_HOT
;
365 else if (tracking_hot_part
== SCROLL_BOTTOM_RECT
)
366 lowertrackstate
= SCRBS_HOT
;
367 else if (tracking_hot_part
== SCROLL_THUMB
)
368 thumbstate
= SCRBS_HOT
;
373 SIZE upsize
, downsize
;
374 int uparrowstate
, downarrowstate
;
377 uparrowstate
= ABS_UPDISABLED
;
378 downarrowstate
= ABS_DOWNDISABLED
;
380 uparrowstate
= ABS_UPNORMAL
;
381 downarrowstate
= ABS_DOWNNORMAL
;
383 if (tracking_win
== hwnd
) {
384 if (tracking_hot_part
== SCROLL_TOP_ARROW
)
385 uparrowstate
= ABS_UPHOT
;
386 else if (tracking_hot_part
== SCROLL_BOTTOM_ARROW
)
387 downarrowstate
= ABS_DOWNHOT
;
391 if (FAILED(GetThemePartSize(theme
, dc
, SBP_ARROWBTN
, uparrowstate
, NULL
, TS_DRAW
, &upsize
))) {
392 WARN("Could not get up arrow size.\n");
396 if (FAILED(GetThemePartSize(theme
, dc
, SBP_ARROWBTN
, downarrowstate
, NULL
, TS_DRAW
, &downsize
))) {
397 WARN("Could not get down arrow size.\n");
401 if (r
.bottom
- r
.top
- upsize
.cy
- downsize
.cy
< SCROLL_MIN_RECT
)
402 upsize
.cy
= downsize
.cy
= (r
.bottom
- r
.top
- SCROLL_MIN_RECT
)/2;
405 partrect
.bottom
= partrect
.top
+ upsize
.cy
;
406 DrawThemeBackground(theme
, dc
, SBP_ARROWBTN
, uparrowstate
, &partrect
, NULL
);
408 trackrect
.top
= partrect
.bottom
;
410 partrect
.bottom
= r
.bottom
;
411 partrect
.top
= partrect
.bottom
- downsize
.cy
;
412 DrawThemeBackground(theme
, dc
, SBP_ARROWBTN
, downarrowstate
, &partrect
, NULL
);
414 trackrect
.bottom
= partrect
.top
;
416 calc_thumb_dimensions(trackrect
.bottom
- trackrect
.top
, &si
, &thumbpos
, &thumbsize
);
419 partrect
.top
= trackrect
.top
;
420 partrect
.bottom
= partrect
.top
+ thumbpos
;
422 DrawThemeBackground(theme
, dc
, SBP_UPPERTRACKVERT
, uppertrackstate
, &partrect
, NULL
);
426 partrect
.top
= trackrect
.top
+ thumbpos
;
427 partrect
.bottom
= partrect
.top
+ thumbsize
;
429 DrawThemeBackground(theme
, dc
, SBP_THUMBBTNVERT
, thumbstate
, &partrect
, NULL
);
431 if (SUCCEEDED(GetThemePartSize(theme
, dc
, SBP_GRIPPERVERT
, thumbstate
, NULL
, TS_DRAW
, &grippersize
))) {
434 if (SUCCEEDED(GetThemeMargins(theme
, dc
, SBP_THUMBBTNVERT
, thumbstate
, TMT_CONTENTMARGINS
, &partrect
, &margins
))) {
435 if (grippersize
.cy
<= (thumbsize
- margins
.cyTopHeight
- margins
.cyBottomHeight
))
436 DrawThemeBackground(theme
, dc
, SBP_GRIPPERVERT
, thumbstate
, &partrect
, NULL
);
441 if (thumbpos
+ thumbsize
< trackrect
.bottom
- trackrect
.top
) {
442 partrect
.bottom
= trackrect
.bottom
;
443 partrect
.top
= trackrect
.top
+ thumbsize
+ thumbpos
;
445 DrawThemeBackground(theme
, dc
, SBP_LOWERTRACKVERT
, lowertrackstate
, &partrect
, NULL
);
448 SIZE leftsize
, rightsize
;
449 int leftarrowstate
, rightarrowstate
;
452 leftarrowstate
= ABS_LEFTDISABLED
;
453 rightarrowstate
= ABS_RIGHTDISABLED
;
455 leftarrowstate
= ABS_LEFTNORMAL
;
456 rightarrowstate
= ABS_RIGHTNORMAL
;
458 if (tracking_win
== hwnd
) {
459 if (tracking_hot_part
== SCROLL_TOP_ARROW
)
460 leftarrowstate
= ABS_LEFTHOT
;
461 else if (tracking_hot_part
== SCROLL_BOTTOM_ARROW
)
462 rightarrowstate
= ABS_RIGHTHOT
;
466 if (FAILED(GetThemePartSize(theme
, dc
, SBP_ARROWBTN
, leftarrowstate
, NULL
, TS_DRAW
, &leftsize
))) {
467 WARN("Could not get left arrow size.\n");
471 if (FAILED(GetThemePartSize(theme
, dc
, SBP_ARROWBTN
, rightarrowstate
, NULL
, TS_DRAW
, &rightsize
))) {
472 WARN("Could not get right arrow size.\n");
476 if (r
.right
- r
.left
- leftsize
.cx
- rightsize
.cx
< SCROLL_MIN_RECT
)
477 leftsize
.cx
= rightsize
.cx
= (r
.right
- r
.left
- SCROLL_MIN_RECT
)/2;
480 partrect
.right
= partrect
.left
+ leftsize
.cx
;
481 DrawThemeBackground(theme
, dc
, SBP_ARROWBTN
, leftarrowstate
, &partrect
, NULL
);
483 trackrect
.left
= partrect
.right
;
485 partrect
.right
= r
.right
;
486 partrect
.left
= partrect
.right
- rightsize
.cx
;
487 DrawThemeBackground(theme
, dc
, SBP_ARROWBTN
, rightarrowstate
, &partrect
, NULL
);
489 trackrect
.right
= partrect
.left
;
491 calc_thumb_dimensions(trackrect
.right
- trackrect
.left
, &si
, &thumbpos
, &thumbsize
);
494 partrect
.left
= trackrect
.left
;
495 partrect
.right
= partrect
.left
+ thumbpos
;
497 DrawThemeBackground(theme
, dc
, SBP_UPPERTRACKHORZ
, uppertrackstate
, &partrect
, NULL
);
501 partrect
.left
= trackrect
.left
+ thumbpos
;
502 partrect
.right
= partrect
.left
+ thumbsize
;
504 DrawThemeBackground(theme
, dc
, SBP_THUMBBTNHORZ
, thumbstate
, &partrect
, NULL
);
506 if (SUCCEEDED(GetThemePartSize(theme
, dc
, SBP_GRIPPERHORZ
, thumbstate
, NULL
, TS_DRAW
, &grippersize
))) {
509 if (SUCCEEDED(GetThemeMargins(theme
, dc
, SBP_THUMBBTNHORZ
, thumbstate
, TMT_CONTENTMARGINS
, &partrect
, &margins
))) {
510 if (grippersize
.cx
<= (thumbsize
- margins
.cxLeftWidth
- margins
.cxRightWidth
))
511 DrawThemeBackground(theme
, dc
, SBP_GRIPPERHORZ
, thumbstate
, &partrect
, NULL
);
516 if (thumbpos
+ thumbsize
< trackrect
.right
- trackrect
.left
) {
517 partrect
.right
= trackrect
.right
;
518 partrect
.left
= trackrect
.left
+ thumbsize
+ thumbpos
;
520 DrawThemeBackground(theme
, dc
, SBP_LOWERTRACKHORZ
, lowertrackstate
, &partrect
, NULL
);
528 LRESULT CALLBACK
THEMING_ScrollbarSubclassProc (HWND hwnd
, UINT msg
,
529 WPARAM wParam
, LPARAM lParam
,
532 const WCHAR
* themeClass
= WC_SCROLLBARW
;
537 TRACE("(%p, 0x%x, %lu, %lu, %lu)\n", hwnd
, msg
, wParam
, lParam
, dwRefData
);
541 result
= THEMING_CallOriginalClass(hwnd
, msg
, wParam
, lParam
);
542 OpenThemeData(hwnd
, themeClass
);
546 theme
= GetWindowTheme(hwnd
);
547 CloseThemeData(theme
);
548 return THEMING_CallOriginalClass(hwnd
, msg
, wParam
, lParam
);
550 case WM_THEMECHANGED
:
551 theme
= GetWindowTheme(hwnd
);
552 CloseThemeData(theme
);
553 OpenThemeData(hwnd
, themeClass
);
556 case WM_SYSCOLORCHANGE
:
557 theme
= GetWindowTheme(hwnd
);
558 if (!theme
) return THEMING_CallOriginalClass(hwnd
, msg
, wParam
, lParam
);
559 /* Do nothing. When themed, a WM_THEMECHANGED will be received, too,
560 * which will do the repaint. */
564 theme
= GetWindowTheme(hwnd
);
565 if (!theme
) return THEMING_CallOriginalClass(hwnd
, msg
, wParam
, lParam
);
567 paint_scrollbar(hwnd
, theme
);
572 theme
= GetWindowTheme(hwnd
);
573 if (!theme
) return THEMING_CallOriginalClass(hwnd
, msg
, wParam
, lParam
);
575 pt
.x
= (short)LOWORD(lParam
);
576 pt
.y
= (short)HIWORD(lParam
);
577 scroll_event(hwnd
, theme
, msg
, pt
);
581 return THEMING_CallOriginalClass(hwnd
, msg
, wParam
, lParam
);