Bug 1685303: part 2) Add debug logging for `Selection::NotifySelectionListeners`...
[gecko.git] / widget / ScrollbarDrawingMac.cpp
blobdcc352ccb61b9cbae35612491f3ead8817a33c8d
1 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2; -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ScrollbarDrawingMac.h"
8 #include "mozilla/gfx/Helpers.h"
9 #include "mozilla/gfx/PathHelpers.h"
10 #include "mozilla/RelativeLuminanceUtils.h"
11 #include "nsLayoutUtils.h"
12 #include "nsIFrame.h"
13 #include "nsLookAndFeel.h"
14 #include "nsContainerFrame.h"
15 #include "nsNativeTheme.h"
17 namespace mozilla {
19 using namespace gfx;
21 namespace widget {
23 static nsIFrame* GetParentScrollbarFrame(nsIFrame* aFrame) {
24 // Walk our parents to find a scrollbar frame
25 nsIFrame* scrollbarFrame = aFrame;
26 do {
27 if (scrollbarFrame->IsScrollbarFrame()) {
28 break;
30 } while ((scrollbarFrame = scrollbarFrame->GetParent()));
32 // We return null if we can't find a parent scrollbar frame
33 return scrollbarFrame;
36 static bool IsParentScrollbarRolledOver(nsIFrame* aFrame) {
37 nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
38 return nsLookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0
39 ? nsNativeTheme::CheckBooleanAttr(scrollbarFrame, nsGkAtoms::hover)
40 : nsNativeTheme::GetContentState(scrollbarFrame,
41 StyleAppearance::None)
42 .HasState(NS_EVENT_STATE_HOVER);
45 LayoutDeviceIntSize ScrollbarDrawingMac::GetMinimumWidgetSize(
46 StyleAppearance aAppearance, nsIFrame* aFrame, float aDpiRatio) {
47 auto fn = [](StyleAppearance aAppearance, nsIFrame* aFrame) -> IntSize {
48 switch (aAppearance) {
49 case StyleAppearance::ScrollbarthumbHorizontal:
50 return IntSize{26, 0};
51 case StyleAppearance::ScrollbarthumbVertical:
52 return IntSize{0, 26};
53 case StyleAppearance::ScrollbarVertical:
54 case StyleAppearance::ScrollbarHorizontal:
55 case StyleAppearance::ScrollbartrackVertical:
56 case StyleAppearance::ScrollbartrackHorizontal: {
57 ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
58 bool isSmall =
59 style->StyleUIReset()->mScrollbarWidth == StyleScrollbarWidth::Thin;
60 if (nsLookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) !=
61 0) {
62 if (isSmall) {
63 return IntSize{14, 14};
65 return IntSize{16, 16};
67 if (isSmall) {
68 return IntSize{11, 11};
70 return IntSize{15, 15};
72 case StyleAppearance::MozMenulistArrowButton:
73 case StyleAppearance::ScrollbarNonDisappearing:
74 return IntSize{15, 15};
75 case StyleAppearance::ScrollbarbuttonUp:
76 case StyleAppearance::ScrollbarbuttonDown:
77 return IntSize{15, 16};
78 case StyleAppearance::ScrollbarbuttonLeft:
79 case StyleAppearance::ScrollbarbuttonRight:
80 return IntSize{16, 15};
81 default:
82 return IntSize{};
86 IntSize minSize = fn(aAppearance, aFrame);
87 if (aDpiRatio >= 2.0f) {
88 return LayoutDeviceIntSize{minSize.width * 2, minSize.height * 2};
90 return LayoutDeviceIntSize{minSize.width, minSize.height};
93 ScrollbarParams ScrollbarDrawingMac::ComputeScrollbarParams(
94 nsIFrame* aFrame, const ComputedStyle& aStyle, bool aIsHorizontal) {
95 ScrollbarParams params;
96 params.overlay =
97 nsLookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0;
98 params.rolledOver = IsParentScrollbarRolledOver(aFrame);
99 params.small =
100 aStyle.StyleUIReset()->mScrollbarWidth == StyleScrollbarWidth::Thin;
101 params.rtl = nsNativeTheme::IsFrameRTL(aFrame);
102 params.horizontal = aIsHorizontal;
103 params.onDarkBackground = nsNativeTheme::IsDarkBackground(aFrame);
104 // Don't use custom scrollbars for overlay scrollbars since they are
105 // generally good enough for use cases of custom scrollbars.
106 if (!params.overlay) {
107 const nsStyleUI* ui = aStyle.StyleUI();
108 if (ui->HasCustomScrollbars()) {
109 const auto& colors = ui->mScrollbarColor.AsColors();
110 params.custom = true;
111 params.trackColor = colors.track.CalcColor(aStyle);
112 params.faceColor = colors.thumb.CalcColor(aStyle);
116 return params;
119 void ScrollbarDrawingMac::DrawScrollbarThumb(DrawTarget& aDT, const Rect& aRect,
120 const ScrollbarParams& aParams) {
121 // Compute the thumb thickness. This varies based on aParams.small,
122 // aParams.overlay and aParams.rolledOver. non-overlay: 6 / 8, overlay
123 // non-hovered: 5 / 7, overlay hovered: 9 / 11
124 float thickness = aParams.small ? 6.0f : 8.0f;
125 if (aParams.overlay) {
126 thickness -= 1.0f;
127 if (aParams.rolledOver) {
128 thickness += 4.0f;
132 // Compute the thumb rect.
133 float outerSpacing = (aParams.overlay || aParams.small) ? 1.0f : 2.0f;
134 Rect thumbRect = aRect;
135 thumbRect.Deflate(1.0f);
136 if (aParams.horizontal) {
137 float bottomEdge = thumbRect.YMost() - outerSpacing;
138 thumbRect.SetBoxY(bottomEdge - thickness, bottomEdge);
139 } else {
140 if (aParams.rtl) {
141 float leftEdge = thumbRect.X() + outerSpacing;
142 thumbRect.SetBoxX(leftEdge, leftEdge + thickness);
143 } else {
144 float rightEdge = thumbRect.XMost() - outerSpacing;
145 thumbRect.SetBoxX(rightEdge - thickness, rightEdge);
149 // Compute the thumb fill color.
150 nscolor faceColor;
151 if (aParams.custom) {
152 faceColor = aParams.faceColor;
153 } else {
154 if (aParams.overlay) {
155 faceColor = aParams.onDarkBackground ? NS_RGBA(255, 255, 255, 128)
156 : NS_RGBA(0, 0, 0, 128);
157 } else {
158 faceColor = aParams.rolledOver ? NS_RGBA(125, 125, 125, 255)
159 : NS_RGBA(194, 194, 194, 255);
163 // Fill the thumb shape with the color.
164 float cornerRadius =
165 (aParams.horizontal ? thumbRect.Height() : thumbRect.Width()) / 2.0f;
166 aDT.FillRoundedRect(RoundedRect(thumbRect, RectCornerRadii(cornerRadius)),
167 ColorPattern(ToDeviceColor(faceColor)));
169 // Overlay scrollbars have an additional stroke around the fill.
170 if (aParams.overlay) {
171 float strokeOutset = aParams.onDarkBackground ? 0.3f : 0.5f;
172 float strokeWidth = aParams.onDarkBackground ? 0.6f : 0.8f;
173 nscolor strokeColor = aParams.onDarkBackground ? NS_RGBA(0, 0, 0, 48)
174 : NS_RGBA(255, 255, 255, 48);
175 Rect thumbStrokeRect = thumbRect;
176 thumbStrokeRect.Inflate(strokeOutset);
177 float strokeRadius = (aParams.horizontal ? thumbStrokeRect.Height()
178 : thumbStrokeRect.Width()) /
179 2.0f;
181 RefPtr<Path> path = MakePathForRoundedRect(aDT, thumbStrokeRect,
182 RectCornerRadii(strokeRadius));
183 aDT.Stroke(path, ColorPattern(ToDeviceColor(strokeColor)),
184 StrokeOptions(strokeWidth));
188 struct ScrollbarTrackDecorationColors {
189 nscolor mInnerColor = 0;
190 nscolor mShadowColor = 0;
191 nscolor mOuterColor = 0;
194 static ScrollbarTrackDecorationColors ComputeScrollbarTrackDecorationColors(
195 nscolor aTrackColor) {
196 ScrollbarTrackDecorationColors result;
197 float luminance = RelativeLuminanceUtils::Compute(aTrackColor);
198 if (luminance >= 0.5f) {
199 result.mInnerColor =
200 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.836f);
201 result.mShadowColor =
202 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.982f);
203 result.mOuterColor =
204 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.886f);
205 } else {
206 result.mInnerColor =
207 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.196f);
208 result.mShadowColor =
209 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.018f);
210 result.mOuterColor =
211 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.129f);
213 return result;
216 void ScrollbarDrawingMac::DrawScrollbarTrack(DrawTarget& aDT, const Rect& aRect,
217 const ScrollbarParams& aParams) {
218 if (aParams.overlay && !aParams.rolledOver) {
219 // Non-hovered overlay scrollbars don't have a track. Draw nothing.
220 return;
223 nscolor trackColor;
224 if (aParams.custom) {
225 trackColor = aParams.trackColor;
226 } else {
227 if (aParams.overlay) {
228 trackColor = aParams.onDarkBackground ? NS_RGBA(201, 201, 201, 38)
229 : NS_RGBA(250, 250, 250, 191);
230 } else {
231 trackColor = NS_RGBA(250, 250, 250, 255);
235 float thickness = aParams.horizontal ? aRect.height : aRect.width;
237 // The scrollbar track is drawn as multiple non-overlapping segments, which
238 // make up lines of different widths and with slightly different shading.
239 ScrollbarTrackDecorationColors colors =
240 ComputeScrollbarTrackDecorationColors(trackColor);
241 struct {
242 nscolor color;
243 float thickness;
244 } segments[] = {
245 {colors.mInnerColor, 1.0f},
246 {colors.mShadowColor, 1.0f},
247 {trackColor, thickness - 3.0f},
248 {colors.mOuterColor, 1.0f},
251 // Iterate over the segments "from inside to outside" and fill each segment.
252 // For horizontal scrollbars, iterate top to bottom.
253 // For vertical scrollbars, iterate left to right or right to left based on
254 // aParams.rtl.
255 float accumulatedThickness = 0.0f;
256 for (const auto& segment : segments) {
257 Rect segmentRect = aRect;
258 float startThickness = accumulatedThickness;
259 float endThickness = startThickness + segment.thickness;
260 if (aParams.horizontal) {
261 segmentRect.SetBoxY(aRect.Y() + startThickness, aRect.Y() + endThickness);
262 } else {
263 if (aParams.rtl) {
264 segmentRect.SetBoxX(aRect.XMost() - endThickness,
265 aRect.XMost() - startThickness);
266 } else {
267 segmentRect.SetBoxX(aRect.X() + startThickness,
268 aRect.X() + endThickness);
271 aDT.FillRect(segmentRect, ColorPattern(ToDeviceColor(segment.color)));
272 accumulatedThickness = endThickness;
276 void ScrollbarDrawingMac::DrawScrollCorner(DrawTarget& aDT, const Rect& aRect,
277 const ScrollbarParams& aParams) {
278 if (aParams.overlay && !aParams.rolledOver) {
279 // Non-hovered overlay scrollbars don't have a corner. Draw nothing.
280 return;
283 // Draw the following scroll corner.
285 // Output: Rectangles:
286 // +---+---+----------+---+ +---+---+----------+---+
287 // | I | S | T ... T | O | | I | S | T ... T | O |
288 // +---+ | | | +---+---+ | |
289 // | S S | T ... T | | | S S | T ... T | . |
290 // +-------+ | . | +-------+----------+ . |
291 // | T ... T | . | | T ... T | . |
292 // | . . | . | | . . | |
293 // | T ... T | | | T ... T | O |
294 // +------------------+ | +------------------+---+
295 // | O ... O | | O ... O |
296 // +----------------------+ +----------------------+
298 float width = aRect.width;
299 float height = aRect.height;
300 nscolor trackColor =
301 aParams.custom ? aParams.trackColor : NS_RGBA(250, 250, 250, 255);
302 ScrollbarTrackDecorationColors colors =
303 ComputeScrollbarTrackDecorationColors(trackColor);
304 struct {
305 nscolor color;
306 Rect relativeRect;
307 } pieces[] = {
308 {colors.mInnerColor, {0.0f, 0.0f, 1.0f, 1.0f}},
309 {colors.mShadowColor, {1.0f, 0.0f, 1.0f, 1.0f}},
310 {colors.mShadowColor, {0.0f, 1.0f, 2.0f, 1.0f}},
311 {trackColor, {2.0f, 0.0f, width - 3.0f, 2.0f}},
312 {trackColor, {0.0f, 2.0f, width - 1.0f, height - 3.0f}},
313 {colors.mOuterColor, {width - 1.0f, 0.0f, 1.0f, height - 1.0f}},
314 {colors.mOuterColor, {0.0f, height - 1.0f, width, 1.0f}},
317 for (const auto& piece : pieces) {
318 Rect pieceRect = piece.relativeRect + aRect.TopLeft();
319 if (aParams.rtl) {
320 pieceRect.x = aRect.XMost() - piece.relativeRect.XMost();
322 aDT.FillRect(pieceRect, ColorPattern(ToDeviceColor(piece.color)));
326 } // namespace widget
327 } // namespace mozilla