summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/win/ScrollbarThemeWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/win/ScrollbarThemeWin.cpp')
-rw-r--r--Source/WebCore/platform/win/ScrollbarThemeWin.cpp403
1 files changed, 403 insertions, 0 deletions
diff --git a/Source/WebCore/platform/win/ScrollbarThemeWin.cpp b/Source/WebCore/platform/win/ScrollbarThemeWin.cpp
new file mode 100644
index 000000000..5a70de515
--- /dev/null
+++ b/Source/WebCore/platform/win/ScrollbarThemeWin.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ScrollbarThemeWin.h"
+
+#include "GDIUtilities.h"
+#include "GraphicsContext.h"
+#include "HWndDC.h"
+#include "LocalWindowsContext.h"
+#include "PlatformMouseEvent.h"
+#include "Scrollbar.h"
+#include "SoftLinking.h"
+#include "SystemInfo.h"
+#include <wtf/win/GDIObject.h>
+
+// Generic state constants
+#define TS_NORMAL 1
+#define TS_HOVER 2
+#define TS_ACTIVE 3
+#define TS_DISABLED 4
+
+#define SP_BUTTON 1
+#define SP_THUMBHOR 2
+#define SP_THUMBVERT 3
+#define SP_TRACKSTARTHOR 4
+#define SP_TRACKENDHOR 5
+#define SP_TRACKSTARTVERT 6
+#define SP_TRACKENDVERT 7
+#define SP_GRIPPERHOR 8
+#define SP_GRIPPERVERT 9
+
+#define TS_UP_BUTTON 0
+#define TS_DOWN_BUTTON 4
+#define TS_LEFT_BUTTON 8
+#define TS_RIGHT_BUTTON 12
+#define TS_UP_BUTTON_HOVER 17
+#define TS_DOWN_BUTTON_HOVER 18
+#define TS_LEFT_BUTTON_HOVER 19
+#define TS_RIGHT_BUTTON_HOVER 20
+
+using namespace std;
+
+namespace WebCore {
+
+static HANDLE scrollbarTheme;
+static bool runningVista;
+
+// FIXME: Refactor the soft-linking code so that it can be shared with RenderThemeWin
+SOFT_LINK_LIBRARY(uxtheme)
+SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList))
+SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme))
+SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, pClipRect))
+SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ())
+SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId))
+
+// Constants used to figure the drag rect outside which we should snap the
+// scrollbar thumb back to its origin. These calculations are based on
+// observing the behavior of the MSVC8 main window scrollbar + some
+// guessing/extrapolation.
+static const int kOffEndMultiplier = 3;
+static const int kOffSideMultiplier = 8;
+
+static void checkAndInitScrollbarTheme()
+{
+ if (uxthemeLibrary() && !scrollbarTheme && IsThemeActive())
+ scrollbarTheme = OpenThemeData(0, L"Scrollbar");
+}
+
+ScrollbarTheme& ScrollbarTheme::nativeTheme()
+{
+ static ScrollbarThemeWin winTheme;
+ return winTheme;
+}
+
+ScrollbarThemeWin::ScrollbarThemeWin()
+{
+ static bool initialized;
+ if (!initialized) {
+ initialized = true;
+ checkAndInitScrollbarTheme();
+ runningVista = (windowsVersion() >= WindowsVista);
+ }
+}
+
+ScrollbarThemeWin::~ScrollbarThemeWin()
+{
+}
+
+static int scrollbarThicknessInPixels()
+{
+ static int thickness = ::GetSystemMetrics(SM_CXVSCROLL);
+ return thickness;
+}
+
+int ScrollbarThemeWin::scrollbarThickness(ScrollbarControlSize)
+{
+ float inverseScaleFactor = 1.0f / deviceScaleFactorForWindow(0);
+ return clampTo<int>(inverseScaleFactor * scrollbarThicknessInPixels());
+}
+
+void ScrollbarThemeWin::themeChanged()
+{
+ if (!scrollbarTheme)
+ return;
+
+ CloseThemeData(scrollbarTheme);
+ scrollbarTheme = 0;
+}
+
+bool ScrollbarThemeWin::invalidateOnMouseEnterExit()
+{
+ return runningVista;
+}
+
+bool ScrollbarThemeWin::hasThumb(Scrollbar& scrollbar)
+{
+ return thumbLength(scrollbar) > 0;
+}
+
+IntRect ScrollbarThemeWin::backButtonRect(Scrollbar& scrollbar, ScrollbarPart part, bool)
+{
+ // Windows just has single arrows.
+ if (part == BackButtonEndPart)
+ return IntRect();
+
+ // Our desired rect is essentially 17x17.
+
+ // Our actual rect will shrink to half the available space when
+ // we have < 34 pixels left. This allows the scrollbar
+ // to scale down and function even at tiny sizes.
+ int thickness = scrollbarThickness();
+ if (scrollbar.orientation() == HorizontalScrollbar)
+ return IntRect(scrollbar.x(), scrollbar.y(),
+ scrollbar.width() < 2 * thickness ? scrollbar.width() / 2 : thickness, thickness);
+ return IntRect(scrollbar.x(), scrollbar.y(),
+ thickness, scrollbar.height() < 2 * thickness ? scrollbar.height() / 2 : thickness);
+}
+
+IntRect ScrollbarThemeWin::forwardButtonRect(Scrollbar& scrollbar, ScrollbarPart part, bool)
+{
+ // Windows just has single arrows.
+ if (part == ForwardButtonStartPart)
+ return IntRect();
+
+ // Our desired rect is essentially 17x17.
+
+ // Our actual rect will shrink to half the available space when
+ // we have < 34 pixels left. This allows the scrollbar
+ // to scale down and function even at tiny sizes.
+ int thickness = scrollbarThickness();
+ if (scrollbar.orientation() == HorizontalScrollbar) {
+ int w = scrollbar.width() < 2 * thickness ? scrollbar.width() / 2 : thickness;
+ return IntRect(scrollbar.x() + scrollbar.width() - w, scrollbar.y(), w, thickness);
+ }
+
+ int h = scrollbar.height() < 2 * thickness ? scrollbar.height() / 2 : thickness;
+ return IntRect(scrollbar.x(), scrollbar.y() + scrollbar.height() - h, thickness, h);
+}
+
+IntRect ScrollbarThemeWin::trackRect(Scrollbar& scrollbar, bool)
+{
+ int thickness = scrollbarThickness();
+ if (scrollbar.orientation() == HorizontalScrollbar) {
+ if (scrollbar.width() < 2 * thickness)
+ return IntRect();
+ return IntRect(scrollbar.x() + thickness, scrollbar.y(), scrollbar.width() - 2 * thickness, thickness);
+ }
+ if (scrollbar.height() < 2 * thickness)
+ return IntRect();
+ return IntRect(scrollbar.x(), scrollbar.y() + thickness, thickness, scrollbar.height() - 2 * thickness);
+}
+
+ScrollbarButtonPressAction ScrollbarThemeWin::handleMousePressEvent(Scrollbar&, const PlatformMouseEvent& event, ScrollbarPart pressedPart)
+{
+ if (event.button() == RightButton)
+ return ScrollbarButtonPressAction::None;
+
+ switch (pressedPart) {
+ case BackTrackPart:
+ case ForwardTrackPart:
+ if (event.shiftKey() && event.button() == LeftButton)
+ return ScrollbarButtonPressAction::CenterOnThumb;
+ break;
+ case ThumbPart:
+ return ScrollbarButtonPressAction::StartDrag;
+ default:
+ break;
+ }
+
+ return ScrollbarButtonPressAction::Scroll;
+}
+
+bool ScrollbarThemeWin::shouldSnapBackToDragOrigin(Scrollbar& scrollbar, const PlatformMouseEvent& evt)
+{
+ // Find the rect within which we shouldn't snap, by expanding the track rect
+ // in both dimensions.
+ IntRect rect = trackRect(scrollbar);
+ const bool horz = scrollbar.orientation() == HorizontalScrollbar;
+ const int thickness = scrollbarThickness(scrollbar.controlSize());
+ rect.inflateX((horz ? kOffEndMultiplier : kOffSideMultiplier) * thickness);
+ rect.inflateY((horz ? kOffSideMultiplier : kOffEndMultiplier) * thickness);
+
+ // Convert the event to local coordinates.
+ IntPoint mousePosition = scrollbar.convertFromContainingWindow(evt.position());
+ mousePosition.move(scrollbar.x(), scrollbar.y());
+
+ // We should snap iff the event is outside our calculated rect.
+ return !rect.contains(mousePosition);
+}
+
+void ScrollbarThemeWin::paintTrackBackground(GraphicsContext& context, Scrollbar& scrollbar, const IntRect& rect)
+{
+ // Just assume a forward track part. We only paint the track as a single piece when there is no thumb.
+ if (!hasThumb(scrollbar))
+ paintTrackPiece(context, scrollbar, rect, ForwardTrackPart);
+}
+
+void ScrollbarThemeWin::paintTrackPiece(GraphicsContext& context, Scrollbar& scrollbar, const IntRect& rect, ScrollbarPart partType)
+{
+ checkAndInitScrollbarTheme();
+
+ bool start = partType == BackTrackPart;
+ int part;
+ if (scrollbar.orientation() == HorizontalScrollbar)
+ part = start ? SP_TRACKSTARTHOR : SP_TRACKENDHOR;
+ else
+ part = start ? SP_TRACKSTARTVERT : SP_TRACKENDVERT;
+
+ int state;
+ if (!scrollbar.enabled())
+ state = TS_DISABLED;
+ else if ((scrollbar.hoveredPart() == BackTrackPart && start) ||
+ (scrollbar.hoveredPart() == ForwardTrackPart && !start))
+ state = (scrollbar.pressedPart() == scrollbar.hoveredPart() ? TS_ACTIVE : TS_HOVER);
+ else
+ state = TS_NORMAL;
+
+ bool alphaBlend = false;
+ if (scrollbarTheme)
+ alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, part, state);
+
+ LocalWindowsContext windowsContext(context, rect, alphaBlend);
+ RECT themeRect(rect);
+
+ if (scrollbarTheme)
+ DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), part, state, &themeRect, 0);
+ else {
+ DWORD color3DFace = ::GetSysColor(COLOR_3DFACE);
+ DWORD colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR);
+ DWORD colorWindow = ::GetSysColor(COLOR_WINDOW);
+ HDC hdc = windowsContext.hdc();
+ if ((color3DFace != colorScrollbar) && (colorWindow != colorScrollbar))
+ ::FillRect(hdc, &themeRect, HBRUSH(COLOR_SCROLLBAR+1));
+ else {
+ static WORD patternBits[8] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
+ auto patternBitmap = adoptGDIObject(::CreateBitmap(8, 8, 1, 1, patternBits));
+ auto brush = adoptGDIObject(::CreatePatternBrush(patternBitmap.get()));
+ SaveDC(hdc);
+ ::SetTextColor(hdc, ::GetSysColor(COLOR_3DHILIGHT));
+ ::SetBkColor(hdc, ::GetSysColor(COLOR_3DFACE));
+ ::SetBrushOrgEx(hdc, rect.x(), rect.y(), NULL);
+ ::SelectObject(hdc, brush.get());
+ ::FillRect(hdc, &themeRect, brush.get());
+ ::RestoreDC(hdc, -1);
+ }
+ }
+
+ if (!alphaBlend && !context.isInTransparencyLayer())
+ DIBPixelData::setRGBABitmapAlpha(windowsContext.hdc(), rect, 255);
+}
+
+void ScrollbarThemeWin::paintButton(GraphicsContext& context, Scrollbar& scrollbar, const IntRect& rect, ScrollbarPart part)
+{
+ checkAndInitScrollbarTheme();
+
+ bool start = (part == BackButtonStartPart);
+ int xpState = 0;
+ int classicState = 0;
+ if (scrollbar.orientation() == HorizontalScrollbar)
+ xpState = start ? TS_LEFT_BUTTON : TS_RIGHT_BUTTON;
+ else
+ xpState = start ? TS_UP_BUTTON : TS_DOWN_BUTTON;
+ classicState = xpState / 4;
+
+ if (!scrollbar.enabled()) {
+ xpState += TS_DISABLED;
+ classicState |= DFCS_INACTIVE;
+ } else if ((scrollbar.hoveredPart() == BackButtonStartPart && start) ||
+ (scrollbar.hoveredPart() == ForwardButtonEndPart && !start)) {
+ if (scrollbar.pressedPart() == scrollbar.hoveredPart()) {
+ xpState += TS_ACTIVE;
+ classicState |= DFCS_PUSHED;
+ classicState |= DFCS_FLAT;
+ } else
+ xpState += TS_HOVER;
+ } else {
+ if (scrollbar.hoveredPart() == NoPart || !runningVista)
+ xpState += TS_NORMAL;
+ else {
+ if (scrollbar.orientation() == HorizontalScrollbar)
+ xpState = start ? TS_LEFT_BUTTON_HOVER : TS_RIGHT_BUTTON_HOVER;
+ else
+ xpState = start ? TS_UP_BUTTON_HOVER : TS_DOWN_BUTTON_HOVER;
+ }
+ }
+
+ bool alphaBlend = false;
+ if (scrollbarTheme)
+ alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, SP_BUTTON, xpState);
+
+ LocalWindowsContext windowsContext(context, rect, alphaBlend);
+ RECT themeRect(rect);
+ if (scrollbarTheme)
+ DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), SP_BUTTON, xpState, &themeRect, 0);
+ else
+ ::DrawFrameControl(windowsContext.hdc(), &themeRect, DFC_SCROLL, classicState);
+
+ if (!alphaBlend && !context.isInTransparencyLayer())
+ DIBPixelData::setRGBABitmapAlpha(windowsContext.hdc(), rect, 255);
+}
+
+static IntRect gripperRect(int thickness, const IntRect& thumbRect)
+{
+ // Center in the thumb.
+ int gripperThickness = thickness / 2;
+ return IntRect(thumbRect.x() + (thumbRect.width() - gripperThickness) / 2,
+ thumbRect.y() + (thumbRect.height() - gripperThickness) / 2,
+ gripperThickness, gripperThickness);
+}
+
+static void paintGripper(Scrollbar& scrollbar, HDC hdc, const IntRect& rect)
+{
+ if (!scrollbarTheme)
+ return; // Classic look has no gripper.
+
+ int state;
+ if (!scrollbar.enabled())
+ state = TS_DISABLED;
+ else if (scrollbar.pressedPart() == ThumbPart)
+ state = TS_ACTIVE; // Thumb always stays active once pressed.
+ else if (scrollbar.hoveredPart() == ThumbPart)
+ state = TS_HOVER;
+ else
+ state = TS_NORMAL;
+
+ RECT themeRect(rect);
+ DrawThemeBackground(scrollbarTheme, hdc, scrollbar.orientation() == HorizontalScrollbar ? SP_GRIPPERHOR : SP_GRIPPERVERT, state, &themeRect, 0);
+}
+
+void ScrollbarThemeWin::paintThumb(GraphicsContext& context, Scrollbar& scrollbar, const IntRect& rect)
+{
+ checkAndInitScrollbarTheme();
+
+ int state;
+ if (!scrollbar.enabled())
+ state = TS_DISABLED;
+ else if (scrollbar.pressedPart() == ThumbPart)
+ state = TS_ACTIVE; // Thumb always stays active once pressed.
+ else if (scrollbar.hoveredPart() == ThumbPart)
+ state = TS_HOVER;
+ else
+ state = TS_NORMAL;
+
+ bool alphaBlend = false;
+ if (scrollbarTheme)
+ alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, scrollbar.orientation() == HorizontalScrollbar ? SP_THUMBHOR : SP_THUMBVERT, state);
+ LocalWindowsContext windowsContext(context, rect, alphaBlend);
+ RECT themeRect(rect);
+ if (scrollbarTheme) {
+ DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), scrollbar.orientation() == HorizontalScrollbar ? SP_THUMBHOR : SP_THUMBVERT, state, &themeRect, 0);
+ paintGripper(scrollbar, windowsContext.hdc(), gripperRect(scrollbarThickness(), rect));
+ } else
+ ::DrawEdge(windowsContext.hdc(), &themeRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
+
+ if (!alphaBlend && !context.isInTransparencyLayer())
+ DIBPixelData::setRGBABitmapAlpha(windowsContext.hdc(), rect, 255);
+}
+
+}
+