Bug 1732409 make getUserMedia() permission prompt requirement independent of "media...
[gecko.git] / widget / ScrollbarDrawingCocoa.cpp
blob211bab4d098d9930dedb07106bc4b101273b6e40
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 "ScrollbarDrawingCocoa.h"
9 #include "mozilla/gfx/Helpers.h"
10 #include "mozilla/RelativeLuminanceUtils.h"
11 #include "mozilla/StaticPrefs_widget.h"
12 #include "nsContainerFrame.h"
13 #include "nsIFrame.h"
14 #include "nsLayoutUtils.h"
15 #include "nsLookAndFeel.h"
16 #include "nsNativeTheme.h"
18 using namespace mozilla;
19 using namespace mozilla::gfx;
20 using namespace mozilla::widget;
22 LayoutDeviceIntSize ScrollbarDrawingCocoa::GetMinimumWidgetSize(
23 nsPresContext* aPresContext, StyleAppearance aAppearance,
24 nsIFrame* aFrame) {
25 MOZ_ASSERT(nsNativeTheme::IsWidgetScrollbarPart(aAppearance));
27 auto minSize = [&] {
28 switch (aAppearance) {
29 case StyleAppearance::ScrollbarthumbHorizontal:
30 return IntSize{26, 0};
31 case StyleAppearance::ScrollbarthumbVertical:
32 return IntSize{0, 26};
33 case StyleAppearance::ScrollbarVertical:
34 case StyleAppearance::ScrollbarHorizontal:
35 case StyleAppearance::ScrollbartrackVertical:
36 case StyleAppearance::ScrollbartrackHorizontal: {
37 ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
38 auto scrollbarWidth = style->StyleUIReset()->mScrollbarWidth;
39 auto size = GetScrollbarSize(
40 scrollbarWidth,
41 LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars));
42 return IntSize{size, size};
44 case StyleAppearance::ScrollbarbuttonUp:
45 case StyleAppearance::ScrollbarbuttonDown:
46 return IntSize{15, 16};
47 case StyleAppearance::ScrollbarbuttonLeft:
48 case StyleAppearance::ScrollbarbuttonRight:
49 return IntSize{16, 15};
50 default:
51 return IntSize{};
53 }();
55 auto dpi = GetDPIRatioForScrollbarPart(aPresContext).scale;
56 if (dpi >= 2.0f) {
57 return LayoutDeviceIntSize{minSize.width * 2, minSize.height * 2};
59 return LayoutDeviceIntSize{minSize.width, minSize.height};
62 /*static*/
63 CSSIntCoord ScrollbarDrawingCocoa::GetScrollbarSize(StyleScrollbarWidth aWidth,
64 bool aOverlay) {
65 bool isSmall = aWidth == StyleScrollbarWidth::Thin;
66 if (aOverlay) {
67 return isSmall ? 14 : 16;
69 return isSmall ? 11 : 15;
72 /*static*/
73 LayoutDeviceIntCoord ScrollbarDrawingCocoa::GetScrollbarSize(
74 StyleScrollbarWidth aWidth, bool aOverlay, DPIRatio aDpiRatio) {
75 CSSIntCoord size = GetScrollbarSize(aWidth, aOverlay);
76 if (aDpiRatio.scale >= 2.0f) {
77 return int32_t(size) * 2;
79 return int32_t(size);
82 auto ScrollbarDrawingCocoa::GetScrollbarSizes(nsPresContext* aPresContext,
83 StyleScrollbarWidth aWidth,
84 Overlay aOverlay)
85 -> ScrollbarSizes {
86 auto size = GetScrollbarSize(aWidth, aOverlay == Overlay::Yes,
87 GetDPIRatioForScrollbarPart(aPresContext));
88 return {size, size};
91 /*static*/
92 auto ScrollbarDrawingCocoa::GetThumbRect(const Rect& aRect,
93 const ScrollbarParams& aParams,
94 float aScale) -> ThumbRect {
95 // This matches the sizing checks in GetMinimumWidgetSize etc.
96 aScale = aScale >= 2.0f ? 2.0f : 1.0f;
98 // Compute the thumb thickness. This varies based on aParams.small,
99 // aParams.overlay and aParams.rolledOver. non-overlay: 6 / 8, overlay
100 // non-hovered: 5 / 7, overlay hovered: 9 / 11
101 float thickness = aParams.isSmall ? 6.0f : 8.0f;
102 if (aParams.isOverlay) {
103 thickness -= 1.0f;
104 if (aParams.isRolledOver) {
105 thickness += 4.0f;
108 thickness *= aScale;
110 // Compute the thumb rect.
111 const float outerSpacing =
112 ((aParams.isOverlay || aParams.isSmall) ? 1.0f : 2.0f) * aScale;
113 Rect thumbRect = aRect;
114 thumbRect.Deflate(1.0f * aScale);
115 if (aParams.isHorizontal) {
116 float bottomEdge = thumbRect.YMost() - outerSpacing;
117 thumbRect.SetBoxY(bottomEdge - thickness, bottomEdge);
118 } else {
119 if (aParams.isRtl) {
120 float leftEdge = thumbRect.X() + outerSpacing;
121 thumbRect.SetBoxX(leftEdge, leftEdge + thickness);
122 } else {
123 float rightEdge = thumbRect.XMost() - outerSpacing;
124 thumbRect.SetBoxX(rightEdge - thickness, rightEdge);
128 // Compute the thumb fill color.
129 nscolor faceColor;
130 if (aParams.isCustom) {
131 faceColor = aParams.faceColor;
132 } else {
133 if (aParams.isOverlay) {
134 faceColor = aParams.isOnDarkBackground ? NS_RGBA(255, 255, 255, 128)
135 : NS_RGBA(0, 0, 0, 128);
136 } else if (aParams.isOnDarkBackground) {
137 faceColor = aParams.isRolledOver ? NS_RGBA(158, 158, 158, 255)
138 : NS_RGBA(117, 117, 117, 255);
139 } else {
140 faceColor = aParams.isRolledOver ? NS_RGBA(125, 125, 125, 255)
141 : NS_RGBA(194, 194, 194, 255);
145 nscolor strokeColor = 0;
146 float strokeOutset = 0.0f;
147 float strokeWidth = 0.0f;
149 // Overlay scrollbars have an additional stroke around the fill.
150 if (aParams.isOverlay) {
151 strokeOutset = (aParams.isOnDarkBackground ? 0.3f : 0.5f) * aScale;
152 strokeWidth = (aParams.isOnDarkBackground ? 0.6f : 0.8f) * aScale;
154 strokeColor = aParams.isOnDarkBackground ? NS_RGBA(0, 0, 0, 48)
155 : NS_RGBA(255, 255, 255, 48);
158 return {thumbRect, faceColor, strokeColor, strokeWidth, strokeOutset};
161 struct ScrollbarTrackDecorationColors {
162 nscolor mInnerColor = 0;
163 nscolor mShadowColor = 0;
164 nscolor mOuterColor = 0;
167 static ScrollbarTrackDecorationColors ComputeScrollbarTrackDecorationColors(
168 nscolor aTrackColor) {
169 ScrollbarTrackDecorationColors result;
170 float luminance = RelativeLuminanceUtils::Compute(aTrackColor);
171 if (luminance >= 0.5f) {
172 result.mInnerColor =
173 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.836f);
174 result.mShadowColor =
175 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.982f);
176 result.mOuterColor =
177 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.886f);
178 } else {
179 result.mInnerColor =
180 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.196f);
181 result.mShadowColor =
182 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.018f);
183 result.mOuterColor =
184 RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.129f);
186 return result;
189 /*static*/
190 bool ScrollbarDrawingCocoa::GetScrollbarTrackRects(
191 const Rect& aRect, const ScrollbarParams& aParams, float aScale,
192 ScrollbarTrackRects& aRects) {
193 if (aParams.isOverlay && !aParams.isRolledOver) {
194 // Non-hovered overlay scrollbars don't have a track. Draw nothing.
195 return false;
198 // This matches the sizing checks in GetMinimumWidgetSize etc.
199 aScale = aScale >= 2.0f ? 2.0f : 1.0f;
201 nscolor trackColor;
202 if (aParams.isCustom) {
203 trackColor = aParams.trackColor;
204 } else {
205 if (aParams.isOverlay) {
206 trackColor = aParams.isOnDarkBackground ? NS_RGBA(201, 201, 201, 38)
207 : NS_RGBA(250, 250, 250, 191);
208 } else {
209 trackColor = aParams.isOnDarkBackground ? NS_RGBA(46, 46, 46, 255)
210 : NS_RGBA(250, 250, 250, 255);
214 float thickness = aParams.isHorizontal ? aRect.height : aRect.width;
216 // The scrollbar track is drawn as multiple non-overlapping segments, which
217 // make up lines of different widths and with slightly different shading.
218 ScrollbarTrackDecorationColors colors =
219 ComputeScrollbarTrackDecorationColors(trackColor);
220 struct {
221 nscolor color;
222 float thickness;
223 } segments[] = {
224 {colors.mInnerColor, 1.0f * aScale},
225 {colors.mShadowColor, 1.0f * aScale},
226 {trackColor, thickness - 3.0f * aScale},
227 {colors.mOuterColor, 1.0f * aScale},
230 // Iterate over the segments "from inside to outside" and fill each segment.
231 // For horizontal scrollbars, iterate top to bottom.
232 // For vertical scrollbars, iterate left to right or right to left based on
233 // aParams.isRtl.
234 auto current = aRects.begin();
235 float accumulatedThickness = 0.0f;
236 for (const auto& segment : segments) {
237 Rect segmentRect = aRect;
238 float startThickness = accumulatedThickness;
239 float endThickness = startThickness + segment.thickness;
240 if (aParams.isHorizontal) {
241 segmentRect.SetBoxY(aRect.Y() + startThickness, aRect.Y() + endThickness);
242 } else {
243 if (aParams.isRtl) {
244 segmentRect.SetBoxX(aRect.XMost() - endThickness,
245 aRect.XMost() - startThickness);
246 } else {
247 segmentRect.SetBoxX(aRect.X() + startThickness,
248 aRect.X() + endThickness);
251 accumulatedThickness = endThickness;
252 *current++ = {segmentRect, segment.color};
255 return true;
258 /*static*/
259 bool ScrollbarDrawingCocoa::GetScrollCornerRects(const Rect& aRect,
260 const ScrollbarParams& aParams,
261 float aScale,
262 ScrollCornerRects& aRects) {
263 if (aParams.isOverlay && !aParams.isRolledOver) {
264 // Non-hovered overlay scrollbars don't have a corner. Draw nothing.
265 return false;
268 // This matches the sizing checks in GetMinimumWidgetSize etc.
269 aScale = aScale >= 2.0f ? 2.0f : 1.0f;
271 // Draw the following scroll corner.
273 // Output: Rectangles:
274 // +---+---+----------+---+ +---+---+----------+---+
275 // | I | S | T ... T | O | | I | S | T ... T | O |
276 // +---+ | | | +---+---+ | |
277 // | S S | T ... T | | | S S | T ... T | . |
278 // +-------+ | . | +-------+----------+ . |
279 // | T ... T | . | | T ... T | . |
280 // | . . | . | | . . | |
281 // | T ... T | | | T ... T | O |
282 // +------------------+ | +------------------+---+
283 // | O ... O | | O ... O |
284 // +----------------------+ +----------------------+
286 float width = aRect.width;
287 float height = aRect.height;
288 nscolor trackColor;
289 if (aParams.isCustom) {
290 trackColor = aParams.trackColor;
291 } else {
292 trackColor = aParams.isOnDarkBackground ? NS_RGBA(46, 46, 46, 255)
293 : NS_RGBA(250, 250, 250, 255);
295 ScrollbarTrackDecorationColors colors =
296 ComputeScrollbarTrackDecorationColors(trackColor);
297 struct {
298 nscolor color;
299 Rect relativeRect;
300 } pieces[] = {
301 {colors.mInnerColor, {0.0f, 0.0f, 1.0f * aScale, 1.0f * aScale}},
302 {colors.mShadowColor,
303 {1.0f * aScale, 0.0f, 1.0f * aScale, 1.0f * aScale}},
304 {colors.mShadowColor,
305 {0.0f, 1.0f * aScale, 2.0f * aScale, 1.0f * aScale}},
306 {trackColor, {2.0f * aScale, 0.0f, width - 3.0f * aScale, 2.0f * aScale}},
307 {trackColor,
308 {0.0f, 2.0f * aScale, width - 1.0f * aScale, height - 3.0f * aScale}},
309 {colors.mOuterColor,
310 {width - 1.0f * aScale, 0.0f, 1.0f * aScale, height - 1.0f * aScale}},
311 {colors.mOuterColor,
312 {0.0f, height - 1.0f * aScale, width, 1.0f * aScale}},
315 auto current = aRects.begin();
316 for (const auto& piece : pieces) {
317 Rect pieceRect = piece.relativeRect + aRect.TopLeft();
318 if (aParams.isRtl) {
319 pieceRect.x = aRect.XMost() - piece.relativeRect.XMost();
321 *current++ = {pieceRect, piece.color};
323 return true;
326 template <typename PaintBackendData>
327 void ScrollbarDrawingCocoa::DoPaintScrollbarThumb(
328 PaintBackendData& aPaintData, const LayoutDeviceRect& aRect,
329 bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
330 const EventStates& aElementState, const EventStates& aDocumentState,
331 const DPIRatio& aDpiRatio) {
332 ScrollbarParams params = ComputeScrollbarParams(aFrame, aStyle, aHorizontal);
333 auto thumb = GetThumbRect(aRect.ToUnknownRect(), params, aDpiRatio.scale);
334 auto thumbRect = LayoutDeviceRect::FromUnknownRect(thumb.mRect);
335 LayoutDeviceCoord radius =
336 (params.isHorizontal ? thumbRect.Height() : thumbRect.Width()) / 2.0f;
337 ThemeDrawing::PaintRoundedRectWithRadius(
338 aPaintData, thumbRect, thumbRect, sRGBColor::FromABGR(thumb.mFillColor),
339 sRGBColor::White(0.0f), 0.0f, radius / aDpiRatio, aDpiRatio);
340 if (!thumb.mStrokeColor) {
341 return;
344 // Paint the stroke if needed.
345 thumbRect.Inflate(thumb.mStrokeOutset + thumb.mStrokeWidth);
346 radius =
347 (params.isHorizontal ? thumbRect.Height() : thumbRect.Width()) / 2.0f;
348 ThemeDrawing::PaintRoundedRectWithRadius(
349 aPaintData, thumbRect, sRGBColor::White(0.0f),
350 sRGBColor::FromABGR(thumb.mStrokeColor), thumb.mStrokeWidth,
351 radius / aDpiRatio, aDpiRatio);
354 bool ScrollbarDrawingCocoa::PaintScrollbarThumb(
355 DrawTarget& aDt, const LayoutDeviceRect& aRect, bool aHorizontal,
356 nsIFrame* aFrame, const ComputedStyle& aStyle,
357 const EventStates& aElementState, const EventStates& aDocumentState,
358 const Colors&, const DPIRatio& aDpiRatio) {
359 // TODO: Maybe respect the UseSystemColors setting?
360 DoPaintScrollbarThumb(aDt, aRect, aHorizontal, aFrame, aStyle, aElementState,
361 aDocumentState, aDpiRatio);
362 return true;
365 bool ScrollbarDrawingCocoa::PaintScrollbarThumb(
366 WebRenderBackendData& aWrData, const LayoutDeviceRect& aRect,
367 bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
368 const EventStates& aElementState, const EventStates& aDocumentState,
369 const Colors&, const DPIRatio& aDpiRatio) {
370 // TODO: Maybe respect the UseSystemColors setting?
371 DoPaintScrollbarThumb(aWrData, aRect, aHorizontal, aFrame, aStyle,
372 aElementState, aDocumentState, aDpiRatio);
373 return true;
376 template <typename PaintBackendData>
377 void ScrollbarDrawingCocoa::DoPaintScrollbarTrack(
378 PaintBackendData& aPaintData, const LayoutDeviceRect& aRect,
379 bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
380 const EventStates& aDocumentState, const DPIRatio& aDpiRatio) {
381 ScrollbarParams params = ComputeScrollbarParams(aFrame, aStyle, aHorizontal);
382 ScrollbarTrackRects rects;
383 if (GetScrollbarTrackRects(aRect.ToUnknownRect(), params, aDpiRatio.scale,
384 rects)) {
385 for (const auto& rect : rects) {
386 ThemeDrawing::FillRect(aPaintData,
387 LayoutDeviceRect::FromUnknownRect(rect.mRect),
388 sRGBColor::FromABGR(rect.mColor));
393 bool ScrollbarDrawingCocoa::PaintScrollbarTrack(
394 DrawTarget& aDt, const LayoutDeviceRect& aRect, bool aHorizontal,
395 nsIFrame* aFrame, const ComputedStyle& aStyle,
396 const EventStates& aDocumentState, const Colors&,
397 const DPIRatio& aDpiRatio) {
398 // TODO: Maybe respect the UseSystemColors setting?
399 DoPaintScrollbarTrack(aDt, aRect, aHorizontal, aFrame, aStyle, aDocumentState,
400 aDpiRatio);
401 return true;
404 bool ScrollbarDrawingCocoa::PaintScrollbarTrack(
405 WebRenderBackendData& aWrData, const LayoutDeviceRect& aRect,
406 bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
407 const EventStates& aDocumentState, const Colors&,
408 const DPIRatio& aDpiRatio) {
409 // TODO: Maybe respect the UseSystemColors setting?
410 DoPaintScrollbarTrack(aWrData, aRect, aHorizontal, aFrame, aStyle,
411 aDocumentState, aDpiRatio);
412 return true;
415 template <typename PaintBackendData>
416 void ScrollbarDrawingCocoa::DoPaintScrollCorner(
417 PaintBackendData& aPaintData, const LayoutDeviceRect& aRect,
418 nsIFrame* aFrame, const ComputedStyle& aStyle,
419 const EventStates& aDocumentState, const DPIRatio& aDpiRatio) {
420 ScrollbarParams params = ComputeScrollbarParams(aFrame, aStyle, false);
421 ScrollCornerRects rects;
422 if (GetScrollCornerRects(aRect.ToUnknownRect(), params, aDpiRatio.scale,
423 rects)) {
424 for (const auto& rect : rects) {
425 ThemeDrawing::FillRect(aPaintData,
426 LayoutDeviceRect::FromUnknownRect(rect.mRect),
427 sRGBColor::FromABGR(rect.mColor));
432 bool ScrollbarDrawingCocoa::PaintScrollCorner(
433 DrawTarget& aDt, const LayoutDeviceRect& aRect, nsIFrame* aFrame,
434 const ComputedStyle& aStyle, const EventStates& aDocumentState,
435 const Colors&, const DPIRatio& aDpiRatio) {
436 // TODO: Maybe respect the UseSystemColors setting?
437 DoPaintScrollCorner(aDt, aRect, aFrame, aStyle, aDocumentState, aDpiRatio);
438 return true;
441 bool ScrollbarDrawingCocoa::PaintScrollCorner(WebRenderBackendData& aWrData,
442 const LayoutDeviceRect& aRect,
443 nsIFrame* aFrame,
444 const ComputedStyle& aStyle,
445 const EventStates& aDocumentState,
446 const Colors&,
447 const DPIRatio& aDpiRatio) {
448 // TODO: Maybe respect the UseSystemColors setting?
449 DoPaintScrollCorner(aWrData, aRect, aFrame, aStyle, aDocumentState,
450 aDpiRatio);
451 return true;
454 void ScrollbarDrawingCocoa::RecomputeScrollbarParams() {
455 uint32_t defaultSize = 17;
456 uint32_t overrideSize =
457 StaticPrefs::widget_non_native_theme_scrollbar_size_override();
458 if (overrideSize > 0) {
459 defaultSize = overrideSize;
461 mHorizontalScrollbarHeight = mVerticalScrollbarWidth = defaultSize;