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/. */
6 #include "nsNativeThemeWin.h"
11 #include "gfxContext.h"
12 #include "gfxPlatform.h"
13 #include "gfxWindowsNativeDrawing.h"
14 #include "gfxWindowsPlatform.h"
15 #include "gfxWindowsSurface.h"
16 #include "mozilla/ClearOnShutdown.h"
17 #include "mozilla/gfx/Types.h" // for Color::FromABGR
18 #include "mozilla/Logging.h"
19 #include "mozilla/RelativeLuminanceUtils.h"
20 #include "mozilla/StaticPrefs_layout.h"
21 #include "mozilla/StaticPrefs_widget.h"
22 #include "mozilla/dom/XULButtonElement.h"
24 #include "nsComboboxControlFrame.h"
25 #include "nsDeviceContext.h"
26 #include "nsGkAtoms.h"
27 #include "nsIContent.h"
28 #include "nsIContentInlines.h"
30 #include "nsLayoutUtils.h"
31 #include "nsLookAndFeel.h"
32 #include "nsNameSpaceManager.h"
34 #include "nsPresContext.h"
37 #include "nsStyleConsts.h"
38 #include "nsTransform2D.h"
43 using namespace mozilla
;
44 using namespace mozilla::gfx
;
45 using namespace mozilla::widget
;
47 using ElementState
= dom::ElementState
;
49 extern mozilla::LazyLogModule gWindowsLog
;
51 namespace mozilla::widget
{
53 nsNativeThemeWin::nsNativeThemeWin()
54 : Theme(ScrollbarStyle()),
55 mProgressDeterminateTimeStamp(TimeStamp::Now()),
56 mProgressIndeterminateTimeStamp(TimeStamp::Now()),
58 mMinimumWidgetSizeCacheValid(),
59 mGutterSizeCacheValid(false) {}
61 nsNativeThemeWin::~nsNativeThemeWin() { nsUXThemeData::Invalidate(); }
63 bool nsNativeThemeWin::IsWidgetAlwaysNonNative(nsIFrame
* aFrame
,
64 StyleAppearance aAppearance
) {
65 return Theme::IsWidgetAlwaysNonNative(aFrame
, aAppearance
) ||
66 aAppearance
== StyleAppearance::Checkbox
||
67 aAppearance
== StyleAppearance::Radio
||
68 aAppearance
== StyleAppearance::MozMenulistArrowButton
||
69 aAppearance
== StyleAppearance::SpinnerUpbutton
||
70 aAppearance
== StyleAppearance::SpinnerDownbutton
;
73 auto nsNativeThemeWin::IsWidgetNonNative(nsIFrame
* aFrame
,
74 StyleAppearance aAppearance
)
76 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
77 return NonNative::Always
;
80 // We only know how to draw light widgets, so we defer to the non-native
81 // theme when appropriate.
82 if (Theme::ThemeSupportsWidget(aFrame
->PresContext(), aFrame
, aAppearance
) &&
83 LookAndFeel::ColorSchemeForFrame(aFrame
) ==
84 LookAndFeel::ColorScheme::Dark
) {
85 return NonNative::BecauseColorMismatch
;
90 static MARGINS
GetCheckboxMargins(HANDLE theme
, HDC hdc
) {
91 MARGINS checkboxContent
= {0};
92 GetThemeMargins(theme
, hdc
, MENU_POPUPCHECK
, MCB_NORMAL
, TMT_CONTENTMARGINS
,
93 nullptr, &checkboxContent
);
94 return checkboxContent
;
97 static SIZE
GetCheckboxBGSize(HANDLE theme
, HDC hdc
) {
99 GetThemePartSize(theme
, hdc
, MENU_POPUPCHECK
, MC_CHECKMARKNORMAL
, nullptr,
100 TS_TRUE
, &checkboxSize
);
102 MARGINS checkboxMargins
= GetCheckboxMargins(theme
, hdc
);
104 int leftMargin
= checkboxMargins
.cxLeftWidth
;
105 int rightMargin
= checkboxMargins
.cxRightWidth
;
106 int topMargin
= checkboxMargins
.cyTopHeight
;
107 int bottomMargin
= checkboxMargins
.cyBottomHeight
;
109 int width
= leftMargin
+ checkboxSize
.cx
+ rightMargin
;
110 int height
= topMargin
+ checkboxSize
.cy
+ bottomMargin
;
117 static SIZE
GetCheckboxBGBounds(HANDLE theme
, HDC hdc
) {
118 MARGINS checkboxBGSizing
= {0};
119 MARGINS checkboxBGContent
= {0};
120 GetThemeMargins(theme
, hdc
, MENU_POPUPCHECKBACKGROUND
, MCB_NORMAL
,
121 TMT_SIZINGMARGINS
, nullptr, &checkboxBGSizing
);
122 GetThemeMargins(theme
, hdc
, MENU_POPUPCHECKBACKGROUND
, MCB_NORMAL
,
123 TMT_CONTENTMARGINS
, nullptr, &checkboxBGContent
);
125 #define posdx(d) ((d) > 0 ? d : 0)
128 posdx(checkboxBGContent
.cxRightWidth
- checkboxBGSizing
.cxRightWidth
) +
129 posdx(checkboxBGContent
.cxLeftWidth
- checkboxBGSizing
.cxLeftWidth
);
131 posdx(checkboxBGContent
.cyTopHeight
- checkboxBGSizing
.cyTopHeight
) +
132 posdx(checkboxBGContent
.cyBottomHeight
- checkboxBGSizing
.cyBottomHeight
);
136 SIZE
ret(GetCheckboxBGSize(theme
, hdc
));
142 static SIZE
GetGutterSize(HANDLE theme
, HDC hdc
) {
144 GetThemePartSize(theme
, hdc
, MENU_POPUPGUTTER
, 0, nullptr, TS_TRUE
,
147 SIZE
checkboxBGSize(GetCheckboxBGBounds(theme
, hdc
));
150 GetThemePartSize(theme
, hdc
, MENU_POPUPITEM
, MPI_NORMAL
, nullptr, TS_TRUE
,
153 // Figure out how big the menuitem's icon will be (if present) at current DPI
154 // Needs the system scale for consistency with Windows Theme API.
155 double scaleFactor
= WinUtils::SystemScaleFactor();
156 int iconDevicePixels
= NSToIntRound(16 * scaleFactor
);
157 SIZE iconSize
= {iconDevicePixels
, iconDevicePixels
};
158 // Not really sure what margins should be used here, but this seems to work in
160 MARGINS margins
= {0};
161 GetThemeMargins(theme
, hdc
, MENU_POPUPCHECKBACKGROUND
, MCB_NORMAL
,
162 TMT_CONTENTMARGINS
, nullptr, &margins
);
163 iconSize
.cx
+= margins
.cxLeftWidth
+ margins
.cxRightWidth
;
164 iconSize
.cy
+= margins
.cyTopHeight
+ margins
.cyBottomHeight
;
166 int width
= std::max(
167 itemSize
.cx
, std::max(iconSize
.cx
, checkboxBGSize
.cx
) + gutterSize
.cx
);
168 int height
= std::max(itemSize
.cy
, std::max(iconSize
.cy
, checkboxBGSize
.cy
));
176 SIZE
nsNativeThemeWin::GetCachedGutterSize(HANDLE theme
) {
177 if (mGutterSizeCacheValid
) {
178 return mGutterSizeCache
;
181 mGutterSizeCache
= GetGutterSize(theme
, nullptr);
182 mGutterSizeCacheValid
= true;
184 return mGutterSizeCache
;
188 * Notes on progress track and meter part constants:
190 * PP_BAR(_VERT) - base progress track
191 * PP_TRANSPARENTBAR(_VERT) - transparent progress track. this only works if
192 * the underlying surface supports alpha. otherwise
193 * theme lib's DrawThemeBackground falls back on
194 * opaque PP_BAR. we currently don't use this.
195 * PP_CHUNK(_VERT) - xp progress meter. this does not draw an xp style
196 * progress w/chunks, it draws fill using the chunk
199 * PP_FILL(_VERT) - progress meter. these have four states/colors.
200 * PP_PULSEOVERLAY(_VERT) - white reflection - an overlay, not sure what this
202 * PP_MOVEOVERLAY(_VERT) - green pulse - the pulse effect overlay on
203 * determined progress bars. we also use this for
204 * indeterminate chunk.
206 * Notes on state constants:
207 * PBBS_NORMAL - green progress
208 * PBBVS_PARTIAL/PBFVS_ERROR - red error progress
209 * PBFS_PAUSED - yellow paused progress
211 * There is no common controls style indeterminate part on vista and up.
215 * Progress bar related constants. These values are found by experimenting and
216 * comparing against native widgets used by the system. They are very unlikely
217 * exact but try to not be too wrong.
219 // The amount of time we animate progress meters parts across the frame.
220 static const double kProgressDeterminateTimeSpan
= 3.0;
221 static const double kProgressIndeterminateTimeSpan
= 5.0;
222 // The width of the overlay used to animate the horizontal progress bar (Vista
224 static const int32_t kProgressHorizontalOverlaySize
= 120;
225 // The height of the overlay used to animate the vertical progress bar (Vista
227 static const int32_t kProgressVerticalOverlaySize
= 45;
228 // The height of the overlay used for the vertical indeterminate progress bar
229 // (Vista and later).
230 static const int32_t kProgressVerticalIndeterminateOverlaySize
= 60;
231 // The width of the overlay used to animate the indeterminate progress bar
232 // (Windows Classic).
233 static const int32_t kProgressClassicOverlaySize
= 40;
236 * GetProgressOverlayStyle - returns the proper overlay part for themed
237 * progress bars based on os and orientation.
239 static int32_t GetProgressOverlayStyle(bool aIsVertical
) {
240 return aIsVertical
? PP_MOVEOVERLAYVERT
: PP_MOVEOVERLAY
;
244 * GetProgressOverlaySize - returns the minimum width or height for themed
245 * progress bar overlays. This includes the width of indeterminate chunks
246 * and vista pulse overlays.
248 static int32_t GetProgressOverlaySize(bool aIsVertical
, bool aIsIndeterminate
) {
250 return aIsIndeterminate
? kProgressVerticalIndeterminateOverlaySize
251 : kProgressVerticalOverlaySize
;
253 return kProgressHorizontalOverlaySize
;
257 * IsProgressMeterFilled - Determines if a progress meter is at 100% fill based
258 * on a comparison of the current value and maximum.
260 static bool IsProgressMeterFilled(nsIFrame
* aFrame
) {
261 NS_ENSURE_TRUE(aFrame
, false);
262 nsIFrame
* parentFrame
= aFrame
->GetParent();
263 NS_ENSURE_TRUE(parentFrame
, false);
264 return nsNativeTheme::GetProgressValue(parentFrame
) ==
265 nsNativeTheme::GetProgressMaxValue(parentFrame
);
269 * CalculateProgressOverlayRect - returns the padded overlay animation rect
270 * used in rendering progress bars. Resulting rects are used in rendering
271 * vista+ pulse overlays and indeterminate progress meters. Graphics should
272 * be rendered at the origin.
274 RECT
nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame
* aFrame
,
277 bool aIsIndeterminate
,
279 NS_ASSERTION(aFrame
, "bad frame pointer");
280 NS_ASSERTION(aWidgetRect
, "bad rect pointer");
282 int32_t frameSize
= aIsVertical
? aWidgetRect
->bottom
- aWidgetRect
->top
283 : aWidgetRect
->right
- aWidgetRect
->left
;
285 // Recycle a set of progress pulse timers - these timers control the position
286 // of all progress overlays and indeterminate chunks that get rendered.
287 double span
= aIsIndeterminate
? kProgressIndeterminateTimeSpan
288 : kProgressDeterminateTimeSpan
;
290 if (!aIsIndeterminate
) {
291 if (TimeStamp::Now() >
292 (mProgressDeterminateTimeStamp
+ TimeDuration::FromSeconds(span
))) {
293 mProgressDeterminateTimeStamp
= TimeStamp::Now();
295 period
= TimeStamp::Now() - mProgressDeterminateTimeStamp
;
297 if (TimeStamp::Now() >
298 (mProgressIndeterminateTimeStamp
+ TimeDuration::FromSeconds(span
))) {
299 mProgressIndeterminateTimeStamp
= TimeStamp::Now();
301 period
= TimeStamp::Now() - mProgressIndeterminateTimeStamp
;
304 double percent
= period
/ TimeDuration::FromSeconds(span
);
306 if (!aIsVertical
&& IsFrameRTL(aFrame
)) percent
= 1 - percent
;
308 RECT overlayRect
= *aWidgetRect
;
311 overlaySize
= GetProgressOverlaySize(aIsVertical
, aIsIndeterminate
);
313 overlaySize
= kProgressClassicOverlaySize
;
316 // Calculate a bounds that is larger than the meters frame such that the
317 // overlay starts and ends completely off the edge of the frame:
318 // [overlay][frame][overlay]
319 // This also yields a nice delay on rotation. Use overlaySize as the minimum
320 // size for [overlay] based on the graphics dims. If [frame] is larger, use
321 // the frame size instead.
322 int trackWidth
= frameSize
> overlaySize
? frameSize
: overlaySize
;
324 int xPos
= aWidgetRect
->left
- trackWidth
;
325 xPos
+= (int)ceil(((double)(trackWidth
* 2) * percent
));
326 overlayRect
.left
= xPos
;
327 overlayRect
.right
= xPos
+ overlaySize
;
329 int yPos
= aWidgetRect
->bottom
+ trackWidth
;
330 yPos
-= (int)ceil(((double)(trackWidth
* 2) * percent
));
331 overlayRect
.bottom
= yPos
;
332 overlayRect
.top
= yPos
- overlaySize
;
338 * DrawProgressMeter - render an appropriate progress meter based on progress
339 * meter style, orientation, and os. Note, this does not render the underlying
342 * @param aFrame the widget frame
343 * @param aAppearance type of widget
344 * @param aTheme progress theme handle
345 * @param aHdc hdc returned by gfxWindowsNativeDrawing
346 * @param aPart the PP_X progress part
347 * @param aState the theme state
348 * @param aWidgetRect bounding rect for the widget
349 * @param aClipRect dirty rect that needs drawing.
350 * @param aAppUnits app units per device pixel
352 void nsNativeThemeWin::DrawThemedProgressMeter(
353 nsIFrame
* aFrame
, StyleAppearance aAppearance
, HANDLE aTheme
, HDC aHdc
,
354 int aPart
, int aState
, RECT
* aWidgetRect
, RECT
* aClipRect
) {
355 if (!aFrame
|| !aTheme
|| !aHdc
) return;
357 NS_ASSERTION(aWidgetRect
, "bad rect pointer");
358 NS_ASSERTION(aClipRect
, "bad clip rect pointer");
360 RECT adjWidgetRect
, adjClipRect
;
361 adjWidgetRect
= *aWidgetRect
;
362 adjClipRect
= *aClipRect
;
364 nsIFrame
* parentFrame
= aFrame
->GetParent();
366 // We have no parent to work with, just bail.
367 NS_WARNING("No parent frame for progress rendering. Can't paint.");
371 ElementState elementState
= GetContentState(parentFrame
, aAppearance
);
372 bool vertical
= IsVerticalProgress(parentFrame
);
373 bool indeterminate
= elementState
.HasState(ElementState::INDETERMINATE
);
374 bool animate
= indeterminate
;
376 // Vista and up progress meter is fill style, rendered here. We render
377 // the pulse overlay in the follow up section below.
378 DrawThemeBackground(aTheme
, aHdc
, aPart
, aState
, &adjWidgetRect
,
380 if (!IsProgressMeterFilled(aFrame
)) {
385 // Indeterminate rendering
386 int32_t overlayPart
= GetProgressOverlayStyle(vertical
);
387 RECT overlayRect
= CalculateProgressOverlayRect(
388 aFrame
, &adjWidgetRect
, vertical
, indeterminate
, false);
389 DrawThemeBackground(aTheme
, aHdc
, overlayPart
, aState
, &overlayRect
,
392 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 60)) {
393 NS_WARNING("unable to animate progress widget!");
398 LayoutDeviceIntMargin
nsNativeThemeWin::GetCachedWidgetBorder(
399 HTHEME aTheme
, nsUXThemeClass aThemeClass
, StyleAppearance aAppearance
,
400 int32_t aPart
, int32_t aState
) {
401 int32_t cacheIndex
= aThemeClass
* THEME_PART_DISTINCT_VALUE_COUNT
+ aPart
;
402 int32_t cacheBitIndex
= cacheIndex
/ 8;
403 uint8_t cacheBit
= 1u << (cacheIndex
% 8);
405 if (mBorderCacheValid
[cacheBitIndex
] & cacheBit
) {
406 return mBorderCache
[cacheIndex
];
410 RECT outerRect
; // Create a fake outer rect.
411 outerRect
.top
= outerRect
.left
= 100;
412 outerRect
.right
= outerRect
.bottom
= 200;
413 RECT
contentRect(outerRect
);
414 HRESULT res
= GetThemeBackgroundContentRect(aTheme
, nullptr, aPart
, aState
,
415 &outerRect
, &contentRect
);
418 return LayoutDeviceIntMargin();
421 // Now compute the delta in each direction and place it in our
422 // nsIntMargin struct.
423 LayoutDeviceIntMargin result
;
424 result
.top
= contentRect
.top
- outerRect
.top
;
425 result
.bottom
= outerRect
.bottom
- contentRect
.bottom
;
426 result
.left
= contentRect
.left
- outerRect
.left
;
427 result
.right
= outerRect
.right
- contentRect
.right
;
429 mBorderCacheValid
[cacheBitIndex
] |= cacheBit
;
430 mBorderCache
[cacheIndex
] = result
;
435 nsresult
nsNativeThemeWin::GetCachedMinimumWidgetSize(
436 nsIFrame
* aFrame
, HANDLE aTheme
, nsUXThemeClass aThemeClass
,
437 StyleAppearance aAppearance
, int32_t aPart
, int32_t aState
,
438 THEMESIZE aSizeReq
, mozilla::LayoutDeviceIntSize
* aResult
) {
439 int32_t cachePart
= aPart
;
441 if (aAppearance
== StyleAppearance::Button
&& aSizeReq
== TS_MIN
) {
442 // In practice, StyleAppearance::Button is the only widget type which has an
443 // aSizeReq that varies for us, and it can only be TS_MIN or TS_TRUE. Just
444 // stuff that extra bit into the aPart part of the cache, since BP_Count is
445 // well below THEME_PART_DISTINCT_VALUE_COUNT anyway.
446 cachePart
= BP_Count
;
449 MOZ_ASSERT(aPart
< THEME_PART_DISTINCT_VALUE_COUNT
);
451 aThemeClass
* THEME_PART_DISTINCT_VALUE_COUNT
+ cachePart
;
452 int32_t cacheBitIndex
= cacheIndex
/ 8;
453 uint8_t cacheBit
= 1u << (cacheIndex
% 8);
455 if (mMinimumWidgetSizeCacheValid
[cacheBitIndex
] & cacheBit
) {
456 *aResult
= mMinimumWidgetSizeCache
[cacheIndex
];
460 HDC hdc
= ::GetDC(NULL
);
462 return NS_ERROR_FAILURE
;
466 GetThemePartSize(aTheme
, hdc
, aPart
, aState
, nullptr, aSizeReq
, &sz
);
467 aResult
->width
= sz
.cx
;
468 aResult
->height
= sz
.cy
;
470 ::ReleaseDC(nullptr, hdc
);
472 mMinimumWidgetSizeCacheValid
[cacheBitIndex
] |= cacheBit
;
473 mMinimumWidgetSizeCache
[cacheIndex
] = *aResult
;
478 mozilla::Maybe
<nsUXThemeClass
> nsNativeThemeWin::GetThemeClass(
479 StyleAppearance aAppearance
) {
480 switch (aAppearance
) {
481 case StyleAppearance::Button
:
482 return Some(eUXButton
);
483 case StyleAppearance::NumberInput
:
484 case StyleAppearance::Textfield
:
485 case StyleAppearance::Textarea
:
486 return Some(eUXEdit
);
487 case StyleAppearance::Toolbox
:
488 return Some(eUXRebar
);
489 case StyleAppearance::Toolbar
:
490 case StyleAppearance::Toolbarbutton
:
491 case StyleAppearance::Separator
:
492 return Some(eUXToolbar
);
493 case StyleAppearance::ProgressBar
:
494 case StyleAppearance::Progresschunk
:
495 return Some(eUXProgress
);
496 case StyleAppearance::Tab
:
497 case StyleAppearance::Tabpanel
:
498 case StyleAppearance::Tabpanels
:
500 case StyleAppearance::Range
:
501 case StyleAppearance::RangeThumb
:
502 return Some(eUXTrackbar
);
503 case StyleAppearance::Menulist
:
504 case StyleAppearance::MenulistButton
:
505 return Some(eUXCombobox
);
506 case StyleAppearance::Treeheadercell
:
507 return Some(eUXHeader
);
508 case StyleAppearance::Listbox
:
509 case StyleAppearance::Treeview
:
510 case StyleAppearance::Treetwistyopen
:
511 case StyleAppearance::Treeitem
:
512 return Some(eUXListview
);
519 nsNativeThemeWin::GetTheme(StyleAppearance aAppearance
) {
520 mozilla::Maybe
<nsUXThemeClass
> themeClass
= GetThemeClass(aAppearance
);
521 if (themeClass
.isNothing()) {
524 return nsUXThemeData::GetTheme(themeClass
.value());
527 int32_t nsNativeThemeWin::StandardGetState(nsIFrame
* aFrame
,
528 StyleAppearance aAppearance
,
530 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
531 if (elementState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
)) {
534 if (elementState
.HasState(ElementState::HOVER
)) {
538 if (elementState
.HasState(ElementState::FOCUSRING
)) {
541 // On Windows, focused buttons are always drawn as such by the native
542 // theme, that's why we check ElementState::FOCUS instead of
543 // ElementState::FOCUSRING.
544 if (aAppearance
== StyleAppearance::Button
&&
545 elementState
.HasState(ElementState::FOCUS
)) {
553 bool nsNativeThemeWin::IsMenuActive(nsIFrame
* aFrame
,
554 StyleAppearance aAppearance
) {
555 nsIContent
* content
= aFrame
->GetContent();
556 if (content
->IsXULElement() &&
557 content
->NodeInfo()->Equals(nsGkAtoms::richlistitem
))
558 return CheckBooleanAttr(aFrame
, nsGkAtoms::selected
);
560 return CheckBooleanAttr(aFrame
, nsGkAtoms::menuactive
);
564 * aPart is filled in with the UXTheme part code. On return, values > 0
565 * are the actual UXTheme part code; -1 means the widget will be drawn by
566 * us; 0 means that we should use part code 0, which isn't a real part code
567 * but elicits some kind of default behaviour from UXTheme when drawing
568 * (but isThemeBackgroundPartiallyTransparent may not work).
570 nsresult
nsNativeThemeWin::GetThemePartAndState(nsIFrame
* aFrame
,
571 StyleAppearance aAppearance
,
574 switch (aAppearance
) {
575 case StyleAppearance::Button
: {
582 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
583 if (elementState
.HasState(ElementState::DISABLED
)) {
584 aState
= TS_DISABLED
;
587 if (IsOpenButton(aFrame
) || IsCheckedButton(aFrame
)) {
592 aState
= StandardGetState(aFrame
, aAppearance
, true);
594 // Check for default dialog buttons. These buttons should always look
596 if (aState
== TS_NORMAL
&& IsDefaultButton(aFrame
)) aState
= TS_FOCUSED
;
599 case StyleAppearance::NumberInput
:
600 case StyleAppearance::Textfield
:
601 case StyleAppearance::Textarea
: {
602 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
604 /* Note: the NOSCROLL type has a rounded corner in each corner. The more
605 * specific HSCROLL, VSCROLL, HVSCROLL types have side and/or top/bottom
606 * edges rendered as straight horizontal lines with sharp corners to
607 * accommodate a scrollbar. However, the scrollbar gets rendered on top
608 * of this for us, so we don't care, and can just use NOSCROLL here.
610 aPart
= TFP_EDITBORDER_NOSCROLL
;
613 aState
= TFS_EDITBORDER_NORMAL
;
614 } else if (elementState
.HasState(ElementState::DISABLED
)) {
615 aState
= TFS_EDITBORDER_DISABLED
;
616 } else if (IsReadOnly(aFrame
)) {
617 /* no special read-only state */
618 aState
= TFS_EDITBORDER_NORMAL
;
619 } else if (elementState
.HasAtLeastOneOfStates(ElementState::ACTIVE
|
620 ElementState::FOCUSRING
)) {
621 aState
= TFS_EDITBORDER_FOCUSED
;
622 } else if (elementState
.HasState(ElementState::HOVER
)) {
623 aState
= TFS_EDITBORDER_HOVER
;
625 aState
= TFS_EDITBORDER_NORMAL
;
630 case StyleAppearance::ProgressBar
: {
631 bool vertical
= IsVerticalProgress(aFrame
);
632 aPart
= vertical
? PP_BARVERT
: PP_BAR
;
633 aState
= PBBS_NORMAL
;
636 case StyleAppearance::Progresschunk
: {
637 nsIFrame
* parentFrame
= aFrame
->GetParent();
638 if (IsVerticalProgress(parentFrame
)) {
644 aState
= PBBVS_NORMAL
;
647 case StyleAppearance::Toolbarbutton
: {
654 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
655 if (elementState
.HasState(ElementState::DISABLED
)) {
656 aState
= TS_DISABLED
;
659 if (IsOpenButton(aFrame
)) {
664 if (elementState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
))
666 else if (elementState
.HasState(ElementState::HOVER
)) {
667 if (IsCheckedButton(aFrame
))
668 aState
= TB_HOVER_CHECKED
;
672 if (IsCheckedButton(aFrame
))
680 case StyleAppearance::Separator
: {
681 aPart
= TP_SEPARATOR
;
685 case StyleAppearance::Range
: {
686 if (IsRangeHorizontal(aFrame
)) {
690 aPart
= TKP_TRACKVERT
;
691 aState
= TRVS_NORMAL
;
695 case StyleAppearance::RangeThumb
: {
696 if (IsRangeHorizontal(aFrame
)) {
697 aPart
= TKP_THUMBBOTTOM
;
699 aPart
= IsFrameRTL(aFrame
) ? TKP_THUMBLEFT
: TKP_THUMBRIGHT
;
701 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
704 } else if (elementState
.HasState(ElementState::DISABLED
)) {
705 aState
= TKP_DISABLED
;
707 if (elementState
.HasState(
708 ElementState::ACTIVE
)) // Hover is not also a requirement for
709 // the thumb, since the drag is not
710 // canceled when you move outside the
713 else if (elementState
.HasState(ElementState::FOCUSRING
))
714 aState
= TKP_FOCUSED
;
715 else if (elementState
.HasState(ElementState::HOVER
))
722 case StyleAppearance::Toolbox
: {
724 aPart
= RP_BACKGROUND
;
727 case StyleAppearance::Toolbar
: {
728 // Use -1 to indicate we don't wish to have the theme background drawn
729 // for this item. We will pass any nessessary information via aState,
730 // and will render the item using separate code.
734 nsIContent
* content
= aFrame
->GetContent();
735 nsIContent
* parent
= content
->GetParent();
736 // XXXzeniko hiding the first toolbar will result in an unwanted margin
737 if (parent
&& parent
->GetFirstChild() == content
) {
743 case StyleAppearance::Treeview
:
744 case StyleAppearance::Listbox
: {
745 aPart
= TREEVIEW_BODY
;
749 case StyleAppearance::Tabpanels
: {
754 case StyleAppearance::Tabpanel
: {
759 case StyleAppearance::Tab
: {
766 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
767 if (elementState
.HasState(ElementState::DISABLED
)) {
768 aState
= TS_DISABLED
;
772 if (IsSelectedTab(aFrame
)) {
773 aPart
= TABP_TAB_SELECTED
;
774 aState
= TS_ACTIVE
; // The selected tab is always "pressed".
776 aState
= StandardGetState(aFrame
, aAppearance
, true);
780 case StyleAppearance::Treeheadercell
: {
787 aState
= StandardGetState(aFrame
, aAppearance
, true);
791 case StyleAppearance::MenulistButton
:
792 case StyleAppearance::Menulist
: {
793 nsIContent
* content
= aFrame
->GetContent();
794 bool useDropBorder
= content
&& content
->IsHTMLElement();
795 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
797 /* On Vista/Win7, we use CBP_DROPBORDER instead of DROPFRAME for HTML
798 * content or for editable menulists; this gives us the thin outline,
799 * instead of the gradient-filled background */
801 aPart
= CBP_DROPBORDER
;
803 aPart
= CBP_DROPFRAME
;
805 if (elementState
.HasState(ElementState::DISABLED
)) {
806 aState
= TS_DISABLED
;
807 } else if (IsReadOnly(aFrame
)) {
809 } else if (IsOpenButton(aFrame
)) {
811 } else if (useDropBorder
&&
812 elementState
.HasState(ElementState::FOCUSRING
)) {
814 } else if (elementState
.HasAllStates(ElementState::HOVER
|
815 ElementState::ACTIVE
)) {
817 } else if (elementState
.HasState(ElementState::HOVER
)) {
828 return NS_ERROR_FAILURE
;
832 static bool AssumeThemePartAndStateAreTransparent(int32_t aPart
,
834 if (!nsUXThemeData::IsHighContrastOn() && aPart
== MENU_POPUPITEM
&&
835 aState
== MBI_NORMAL
) {
841 // When running with per-monitor DPI (on Win8.1+), and rendering on a display
842 // with a different DPI setting from the system's default scaling, we need to
843 // apply scaling to native-themed elements as the Windows theme APIs assume
844 // the system default resolution.
845 static inline double GetThemeDpiScaleFactor(nsPresContext
* aPresContext
) {
846 if (WinUtils::IsPerMonitorDPIAware() ||
847 StaticPrefs::layout_css_devPixelsPerPx() > 0.0) {
848 nsCOMPtr
<nsIWidget
> rootWidget
= aPresContext
->GetRootWidget();
850 double systemScale
= WinUtils::SystemScaleFactor();
851 return rootWidget
->GetDefaultScale().scale
/ systemScale
;
857 static inline double GetThemeDpiScaleFactor(nsIFrame
* aFrame
) {
858 return GetThemeDpiScaleFactor(aFrame
->PresContext());
862 nsNativeThemeWin::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
863 StyleAppearance aAppearance
,
865 const nsRect
& aDirtyRect
,
866 DrawOverflow aDrawOverflow
) {
867 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
868 return Theme::DrawWidgetBackground(aContext
, aFrame
, aAppearance
, aRect
,
869 aDirtyRect
, aDrawOverflow
);
872 HANDLE theme
= GetTheme(aAppearance
);
874 return ClassicDrawWidgetBackground(aContext
, aFrame
, aAppearance
, aRect
,
877 // ^^ without the right sdk, assume xp theming and fall through.
879 nsresult rv
= GetThemePartAndState(aFrame
, aAppearance
, part
, state
);
880 if (NS_FAILED(rv
)) return rv
;
882 if (AssumeThemePartAndStateAreTransparent(part
, state
)) {
886 gfxContextMatrixAutoSaveRestore
save(aContext
);
888 double themeScale
= GetThemeDpiScaleFactor(aFrame
);
889 if (themeScale
!= 1.0) {
891 aContext
->CurrentMatrix().PreScale(themeScale
, themeScale
));
894 gfxFloat p2a
= gfxFloat(aFrame
->PresContext()->AppUnitsPerDevPixel());
897 gfxRect
tr(aRect
.X(), aRect
.Y(), aRect
.Width(), aRect
.Height()),
898 dr(aDirtyRect
.X(), aDirtyRect
.Y(), aDirtyRect
.Width(),
899 aDirtyRect
.Height());
901 tr
.Scale(1.0 / (p2a
* themeScale
));
902 dr
.Scale(1.0 / (p2a
* themeScale
));
904 gfxWindowsNativeDrawing
nativeDrawing(
905 aContext
, dr
, GetWidgetNativeDrawingFlags(aAppearance
));
909 HDC hdc
= nativeDrawing
.BeginNativeDrawing();
910 if (!hdc
) return NS_ERROR_FAILURE
;
912 nativeDrawing
.TransformToNativeRect(tr
, widgetRect
);
913 nativeDrawing
.TransformToNativeRect(dr
, clipRect
);
917 MOZ_LOG(gWindowsLog
, LogLevel::Error
,
918 (stderr
, "xform: %f %f %f %f [%f %f]\n", m
._11
, m
._21
, m
._12
, m
._22
,
920 MOZ_LOG(gWindowsLog
, LogLevel::Error
,
921 (stderr
, "tr: [%d %d %d %d]\ndr: [%d %d %d %d]\noff: [%f %f]\n",
922 tr
.x
, tr
.y
, tr
.width
, tr
.height
, dr
.x
, dr
.y
, dr
.width
, dr
.height
,
923 offset
.x
, offset
.y
));
927 if (aAppearance
== StyleAppearance::Tab
) {
928 // For left edge and right edge tabs, we need to adjust the widget
929 // rects and clip rects so that the edges don't get drawn.
930 bool isLeft
= IsLeftToSelectedTab(aFrame
);
931 bool isRight
= !isLeft
&& IsRightToSelectedTab(aFrame
);
933 if (isLeft
|| isRight
) {
934 // HACK ALERT: There appears to be no way to really obtain this value, so
935 // we're forced to just use the default value for Luna (which also happens
936 // to be correct for all the other skins I've tried).
937 int32_t edgeSize
= 2;
939 // Armed with the size of the edge, we now need to either shift to the
940 // left or to the right. The clip rect won't include this extra area, so
941 // we know that we're effectively shifting the edge out of view (such that
942 // it won't be painted).
944 // The right edge should not be drawn. Extend our rect by the edge
946 widgetRect
.right
+= edgeSize
;
948 // The left edge should not be drawn. Move the widget rect's left coord
950 widgetRect
.left
-= edgeSize
;
954 // widgetRect is the bounding box for a widget, yet the scale track is only
955 // a small portion of this size, so the edges of the scale need to be
956 // adjusted to the real size of the track.
957 if (aAppearance
== StyleAppearance::Range
) {
959 GetThemeBackgroundContentRect(theme
, hdc
, part
, state
, &widgetRect
,
963 GetThemePartSize(theme
, hdc
, part
, state
, &widgetRect
, TS_TRUE
, &siz
);
965 // When rounding is necessary, we round the position of the track
966 // away from the chevron of the thumb to make it look better.
967 if (IsRangeHorizontal(aFrame
)) {
968 contentRect
.top
+= (contentRect
.bottom
- contentRect
.top
- siz
.cy
) / 2;
969 contentRect
.bottom
= contentRect
.top
+ siz
.cy
;
971 if (!IsFrameRTL(aFrame
)) {
972 contentRect
.left
+= (contentRect
.right
- contentRect
.left
- siz
.cx
) / 2;
973 contentRect
.right
= contentRect
.left
+ siz
.cx
;
976 (contentRect
.right
- contentRect
.left
- siz
.cx
) / 2;
977 contentRect
.left
= contentRect
.right
- siz
.cx
;
981 DrawThemeBackground(theme
, hdc
, part
, state
, &contentRect
, &clipRect
);
982 } else if (aAppearance
== StyleAppearance::NumberInput
||
983 aAppearance
== StyleAppearance::Textfield
||
984 aAppearance
== StyleAppearance::Textarea
) {
985 DrawThemeBackground(theme
, hdc
, part
, state
, &widgetRect
, &clipRect
);
987 if (state
== TFS_EDITBORDER_DISABLED
) {
988 InflateRect(&widgetRect
, -1, -1);
989 ::FillRect(hdc
, &widgetRect
, reinterpret_cast<HBRUSH
>(COLOR_BTNFACE
+ 1));
991 } else if (aAppearance
== StyleAppearance::ProgressBar
) {
992 // DrawThemeBackground renders each corner with a solid white pixel.
993 // Restore these pixels to the underlying color. Tracks are rendered
994 // using alpha recovery, so this makes the corners transparent.
996 color
= GetPixel(hdc
, widgetRect
.left
, widgetRect
.top
);
997 DrawThemeBackground(theme
, hdc
, part
, state
, &widgetRect
, &clipRect
);
998 SetPixel(hdc
, widgetRect
.left
, widgetRect
.top
, color
);
999 SetPixel(hdc
, widgetRect
.right
- 1, widgetRect
.top
, color
);
1000 SetPixel(hdc
, widgetRect
.right
- 1, widgetRect
.bottom
- 1, color
);
1001 SetPixel(hdc
, widgetRect
.left
, widgetRect
.bottom
- 1, color
);
1002 } else if (aAppearance
== StyleAppearance::Progresschunk
) {
1003 DrawThemedProgressMeter(aFrame
, aAppearance
, theme
, hdc
, part
, state
,
1004 &widgetRect
, &clipRect
);
1006 // If part is negative, the element wishes us to not render a themed
1007 // background, instead opting to be drawn specially below.
1008 else if (part
>= 0) {
1009 DrawThemeBackground(theme
, hdc
, part
, state
, &widgetRect
, &clipRect
);
1012 // Draw focus rectangles for range elements
1013 // XXX it'd be nice to draw these outside of the frame
1014 if (aAppearance
== StyleAppearance::Range
) {
1015 ElementState contentState
= GetContentState(aFrame
, aAppearance
);
1017 if (contentState
.HasState(ElementState::FOCUSRING
)) {
1019 HPEN hPen
= nullptr;
1021 uint8_t id
= SaveDC(hdc
);
1023 ::SelectClipRgn(hdc
, nullptr);
1024 ::GetViewportOrgEx(hdc
, &vpOrg
);
1025 ::SetBrushOrgEx(hdc
, vpOrg
.x
+ widgetRect
.left
, vpOrg
.y
+ widgetRect
.top
,
1027 ::SetTextColor(hdc
, 0);
1028 ::DrawFocusRect(hdc
, &widgetRect
);
1029 ::RestoreDC(hdc
, id
);
1031 ::DeleteObject(hPen
);
1034 } else if (aAppearance
== StyleAppearance::Toolbar
&& state
== 0) {
1035 // Draw toolbar separator lines above all toolbars except the first one.
1036 // The lines are part of the Rebar theme, which is loaded for
1037 // StyleAppearance::Toolbox.
1038 theme
= GetTheme(StyleAppearance::Toolbox
);
1039 if (!theme
) return NS_ERROR_FAILURE
;
1041 widgetRect
.bottom
= widgetRect
.top
+ TB_SEPARATOR_HEIGHT
;
1042 DrawThemeEdge(theme
, hdc
, RP_BAND
, 0, &widgetRect
, EDGE_ETCHED
, BF_TOP
,
1046 nativeDrawing
.EndNativeDrawing();
1048 if (nativeDrawing
.ShouldRenderAgain()) goto RENDER_AGAIN
;
1050 nativeDrawing
.PaintToContext();
1055 bool nsNativeThemeWin::CreateWebRenderCommandsForWidget(
1056 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
1057 const layers::StackingContextHelper
& aSc
,
1058 layers::RenderRootStateManager
* aManager
, nsIFrame
* aFrame
,
1059 StyleAppearance aAppearance
, const nsRect
& aRect
) {
1060 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1061 return Theme::CreateWebRenderCommandsForWidget(
1062 aBuilder
, aResources
, aSc
, aManager
, aFrame
, aAppearance
, aRect
);
1067 static void ScaleForFrameDPI(LayoutDeviceIntMargin
* aMargin
, nsIFrame
* aFrame
) {
1068 double themeScale
= GetThemeDpiScaleFactor(aFrame
);
1069 if (themeScale
!= 1.0) {
1070 aMargin
->top
= NSToIntRound(aMargin
->top
* themeScale
);
1071 aMargin
->left
= NSToIntRound(aMargin
->left
* themeScale
);
1072 aMargin
->bottom
= NSToIntRound(aMargin
->bottom
* themeScale
);
1073 aMargin
->right
= NSToIntRound(aMargin
->right
* themeScale
);
1077 static void ScaleForFrameDPI(LayoutDeviceIntSize
* aSize
, nsIFrame
* aFrame
) {
1078 double themeScale
= GetThemeDpiScaleFactor(aFrame
);
1079 if (themeScale
!= 1.0) {
1080 aSize
->width
= NSToIntRound(aSize
->width
* themeScale
);
1081 aSize
->height
= NSToIntRound(aSize
->height
* themeScale
);
1085 LayoutDeviceIntMargin
nsNativeThemeWin::GetWidgetBorder(
1086 nsDeviceContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1087 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
1088 return Theme::GetWidgetBorder(aContext
, aFrame
, aAppearance
);
1091 LayoutDeviceIntMargin result
;
1092 mozilla::Maybe
<nsUXThemeClass
> themeClass
= GetThemeClass(aAppearance
);
1093 HTHEME theme
= NULL
;
1094 if (!themeClass
.isNothing()) {
1095 theme
= nsUXThemeData::GetTheme(themeClass
.value());
1098 result
= ClassicGetWidgetBorder(aContext
, aFrame
, aAppearance
);
1099 ScaleForFrameDPI(&result
, aFrame
);
1103 if (!WidgetIsContainer(aAppearance
) ||
1104 aAppearance
== StyleAppearance::Toolbox
||
1105 aAppearance
== StyleAppearance::Tabpanel
)
1106 return result
; // Don't worry about it.
1108 int32_t part
, state
;
1109 nsresult rv
= GetThemePartAndState(aFrame
, aAppearance
, part
, state
);
1110 if (NS_FAILED(rv
)) return result
;
1112 if (aAppearance
== StyleAppearance::Toolbar
) {
1113 // make space for the separator line above all toolbars but the first
1114 if (state
== 0) result
.top
= TB_SEPARATOR_HEIGHT
;
1118 result
= GetCachedWidgetBorder(theme
, themeClass
.value(), aAppearance
, part
,
1121 // Remove the edges for tabs that are before or after the selected tab,
1122 if (aAppearance
== StyleAppearance::Tab
) {
1123 if (IsLeftToSelectedTab(aFrame
))
1124 // Remove the right edge, since we won't be drawing it.
1126 else if (IsRightToSelectedTab(aFrame
))
1127 // Remove the left edge, since we won't be drawing it.
1131 if (aFrame
&& (aAppearance
== StyleAppearance::NumberInput
||
1132 aAppearance
== StyleAppearance::Textfield
||
1133 aAppearance
== StyleAppearance::Textarea
)) {
1134 nsIContent
* content
= aFrame
->GetContent();
1135 if (content
&& content
->IsHTMLElement()) {
1136 // We need to pad textfields by 1 pixel, since the caret will draw
1137 // flush against the edge by default if we don't.
1139 result
.left
.value
++;
1140 result
.bottom
.value
++;
1141 result
.right
.value
++;
1145 ScaleForFrameDPI(&result
, aFrame
);
1149 bool nsNativeThemeWin::GetWidgetPadding(nsDeviceContext
* aContext
,
1151 StyleAppearance aAppearance
,
1152 LayoutDeviceIntMargin
* aResult
) {
1153 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
1154 return Theme::GetWidgetPadding(aContext
, aFrame
, aAppearance
, aResult
);
1158 HANDLE theme
= GetTheme(aAppearance
);
1160 ok
= ClassicGetWidgetPadding(aContext
, aFrame
, aAppearance
, aResult
);
1161 ScaleForFrameDPI(aResult
, aFrame
);
1165 /* textfields need extra pixels on all sides, otherwise they wrap their
1166 * content too tightly. The actual border is drawn 1px inside the specified
1167 * rectangle, so Gecko will end up making the contents look too small.
1168 * Instead, we add 2px padding for the contents and fix this. (Used to be 1px
1169 * added, see bug 430212)
1171 if (aAppearance
== StyleAppearance::NumberInput
||
1172 aAppearance
== StyleAppearance::Textfield
||
1173 aAppearance
== StyleAppearance::Textarea
) {
1174 aResult
->top
= aResult
->bottom
= 2;
1175 aResult
->left
= aResult
->right
= 2;
1176 ScaleForFrameDPI(aResult
, aFrame
);
1178 } else if (IsHTMLContent(aFrame
) &&
1179 (aAppearance
== StyleAppearance::Menulist
||
1180 aAppearance
== StyleAppearance::MenulistButton
)) {
1181 /* For content menulist controls, we need an extra pixel so that we have
1182 * room to draw our focus rectangle stuff. Otherwise, the focus rect might
1183 * overlap the control's border.
1185 aResult
->top
= aResult
->bottom
= 1;
1186 aResult
->left
= aResult
->right
= 1;
1187 ScaleForFrameDPI(aResult
, aFrame
);
1191 int32_t right
, left
, top
, bottom
;
1192 right
= left
= top
= bottom
= 0;
1193 switch (aAppearance
) {
1194 case StyleAppearance::Button
:
1195 if (aFrame
->GetContent()->IsXULElement()) {
1205 if (IsFrameRTL(aFrame
)) {
1206 aResult
->right
= left
;
1207 aResult
->left
= right
;
1209 aResult
->right
= right
;
1210 aResult
->left
= left
;
1213 aResult
->bottom
= bottom
;
1215 ScaleForFrameDPI(aResult
, aFrame
);
1219 bool nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext
* aContext
,
1221 StyleAppearance aAppearance
,
1222 nsRect
* aOverflowRect
) {
1223 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1224 return Theme::GetWidgetOverflow(aContext
, aFrame
, aAppearance
,
1228 /* This is disabled for now, because it causes invalidation problems --
1229 * see bug 420381. The effect of not updating the overflow area is that
1230 * for dropdown buttons in content areas, there is a 1px border on 3 sides
1231 * where, if invalidated, the dropdown control probably won't be repainted.
1232 * This is fairly minor, as by default there is nothing in that area, and
1233 * a border only shows up if the widget is being hovered.
1235 * TODO(jwatt): Figure out what do to about
1236 * StyleAppearance::MozMenulistArrowButton too.
1239 /* We explicitly draw dropdown buttons in HTML content 1px bigger up, right,
1240 * and bottom so that they overlap the dropdown's border like they're
1243 if (aAppearance
== StyleAppearance::MenulistButton
&&
1244 IsHTMLContent(aFrame
) &&
1245 !IsWidgetStyled(aFrame
->GetParent()->PresContext(),
1246 aFrame
->GetParent(),
1247 StyleAppearance::Menulist
))
1249 int32_t p2a
= aContext
->AppUnitsPerDevPixel();
1250 /* Note: no overflow on the left */
1251 nsMargin
m(p2a
, p2a
, p2a
, 0);
1252 aOverflowRect
->Inflate (m
);
1260 LayoutDeviceIntSize
nsNativeThemeWin::GetMinimumWidgetSize(
1261 nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
1262 StyleAppearance aAppearance
) {
1263 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
1264 return Theme::GetMinimumWidgetSize(aPresContext
, aFrame
, aAppearance
);
1267 mozilla::Maybe
<nsUXThemeClass
> themeClass
= GetThemeClass(aAppearance
);
1268 HTHEME theme
= NULL
;
1269 if (!themeClass
.isNothing()) {
1270 theme
= nsUXThemeData::GetTheme(themeClass
.value());
1273 auto result
= ClassicGetMinimumWidgetSize(aFrame
, aAppearance
);
1274 ScaleForFrameDPI(&result
, aFrame
);
1278 switch (aAppearance
) {
1279 case StyleAppearance::NumberInput
:
1280 case StyleAppearance::Textfield
:
1281 case StyleAppearance::Toolbox
:
1282 case StyleAppearance::Toolbar
:
1283 case StyleAppearance::Progresschunk
:
1284 case StyleAppearance::Tabpanels
:
1285 case StyleAppearance::Tabpanel
:
1286 case StyleAppearance::Listbox
:
1287 case StyleAppearance::Treeview
:
1288 return {}; // Don't worry about it.
1293 // Call GetSystemMetrics to determine size for WinXP scrollbars
1294 // (GetThemeSysSize API returns the optimal size for the theme, but
1295 // Windows appears to always use metrics when drawing standard scrollbars)
1296 THEMESIZE sizeReq
= TS_TRUE
; // Best-fit size
1297 switch (aAppearance
) {
1298 case StyleAppearance::ProgressBar
:
1299 // Best-fit size for progress meters is too large for most
1300 // themes. We want these widgets to be able to really shrink
1301 // down, so use the min-size request value (of 0).
1305 case StyleAppearance::RangeThumb
: {
1306 LayoutDeviceIntSize
result(12, 20);
1307 if (!IsRangeHorizontal(aFrame
)) {
1308 std::swap(result
.width
, result
.height
);
1310 ScaleForFrameDPI(&result
, aFrame
);
1314 case StyleAppearance::Separator
: {
1315 // that's 2px left margin, 2px right margin and 2px separator
1316 // (the margin is drawn as part of the separator, though)
1317 LayoutDeviceIntSize
result(6, 0);
1318 ScaleForFrameDPI(&result
, aFrame
);
1322 case StyleAppearance::Button
:
1323 // We should let HTML buttons shrink to their min size.
1324 // FIXME bug 403934: We should probably really separate
1325 // GetPreferredWidgetSize from GetMinimumWidgetSize, so callers can
1326 // use the one they want.
1327 if (aFrame
->GetContent()->IsHTMLElement()) {
1336 int32_t part
, state
;
1337 nsresult rv
= GetThemePartAndState(aFrame
, aAppearance
, part
, state
);
1338 if (NS_FAILED(rv
)) {
1342 LayoutDeviceIntSize result
;
1343 rv
= GetCachedMinimumWidgetSize(aFrame
, theme
, themeClass
.value(),
1344 aAppearance
, part
, state
, sizeReq
, &result
);
1345 ScaleForFrameDPI(&result
, aFrame
);
1350 nsNativeThemeWin::WidgetStateChanged(nsIFrame
* aFrame
,
1351 StyleAppearance aAppearance
,
1352 nsAtom
* aAttribute
, bool* aShouldRepaint
,
1353 const nsAttrValue
* aOldValue
) {
1354 // Some widget types just never change state.
1355 if (aAppearance
== StyleAppearance::Toolbox
||
1356 aAppearance
== StyleAppearance::Toolbar
||
1357 aAppearance
== StyleAppearance::Progresschunk
||
1358 aAppearance
== StyleAppearance::ProgressBar
||
1359 aAppearance
== StyleAppearance::Tabpanels
||
1360 aAppearance
== StyleAppearance::Tabpanel
||
1361 aAppearance
== StyleAppearance::Separator
) {
1362 *aShouldRepaint
= false;
1366 // We need to repaint the dropdown arrow in vista HTML combobox controls when
1367 // the control is closed to get rid of the hover effect.
1368 if ((aAppearance
== StyleAppearance::Menulist
||
1369 aAppearance
== StyleAppearance::MenulistButton
) &&
1370 nsNativeTheme::IsHTMLContent(aFrame
)) {
1371 *aShouldRepaint
= true;
1375 // XXXdwh Not sure what can really be done here. Can at least guess for
1376 // specific widgets that they're highly unlikely to have certain states.
1377 // For example, a toolbar doesn't care about any states.
1379 // Hover/focus/active changed. Always repaint.
1380 *aShouldRepaint
= true;
1382 // Check the attribute to see if it's relevant.
1383 // disabled, checked, dlgtype, default, etc.
1384 *aShouldRepaint
= false;
1385 if (aAttribute
== nsGkAtoms::disabled
|| aAttribute
== nsGkAtoms::checked
||
1386 aAttribute
== nsGkAtoms::selected
||
1387 aAttribute
== nsGkAtoms::visuallyselected
||
1388 aAttribute
== nsGkAtoms::readonly
|| aAttribute
== nsGkAtoms::open
||
1389 aAttribute
== nsGkAtoms::menuactive
|| aAttribute
== nsGkAtoms::focused
)
1390 *aShouldRepaint
= true;
1397 nsNativeThemeWin::ThemeChanged() {
1398 nsUXThemeData::Invalidate();
1399 memset(mBorderCacheValid
, 0, sizeof(mBorderCacheValid
));
1400 memset(mMinimumWidgetSizeCacheValid
, 0, sizeof(mMinimumWidgetSizeCacheValid
));
1401 mGutterSizeCacheValid
= false;
1405 bool nsNativeThemeWin::ThemeSupportsWidget(nsPresContext
* aPresContext
,
1407 StyleAppearance aAppearance
) {
1408 // XXXdwh We can go even further and call the API to ask if support exists for
1409 // specific widgets.
1411 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
1412 return Theme::ThemeSupportsWidget(aPresContext
, aFrame
, aAppearance
);
1415 HANDLE theme
= GetTheme(aAppearance
);
1416 if (theme
|| ClassicThemeSupportsWidget(aFrame
, aAppearance
))
1417 // turn off theming for some HTML widgets styled by the page
1418 return !IsWidgetStyled(aPresContext
, aFrame
, aAppearance
);
1423 bool nsNativeThemeWin::ThemeDrawsFocusForWidget(nsIFrame
* aFrame
,
1424 StyleAppearance aAppearance
) {
1425 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1426 return Theme::ThemeDrawsFocusForWidget(aFrame
, aAppearance
);
1428 switch (aAppearance
) {
1429 case StyleAppearance::Menulist
:
1430 case StyleAppearance::MenulistButton
:
1431 case StyleAppearance::Textarea
:
1432 case StyleAppearance::Textfield
:
1433 case StyleAppearance::NumberInput
:
1440 bool nsNativeThemeWin::ThemeNeedsComboboxDropmarker() { return true; }
1442 nsITheme::Transparency
nsNativeThemeWin::GetWidgetTransparency(
1443 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1444 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1445 return Theme::GetWidgetTransparency(aFrame
, aAppearance
);
1448 switch (aAppearance
) {
1449 case StyleAppearance::ProgressBar
:
1450 case StyleAppearance::Progresschunk
:
1451 case StyleAppearance::Range
:
1452 return eTransparent
;
1457 HANDLE theme
= GetTheme(aAppearance
);
1458 // For the classic theme we don't really have a way of knowing
1460 return eUnknownTransparency
;
1463 int32_t part
, state
;
1464 nsresult rv
= GetThemePartAndState(aFrame
, aAppearance
, part
, state
);
1465 // Fail conservatively
1466 NS_ENSURE_SUCCESS(rv
, eUnknownTransparency
);
1469 // Not a real part code, so IsThemeBackgroundPartiallyTransparent may
1470 // not work, so don't call it.
1471 return eUnknownTransparency
;
1474 if (IsThemeBackgroundPartiallyTransparent(theme
, part
, state
))
1475 return eTransparent
;
1479 /* Windows 9x/NT/2000/Classic XP Theme Support */
1481 bool nsNativeThemeWin::ClassicThemeSupportsWidget(nsIFrame
* aFrame
,
1482 StyleAppearance aAppearance
) {
1483 switch (aAppearance
) {
1484 case StyleAppearance::Button
:
1485 case StyleAppearance::NumberInput
:
1486 case StyleAppearance::Textfield
:
1487 case StyleAppearance::Textarea
:
1488 case StyleAppearance::Range
:
1489 case StyleAppearance::RangeThumb
:
1490 case StyleAppearance::Menulist
:
1491 case StyleAppearance::MenulistButton
:
1492 case StyleAppearance::Listbox
:
1493 case StyleAppearance::Treeview
:
1494 case StyleAppearance::ProgressBar
:
1495 case StyleAppearance::Progresschunk
:
1496 case StyleAppearance::Tab
:
1497 case StyleAppearance::Tabpanel
:
1498 case StyleAppearance::Tabpanels
:
1505 LayoutDeviceIntMargin
nsNativeThemeWin::ClassicGetWidgetBorder(
1506 nsDeviceContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1507 LayoutDeviceIntMargin result
;
1508 switch (aAppearance
) {
1509 case StyleAppearance::Button
:
1510 result
.top
= result
.left
= result
.bottom
= result
.right
= 2;
1512 case StyleAppearance::Listbox
:
1513 case StyleAppearance::Treeview
:
1514 case StyleAppearance::Menulist
:
1515 case StyleAppearance::MenulistButton
:
1516 case StyleAppearance::Tab
:
1517 case StyleAppearance::NumberInput
:
1518 case StyleAppearance::Textfield
:
1519 case StyleAppearance::Textarea
:
1520 result
.top
= result
.left
= result
.bottom
= result
.right
= 2;
1522 case StyleAppearance::ProgressBar
:
1523 result
.top
= result
.left
= result
.bottom
= result
.right
= 1;
1526 result
.top
= result
.bottom
= result
.left
= result
.right
= 0;
1532 bool nsNativeThemeWin::ClassicGetWidgetPadding(nsDeviceContext
* aContext
,
1534 StyleAppearance aAppearance
,
1535 LayoutDeviceIntMargin
* aResult
) {
1536 switch (aAppearance
) {
1537 case StyleAppearance::ProgressBar
:
1538 (*aResult
).top
= (*aResult
).left
= (*aResult
).bottom
= (*aResult
).right
=
1546 LayoutDeviceIntSize
nsNativeThemeWin::ClassicGetMinimumWidgetSize(
1547 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1548 LayoutDeviceIntSize result
;
1549 switch (aAppearance
) {
1550 case StyleAppearance::RangeThumb
: {
1551 if (IsRangeHorizontal(aFrame
)) {
1560 case StyleAppearance::Menulist
:
1561 case StyleAppearance::MenulistButton
:
1562 case StyleAppearance::Button
:
1563 case StyleAppearance::Listbox
:
1564 case StyleAppearance::Treeview
:
1565 case StyleAppearance::NumberInput
:
1566 case StyleAppearance::Textfield
:
1567 case StyleAppearance::Textarea
:
1568 case StyleAppearance::Progresschunk
:
1569 case StyleAppearance::ProgressBar
:
1570 case StyleAppearance::Tab
:
1571 case StyleAppearance::Tabpanel
:
1572 case StyleAppearance::Tabpanels
:
1573 // no minimum widget size
1582 nsresult
nsNativeThemeWin::ClassicGetThemePartAndState(
1583 nsIFrame
* aFrame
, StyleAppearance aAppearance
, int32_t& aPart
,
1584 int32_t& aState
, bool& aFocused
) {
1586 switch (aAppearance
) {
1587 case StyleAppearance::Button
: {
1589 aState
= DFCS_BUTTONPUSH
;
1592 ElementState contentState
= GetContentState(aFrame
, aAppearance
);
1593 if (contentState
.HasState(ElementState::DISABLED
)) {
1594 aState
|= DFCS_INACTIVE
;
1595 } else if (IsOpenButton(aFrame
)) {
1596 aState
|= DFCS_PUSHED
;
1597 } else if (IsCheckedButton(aFrame
)) {
1598 aState
|= DFCS_CHECKED
;
1600 if (contentState
.HasAllStates(ElementState::ACTIVE
|
1601 ElementState::HOVER
)) {
1602 aState
|= DFCS_PUSHED
;
1603 // The down state is flat if the button is focusable
1604 if (aFrame
->StyleUI()->UserFocus() == StyleUserFocus::Normal
) {
1605 if (!aFrame
->GetContent()->IsHTMLElement()) aState
|= DFCS_FLAT
;
1610 // On Windows, focused buttons are always drawn as such by the native
1611 // theme, that's why we check ElementState::FOCUS instead of
1612 // ElementState::FOCUSRING.
1613 if (contentState
.HasState(ElementState::FOCUS
) ||
1614 (aState
== DFCS_BUTTONPUSH
&& IsDefaultButton(aFrame
))) {
1621 case StyleAppearance::Listbox
:
1622 case StyleAppearance::Treeview
:
1623 case StyleAppearance::NumberInput
:
1624 case StyleAppearance::Textfield
:
1625 case StyleAppearance::Textarea
:
1626 case StyleAppearance::Menulist
:
1627 case StyleAppearance::MenulistButton
:
1628 case StyleAppearance::Range
:
1629 case StyleAppearance::RangeThumb
:
1630 case StyleAppearance::Progresschunk
:
1631 case StyleAppearance::ProgressBar
:
1632 case StyleAppearance::Tab
:
1633 case StyleAppearance::Tabpanel
:
1634 case StyleAppearance::Tabpanels
:
1635 // these don't use DrawFrameControl
1638 return NS_ERROR_FAILURE
;
1642 // Draw classic Windows tab
1643 // (no system API for this, but DrawEdge can draw all the parts of a tab)
1644 static void DrawTab(HDC hdc
, const RECT
& R
, int32_t aPosition
, bool aSelected
,
1645 bool aDrawLeft
, bool aDrawRight
) {
1646 int32_t leftFlag
, topFlag
, rightFlag
, lightFlag
, shadeFlag
;
1647 RECT topRect
, sideRect
, bottomRect
, lightRect
, shadeRect
;
1648 int32_t selectedOffset
, lOffset
, rOffset
;
1650 selectedOffset
= aSelected
? 1 : 0;
1651 lOffset
= aDrawLeft
? 2 : 0;
1652 rOffset
= aDrawRight
? 2 : 0;
1654 // Get info for tab orientation/position (Left, Top, Right, Bottom)
1655 switch (aPosition
) {
1659 rightFlag
= BF_BOTTOM
;
1660 lightFlag
= BF_DIAGONAL_ENDTOPRIGHT
;
1661 shadeFlag
= BF_DIAGONAL_ENDBOTTOMRIGHT
;
1663 ::SetRect(&topRect
, R
.left
, R
.top
+ lOffset
, R
.right
, R
.bottom
- rOffset
);
1664 ::SetRect(&sideRect
, R
.left
+ 2, R
.top
, R
.right
- 2 + selectedOffset
,
1666 ::SetRect(&bottomRect
, R
.right
- 2, R
.top
, R
.right
, R
.bottom
);
1667 ::SetRect(&lightRect
, R
.left
, R
.top
, R
.left
+ 3, R
.top
+ 3);
1668 ::SetRect(&shadeRect
, R
.left
+ 1, R
.bottom
- 2, R
.left
+ 2, R
.bottom
- 1);
1673 rightFlag
= BF_RIGHT
;
1674 lightFlag
= BF_DIAGONAL_ENDTOPRIGHT
;
1675 shadeFlag
= BF_DIAGONAL_ENDBOTTOMRIGHT
;
1677 ::SetRect(&topRect
, R
.left
+ lOffset
, R
.top
, R
.right
- rOffset
, R
.bottom
);
1678 ::SetRect(&sideRect
, R
.left
, R
.top
+ 2, R
.right
,
1679 R
.bottom
- 1 + selectedOffset
);
1680 ::SetRect(&bottomRect
, R
.left
, R
.bottom
- 1, R
.right
, R
.bottom
);
1681 ::SetRect(&lightRect
, R
.left
, R
.top
, R
.left
+ 3, R
.top
+ 3);
1682 ::SetRect(&shadeRect
, R
.right
- 2, R
.top
+ 1, R
.right
- 1, R
.top
+ 2);
1687 rightFlag
= BF_BOTTOM
;
1688 lightFlag
= BF_DIAGONAL_ENDTOPLEFT
;
1689 shadeFlag
= BF_DIAGONAL_ENDBOTTOMLEFT
;
1691 ::SetRect(&topRect
, R
.left
, R
.top
+ lOffset
, R
.right
, R
.bottom
- rOffset
);
1692 ::SetRect(&sideRect
, R
.left
+ 2 - selectedOffset
, R
.top
, R
.right
- 2,
1694 ::SetRect(&bottomRect
, R
.left
, R
.top
, R
.left
+ 2, R
.bottom
);
1695 ::SetRect(&lightRect
, R
.right
- 3, R
.top
, R
.right
- 1, R
.top
+ 2);
1696 ::SetRect(&shadeRect
, R
.right
- 2, R
.bottom
- 3, R
.right
, R
.bottom
- 1);
1700 topFlag
= BF_BOTTOM
;
1701 rightFlag
= BF_RIGHT
;
1702 lightFlag
= BF_DIAGONAL_ENDTOPLEFT
;
1703 shadeFlag
= BF_DIAGONAL_ENDBOTTOMLEFT
;
1705 ::SetRect(&topRect
, R
.left
+ lOffset
, R
.top
, R
.right
- rOffset
, R
.bottom
);
1706 ::SetRect(&sideRect
, R
.left
, R
.top
+ 2 - selectedOffset
, R
.right
,
1708 ::SetRect(&bottomRect
, R
.left
, R
.top
, R
.right
, R
.top
+ 2);
1709 ::SetRect(&lightRect
, R
.left
, R
.bottom
- 3, R
.left
+ 2, R
.bottom
- 1);
1710 ::SetRect(&shadeRect
, R
.right
- 2, R
.bottom
- 3, R
.right
, R
.bottom
- 1);
1717 ::FillRect(hdc
, &R
, (HBRUSH
)(COLOR_3DFACE
+ 1));
1720 ::DrawEdge(hdc
, &topRect
, EDGE_RAISED
, BF_SOFT
| topFlag
);
1723 if (!aSelected
) ::DrawEdge(hdc
, &bottomRect
, EDGE_RAISED
, BF_SOFT
| topFlag
);
1726 if (!aDrawLeft
) leftFlag
= 0;
1727 if (!aDrawRight
) rightFlag
= 0;
1728 ::DrawEdge(hdc
, &sideRect
, EDGE_RAISED
, BF_SOFT
| leftFlag
| rightFlag
);
1730 // Tab Diagonal Corners
1731 if (aDrawLeft
) ::DrawEdge(hdc
, &lightRect
, EDGE_RAISED
, BF_SOFT
| lightFlag
);
1733 if (aDrawRight
) ::DrawEdge(hdc
, &shadeRect
, EDGE_RAISED
, BF_SOFT
| shadeFlag
);
1736 void nsNativeThemeWin::DrawCheckedRect(HDC hdc
, const RECT
& rc
, int32_t fore
,
1737 int32_t back
, HBRUSH defaultBack
) {
1738 static WORD patBits
[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55};
1740 HBITMAP patBmp
= ::CreateBitmap(8, 8, 1, 1, patBits
);
1742 HBRUSH brush
= (HBRUSH
)::CreatePatternBrush(patBmp
);
1744 COLORREF oldForeColor
= ::SetTextColor(hdc
, ::GetSysColor(fore
));
1745 COLORREF oldBackColor
= ::SetBkColor(hdc
, ::GetSysColor(back
));
1748 ::UnrealizeObject(brush
);
1749 ::GetViewportOrgEx(hdc
, &vpOrg
);
1750 ::SetBrushOrgEx(hdc
, vpOrg
.x
+ rc
.left
, vpOrg
.y
+ rc
.top
, nullptr);
1751 HBRUSH oldBrush
= (HBRUSH
)::SelectObject(hdc
, brush
);
1752 ::FillRect(hdc
, &rc
, brush
);
1753 ::SetTextColor(hdc
, oldForeColor
);
1754 ::SetBkColor(hdc
, oldBackColor
);
1755 ::SelectObject(hdc
, oldBrush
);
1756 ::DeleteObject(brush
);
1758 ::FillRect(hdc
, &rc
, defaultBack
);
1760 ::DeleteObject(patBmp
);
1764 nsresult
nsNativeThemeWin::ClassicDrawWidgetBackground(
1765 gfxContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
,
1766 const nsRect
& aRect
, const nsRect
& aDirtyRect
) {
1767 int32_t part
, state
;
1770 rv
= ClassicGetThemePartAndState(aFrame
, aAppearance
, part
, state
, focused
);
1771 if (NS_FAILED(rv
)) return rv
;
1773 if (AssumeThemePartAndStateAreTransparent(part
, state
)) {
1777 gfxFloat p2a
= gfxFloat(aFrame
->PresContext()->AppUnitsPerDevPixel());
1779 gfxRect
tr(aRect
.X(), aRect
.Y(), aRect
.Width(), aRect
.Height()),
1780 dr(aDirtyRect
.X(), aDirtyRect
.Y(), aDirtyRect
.Width(),
1781 aDirtyRect
.Height());
1783 tr
.Scale(1.0 / p2a
);
1784 dr
.Scale(1.0 / p2a
);
1786 gfxWindowsNativeDrawing
nativeDrawing(
1787 aContext
, dr
, GetWidgetNativeDrawingFlags(aAppearance
));
1791 HDC hdc
= nativeDrawing
.BeginNativeDrawing();
1792 if (!hdc
) return NS_ERROR_FAILURE
;
1794 nativeDrawing
.TransformToNativeRect(tr
, widgetRect
);
1797 switch (aAppearance
) {
1799 case StyleAppearance::Button
: {
1801 // draw dark button focus border first
1802 if (HBRUSH brush
= ::GetSysColorBrush(COLOR_3DDKSHADOW
)) {
1803 ::FrameRect(hdc
, &widgetRect
, brush
);
1805 InflateRect(&widgetRect
, -1, -1);
1807 // setup DC to make DrawFrameControl draw correctly
1808 int32_t oldTA
= ::SetTextAlign(hdc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
1809 ::DrawFrameControl(hdc
, &widgetRect
, part
, state
);
1810 ::SetTextAlign(hdc
, oldTA
);
1813 // Draw controls with 2px 3D inset border
1814 case StyleAppearance::NumberInput
:
1815 case StyleAppearance::Textfield
:
1816 case StyleAppearance::Textarea
:
1817 case StyleAppearance::Listbox
:
1818 case StyleAppearance::Menulist
:
1819 case StyleAppearance::MenulistButton
: {
1821 ::DrawEdge(hdc
, &widgetRect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1823 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1825 // Fill in background
1827 if (elementState
.HasState(ElementState::DISABLED
) ||
1828 (aFrame
->GetContent()->IsXULElement() && IsReadOnly(aFrame
)))
1829 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_BTNFACE
+ 1));
1831 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_WINDOW
+ 1));
1835 case StyleAppearance::Treeview
: {
1837 ::DrawEdge(hdc
, &widgetRect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1839 // Fill in window color background
1840 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_WINDOW
+ 1));
1844 // Draw 3D face background controls
1845 case StyleAppearance::ProgressBar
:
1847 ::DrawEdge(hdc
, &widgetRect
, BDR_SUNKENOUTER
, BF_RECT
| BF_MIDDLE
);
1848 InflateRect(&widgetRect
, -1, -1);
1850 case StyleAppearance::Tabpanel
: {
1851 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_BTNFACE
+ 1));
1854 case StyleAppearance::RangeThumb
: {
1855 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1857 ::DrawEdge(hdc
, &widgetRect
, EDGE_RAISED
,
1858 BF_RECT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1859 if (elementState
.HasState(ElementState::DISABLED
)) {
1860 DrawCheckedRect(hdc
, widgetRect
, COLOR_3DFACE
, COLOR_3DHILIGHT
,
1861 (HBRUSH
)COLOR_3DHILIGHT
);
1866 // Draw scale track background
1867 case StyleAppearance::Range
: {
1868 const int32_t trackWidth
= 4;
1869 // When rounding is necessary, we round the position of the track
1870 // away from the chevron of the thumb to make it look better.
1871 if (IsRangeHorizontal(aFrame
)) {
1872 widgetRect
.top
+= (widgetRect
.bottom
- widgetRect
.top
- trackWidth
) / 2;
1873 widgetRect
.bottom
= widgetRect
.top
+ trackWidth
;
1875 if (!IsFrameRTL(aFrame
)) {
1877 (widgetRect
.right
- widgetRect
.left
- trackWidth
) / 2;
1878 widgetRect
.right
= widgetRect
.left
+ trackWidth
;
1881 (widgetRect
.right
- widgetRect
.left
- trackWidth
) / 2;
1882 widgetRect
.left
= widgetRect
.right
- trackWidth
;
1886 ::DrawEdge(hdc
, &widgetRect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1887 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)GetStockObject(GRAY_BRUSH
));
1891 case StyleAppearance::Progresschunk
: {
1892 nsIFrame
* stateFrame
= aFrame
->GetParent();
1893 ElementState elementState
= GetContentState(stateFrame
, aAppearance
);
1895 const bool indeterminate
=
1896 elementState
.HasState(ElementState::INDETERMINATE
);
1897 bool vertical
= IsVerticalProgress(stateFrame
);
1899 nsIContent
* content
= aFrame
->GetContent();
1900 if (!indeterminate
|| !content
) {
1901 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_HIGHLIGHT
+ 1));
1905 RECT overlayRect
= CalculateProgressOverlayRect(
1906 aFrame
, &widgetRect
, vertical
, indeterminate
, true);
1908 ::FillRect(hdc
, &overlayRect
, (HBRUSH
)(COLOR_HIGHLIGHT
+ 1));
1910 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 30)) {
1911 NS_WARNING("unable to animate progress widget!");
1917 case StyleAppearance::Tab
: {
1918 DrawTab(hdc
, widgetRect
, IsBottomTab(aFrame
) ? BF_BOTTOM
: BF_TOP
,
1919 IsSelectedTab(aFrame
), !IsRightToSelectedTab(aFrame
),
1920 !IsLeftToSelectedTab(aFrame
));
1924 case StyleAppearance::Tabpanels
:
1925 ::DrawEdge(hdc
, &widgetRect
, EDGE_RAISED
,
1926 BF_SOFT
| BF_MIDDLE
| BF_LEFT
| BF_RIGHT
| BF_BOTTOM
);
1931 rv
= NS_ERROR_FAILURE
;
1935 nativeDrawing
.EndNativeDrawing();
1937 if (NS_FAILED(rv
)) return rv
;
1939 if (nativeDrawing
.ShouldRenderAgain()) goto RENDER_AGAIN
;
1941 nativeDrawing
.PaintToContext();
1946 uint32_t nsNativeThemeWin::GetWidgetNativeDrawingFlags(
1947 StyleAppearance aAppearance
) {
1948 switch (aAppearance
) {
1949 case StyleAppearance::Button
:
1950 case StyleAppearance::NumberInput
:
1951 case StyleAppearance::Textfield
:
1952 case StyleAppearance::Textarea
:
1953 case StyleAppearance::Menulist
:
1954 case StyleAppearance::MenulistButton
:
1955 return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA
|
1956 gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE
|
1957 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM
;
1959 // need to check these others
1961 return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA
|
1962 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE
|
1963 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM
;
1967 } // namespace mozilla::widget
1969 ///////////////////////////////////////////
1971 ///////////////////////////////////////////
1973 already_AddRefed
<Theme
> do_CreateNativeThemeDoNotUseDirectly() {
1974 return do_AddRef(new nsNativeThemeWin());