Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586/* * Theming - Scrollbar control * * Copyright (c) 2015 Mark Harmstone * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * */
#include <stdarg.h>#include <string.h>#include <stdlib.h>
#include "windef.h"#include "winbase.h"#include "wingdi.h"#include "winuser.h"#include "uxtheme.h"#include "vssym32.h"#include "comctl32.h"#include "wine/debug.h"
/* Minimum size of the thumb in pixels */#define SCROLL_MIN_THUMB 6
/* Minimum size of the rectangle between the arrows */#define SCROLL_MIN_RECT 4
enum SCROLL_HITTEST{ SCROLL_NOWHERE, /* Outside the scroll bar */ SCROLL_TOP_ARROW, /* Top or left arrow */ SCROLL_TOP_RECT, /* Rectangle between the top arrow and the thumb */ SCROLL_THUMB, /* Thumb rectangle */ SCROLL_BOTTOM_RECT, /* Rectangle between the thumb and the bottom arrow */ SCROLL_BOTTOM_ARROW /* Bottom or right arrow */};
static HWND tracking_win = 0;static enum SCROLL_HITTEST tracking_hot_part = SCROLL_NOWHERE;
WINE_DEFAULT_DEBUG_CHANNEL(theme_scroll);
static void calc_thumb_dimensions(unsigned int size, SCROLLINFO *si, unsigned int *thumbpos, unsigned int *thumbsize){ if (size <= SCROLL_MIN_RECT) *thumbpos = *thumbsize = 0; else if (si->nPage > si->nMax - si->nMin) *thumbpos = *thumbsize = 0; else { if (si->nPage > 0) { *thumbsize = MulDiv(size, si->nPage, si->nMax - si->nMin + 1); if (*thumbsize < SCROLL_MIN_THUMB) *thumbsize = SCROLL_MIN_THUMB; } else *thumbsize = GetSystemMetrics(SM_CXVSCROLL);
if (size < *thumbsize) *thumbpos = *thumbsize = 0; else { int max = si->nMax - max(si->nPage - 1, 0); size -= *thumbsize; if (si->nMin >= max) *thumbpos = 0; else *thumbpos = MulDiv(size, si->nTrackPos - si->nMin, max - si->nMin); } }}
static enum SCROLL_HITTEST hit_test(HWND hwnd, HTHEME theme, POINT pt){ RECT r; DWORD style = GetWindowLongW(hwnd, GWL_STYLE); BOOL vertical = style & SBS_VERT; SIZE sz; SCROLLINFO si; unsigned int offset, size, upsize, downsize, thumbpos, thumbsize;
GetWindowRect(hwnd, &r); OffsetRect(&r, -r.left, -r.top);
if (vertical) { offset = pt.y; size = r.bottom;
if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_UPNORMAL, NULL, TS_DRAW, &sz))) { WARN("Could not get up arrow size.\n"); upsize = 0; } else upsize = sz.cy;
if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_DOWNNORMAL, NULL, TS_DRAW, &sz))) { WARN("Could not get down arrow size.\n"); downsize = 0; } else downsize = sz.cy; } else { offset = pt.x; size = r.right;
if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_LEFTNORMAL, NULL, TS_DRAW, &sz))) { WARN("Could not get left arrow size.\n"); upsize = 0; } else upsize = sz.cx;
if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_RIGHTNORMAL, NULL, TS_DRAW, &sz))) { WARN("Could not get right arrow size.\n"); downsize = 0; } else downsize = sz.cx; }
if (pt.x < 0 || pt.x > r.right || pt.y < 0 || pt.y > r.bottom) return SCROLL_NOWHERE;
if (size < SCROLL_MIN_RECT + upsize + downsize) upsize = downsize = (size - SCROLL_MIN_RECT)/2;
if (offset < upsize) return SCROLL_TOP_ARROW;
if (offset > size - downsize) return SCROLL_BOTTOM_ARROW;
si.cbSize = sizeof(si); si.fMask = SIF_ALL; if (!GetScrollInfo(hwnd, SB_CTL, &si)) { WARN("GetScrollInfo failed.\n"); return SCROLL_NOWHERE; }
calc_thumb_dimensions(size - upsize - downsize, &si, &thumbpos, &thumbsize);
if (offset < upsize + thumbpos) return SCROLL_TOP_RECT; else if (offset < upsize + thumbpos + thumbsize) return SCROLL_THUMB; else return SCROLL_BOTTOM_RECT;}
static void redraw_part(HWND hwnd, HTHEME theme, enum SCROLL_HITTEST part){ DWORD style = GetWindowLongW(hwnd, GWL_STYLE); BOOL vertical = style & SBS_VERT; SIZE sz; RECT r, partrect; unsigned int size, upsize, downsize;
if (part == SCROLL_NOWHERE) { /* redraw everything */ InvalidateRect(hwnd, NULL, TRUE); return; }
GetWindowRect(hwnd, &r); OffsetRect(&r, -r.left, -r.top);
if (vertical) { size = r.bottom;
if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_UPNORMAL, NULL, TS_DRAW, &sz))) { WARN("Could not get up arrow size.\n"); upsize = 0; } else upsize = sz.cy;
if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_DOWNNORMAL, NULL, TS_DRAW, &sz))) { WARN("Could not get down arrow size.\n"); downsize = 0; } else downsize = sz.cy; } else { size = r.right;
if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_LEFTNORMAL, NULL, TS_DRAW, &sz))) { WARN("Could not get left arrow size.\n"); upsize = 0; } else upsize = sz.cx;
if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_RIGHTNORMAL, NULL, TS_DRAW, &sz))) { WARN("Could not get right arrow size.\n"); downsize = 0; } else downsize = sz.cx; }
if (size < SCROLL_MIN_RECT + upsize + downsize) upsize = downsize = (size - SCROLL_MIN_RECT)/2;
partrect = r;
if (part == SCROLL_TOP_ARROW) { if (vertical) partrect.bottom = partrect.top + upsize; else partrect.right = partrect.left + upsize; } else if (part == SCROLL_BOTTOM_ARROW) { if (vertical) partrect.top = partrect.bottom - downsize; else partrect.left = partrect.right - downsize; } else { unsigned int thumbpos, thumbsize; SCROLLINFO si;
si.cbSize = sizeof(si); si.fMask = SIF_ALL; if (!GetScrollInfo(hwnd, SB_CTL, &si)) { WARN("GetScrollInfo failed.\n"); return; }
calc_thumb_dimensions(size - upsize - downsize, &si, &thumbpos, &thumbsize);
if (part == SCROLL_TOP_RECT) { if (vertical) { partrect.top = r.top + upsize; partrect.bottom = partrect.top + thumbpos; } else { partrect.left = r.left + upsize; partrect.right = partrect.left + thumbpos; } } else if (part == SCROLL_THUMB) { if (vertical) { partrect.top = r.top + upsize + thumbpos; partrect.bottom = partrect.top + thumbsize; } else { partrect.left = r.left + upsize + thumbpos; partrect.right = partrect.left + thumbsize; } } else if (part == SCROLL_BOTTOM_RECT) { if (vertical) { partrect.top = r.top + upsize + thumbpos + thumbsize; partrect.bottom = r.bottom - downsize; } else { partrect.left = r.left + upsize + thumbpos + thumbsize; partrect.right = r.right - downsize; } } }
InvalidateRect(hwnd, &partrect, TRUE);}
static void scroll_event(HWND hwnd, HTHEME theme, UINT msg, POINT pt){ enum SCROLL_HITTEST hittest; TRACKMOUSEEVENT tme;
if (GetWindowLongW(hwnd, GWL_STYLE) & (SBS_SIZEGRIP | SBS_SIZEBOX)) return;
hittest = hit_test(hwnd, theme, pt);
switch (msg) { case WM_MOUSEMOVE: hittest = hit_test(hwnd, theme, pt); tracking_win = hwnd; break;
case WM_MOUSELEAVE: if (tracking_win == hwnd) { hittest = SCROLL_NOWHERE; } break; }
tme.cbSize = sizeof(tme); tme.dwFlags = TME_QUERY; TrackMouseEvent(&tme);
if (!(tme.dwFlags & TME_LEAVE) || tme.hwndTrack != hwnd) { tme.dwFlags = TME_LEAVE; tme.hwndTrack = hwnd; TrackMouseEvent(&tme); }
if (tracking_win != hwnd && msg == WM_MOUSELEAVE) { redraw_part(hwnd, theme, SCROLL_NOWHERE); return; }
if (tracking_win == hwnd && hittest != tracking_hot_part) { enum SCROLL_HITTEST oldhotpart = tracking_hot_part;
tracking_hot_part = hittest;
if (hittest != SCROLL_NOWHERE) redraw_part(hwnd, theme, hittest); else tracking_win = 0;
if (oldhotpart != SCROLL_NOWHERE) redraw_part(hwnd, theme, oldhotpart); }}
static void paint_scrollbar(HWND hwnd, HTHEME theme){ HDC dc; PAINTSTRUCT ps; RECT r; DWORD style = GetWindowLongW(hwnd, GWL_STYLE); BOOL vertical = style & SBS_VERT; BOOL disabled = !IsWindowEnabled(hwnd);
GetWindowRect(hwnd, &r); OffsetRect(&r, -r.left, -r.top);
dc = BeginPaint(hwnd, &ps);
if (style & SBS_SIZEBOX || style & SBS_SIZEGRIP) { int state;
if (style & SBS_SIZEBOXTOPLEFTALIGN) state = SZB_TOPLEFTALIGN; else state = SZB_RIGHTALIGN;
DrawThemeBackground(theme, dc, SBP_SIZEBOX, state, &r, NULL); } else { SCROLLBARINFO sbi; SCROLLINFO si; unsigned int thumbpos, thumbsize; int uppertrackstate, lowertrackstate, thumbstate; RECT partrect, trackrect; SIZE grippersize;
sbi.cbSize = sizeof(sbi); GetScrollBarInfo(hwnd, OBJID_CLIENT, &sbi);
si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_CTL, &si);
trackrect = r;
if (disabled) { uppertrackstate = SCRBS_DISABLED; lowertrackstate = SCRBS_DISABLED; thumbstate = SCRBS_DISABLED; } else { uppertrackstate = SCRBS_NORMAL; lowertrackstate = SCRBS_NORMAL; thumbstate = SCRBS_NORMAL;
if (tracking_win == hwnd) { if (tracking_hot_part == SCROLL_TOP_RECT) uppertrackstate = SCRBS_HOT; else if (tracking_hot_part == SCROLL_BOTTOM_RECT) lowertrackstate = SCRBS_HOT; else if (tracking_hot_part == SCROLL_THUMB) thumbstate = SCRBS_HOT; } }
if (vertical) { SIZE upsize, downsize; int uparrowstate, downarrowstate;
if (disabled) { uparrowstate = ABS_UPDISABLED; downarrowstate = ABS_DOWNDISABLED; } else { uparrowstate = ABS_UPNORMAL; downarrowstate = ABS_DOWNNORMAL;
if (tracking_win == hwnd) { if (tracking_hot_part == SCROLL_TOP_ARROW) uparrowstate = ABS_UPHOT; else if (tracking_hot_part == SCROLL_BOTTOM_ARROW) downarrowstate = ABS_DOWNHOT; } }
if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, uparrowstate, NULL, TS_DRAW, &upsize))) { WARN("Could not get up arrow size.\n"); return; }
if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, downarrowstate, NULL, TS_DRAW, &downsize))) { WARN("Could not get down arrow size.\n"); return; }
if (r.bottom - r.top - upsize.cy - downsize.cy < SCROLL_MIN_RECT) upsize.cy = downsize.cy = (r.bottom - r.top - SCROLL_MIN_RECT)/2;
partrect = r; partrect.bottom = partrect.top + upsize.cy; DrawThemeBackground(theme, dc, SBP_ARROWBTN, uparrowstate, &partrect, NULL);
trackrect.top = partrect.bottom;
partrect.bottom = r.bottom; partrect.top = partrect.bottom - downsize.cy; DrawThemeBackground(theme, dc, SBP_ARROWBTN, downarrowstate, &partrect, NULL);
trackrect.bottom = partrect.top;
calc_thumb_dimensions(trackrect.bottom - trackrect.top, &si, &thumbpos, &thumbsize);
if (thumbpos > 0) { partrect.top = trackrect.top; partrect.bottom = partrect.top + thumbpos;
DrawThemeBackground(theme, dc, SBP_UPPERTRACKVERT, uppertrackstate, &partrect, NULL); }
if (thumbsize > 0) { partrect.top = trackrect.top + thumbpos; partrect.bottom = partrect.top + thumbsize;
DrawThemeBackground(theme, dc, SBP_THUMBBTNVERT, thumbstate, &partrect, NULL);
if (SUCCEEDED(GetThemePartSize(theme, dc, SBP_GRIPPERVERT, thumbstate, NULL, TS_DRAW, &grippersize))) { MARGINS margins;
if (SUCCEEDED(GetThemeMargins(theme, dc, SBP_THUMBBTNVERT, thumbstate, TMT_CONTENTMARGINS, &partrect, &margins))) { if (grippersize.cy <= (thumbsize - margins.cyTopHeight - margins.cyBottomHeight)) DrawThemeBackground(theme, dc, SBP_GRIPPERVERT, thumbstate, &partrect, NULL); } } }
if (thumbpos + thumbsize < trackrect.bottom - trackrect.top) { partrect.bottom = trackrect.bottom; partrect.top = trackrect.top + thumbsize + thumbpos;
DrawThemeBackground(theme, dc, SBP_LOWERTRACKVERT, lowertrackstate, &partrect, NULL); } } else { SIZE leftsize, rightsize; int leftarrowstate, rightarrowstate;
if (disabled) { leftarrowstate = ABS_LEFTDISABLED; rightarrowstate = ABS_RIGHTDISABLED; } else { leftarrowstate = ABS_LEFTNORMAL; rightarrowstate = ABS_RIGHTNORMAL;
if (tracking_win == hwnd) { if (tracking_hot_part == SCROLL_TOP_ARROW) leftarrowstate = ABS_LEFTHOT; else if (tracking_hot_part == SCROLL_BOTTOM_ARROW) rightarrowstate = ABS_RIGHTHOT; } }
if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, leftarrowstate, NULL, TS_DRAW, &leftsize))) { WARN("Could not get left arrow size.\n"); return; }
if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, rightarrowstate, NULL, TS_DRAW, &rightsize))) { WARN("Could not get right arrow size.\n"); return; }
if (r.right - r.left - leftsize.cx - rightsize.cx < SCROLL_MIN_RECT) leftsize.cx = rightsize.cx = (r.right - r.left - SCROLL_MIN_RECT)/2;
partrect = r; partrect.right = partrect.left + leftsize.cx; DrawThemeBackground(theme, dc, SBP_ARROWBTN, leftarrowstate, &partrect, NULL);
trackrect.left = partrect.right;
partrect.right = r.right; partrect.left = partrect.right - rightsize.cx; DrawThemeBackground(theme, dc, SBP_ARROWBTN, rightarrowstate, &partrect, NULL);
trackrect.right = partrect.left;
calc_thumb_dimensions(trackrect.right - trackrect.left, &si, &thumbpos, &thumbsize);
if (thumbpos > 0) { partrect.left = trackrect.left; partrect.right = partrect.left + thumbpos;
DrawThemeBackground(theme, dc, SBP_UPPERTRACKHORZ, uppertrackstate, &partrect, NULL); }
if (thumbsize > 0) { partrect.left = trackrect.left + thumbpos; partrect.right = partrect.left + thumbsize;
DrawThemeBackground(theme, dc, SBP_THUMBBTNHORZ, thumbstate, &partrect, NULL);
if (SUCCEEDED(GetThemePartSize(theme, dc, SBP_GRIPPERHORZ, thumbstate, NULL, TS_DRAW, &grippersize))) { MARGINS margins;
if (SUCCEEDED(GetThemeMargins(theme, dc, SBP_THUMBBTNHORZ, thumbstate, TMT_CONTENTMARGINS, &partrect, &margins))) { if (grippersize.cx <= (thumbsize - margins.cxLeftWidth - margins.cxRightWidth)) DrawThemeBackground(theme, dc, SBP_GRIPPERHORZ, thumbstate, &partrect, NULL); } } }
if (thumbpos + thumbsize < trackrect.right - trackrect.left) { partrect.right = trackrect.right; partrect.left = trackrect.left + thumbsize + thumbpos;
DrawThemeBackground(theme, dc, SBP_LOWERTRACKHORZ, lowertrackstate, &partrect, NULL); } } }
EndPaint(hwnd, &ps);}
LRESULT CALLBACK THEMING_ScrollbarSubclassProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR dwRefData){ const WCHAR* themeClass = WC_SCROLLBARW; HTHEME theme; LRESULT result; POINT pt;
TRACE("(%p, 0x%x, %lu, %lu, %lu)\n", hwnd, msg, wParam, lParam, dwRefData);
switch (msg) { case WM_CREATE: result = THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); OpenThemeData(hwnd, themeClass); return result;
case WM_DESTROY: theme = GetWindowTheme(hwnd); CloseThemeData(theme); return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
case WM_THEMECHANGED: theme = GetWindowTheme(hwnd); CloseThemeData(theme); OpenThemeData(hwnd, themeClass); break;
case WM_SYSCOLORCHANGE: theme = GetWindowTheme(hwnd); if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); /* Do nothing. When themed, a WM_THEMECHANGED will be received, too, * which will do the repaint. */ break;
case WM_PAINT: theme = GetWindowTheme(hwnd); if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
paint_scrollbar(hwnd, theme); break;
case WM_MOUSEMOVE: case WM_MOUSELEAVE: theme = GetWindowTheme(hwnd); if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
pt.x = (short)LOWORD(lParam); pt.y = (short)HIWORD(lParam); scroll_event(hwnd, theme, msg, pt); break;
default: return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); }
return 0;}