1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "ThemeCocoa.h"
10 #include "ThemeDrawing.h"
12 #include "mozilla/MathAlgorithms.h"
13 #include "mozilla/ClearOnShutdown.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/HTMLMeterElement.h"
16 #include "mozilla/dom/HTMLProgressElement.h"
17 #include "mozilla/gfx/Rect.h"
18 #include "mozilla/gfx/Types.h"
19 #include "mozilla/gfx/Filters.h"
20 #include "mozilla/RelativeLuminanceUtils.h"
21 #include "mozilla/StaticPrefs_widget.h"
22 #include "mozilla/webrender/WebRenderAPI.h"
23 #include "nsCSSColorUtils.h"
24 #include "nsCSSRendering.h"
25 #include "nsScrollbarFrame.h"
26 #include "nsIScrollableFrame.h"
27 #include "nsIScrollbarMediator.h"
28 #include "nsDeviceContext.h"
29 #include "nsLayoutUtils.h"
30 #include "nsRangeFrame.h"
31 #include "PathHelpers.h"
32 #include "ScrollbarDrawingAndroid.h"
33 #include "ScrollbarDrawingCocoa.h"
34 #include "ScrollbarDrawingGTK.h"
35 #include "ScrollbarDrawingWin.h"
36 #include "ScrollbarDrawingWin11.h"
39 # include "mozilla/WindowsVersion.h"
42 using namespace mozilla
;
43 using namespace mozilla::dom
;
44 using namespace mozilla::gfx
;
45 using namespace mozilla::widget
;
49 static constexpr gfx::sRGBColor
sColorGrey10(
50 gfx::sRGBColor::UnusualFromARGB(0xffe9e9ed));
51 static constexpr gfx::sRGBColor
sColorGrey10Alpha50(
52 gfx::sRGBColor::UnusualFromARGB(0x7fe9e9ed));
53 static constexpr gfx::sRGBColor
sColorGrey20(
54 gfx::sRGBColor::UnusualFromARGB(0xffd0d0d7));
55 static constexpr gfx::sRGBColor
sColorGrey30(
56 gfx::sRGBColor::UnusualFromARGB(0xffb1b1b9));
57 static constexpr gfx::sRGBColor
sColorGrey40(
58 gfx::sRGBColor::UnusualFromARGB(0xff8f8f9d));
59 static constexpr gfx::sRGBColor
sColorGrey40Alpha50(
60 gfx::sRGBColor::UnusualFromARGB(0x7f8f8f9d));
61 static constexpr gfx::sRGBColor
sColorGrey50(
62 gfx::sRGBColor::UnusualFromARGB(0xff676774));
63 static constexpr gfx::sRGBColor
sColorGrey60(
64 gfx::sRGBColor::UnusualFromARGB(0xff484851));
66 static constexpr gfx::sRGBColor
sColorMeterGreen10(
67 gfx::sRGBColor::UnusualFromARGB(0xff00ab60));
68 static constexpr gfx::sRGBColor
sColorMeterGreen20(
69 gfx::sRGBColor::UnusualFromARGB(0xff056139));
70 static constexpr gfx::sRGBColor
sColorMeterYellow10(
71 gfx::sRGBColor::UnusualFromARGB(0xffffbd4f));
72 static constexpr gfx::sRGBColor
sColorMeterYellow20(
73 gfx::sRGBColor::UnusualFromARGB(0xffd2811e));
74 static constexpr gfx::sRGBColor
sColorMeterRed10(
75 gfx::sRGBColor::UnusualFromARGB(0xffe22850));
76 static constexpr gfx::sRGBColor
sColorMeterRed20(
77 gfx::sRGBColor::UnusualFromARGB(0xff810220));
79 static const CSSCoord kMinimumRangeThumbSize
= 20.0f
;
80 static const CSSCoord kMinimumDropdownArrowButtonWidth
= 18.0f
;
81 static const CSSCoord kMinimumSpinnerButtonWidth
= 18.0f
;
82 static const CSSCoord kMinimumSpinnerButtonHeight
= 9.0f
;
83 static const CSSCoord kButtonBorderWidth
= 1.0f
;
84 static const CSSCoord kMenulistBorderWidth
= 1.0f
;
85 static const CSSCoord kTextFieldBorderWidth
= 1.0f
;
86 static const CSSCoord kRangeHeight
= 6.0f
;
87 static const CSSCoord kProgressbarHeight
= 6.0f
;
88 static const CSSCoord kMeterHeight
= 12.0f
;
90 // nsCheckboxRadioFrame takes the bottom of the content box as the baseline.
91 // This border-width makes its baseline 2px under the bottom, which is nice.
92 static constexpr CSSCoord kCheckboxRadioBorderWidth
= 2.0f
;
94 static constexpr sRGBColor sTransparent
= sRGBColor::White(0.0);
96 // This pushes and pops a clip rect to the draw target.
98 // This is done to reduce fuzz in places where we may have antialiasing,
99 // because skia is not clip-invariant: given different clips, it does not
100 // guarantee the same result, even if the painted content doesn't intersect
103 // This is a bit sad, overall, but...
104 struct MOZ_RAII AutoClipRect
{
105 AutoClipRect(DrawTarget
& aDt
, const LayoutDeviceRect
& aRect
) : mDt(aDt
) {
106 mDt
.PushClipRect(aRect
.ToUnknownRect());
109 ~AutoClipRect() { mDt
.PopClip(); }
115 static StaticRefPtr
<Theme
> gNativeInstance
;
116 static StaticRefPtr
<Theme
> gNonNativeInstance
;
117 static StaticRefPtr
<Theme
> gRDMInstance
;
122 already_AddRefed
<Theme
> do_CreateNativeThemeDoNotUseDirectly() {
123 // Android doesn't have a native theme.
124 return do_AddRef(new Theme(Theme::ScrollbarStyle()));
128 already_AddRefed
<nsITheme
> do_GetBasicNativeThemeDoNotUseDirectly() {
129 if (MOZ_UNLIKELY(!gNonNativeInstance
)) {
130 UniquePtr
<ScrollbarDrawing
> scrollbarDrawing
= Theme::ScrollbarStyle();
131 #ifdef MOZ_WIDGET_COCOA
132 gNonNativeInstance
= new ThemeCocoa(std::move(scrollbarDrawing
));
134 gNonNativeInstance
= new Theme(std::move(scrollbarDrawing
));
136 ClearOnShutdown(&gNonNativeInstance
);
138 return do_AddRef(gNonNativeInstance
);
141 already_AddRefed
<nsITheme
> do_GetNativeThemeDoNotUseDirectly() {
142 if (MOZ_UNLIKELY(!gNativeInstance
)) {
143 gNativeInstance
= do_CreateNativeThemeDoNotUseDirectly();
144 ClearOnShutdown(&gNativeInstance
);
146 return do_AddRef(gNativeInstance
);
149 already_AddRefed
<nsITheme
> do_GetRDMThemeDoNotUseDirectly() {
150 if (MOZ_UNLIKELY(!gRDMInstance
)) {
151 gRDMInstance
= new Theme(MakeUnique
<ScrollbarDrawingAndroid
>());
152 ClearOnShutdown(&gRDMInstance
);
154 return do_AddRef(gRDMInstance
);
157 namespace mozilla::widget
{
159 NS_IMPL_ISUPPORTS_INHERITED(Theme
, nsNativeTheme
, nsITheme
)
161 static constexpr nsLiteralCString kPrefs
[] = {
162 "widget.non-native-theme.use-theme-accent"_ns
,
163 "widget.non-native-theme.win.scrollbar.use-system-size"_ns
,
164 "widget.non-native-theme.scrollbar.size.override"_ns
,
165 "widget.non-native-theme.scrollbar.style"_ns
,
169 for (const auto& pref
: kPrefs
) {
170 Preferences::RegisterCallback(PrefChangedCallback
, pref
);
172 LookAndFeelChanged();
175 void Theme::Shutdown() {
176 for (const auto& pref
: kPrefs
) {
177 Preferences::UnregisterCallback(PrefChangedCallback
, pref
);
182 void Theme::LookAndFeelChanged() {
183 ThemeColors::RecomputeAccentColors();
184 if (gNonNativeInstance
) {
185 gNonNativeInstance
->SetScrollbarDrawing(ScrollbarStyle());
187 if (gNativeInstance
) {
188 gNativeInstance
->SetScrollbarDrawing(ScrollbarStyle());
192 auto Theme::GetDPIRatio(nsPresContext
* aPc
, StyleAppearance aAppearance
)
194 // Widgets react to zoom, except scrollbars.
195 if (IsWidgetScrollbarPart(aAppearance
)) {
196 return GetScrollbarDrawing().GetDPIRatioForScrollbarPart(aPc
);
198 return DPIRatio(float(AppUnitsPerCSSPixel()) / aPc
->AppUnitsPerDevPixel());
201 auto Theme::GetDPIRatio(nsIFrame
* aFrame
, StyleAppearance aAppearance
)
203 return GetDPIRatio(aFrame
->PresContext(), aAppearance
);
206 // Checkbox and radio need to preserve aspect-ratio for compat. We also snap the
207 // size to exact device pixels to avoid snapping disorting the circles.
208 static LayoutDeviceRect
CheckBoxRadioRect(const LayoutDeviceRect
& aRect
) {
209 // Place a square rect in the center of aRect.
210 auto size
= std::trunc(std::min(aRect
.width
, aRect
.height
));
211 auto position
= aRect
.Center() - LayoutDevicePoint(size
* 0.5, size
* 0.5);
212 return LayoutDeviceRect(position
, LayoutDeviceSize(size
, size
));
215 std::tuple
<sRGBColor
, sRGBColor
, sRGBColor
> Theme::ComputeCheckboxColors(
216 const ElementState
& aState
, StyleAppearance aAppearance
,
217 const Colors
& aColors
) {
218 MOZ_ASSERT(aAppearance
== StyleAppearance::Checkbox
||
219 aAppearance
== StyleAppearance::Radio
);
221 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
222 bool isChecked
= aState
.HasState(ElementState::CHECKED
);
223 bool isIndeterminate
= aAppearance
== StyleAppearance::Checkbox
&&
224 aState
.HasState(ElementState::INDETERMINATE
);
226 if (isChecked
|| isIndeterminate
) {
228 auto bg
= ComputeBorderColor(aState
, aColors
, OutlineCoversBorder::No
);
229 auto fg
= aColors
.HighContrast()
230 ? aColors
.System(StyleSystemColor::Graytext
)
231 : sRGBColor::White(.8f
);
232 return std::make_tuple(bg
, bg
, fg
);
235 if (aColors
.HighContrast()) {
236 auto bg
= aColors
.System(StyleSystemColor::Selecteditem
);
237 auto fg
= aColors
.System(StyleSystemColor::Selecteditemtext
);
238 return std::make_tuple(bg
, bg
, fg
);
242 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
243 bool isHovered
= aState
.HasState(ElementState::HOVER
);
244 const auto& bg
= isActive
? aColors
.Accent().GetDarker()
245 : isHovered
? aColors
.Accent().GetDark()
246 : aColors
.Accent().Get();
247 const auto& fg
= aColors
.Accent().GetForeground();
248 return std::make_tuple(bg
, bg
, fg
);
252 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::No
);
253 // We don't paint a checkmark in this case so any color would do.
254 return std::make_tuple(bg
, border
, sTransparent
);
257 sRGBColor
Theme::ComputeBorderColor(const ElementState
& aState
,
258 const Colors
& aColors
,
259 OutlineCoversBorder aOutlineCoversBorder
) {
260 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
261 if (aColors
.HighContrast()) {
262 return aColors
.System(isDisabled
? StyleSystemColor::Graytext
263 : StyleSystemColor::Buttontext
);
266 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
267 bool isHovered
= aState
.HasState(ElementState::HOVER
);
268 bool isFocused
= aState
.HasState(ElementState::FOCUSRING
);
270 return sColorGrey40Alpha50
;
272 if (isFocused
&& aOutlineCoversBorder
== OutlineCoversBorder::Yes
) {
273 // If we draw the outline over the border, prevent issues where the border
274 // shows underneath if it snaps in the wrong direction by using a
275 // transparent border. An alternative to this is ensuring that we snap the
276 // offset in PaintRoundedFocusRect the same was a we snap border widths, so
277 // that negative offsets are guaranteed to cover the border.
278 // But this looks harder to mess up.
281 bool dark
= aColors
.IsDark();
283 return dark
? sColorGrey20
: sColorGrey60
;
286 return dark
? sColorGrey30
: sColorGrey50
;
291 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeButtonColors(
292 const ElementState
& aState
, const Colors
& aColors
, nsIFrame
* aFrame
) {
294 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
295 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
296 bool isHovered
= aState
.HasState(ElementState::HOVER
);
298 nscolor backgroundColor
= [&] {
300 return aColors
.SystemNs(StyleSystemColor::MozButtondisabledface
);
303 return aColors
.SystemNs(StyleSystemColor::MozButtonactiveface
);
306 return aColors
.SystemNs(StyleSystemColor::MozButtonhoverface
);
308 return aColors
.SystemNs(StyleSystemColor::Buttonface
);
311 if (aState
.HasState(ElementState::AUTOFILL
)) {
312 backgroundColor
= NS_ComposeColors(
314 aColors
.SystemNs(StyleSystemColor::MozAutofillBackground
));
317 const sRGBColor borderColor
=
318 ComputeBorderColor(aState
, aColors
, OutlineCoversBorder::Yes
);
319 return std::make_pair(sRGBColor::FromABGR(backgroundColor
), borderColor
);
322 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeTextfieldColors(
323 const ElementState
& aState
, const Colors
& aColors
,
324 OutlineCoversBorder aOutlineCoversBorder
) {
325 nscolor backgroundColor
= [&] {
326 if (aState
.HasState(ElementState::DISABLED
)) {
327 return aColors
.SystemNs(StyleSystemColor::MozDisabledfield
);
329 return aColors
.SystemNs(StyleSystemColor::Field
);
332 if (aState
.HasState(ElementState::AUTOFILL
)) {
333 backgroundColor
= NS_ComposeColors(
335 aColors
.SystemNs(StyleSystemColor::MozAutofillBackground
));
338 const sRGBColor borderColor
=
339 ComputeBorderColor(aState
, aColors
, aOutlineCoversBorder
);
340 return std::make_pair(sRGBColor::FromABGR(backgroundColor
), borderColor
);
343 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeProgressColors(
344 const ElementState
& aState
, const Colors
& aColors
) {
345 if (aColors
.HighContrast()) {
346 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
347 StyleSystemColor::Buttontext
);
351 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
352 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
353 bool isHovered
= aState
.HasState(ElementState::HOVER
);
356 return std::make_pair(sColorGrey40Alpha50
, sColorGrey40Alpha50
);
358 if (isActive
|| isHovered
) {
359 return std::make_pair(aColors
.Accent().GetDark(),
360 aColors
.Accent().GetDarker());
362 return std::make_pair(aColors
.Accent().Get(), aColors
.Accent().GetDark());
365 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeTrackColors(
366 const ElementState
& aState
, const Colors
& aColors
) {
367 if (aColors
.HighContrast()) {
368 return aColors
.SystemPair(StyleSystemColor::Window
,
369 StyleSystemColor::Buttontext
);
372 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
373 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
374 bool isHovered
= aState
.HasState(ElementState::HOVER
);
377 return std::make_pair(sColorGrey10Alpha50
, sColorGrey40Alpha50
);
379 if (isActive
|| isHovered
) {
380 return std::make_pair(sColorGrey20
, sColorGrey50
);
382 return std::make_pair(sColorGrey10
, sColorGrey40
);
385 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeThumbColors(
386 const ElementState
& aState
, const Colors
& aColors
) {
387 if (aColors
.HighContrast()) {
388 return aColors
.SystemPair(StyleSystemColor::Selecteditemtext
,
389 StyleSystemColor::Selecteditem
);
393 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
394 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
395 bool isHovered
= aState
.HasState(ElementState::HOVER
);
397 const sRGBColor
& backgroundColor
= [&] {
402 return aColors
.Accent().Get();
410 const sRGBColor borderColor
= sRGBColor::OpaqueWhite();
411 return std::make_pair(backgroundColor
, borderColor
);
414 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeProgressColors(
415 const Colors
& aColors
) {
416 if (aColors
.HighContrast()) {
417 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
418 StyleSystemColor::Buttontext
);
420 return std::make_pair(aColors
.Accent().Get(), aColors
.Accent().GetDark());
423 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeProgressTrackColors(
424 const Colors
& aColors
) {
425 if (aColors
.HighContrast()) {
426 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
427 StyleSystemColor::Buttontext
);
429 return std::make_pair(sColorGrey10
, sColorGrey40
);
432 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeMeterchunkColors(
433 const ElementState
& aMeterState
, const Colors
& aColors
) {
434 if (aColors
.HighContrast()) {
435 return ComputeProgressColors(aColors
);
437 sRGBColor borderColor
= sColorMeterGreen20
;
438 sRGBColor chunkColor
= sColorMeterGreen10
;
440 if (aMeterState
.HasState(ElementState::SUB_OPTIMUM
)) {
441 borderColor
= sColorMeterYellow20
;
442 chunkColor
= sColorMeterYellow10
;
443 } else if (aMeterState
.HasState(ElementState::SUB_SUB_OPTIMUM
)) {
444 borderColor
= sColorMeterRed20
;
445 chunkColor
= sColorMeterRed10
;
448 return std::make_pair(chunkColor
, borderColor
);
451 std::array
<sRGBColor
, 3> Theme::ComputeFocusRectColors(const Colors
& aColors
) {
452 if (aColors
.HighContrast()) {
453 return {aColors
.System(StyleSystemColor::Selecteditem
),
454 aColors
.System(StyleSystemColor::Buttontext
),
455 aColors
.System(StyleSystemColor::Window
)};
457 const auto& accent
= aColors
.Accent();
458 const sRGBColor middle
=
459 aColors
.IsDark() ? sRGBColor::Black(.3f
) : sRGBColor::White(.3f
);
460 return {accent
.Get(), middle
, accent
.GetLight()};
463 template <typename PaintBackendData
>
464 void Theme::PaintRoundedFocusRect(PaintBackendData
& aBackendData
,
465 const LayoutDeviceRect
& aRect
,
466 const Colors
& aColors
, DPIRatio aDpiRatio
,
467 CSSCoord aRadius
, CSSCoord aOffset
) {
468 // NOTE(emilio): If the widths or offsets here change, make sure to tweak
469 // the GetWidgetOverflow path for FocusOutline.
470 auto [innerColor
, middleColor
, outerColor
] = ComputeFocusRectColors(aColors
);
472 LayoutDeviceRect
focusRect(aRect
);
474 // The focus rect is painted outside of the border area (aRect), see:
476 // data:text/html,<div style="border: 1px solid; outline: 2px solid
479 // But some controls might provide a negative offset to cover the border, if
481 CSSCoord strokeWidth
= 2.0f
;
482 auto strokeWidthDevPx
=
483 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
484 CSSCoord strokeRadius
= aRadius
;
485 focusRect
.Inflate(aOffset
* aDpiRatio
+ strokeWidthDevPx
);
487 ThemeDrawing::PaintRoundedRectWithRadius(
488 aBackendData
, focusRect
, sTransparent
, innerColor
, strokeWidth
,
489 strokeRadius
, aDpiRatio
);
491 strokeWidth
= CSSCoord(1.0f
);
493 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
494 strokeRadius
+= strokeWidth
;
495 focusRect
.Inflate(strokeWidthDevPx
);
497 ThemeDrawing::PaintRoundedRectWithRadius(
498 aBackendData
, focusRect
, sTransparent
, middleColor
, strokeWidth
,
499 strokeRadius
, aDpiRatio
);
501 strokeWidth
= CSSCoord(2.0f
);
503 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
504 strokeRadius
+= strokeWidth
;
505 focusRect
.Inflate(strokeWidthDevPx
);
507 ThemeDrawing::PaintRoundedRectWithRadius(
508 aBackendData
, focusRect
, sTransparent
, outerColor
, strokeWidth
,
509 strokeRadius
, aDpiRatio
);
512 void Theme::PaintCheckboxControl(DrawTarget
& aDrawTarget
,
513 const LayoutDeviceRect
& aRect
,
514 const ElementState
& aState
,
515 const Colors
& aColors
, DPIRatio aDpiRatio
) {
516 auto [backgroundColor
, borderColor
, checkColor
] =
517 ComputeCheckboxColors(aState
, StyleAppearance::Checkbox
, aColors
);
519 const CSSCoord radius
= 2.0f
;
520 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
521 if (backgroundColor
== borderColor
) {
524 ThemeDrawing::PaintRoundedRectWithRadius(aDrawTarget
, aRect
,
525 backgroundColor
, borderColor
,
526 borderWidth
, radius
, aDpiRatio
);
529 if (aState
.HasState(ElementState::INDETERMINATE
)) {
530 PaintIndeterminateMark(aDrawTarget
, aRect
, checkColor
);
531 } else if (aState
.HasState(ElementState::CHECKED
)) {
532 PaintCheckMark(aDrawTarget
, aRect
, checkColor
);
535 if (aState
.HasState(ElementState::FOCUSRING
)) {
536 PaintRoundedFocusRect(aDrawTarget
, aRect
, aColors
, aDpiRatio
, 5.0f
, 1.0f
);
540 constexpr CSSCoord kCheckboxRadioContentBoxSize
= 10.0f
;
541 constexpr CSSCoord kCheckboxRadioBorderBoxSize
=
542 kCheckboxRadioContentBoxSize
+ kCheckboxRadioBorderWidth
* 2.0f
;
544 void Theme::PaintCheckMark(DrawTarget
& aDrawTarget
,
545 const LayoutDeviceRect
& aRect
,
546 const sRGBColor
& aColor
) {
547 // Points come from the coordinates on a 14X14 (kCheckboxRadioBorderBoxSize)
548 // unit box centered at 0,0
549 const float checkPolygonX
[] = {-4.5f
, -1.5f
, -0.5f
, 5.0f
, 4.75f
,
550 3.5f
, -0.5f
, -1.5f
, -3.5f
};
551 const float checkPolygonY
[] = {0.5f
, 4.0f
, 4.0f
, -2.5f
, -4.0f
,
552 -4.0f
, 1.0f
, 1.25f
, -1.0f
};
553 const int32_t checkNumPoints
= sizeof(checkPolygonX
) / sizeof(float);
555 ThemeDrawing::ScaleToFillRect(aRect
, kCheckboxRadioBorderBoxSize
);
556 auto center
= aRect
.Center().ToUnknownPoint();
558 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
559 Point p
= center
+ Point(checkPolygonX
[0] * scale
, checkPolygonY
[0] * scale
);
561 for (int32_t i
= 1; i
< checkNumPoints
; i
++) {
562 p
= center
+ Point(checkPolygonX
[i
] * scale
, checkPolygonY
[i
] * scale
);
565 RefPtr
<Path
> path
= builder
->Finish();
567 aDrawTarget
.Fill(path
, ColorPattern(ToDeviceColor(aColor
)));
570 void Theme::PaintIndeterminateMark(DrawTarget
& aDrawTarget
,
571 const LayoutDeviceRect
& aRect
,
572 const sRGBColor
& aColor
) {
573 const CSSCoord borderWidth
= 2.0f
;
575 ThemeDrawing::ScaleToFillRect(aRect
, kCheckboxRadioBorderBoxSize
);
577 Rect rect
= aRect
.ToUnknownRect();
578 rect
.y
+= (rect
.height
/ 2) - (borderWidth
* scale
/ 2);
579 rect
.height
= borderWidth
* scale
;
580 rect
.x
+= (borderWidth
* scale
) + (borderWidth
* scale
/ 8);
581 rect
.width
-= ((borderWidth
* scale
) + (borderWidth
* scale
/ 8)) * 2;
583 aDrawTarget
.FillRect(rect
, ColorPattern(ToDeviceColor(aColor
)));
586 template <typename PaintBackendData
>
587 void Theme::PaintStrokedCircle(PaintBackendData
& aPaintData
,
588 const LayoutDeviceRect
& aRect
,
589 const sRGBColor
& aBackgroundColor
,
590 const sRGBColor
& aBorderColor
,
591 const CSSCoord aBorderWidth
,
592 DPIRatio aDpiRatio
) {
593 auto radius
= LayoutDeviceCoord(aRect
.Size().width
) / aDpiRatio
;
594 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, aBackgroundColor
,
595 aBorderColor
, aBorderWidth
, radius
,
599 void Theme::PaintCircleShadow(WebRenderBackendData
& aWrData
,
600 const LayoutDeviceRect
& aBoxRect
,
601 const LayoutDeviceRect
& aClipRect
,
602 float aShadowAlpha
, const CSSPoint
& aShadowOffset
,
603 CSSCoord aShadowBlurStdDev
, DPIRatio aDpiRatio
) {
604 const bool kBackfaceIsVisible
= true;
605 const LayoutDeviceCoord stdDev
= aShadowBlurStdDev
* aDpiRatio
;
606 const LayoutDevicePoint shadowOffset
= aShadowOffset
* aDpiRatio
;
607 const IntSize inflation
=
608 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
609 LayoutDeviceRect shadowRect
= aBoxRect
;
610 shadowRect
.MoveBy(shadowOffset
);
611 shadowRect
.Inflate(inflation
.width
, inflation
.height
);
612 const auto boxRect
= wr::ToLayoutRect(aBoxRect
);
613 aWrData
.mBuilder
.PushBoxShadow(
614 wr::ToLayoutRect(shadowRect
), wr::ToLayoutRect(aClipRect
),
615 kBackfaceIsVisible
, boxRect
,
616 wr::ToLayoutVector2D(aShadowOffset
* aDpiRatio
),
617 wr::ToColorF(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)), stdDev
,
618 /* aSpread = */ 0.0f
,
619 wr::ToBorderRadius(gfx::RectCornerRadii(aBoxRect
.Size().width
)),
620 wr::BoxShadowClipMode::Outset
);
623 void Theme::PaintCircleShadow(DrawTarget
& aDrawTarget
,
624 const LayoutDeviceRect
& aBoxRect
,
625 const LayoutDeviceRect
& aClipRect
,
626 float aShadowAlpha
, const CSSPoint
& aShadowOffset
,
627 CSSCoord aShadowBlurStdDev
, DPIRatio aDpiRatio
) {
628 Float stdDev
= aShadowBlurStdDev
* aDpiRatio
;
629 Point offset
= (aShadowOffset
* aDpiRatio
).ToUnknownPoint();
631 RefPtr
<FilterNode
> blurFilter
=
632 aDrawTarget
.CreateFilter(FilterType::GAUSSIAN_BLUR
);
637 blurFilter
->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION
, stdDev
);
640 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
641 Rect inflatedRect
= aBoxRect
.ToUnknownRect();
642 inflatedRect
.Inflate(inflation
.width
, inflation
.height
);
643 Rect sourceRectInFilterSpace
=
644 inflatedRect
- aBoxRect
.TopLeft().ToUnknownPoint();
645 Point destinationPointOfSourceRect
= inflatedRect
.TopLeft() + offset
;
647 IntSize dtSize
= RoundedToInt(aBoxRect
.Size().ToUnknownSize());
648 RefPtr
<DrawTarget
> ellipseDT
= aDrawTarget
.CreateSimilarDrawTargetForFilter(
649 dtSize
, SurfaceFormat::A8
, blurFilter
, blurFilter
,
650 sourceRectInFilterSpace
, destinationPointOfSourceRect
);
655 AutoClipRect
clipRect(aDrawTarget
, aClipRect
);
657 RefPtr
<Path
> ellipse
= MakePathForEllipse(
658 *ellipseDT
, (aBoxRect
- aBoxRect
.TopLeft()).Center().ToUnknownPoint(),
659 aBoxRect
.Size().ToUnknownSize());
660 ellipseDT
->Fill(ellipse
,
661 ColorPattern(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)));
662 RefPtr
<SourceSurface
> ellipseSurface
= ellipseDT
->Snapshot();
664 blurFilter
->SetInput(IN_GAUSSIAN_BLUR_IN
, ellipseSurface
);
665 aDrawTarget
.DrawFilter(blurFilter
, sourceRectInFilterSpace
,
666 destinationPointOfSourceRect
);
669 template <typename PaintBackendData
>
670 void Theme::PaintRadioControl(PaintBackendData
& aPaintData
,
671 const LayoutDeviceRect
& aRect
,
672 const ElementState
& aState
, const Colors
& aColors
,
673 DPIRatio aDpiRatio
) {
674 auto [backgroundColor
, borderColor
, checkColor
] =
675 ComputeCheckboxColors(aState
, StyleAppearance::Radio
, aColors
);
677 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
678 if (backgroundColor
== borderColor
) {
681 PaintStrokedCircle(aPaintData
, aRect
, backgroundColor
, borderColor
,
682 borderWidth
, aDpiRatio
);
685 if (aState
.HasState(ElementState::CHECKED
)) {
686 LayoutDeviceRect
rect(aRect
);
687 auto width
= LayoutDeviceCoord(
688 ThemeDrawing::SnapBorderWidth(kCheckboxRadioBorderWidth
, aDpiRatio
));
691 PaintStrokedCircle(aPaintData
, rect
, backgroundColor
, checkColor
,
692 kCheckboxRadioBorderWidth
, aDpiRatio
);
695 if (aState
.HasState(ElementState::FOCUSRING
)) {
696 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
, 5.0f
, 1.0f
);
700 template <typename PaintBackendData
>
701 void Theme::PaintTextField(PaintBackendData
& aPaintData
,
702 const LayoutDeviceRect
& aRect
,
703 const ElementState
& aState
, const Colors
& aColors
,
704 DPIRatio aDpiRatio
) {
705 auto [backgroundColor
, borderColor
] =
706 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::Yes
);
708 const CSSCoord radius
= 2.0f
;
710 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
711 borderColor
, kTextFieldBorderWidth
,
714 if (aState
.HasState(ElementState::FOCUSRING
)) {
715 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
716 radius
+ kTextFieldBorderWidth
,
717 -kTextFieldBorderWidth
);
721 template <typename PaintBackendData
>
722 void Theme::PaintListbox(PaintBackendData
& aPaintData
,
723 const LayoutDeviceRect
& aRect
,
724 const ElementState
& aState
, const Colors
& aColors
,
725 DPIRatio aDpiRatio
) {
726 const CSSCoord radius
= 2.0f
;
727 auto [backgroundColor
, borderColor
] =
728 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::Yes
);
730 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
731 borderColor
, kMenulistBorderWidth
,
734 if (aState
.HasState(ElementState::FOCUSRING
)) {
735 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
736 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
740 template <typename PaintBackendData
>
741 void Theme::PaintMenulist(PaintBackendData
& aDrawTarget
,
742 const LayoutDeviceRect
& aRect
,
743 const ElementState
& aState
, const Colors
& aColors
,
744 DPIRatio aDpiRatio
) {
745 const CSSCoord radius
= 4.0f
;
746 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aColors
);
748 ThemeDrawing::PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
,
749 borderColor
, kMenulistBorderWidth
,
752 if (aState
.HasState(ElementState::FOCUSRING
)) {
753 PaintRoundedFocusRect(aDrawTarget
, aRect
, aColors
, aDpiRatio
,
754 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
758 enum class PhysicalArrowDirection
{
764 void Theme::PaintMenuArrow(StyleAppearance aAppearance
, nsIFrame
* aFrame
,
765 DrawTarget
& aDrawTarget
,
766 const LayoutDeviceRect
& aRect
) {
767 // not const: these may be negated in-place below
768 float polygonX
[] = {-4.0f
, -0.5f
, 0.5f
, 4.0f
, 4.0f
,
769 3.0f
, 0.0f
, 0.0f
, -3.0f
, -4.0f
};
770 float polygonY
[] = {-1, 3.0f
, 3.0f
, -1.0f
, -2.0f
,
771 -2.0f
, 1.5f
, 1.5f
, -2.0f
, -2.0f
};
773 const bool isMenuList
=
774 aAppearance
== StyleAppearance::MozMenulistArrowButton
;
775 const float kPolygonSize
= kMinimumDropdownArrowButtonWidth
;
777 const auto direction
= [&] {
778 const auto wm
= aFrame
->GetWritingMode();
780 return wm
.IsPhysicalRTL() ? PhysicalArrowDirection::Left
781 : PhysicalArrowDirection::Right
;
783 switch (wm
.GetBlockDir()) {
784 case WritingMode::BlockDir::eBlockLR
:
785 return PhysicalArrowDirection::Right
;
786 case WritingMode::BlockDir::eBlockRL
:
787 return PhysicalArrowDirection::Left
;
788 case WritingMode::BlockDir::eBlockTB
:
789 return PhysicalArrowDirection::Bottom
;
791 MOZ_ASSERT_UNREACHABLE("Unknown direction?");
792 return PhysicalArrowDirection::Bottom
;
795 auto const [xs
, ys
] = [&] {
796 using Pair
= std::pair
<const float*, const float*>;
798 case PhysicalArrowDirection::Left
:
799 // rotate 90°: [[0,1],[-1,0]]
800 for (float& f
: polygonY
) {
803 return Pair(polygonY
, polygonX
);
805 case PhysicalArrowDirection::Right
:
806 // rotate 270°: [[0,-1],[1,0]]
807 for (float& f
: polygonX
) {
810 return Pair(polygonY
, polygonX
);
812 case PhysicalArrowDirection::Bottom
:
813 // rotate 0°: [[1,0],[0,1]]
814 return Pair(polygonX
, polygonY
);
816 MOZ_ASSERT_UNREACHABLE("Unknown direction?");
817 return Pair(polygonX
, polygonY
);
820 const auto arrowColor
= sRGBColor::FromABGR(
821 nsLayoutUtils::GetColor(aFrame
, &nsStyleText::mWebkitTextFillColor
));
822 ThemeDrawing::PaintArrow(aDrawTarget
, aRect
, xs
, ys
, kPolygonSize
,
823 ArrayLength(polygonX
), arrowColor
);
826 void Theme::PaintSpinnerButton(nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
,
827 const LayoutDeviceRect
& aRect
,
828 const ElementState
& aState
,
829 StyleAppearance aAppearance
,
830 const Colors
& aColors
, DPIRatio aDpiRatio
) {
831 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aColors
);
833 aDrawTarget
.FillRect(aRect
.ToUnknownRect(),
834 ColorPattern(ToDeviceColor(backgroundColor
)));
836 const float kPolygonX
[] = {-3.5f
, -0.5f
, 0.5f
, 3.5f
, 3.5f
,
837 2.5f
, 0.0f
, 0.0f
, -2.5f
, -3.5f
};
838 float polygonY
[] = {-1.5f
, 1.5f
, 1.5f
, -1.5f
, -2.5f
,
839 -2.5f
, 0.0f
, 0.0f
, -2.5f
, -2.5f
};
841 const float kPolygonSize
= kMinimumSpinnerButtonHeight
;
842 if (aAppearance
== StyleAppearance::SpinnerUpbutton
) {
843 for (auto& coord
: polygonY
) {
848 ThemeDrawing::PaintArrow(aDrawTarget
, aRect
, kPolygonX
, polygonY
,
849 kPolygonSize
, ArrayLength(kPolygonX
), borderColor
);
852 template <typename PaintBackendData
>
853 void Theme::PaintRange(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
854 const LayoutDeviceRect
& aRect
,
855 const ElementState
& aState
, const Colors
& aColors
,
856 DPIRatio aDpiRatio
, bool aHorizontal
) {
857 nsRangeFrame
* rangeFrame
= do_QueryFrame(aFrame
);
862 auto tickMarks
= rangeFrame
->TickMarks();
863 double progress
= rangeFrame
->GetValueAsFractionOfRange();
865 LayoutDeviceRect
thumbRect(0, 0, kMinimumRangeThumbSize
* aDpiRatio
,
866 kMinimumRangeThumbSize
* aDpiRatio
);
867 LayoutDeviceRect
progressClipRect(aRect
);
868 LayoutDeviceRect
trackClipRect(aRect
);
869 const LayoutDeviceCoord verticalSize
= kRangeHeight
* aDpiRatio
;
870 const LayoutDeviceCoord
tickMarkWidth(
871 ThemeDrawing::SnapBorderWidth(1.0f
, aDpiRatio
));
872 const LayoutDeviceCoord
tickMarkHeight(
873 ThemeDrawing::SnapBorderWidth(5.0f
, aDpiRatio
));
874 LayoutDevicePoint tickMarkOrigin
, tickMarkDirection
;
875 LayoutDeviceSize tickMarkSize
;
877 rect
.height
= verticalSize
;
878 rect
.y
= aRect
.y
+ (aRect
.height
- rect
.height
) / 2;
879 tickMarkSize
= LayoutDeviceSize(tickMarkWidth
, tickMarkHeight
);
880 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) / 2;
882 if (IsFrameRTL(aFrame
)) {
884 LayoutDevicePoint(aRect
.XMost() - thumbRect
.width
/ 2, aRect
.YMost());
885 tickMarkDirection
= LayoutDevicePoint(-1.0f
, 0.0f
);
887 aRect
.x
+ (aRect
.width
- thumbRect
.width
) * (1.0 - progress
);
888 float midPoint
= thumbRect
.Center().X();
889 trackClipRect
.SetBoxX(aRect
.X(), midPoint
);
890 progressClipRect
.SetBoxX(midPoint
, aRect
.XMost());
893 LayoutDevicePoint(aRect
.x
+ thumbRect
.width
/ 2, aRect
.YMost());
894 tickMarkDirection
= LayoutDevicePoint(1.0, 0.0f
);
895 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) * progress
;
896 float midPoint
= thumbRect
.Center().X();
897 progressClipRect
.SetBoxX(aRect
.X(), midPoint
);
898 trackClipRect
.SetBoxX(midPoint
, aRect
.XMost());
901 rect
.width
= verticalSize
;
902 rect
.x
= aRect
.x
+ (aRect
.width
- rect
.width
) / 2;
903 tickMarkOrigin
= LayoutDevicePoint(aRect
.XMost() - tickMarkHeight
/ 4,
904 aRect
.YMost() - thumbRect
.width
/ 2);
905 tickMarkDirection
= LayoutDevicePoint(0.0f
, -1.0f
);
906 tickMarkSize
= LayoutDeviceSize(tickMarkHeight
, tickMarkWidth
);
907 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) / 2;
909 if (rangeFrame
->IsUpwards()) {
911 aRect
.y
+ (aRect
.height
- thumbRect
.height
) * (1.0 - progress
);
912 float midPoint
= thumbRect
.Center().Y();
913 trackClipRect
.SetBoxY(aRect
.Y(), midPoint
);
914 progressClipRect
.SetBoxY(midPoint
, aRect
.YMost());
916 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) * progress
;
917 float midPoint
= thumbRect
.Center().Y();
918 trackClipRect
.SetBoxY(midPoint
, aRect
.YMost());
919 progressClipRect
.SetBoxY(aRect
.Y(), midPoint
);
923 const CSSCoord borderWidth
= 1.0f
;
924 const CSSCoord radius
= 3.0f
;
926 auto [progressColor
, progressBorderColor
] =
927 ComputeRangeProgressColors(aState
, aColors
);
928 auto [trackColor
, trackBorderColor
] =
929 ComputeRangeTrackColors(aState
, aColors
);
930 auto tickMarkColor
= trackBorderColor
;
932 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, progressClipRect
,
933 progressColor
, progressBorderColor
,
934 borderWidth
, radius
, aDpiRatio
);
936 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, trackClipRect
,
937 trackColor
, trackBorderColor
,
938 borderWidth
, radius
, aDpiRatio
);
940 if (!aState
.HasState(ElementState::DISABLED
)) {
941 // Ensure the shadow doesn't expand outside of our overflow rect declared in
942 // GetWidgetOverflow().
943 auto overflowRect
= aRect
;
944 overflowRect
.Inflate(CSSCoord(6.0f
) * aDpiRatio
);
946 PaintCircleShadow(aPaintData
, thumbRect
, overflowRect
, 0.3f
,
947 CSSPoint(0.0f
, 2.0f
), 2.0f
, aDpiRatio
);
950 tickMarkDirection
.x
*= aRect
.width
- thumbRect
.width
;
951 tickMarkDirection
.y
*= aRect
.height
- thumbRect
.height
;
953 LayoutDevicePoint(tickMarkSize
.width
, tickMarkSize
.height
) / 2;
954 auto tickMarkRect
= LayoutDeviceRect(tickMarkOrigin
, tickMarkSize
);
955 for (auto tickMark
: tickMarks
) {
956 auto tickMarkOffset
=
958 float(rangeFrame
->GetDoubleAsFractionOfRange(tickMark
));
959 ThemeDrawing::FillRect(aPaintData
, tickMarkRect
+ tickMarkOffset
,
963 // Draw the thumb on top.
964 const CSSCoord thumbBorderWidth
= 2.0f
;
965 auto [thumbColor
, thumbBorderColor
] =
966 ComputeRangeThumbColors(aState
, aColors
);
968 PaintStrokedCircle(aPaintData
, thumbRect
, thumbColor
, thumbBorderColor
,
969 thumbBorderWidth
, aDpiRatio
);
971 if (aState
.HasState(ElementState::FOCUSRING
)) {
972 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
, radius
, 1.0f
);
976 template <typename PaintBackendData
>
977 void Theme::PaintProgress(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
978 const LayoutDeviceRect
& aRect
,
979 const ElementState
& aState
, const Colors
& aColors
,
980 DPIRatio aDpiRatio
, bool aIsMeter
) {
981 const CSSCoord borderWidth
= 1.0f
;
982 const CSSCoord radius
= aIsMeter
? 6.0f
: 3.0f
;
984 LayoutDeviceRect
rect(aRect
);
985 const LayoutDeviceCoord thickness
=
986 (aIsMeter
? kMeterHeight
: kProgressbarHeight
) * aDpiRatio
;
988 const bool isHorizontal
= !nsNativeTheme::IsVerticalProgress(aFrame
);
990 // Center it vertically.
991 rect
.y
+= (rect
.height
- thickness
) / 2;
992 rect
.height
= thickness
;
994 // Center it horizontally.
995 rect
.x
+= (rect
.width
- thickness
) / 2;
996 rect
.width
= thickness
;
1000 // Paint the track, unclipped.
1001 auto [backgroundColor
, borderColor
] = ComputeProgressTrackColors(aColors
);
1002 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, rect
,
1003 backgroundColor
, borderColor
,
1004 borderWidth
, radius
, aDpiRatio
);
1007 // Now paint the chunk, clipped as needed.
1008 LayoutDeviceRect clipRect
= rect
;
1009 if (aState
.HasState(ElementState::INDETERMINATE
)) {
1010 // For indeterminate progress, we paint an animated chunk of 1/3 of the
1013 // Animation speed and math borrowed from GTK.
1014 const LayoutDeviceCoord size
= isHorizontal
? rect
.width
: rect
.height
;
1015 const LayoutDeviceCoord barSize
= size
* 0.3333f
;
1016 const LayoutDeviceCoord travel
= 2.0f
* (size
- barSize
);
1018 // Period equals to travel / pixelsPerMillisecond where pixelsPerMillisecond
1019 // equals progressSize / 1000.0. This is equivalent to 1600.
1020 const unsigned kPeriod
= 1600;
1022 const int t
= PR_IntervalToMilliseconds(PR_IntervalNow()) % kPeriod
;
1023 const LayoutDeviceCoord dx
= travel
* float(t
) / float(kPeriod
);
1025 rect
.width
= barSize
;
1026 rect
.x
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
1028 rect
.height
= barSize
;
1029 rect
.y
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
1032 // Queue the next frame if needed.
1033 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 60)) {
1034 NS_WARNING("Couldn't refresh indeterminate <progress>");
1037 // This is the progress chunk, clip it to the right amount.
1038 double position
= [&] {
1040 auto* meter
= dom::HTMLMeterElement::FromNode(aFrame
->GetContent());
1044 return meter
->Position();
1046 auto* progress
= dom::HTMLProgressElement::FromNode(aFrame
->GetContent());
1050 return progress
->Position();
1053 double clipWidth
= rect
.width
* position
;
1054 clipRect
.width
= clipWidth
;
1055 if (IsFrameRTL(aFrame
)) {
1056 clipRect
.x
+= rect
.width
- clipWidth
;
1059 double clipHeight
= rect
.height
* position
;
1060 clipRect
.height
= clipHeight
;
1061 clipRect
.y
+= rect
.height
- clipHeight
;
1065 auto [backgroundColor
, borderColor
] =
1066 aIsMeter
? ComputeMeterchunkColors(aState
, aColors
)
1067 : ComputeProgressColors(aColors
);
1068 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, clipRect
,
1069 backgroundColor
, borderColor
,
1070 borderWidth
, radius
, aDpiRatio
);
1073 template <typename PaintBackendData
>
1074 void Theme::PaintButton(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
1075 const LayoutDeviceRect
& aRect
,
1076 const ElementState
& aState
, const Colors
& aColors
,
1077 DPIRatio aDpiRatio
) {
1078 const CSSCoord radius
= 4.0f
;
1079 auto [backgroundColor
, borderColor
] =
1080 ComputeButtonColors(aState
, aColors
, aFrame
);
1082 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
1083 borderColor
, kButtonBorderWidth
,
1086 if (aState
.HasState(ElementState::FOCUSRING
)) {
1087 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
1088 radius
+ kButtonBorderWidth
, -kButtonBorderWidth
);
1093 Theme::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
1094 StyleAppearance aAppearance
, const nsRect
& aRect
,
1095 const nsRect
& /* aDirtyRect */,
1096 DrawOverflow aDrawOverflow
) {
1097 if (!DoDrawWidgetBackground(*aContext
->GetDrawTarget(), aFrame
, aAppearance
,
1098 aRect
, aDrawOverflow
)) {
1099 return NS_ERROR_NOT_IMPLEMENTED
;
1104 bool Theme::CreateWebRenderCommandsForWidget(
1105 mozilla::wr::DisplayListBuilder
& aBuilder
,
1106 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
1107 const mozilla::layers::StackingContextHelper
& aSc
,
1108 mozilla::layers::RenderRootStateManager
* aManager
, nsIFrame
* aFrame
,
1109 StyleAppearance aAppearance
, const nsRect
& aRect
) {
1110 if (!StaticPrefs::widget_non_native_theme_webrender()) {
1113 WebRenderBackendData data
{aBuilder
, aResources
, aSc
, aManager
};
1114 return DoDrawWidgetBackground(data
, aFrame
, aAppearance
, aRect
,
1118 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1119 nscoord aTwipsPerPixel
, DrawTarget
& aDt
) {
1120 return LayoutDeviceRect::FromUnknownRect(
1121 NSRectToSnappedRect(aRect
, aTwipsPerPixel
, aDt
));
1124 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1125 nscoord aTwipsPerPixel
,
1126 WebRenderBackendData
& aDt
) {
1127 // TODO: Do we need to do any more snapping here?
1128 return LayoutDeviceRect::FromAppUnits(aRect
, aTwipsPerPixel
);
1131 static ScrollbarDrawing::ScrollbarKind
ComputeScrollbarKind(
1132 nsIFrame
* aFrame
, bool aIsHorizontal
) {
1133 if (aIsHorizontal
) {
1134 return ScrollbarDrawing::ScrollbarKind::Horizontal
;
1136 nsIFrame
* scrollbar
= ScrollbarDrawing::GetParentScrollbarFrame(aFrame
);
1137 if (NS_WARN_IF(!scrollbar
)) {
1138 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1140 MOZ_ASSERT(scrollbar
->IsScrollbarFrame());
1141 nsIScrollbarMediator
* sm
=
1142 static_cast<nsScrollbarFrame
*>(scrollbar
)->GetScrollbarMediator();
1143 if (NS_WARN_IF(!sm
)) {
1144 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1146 return sm
->IsScrollbarOnRight()
1147 ? ScrollbarDrawing::ScrollbarKind::VerticalRight
1148 : ScrollbarDrawing::ScrollbarKind::VerticalLeft
;
1151 static ScrollbarDrawing::ScrollbarKind
ComputeScrollbarKindForScrollCorner(
1153 nsIScrollableFrame
* sf
= do_QueryFrame(aFrame
->GetParent());
1155 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1157 return sf
->IsScrollbarOnRight()
1158 ? ScrollbarDrawing::ScrollbarKind::VerticalRight
1159 : ScrollbarDrawing::ScrollbarKind::VerticalLeft
;
1162 template <typename PaintBackendData
>
1163 bool Theme::DoDrawWidgetBackground(PaintBackendData
& aPaintData
,
1165 StyleAppearance aAppearance
,
1166 const nsRect
& aRect
,
1167 DrawOverflow aDrawOverflow
) {
1168 static_assert(std::is_same_v
<PaintBackendData
, DrawTarget
> ||
1169 std::is_same_v
<PaintBackendData
, WebRenderBackendData
>);
1171 const nsPresContext
* pc
= aFrame
->PresContext();
1172 const nscoord twipsPerPixel
= pc
->AppUnitsPerDevPixel();
1173 const auto devPxRect
= ToSnappedRect(aRect
, twipsPerPixel
, aPaintData
);
1175 const DocumentState docState
= pc
->Document()->GetDocumentState();
1176 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1177 // Paint the outline iff we're asked to draw overflow and we have
1178 // outline-style: auto.
1179 if (aDrawOverflow
== DrawOverflow::Yes
&&
1180 aFrame
->StyleOutline()->mOutlineStyle
.IsAuto()) {
1181 elementState
|= ElementState::FOCUSRING
;
1183 elementState
&= ~ElementState::FOCUSRING
;
1186 // Hack to avoid skia fuzziness: Add a dummy clip if the widget doesn't
1187 // overflow devPxRect.
1188 Maybe
<AutoClipRect
> maybeClipRect
;
1189 if constexpr (std::is_same_v
<PaintBackendData
, DrawTarget
>) {
1190 if (aAppearance
!= StyleAppearance::FocusOutline
&&
1191 aAppearance
!= StyleAppearance::Range
&&
1192 !elementState
.HasState(ElementState::FOCUSRING
)) {
1193 maybeClipRect
.emplace(aPaintData
, devPxRect
);
1197 const Colors
colors(aFrame
, aAppearance
);
1198 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1200 switch (aAppearance
) {
1201 case StyleAppearance::Radio
: {
1202 auto rect
= CheckBoxRadioRect(devPxRect
);
1203 PaintRadioControl(aPaintData
, rect
, elementState
, colors
, dpiRatio
);
1206 case StyleAppearance::Checkbox
: {
1207 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1208 // TODO: Need to figure out how to best draw this using WR.
1211 auto rect
= CheckBoxRadioRect(devPxRect
);
1212 PaintCheckboxControl(aPaintData
, rect
, elementState
, colors
, dpiRatio
);
1216 case StyleAppearance::Textarea
:
1217 case StyleAppearance::Textfield
:
1218 case StyleAppearance::NumberInput
:
1219 PaintTextField(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1221 case StyleAppearance::Listbox
:
1222 PaintListbox(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1224 case StyleAppearance::MenulistButton
:
1225 case StyleAppearance::Menulist
:
1226 PaintMenulist(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1228 case StyleAppearance::Menuarrow
:
1229 case StyleAppearance::MozMenulistArrowButton
:
1230 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1231 // TODO: Need to figure out how to best draw this using WR.
1234 PaintMenuArrow(aAppearance
, aFrame
, aPaintData
, devPxRect
);
1237 case StyleAppearance::Tooltip
: {
1238 const CSSCoord
strokeWidth(1.0f
);
1239 const CSSCoord
strokeRadius(2.0f
);
1240 ThemeDrawing::PaintRoundedRectWithRadius(
1241 aPaintData
, devPxRect
,
1242 colors
.System(StyleSystemColor::Infobackground
),
1243 colors
.System(StyleSystemColor::Infotext
), strokeWidth
, strokeRadius
,
1247 case StyleAppearance::Menuitem
: {
1248 ThemeDrawing::FillRect(aPaintData
, devPxRect
, [&] {
1249 if (CheckBooleanAttr(aFrame
, nsGkAtoms::menuactive
)) {
1250 if (elementState
.HasState(ElementState::DISABLED
)) {
1251 return colors
.System(StyleSystemColor::MozMenuhoverdisabled
);
1253 return colors
.System(StyleSystemColor::MozMenuhover
);
1255 return sTransparent
;
1259 case StyleAppearance::SpinnerUpbutton
:
1260 case StyleAppearance::SpinnerDownbutton
:
1261 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1262 // TODO: Need to figure out how to best draw this using WR.
1265 PaintSpinnerButton(aFrame
, aPaintData
, devPxRect
, elementState
,
1266 aAppearance
, colors
, dpiRatio
);
1269 case StyleAppearance::Range
:
1270 PaintRange(aFrame
, aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
,
1271 IsRangeHorizontal(aFrame
));
1273 case StyleAppearance::RangeThumb
:
1274 // Painted as part of StyleAppearance::Range.
1276 case StyleAppearance::ProgressBar
:
1277 PaintProgress(aFrame
, aPaintData
, devPxRect
, elementState
, colors
,
1279 /* aIsMeter = */ false);
1281 case StyleAppearance::Progresschunk
:
1282 /* Painted as part of the progress bar */
1284 case StyleAppearance::Meter
:
1285 PaintProgress(aFrame
, aPaintData
, devPxRect
, elementState
, colors
,
1287 /* aIsMeter = */ true);
1289 case StyleAppearance::Meterchunk
:
1290 /* Painted as part of the meter bar */
1292 case StyleAppearance::ScrollbarthumbHorizontal
:
1293 case StyleAppearance::ScrollbarthumbVertical
: {
1295 aAppearance
== StyleAppearance::ScrollbarthumbHorizontal
;
1296 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1297 return GetScrollbarDrawing().PaintScrollbarThumb(
1298 aPaintData
, devPxRect
, kind
, aFrame
,
1299 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1302 case StyleAppearance::ScrollbartrackHorizontal
:
1303 case StyleAppearance::ScrollbartrackVertical
: {
1305 aAppearance
== StyleAppearance::ScrollbartrackHorizontal
;
1306 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1307 return GetScrollbarDrawing().PaintScrollbarTrack(
1308 aPaintData
, devPxRect
, kind
, aFrame
,
1309 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
, colors
,
1312 case StyleAppearance::ScrollbarHorizontal
:
1313 case StyleAppearance::ScrollbarVertical
: {
1314 bool isHorizontal
= aAppearance
== StyleAppearance::ScrollbarHorizontal
;
1315 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1316 return GetScrollbarDrawing().PaintScrollbar(
1317 aPaintData
, devPxRect
, kind
, aFrame
,
1318 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1321 case StyleAppearance::Scrollcorner
: {
1322 auto kind
= ComputeScrollbarKindForScrollCorner(aFrame
);
1323 return GetScrollbarDrawing().PaintScrollCorner(
1324 aPaintData
, devPxRect
, kind
, aFrame
,
1325 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
, colors
,
1328 case StyleAppearance::ScrollbarbuttonUp
:
1329 case StyleAppearance::ScrollbarbuttonDown
:
1330 case StyleAppearance::ScrollbarbuttonLeft
:
1331 case StyleAppearance::ScrollbarbuttonRight
: {
1332 // For scrollbar-width:thin, we don't display the buttons.
1333 if (!ScrollbarDrawing::IsScrollbarWidthThin(aFrame
)) {
1334 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1335 // TODO: Need to figure out how to best draw this using WR.
1339 aAppearance
== StyleAppearance::ScrollbarbuttonLeft
||
1340 aAppearance
== StyleAppearance::ScrollbarbuttonRight
;
1341 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1342 GetScrollbarDrawing().PaintScrollbarButton(
1343 aPaintData
, aAppearance
, devPxRect
, kind
, aFrame
,
1344 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1350 case StyleAppearance::Button
:
1351 PaintButton(aFrame
, aPaintData
, devPxRect
, elementState
, colors
,
1354 case StyleAppearance::FocusOutline
:
1355 PaintAutoStyleOutline(aFrame
, aPaintData
, devPxRect
, colors
, dpiRatio
);
1358 // Various appearance values are used for XUL elements. Normally these
1359 // will not be available in content documents (and thus in the content
1360 // processes where the native basic theme can be used), but tests are
1361 // run with the remote XUL pref enabled and so we can get in here. So
1362 // we just return an error rather than assert.
1369 template <typename PaintBackendData
>
1370 void Theme::PaintAutoStyleOutline(nsIFrame
* aFrame
,
1371 PaintBackendData
& aPaintData
,
1372 const LayoutDeviceRect
& aRect
,
1373 const Colors
& aColors
, DPIRatio aDpiRatio
) {
1374 const auto& accentColor
= aColors
.Accent();
1375 const bool solid
= StaticPrefs::widget_non_native_theme_solid_outline_style();
1376 LayoutDeviceCoord
strokeWidth(ThemeDrawing::SnapBorderWidth(2.0f
, aDpiRatio
));
1378 LayoutDeviceRect
rect(aRect
);
1379 rect
.Inflate(strokeWidth
);
1381 const nscoord a2d
= aFrame
->PresContext()->AppUnitsPerDevPixel();
1382 nscoord cssOffset
= aFrame
->StyleOutline()->mOutlineOffset
.ToAppUnits();
1383 nscoord cssRadii
[8] = {0};
1384 if (!aFrame
->GetBorderRadii(cssRadii
)) {
1385 const auto twoPixels
= 2 * AppUnitsPerCSSPixel();
1386 const nscoord radius
=
1387 cssOffset
>= 0 ? twoPixels
: std::max(twoPixels
+ cssOffset
, 0);
1388 cssOffset
= -twoPixels
;
1389 for (auto& r
: cssRadii
) {
1394 auto offset
= LayoutDevicePixel::FromAppUnits(cssOffset
, a2d
);
1395 RectCornerRadii innerRadii
;
1396 nsCSSRendering::ComputePixelRadii(cssRadii
, a2d
, &innerRadii
);
1398 // NOTE(emilio): This doesn't use PaintRoundedRectWithRadius because we need
1399 // to support arbitrary radii.
1400 auto DrawRect
= [&](const sRGBColor
& aColor
) {
1401 RectCornerRadii outerRadii
;
1402 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1403 const Float widths
[4] = {strokeWidth
+ offset
, strokeWidth
+ offset
,
1404 strokeWidth
+ offset
, strokeWidth
+ offset
};
1405 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1406 const auto dest
= wr::ToLayoutRect(rect
);
1408 wr::ToBorderSide(ToDeviceColor(aColor
), StyleBorderStyle::Solid
);
1409 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
1410 const bool kBackfaceIsVisible
= true;
1411 const auto wrWidths
= wr::ToBorderWidths(strokeWidth
, strokeWidth
,
1412 strokeWidth
, strokeWidth
);
1413 const auto wrRadius
= wr::ToBorderRadius(outerRadii
);
1414 aPaintData
.mBuilder
.PushBorder(dest
, dest
, kBackfaceIsVisible
, wrWidths
,
1415 {sides
, 4}, wrRadius
);
1417 const LayoutDeviceCoord halfWidth
= strokeWidth
* 0.5f
;
1418 const Float widths
[4] = {halfWidth
+ offset
, halfWidth
+ offset
,
1419 halfWidth
+ offset
, halfWidth
+ offset
};
1420 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1421 LayoutDeviceRect
dest(rect
);
1422 dest
.Deflate(halfWidth
);
1424 MakePathForRoundedRect(aPaintData
, dest
.ToUnknownRect(), outerRadii
);
1425 aPaintData
.Stroke(path
, ColorPattern(ToDeviceColor(aColor
)),
1426 StrokeOptions(strokeWidth
));
1430 DrawRect(accentColor
.Get());
1436 offset
+= strokeWidth
;
1439 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(1.0f
, aDpiRatio
));
1440 rect
.Inflate(strokeWidth
);
1442 DrawRect(accentColor
.GetForeground());
1445 LayoutDeviceIntMargin
Theme::GetWidgetBorder(nsDeviceContext
* aContext
,
1447 StyleAppearance aAppearance
) {
1448 switch (aAppearance
) {
1449 case StyleAppearance::Textfield
:
1450 case StyleAppearance::Textarea
:
1451 case StyleAppearance::NumberInput
:
1452 case StyleAppearance::Listbox
:
1453 case StyleAppearance::Menulist
:
1454 case StyleAppearance::MenulistButton
:
1455 case StyleAppearance::Button
:
1456 // Return the border size from the UA sheet, even though what we paint
1457 // doesn't actually match that. We know this is the UA sheet border
1458 // because we disable native theming when different border widths are
1459 // specified by authors, see Theme::IsWidgetStyled.
1461 // The Rounded() bit is technically redundant, but needed to appease the
1462 // type system, we should always end up with full device pixels due to
1463 // round_border_to_device_pixels at style time.
1464 return LayoutDeviceIntMargin::FromAppUnits(
1465 aFrame
->StyleBorder()->GetComputedBorder(),
1466 aFrame
->PresContext()->AppUnitsPerDevPixel())
1468 case StyleAppearance::Checkbox
:
1469 case StyleAppearance::Radio
: {
1470 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1471 LayoutDeviceIntCoord w
=
1472 ThemeDrawing::SnapBorderWidth(kCheckboxRadioBorderWidth
, dpiRatio
);
1473 return LayoutDeviceIntMargin(w
, w
, w
, w
);
1476 return LayoutDeviceIntMargin();
1480 bool Theme::GetWidgetPadding(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
1481 StyleAppearance aAppearance
,
1482 LayoutDeviceIntMargin
* aResult
) {
1483 switch (aAppearance
) {
1484 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1485 // and have a meaningful baseline, so they can't have
1486 // author-specified padding.
1487 case StyleAppearance::Radio
:
1488 case StyleAppearance::Checkbox
:
1489 aResult
->SizeTo(0, 0, 0, 0);
1497 bool Theme::GetWidgetOverflow(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
1498 StyleAppearance aAppearance
,
1499 nsRect
* aOverflowRect
) {
1500 CSSIntMargin overflow
;
1501 switch (aAppearance
) {
1502 case StyleAppearance::FocusOutline
: {
1503 // 2px * one segment, or 2px + 1px
1505 StaticPrefs::widget_non_native_theme_solid_outline_style() ? 2 : 3;
1506 overflow
.SizeTo(width
, width
, width
, width
);
1509 case StyleAppearance::Radio
:
1510 case StyleAppearance::Checkbox
:
1511 case StyleAppearance::Range
:
1512 // 2px for each outline segment, plus 1px separation, plus we paint with a
1513 // 1px extra offset, so 6px.
1514 overflow
.SizeTo(6, 6, 6, 6);
1516 case StyleAppearance::Textarea
:
1517 case StyleAppearance::Textfield
:
1518 case StyleAppearance::NumberInput
:
1519 case StyleAppearance::Listbox
:
1520 case StyleAppearance::MenulistButton
:
1521 case StyleAppearance::Menulist
:
1522 case StyleAppearance::Button
:
1523 // 2px for each segment, plus 1px separation, but we paint 1px inside
1524 // the border area so 4px overflow.
1525 overflow
.SizeTo(4, 4, 4, 4);
1531 // TODO: This should convert from device pixels to app units, not from CSS
1532 // pixels. And it should take the dpi ratio into account.
1533 // Using CSS pixels can cause the overflow to be too small if the page is
1535 aOverflowRect
->Inflate(CSSPixel::ToAppUnits(overflow
));
1539 LayoutDeviceIntCoord
Theme::GetScrollbarSize(const nsPresContext
* aPresContext
,
1540 StyleScrollbarWidth aWidth
,
1542 return GetScrollbarDrawing().GetScrollbarSize(aPresContext
, aWidth
, aOverlay
);
1545 nscoord
Theme::GetCheckboxRadioPrefSize() {
1546 return CSSPixel::ToAppUnits(kCheckboxRadioContentBoxSize
);
1550 UniquePtr
<ScrollbarDrawing
> Theme::ScrollbarStyle() {
1551 switch (StaticPrefs::widget_non_native_theme_scrollbar_style()) {
1553 return MakeUnique
<ScrollbarDrawingCocoa
>();
1555 return MakeUnique
<ScrollbarDrawingGTK
>();
1557 return MakeUnique
<ScrollbarDrawingAndroid
>();
1559 return MakeUnique
<ScrollbarDrawingWin
>();
1561 return MakeUnique
<ScrollbarDrawingWin11
>();
1565 // Default to native scrollbar style for each platform.
1567 if (IsWin11OrLater()) {
1568 return MakeUnique
<ScrollbarDrawingWin11
>();
1570 return MakeUnique
<ScrollbarDrawingWin
>();
1571 #elif MOZ_WIDGET_COCOA
1572 return MakeUnique
<ScrollbarDrawingCocoa
>();
1573 #elif MOZ_WIDGET_GTK
1574 return MakeUnique
<ScrollbarDrawingGTK
>();
1576 return MakeUnique
<ScrollbarDrawingAndroid
>();
1578 # error "Unknown platform, need scrollbar implementation."
1582 LayoutDeviceIntSize
Theme::GetMinimumWidgetSize(nsPresContext
* aPresContext
,
1584 StyleAppearance aAppearance
) {
1585 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1587 if (IsWidgetScrollbarPart(aAppearance
)) {
1588 return GetScrollbarDrawing().GetMinimumWidgetSize(aPresContext
, aAppearance
,
1592 LayoutDeviceIntSize result
;
1593 switch (aAppearance
) {
1594 case StyleAppearance::RangeThumb
:
1595 result
.SizeTo((kMinimumRangeThumbSize
* dpiRatio
).Rounded(),
1596 (kMinimumRangeThumbSize
* dpiRatio
).Rounded());
1598 case StyleAppearance::MozMenulistArrowButton
:
1599 result
.width
= (kMinimumDropdownArrowButtonWidth
* dpiRatio
).Rounded();
1601 case StyleAppearance::SpinnerUpbutton
:
1602 case StyleAppearance::SpinnerDownbutton
:
1603 result
.width
= (kMinimumSpinnerButtonWidth
* dpiRatio
).Rounded();
1604 result
.height
= (kMinimumSpinnerButtonHeight
* dpiRatio
).Rounded();
1612 nsITheme::Transparency
Theme::GetWidgetTransparency(
1613 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1614 if (auto scrollbar
= GetScrollbarDrawing().GetScrollbarPartTransparency(
1615 aFrame
, aAppearance
)) {
1618 if (aAppearance
== StyleAppearance::Tooltip
) {
1619 // We draw a rounded rect, so we need transparency.
1620 return eTransparent
;
1622 return eUnknownTransparency
;
1626 Theme::WidgetStateChanged(nsIFrame
* aFrame
, StyleAppearance aAppearance
,
1627 nsAtom
* aAttribute
, bool* aShouldRepaint
,
1628 const nsAttrValue
* aOldValue
) {
1630 // Hover/focus/active changed. Always repaint.
1631 *aShouldRepaint
= true;
1633 // Check the attribute to see if it's relevant.
1634 // disabled, checked, dlgtype, default, etc.
1635 *aShouldRepaint
= false;
1636 if (aAttribute
== nsGkAtoms::disabled
|| aAttribute
== nsGkAtoms::checked
||
1637 aAttribute
== nsGkAtoms::selected
||
1638 aAttribute
== nsGkAtoms::visuallyselected
||
1639 aAttribute
== nsGkAtoms::menuactive
||
1640 aAttribute
== nsGkAtoms::sortDirection
||
1641 aAttribute
== nsGkAtoms::focused
|| aAttribute
== nsGkAtoms::_default
||
1642 aAttribute
== nsGkAtoms::open
|| aAttribute
== nsGkAtoms::hover
) {
1643 *aShouldRepaint
= true;
1651 Theme::ThemeChanged() { return NS_OK
; }
1653 bool Theme::WidgetAppearanceDependsOnWindowFocus(StyleAppearance aAppearance
) {
1654 return IsWidgetScrollbarPart(aAppearance
);
1657 nsITheme::ThemeGeometryType
Theme::ThemeGeometryTypeForWidget(
1658 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1659 return eThemeGeometryTypeUnknown
;
1662 bool Theme::ThemeSupportsWidget(nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
1663 StyleAppearance aAppearance
) {
1664 switch (aAppearance
) {
1665 case StyleAppearance::Radio
:
1666 case StyleAppearance::Checkbox
:
1667 case StyleAppearance::FocusOutline
:
1668 case StyleAppearance::Textarea
:
1669 case StyleAppearance::Textfield
:
1670 case StyleAppearance::Range
:
1671 case StyleAppearance::RangeThumb
:
1672 case StyleAppearance::ProgressBar
:
1673 case StyleAppearance::Progresschunk
:
1674 case StyleAppearance::Meter
:
1675 case StyleAppearance::Meterchunk
:
1676 case StyleAppearance::ScrollbarbuttonUp
:
1677 case StyleAppearance::ScrollbarbuttonDown
:
1678 case StyleAppearance::ScrollbarbuttonLeft
:
1679 case StyleAppearance::ScrollbarbuttonRight
:
1680 case StyleAppearance::ScrollbarthumbHorizontal
:
1681 case StyleAppearance::ScrollbarthumbVertical
:
1682 case StyleAppearance::ScrollbartrackHorizontal
:
1683 case StyleAppearance::ScrollbartrackVertical
:
1684 case StyleAppearance::ScrollbarHorizontal
:
1685 case StyleAppearance::ScrollbarVertical
:
1686 case StyleAppearance::Scrollcorner
:
1687 case StyleAppearance::Button
:
1688 case StyleAppearance::Listbox
:
1689 case StyleAppearance::Menulist
:
1690 case StyleAppearance::MenulistButton
:
1691 case StyleAppearance::NumberInput
:
1692 case StyleAppearance::MozMenulistArrowButton
:
1693 case StyleAppearance::Menuarrow
:
1694 case StyleAppearance::SpinnerUpbutton
:
1695 case StyleAppearance::SpinnerDownbutton
:
1696 case StyleAppearance::Menuitem
:
1697 case StyleAppearance::Tooltip
:
1698 return !IsWidgetStyled(aPresContext
, aFrame
, aAppearance
);
1704 bool Theme::WidgetIsContainer(StyleAppearance aAppearance
) {
1705 switch (aAppearance
) {
1706 case StyleAppearance::MozMenulistArrowButton
:
1707 case StyleAppearance::Radio
:
1708 case StyleAppearance::Checkbox
:
1715 bool Theme::ThemeDrawsFocusForWidget(nsIFrame
*, StyleAppearance
) {
1719 bool Theme::ThemeNeedsComboboxDropmarker() { return true; }
1721 bool Theme::ThemeSupportsScrollbarButtons() {
1722 return GetScrollbarDrawing().ShouldDrawScrollbarButtons();
1725 } // namespace mozilla::widget