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/WindowsVersion.h"
23 #include "mozilla/dom/XULButtonElement.h"
25 #include "nsComboboxControlFrame.h"
26 #include "nsDeviceContext.h"
27 #include "nsGkAtoms.h"
28 #include "nsIContent.h"
29 #include "nsIContentInlines.h"
31 #include "nsLayoutUtils.h"
32 #include "nsLookAndFeel.h"
33 #include "nsNameSpaceManager.h"
35 #include "nsPresContext.h"
38 #include "nsStyleConsts.h"
39 #include "nsTransform2D.h"
44 using namespace mozilla
;
45 using namespace mozilla::gfx
;
46 using namespace mozilla::widget
;
48 using ElementState
= dom::ElementState
;
50 extern mozilla::LazyLogModule gWindowsLog
;
52 namespace mozilla::widget
{
54 nsNativeThemeWin::nsNativeThemeWin()
55 : Theme(ScrollbarStyle()),
56 mProgressDeterminateTimeStamp(TimeStamp::Now()),
57 mProgressIndeterminateTimeStamp(TimeStamp::Now()),
59 mMinimumWidgetSizeCacheValid(),
60 mGutterSizeCacheValid(false) {
61 // If there is a relevant change in forms.css for windows platform,
62 // static widget style variables (e.g. sButtonBorderSize) should be
63 // reinitialized here.
66 nsNativeThemeWin::~nsNativeThemeWin() { nsUXThemeData::Invalidate(); }
68 auto nsNativeThemeWin::IsWidgetNonNative(nsIFrame
* aFrame
,
69 StyleAppearance aAppearance
)
71 if (IsWidgetScrollbarPart(aAppearance
) ||
72 aAppearance
== StyleAppearance::FocusOutline
) {
73 return NonNative::Always
;
76 // We only know how to draw light widgets, so we defer to the non-native
77 // theme when appropriate.
78 if (Theme::ThemeSupportsWidget(aFrame
->PresContext(), aFrame
, aAppearance
) &&
79 LookAndFeel::ColorSchemeForFrame(aFrame
) ==
80 LookAndFeel::ColorScheme::Dark
) {
81 return NonNative::BecauseColorMismatch
;
86 static int32_t GetTopLevelWindowActiveState(nsIFrame
* aFrame
) {
87 // Used by window frame and button box rendering. We can end up in here in
88 // the content process when rendering one of these moz styles freely in a
89 // page. Bail in this case, there is no applicable window focus state.
90 if (!XRE_IsParentProcess()) {
91 return mozilla::widget::themeconst::FS_INACTIVE
;
93 // All headless windows are considered active so they are painted.
94 if (gfxPlatform::IsHeadless()) {
95 return mozilla::widget::themeconst::FS_ACTIVE
;
97 // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
98 // until it finds a real window.
99 nsIWidget
* widget
= aFrame
->GetNearestWidget();
100 nsWindow
* window
= static_cast<nsWindow
*>(widget
);
101 if (!window
) return mozilla::widget::themeconst::FS_INACTIVE
;
102 if (widget
&& !window
->IsTopLevelWidget() &&
103 !(window
= window
->GetParentWindowBase(false)))
104 return mozilla::widget::themeconst::FS_INACTIVE
;
106 if (window
->GetWindowHandle() == ::GetActiveWindow())
107 return mozilla::widget::themeconst::FS_ACTIVE
;
108 return mozilla::widget::themeconst::FS_INACTIVE
;
111 static int32_t GetWindowFrameButtonState(nsIFrame
* aFrame
,
112 ElementState elementState
) {
113 if (GetTopLevelWindowActiveState(aFrame
) ==
114 mozilla::widget::themeconst::FS_INACTIVE
) {
115 if (elementState
.HasState(ElementState::HOVER
))
116 return mozilla::widget::themeconst::BS_HOT
;
117 return mozilla::widget::themeconst::BS_INACTIVE
;
120 if (elementState
.HasState(ElementState::HOVER
)) {
121 if (elementState
.HasState(ElementState::ACTIVE
))
122 return mozilla::widget::themeconst::BS_PUSHED
;
123 return mozilla::widget::themeconst::BS_HOT
;
125 return mozilla::widget::themeconst::BS_NORMAL
;
128 static int32_t GetClassicWindowFrameButtonState(ElementState elementState
) {
129 if (elementState
.HasState(ElementState::ACTIVE
) &&
130 elementState
.HasState(ElementState::HOVER
))
131 return DFCS_BUTTONPUSH
| DFCS_PUSHED
;
132 return DFCS_BUTTONPUSH
;
135 static bool IsTopLevelMenu(nsIFrame
* aFrame
) {
136 auto* menu
= dom::XULButtonElement::FromNodeOrNull(aFrame
->GetContent());
137 return menu
&& menu
->IsOnMenuBar();
140 static MARGINS
GetCheckboxMargins(HANDLE theme
, HDC hdc
) {
141 MARGINS checkboxContent
= {0};
142 GetThemeMargins(theme
, hdc
, MENU_POPUPCHECK
, MCB_NORMAL
, TMT_CONTENTMARGINS
,
143 nullptr, &checkboxContent
);
144 return checkboxContent
;
147 static SIZE
GetCheckboxBGSize(HANDLE theme
, HDC hdc
) {
149 GetThemePartSize(theme
, hdc
, MENU_POPUPCHECK
, MC_CHECKMARKNORMAL
, nullptr,
150 TS_TRUE
, &checkboxSize
);
152 MARGINS checkboxMargins
= GetCheckboxMargins(theme
, hdc
);
154 int leftMargin
= checkboxMargins
.cxLeftWidth
;
155 int rightMargin
= checkboxMargins
.cxRightWidth
;
156 int topMargin
= checkboxMargins
.cyTopHeight
;
157 int bottomMargin
= checkboxMargins
.cyBottomHeight
;
159 int width
= leftMargin
+ checkboxSize
.cx
+ rightMargin
;
160 int height
= topMargin
+ checkboxSize
.cy
+ bottomMargin
;
167 static SIZE
GetCheckboxBGBounds(HANDLE theme
, HDC hdc
) {
168 MARGINS checkboxBGSizing
= {0};
169 MARGINS checkboxBGContent
= {0};
170 GetThemeMargins(theme
, hdc
, MENU_POPUPCHECKBACKGROUND
, MCB_NORMAL
,
171 TMT_SIZINGMARGINS
, nullptr, &checkboxBGSizing
);
172 GetThemeMargins(theme
, hdc
, MENU_POPUPCHECKBACKGROUND
, MCB_NORMAL
,
173 TMT_CONTENTMARGINS
, nullptr, &checkboxBGContent
);
175 #define posdx(d) ((d) > 0 ? d : 0)
178 posdx(checkboxBGContent
.cxRightWidth
- checkboxBGSizing
.cxRightWidth
) +
179 posdx(checkboxBGContent
.cxLeftWidth
- checkboxBGSizing
.cxLeftWidth
);
181 posdx(checkboxBGContent
.cyTopHeight
- checkboxBGSizing
.cyTopHeight
) +
182 posdx(checkboxBGContent
.cyBottomHeight
- checkboxBGSizing
.cyBottomHeight
);
186 SIZE
ret(GetCheckboxBGSize(theme
, hdc
));
192 static SIZE
GetGutterSize(HANDLE theme
, HDC hdc
) {
194 GetThemePartSize(theme
, hdc
, MENU_POPUPGUTTER
, 0, nullptr, TS_TRUE
,
197 SIZE
checkboxBGSize(GetCheckboxBGBounds(theme
, hdc
));
200 GetThemePartSize(theme
, hdc
, MENU_POPUPITEM
, MPI_NORMAL
, nullptr, TS_TRUE
,
203 // Figure out how big the menuitem's icon will be (if present) at current DPI
204 // Needs the system scale for consistency with Windows Theme API.
205 double scaleFactor
= WinUtils::SystemScaleFactor();
206 int iconDevicePixels
= NSToIntRound(16 * scaleFactor
);
207 SIZE iconSize
= {iconDevicePixels
, iconDevicePixels
};
208 // Not really sure what margins should be used here, but this seems to work in
210 MARGINS margins
= {0};
211 GetThemeMargins(theme
, hdc
, MENU_POPUPCHECKBACKGROUND
, MCB_NORMAL
,
212 TMT_CONTENTMARGINS
, nullptr, &margins
);
213 iconSize
.cx
+= margins
.cxLeftWidth
+ margins
.cxRightWidth
;
214 iconSize
.cy
+= margins
.cyTopHeight
+ margins
.cyBottomHeight
;
216 int width
= std::max(
217 itemSize
.cx
, std::max(iconSize
.cx
, checkboxBGSize
.cx
) + gutterSize
.cx
);
218 int height
= std::max(itemSize
.cy
, std::max(iconSize
.cy
, checkboxBGSize
.cy
));
226 SIZE
nsNativeThemeWin::GetCachedGutterSize(HANDLE theme
) {
227 if (mGutterSizeCacheValid
) {
228 return mGutterSizeCache
;
231 mGutterSizeCache
= GetGutterSize(theme
, nullptr);
232 mGutterSizeCacheValid
= true;
234 return mGutterSizeCache
;
237 /* DrawThemeBGRTLAware - render a theme part based on rtl state.
238 * Some widgets are not direction-neutral and need to be drawn reversed for
239 * RTL. Windows provides a way to do this with SetLayout, but this reverses
240 * the entire drawing area of a given device context, which means that its
241 * use will also affect the positioning of the widget. There are two ways
242 * to work around this:
244 * Option 1: Alter the position of the rect that we send so that we cancel
245 * out the positioning effects of SetLayout
246 * Option 2: Create a memory DC with the widgetRect's dimensions, draw onto
247 * that, and then transfer the results back to our DC
249 * This function tries to implement option 1, under the assumption that the
250 * correct way to reverse the effects of SetLayout is to translate the rect
251 * such that the offset from the DC bitmap's left edge to the old rect's
252 * left edge is equal to the offset from the DC bitmap's right edge to the
253 * new rect's right edge. In other words,
254 * (oldRect.left + vpOrg.x) == ((dcBMP.width - vpOrg.x) - newRect.right)
256 static HRESULT
DrawThemeBGRTLAware(HANDLE aTheme
, HDC aHdc
, int aPart
,
257 int aState
, const RECT
* aWidgetRect
,
258 const RECT
* aClipRect
, bool aIsRtl
) {
259 NS_ASSERTION(aTheme
, "Bad theme handle.");
260 NS_ASSERTION(aHdc
, "Bad hdc.");
261 NS_ASSERTION(aWidgetRect
, "Bad rect.");
262 NS_ASSERTION(aClipRect
, "Bad clip rect.");
265 return DrawThemeBackground(aTheme
, aHdc
, aPart
, aState
, aWidgetRect
,
269 HGDIOBJ hObj
= GetCurrentObject(aHdc
, OBJ_BITMAP
);
273 if (hObj
&& GetObject(hObj
, sizeof(bitmap
), &bitmap
) &&
274 GetViewportOrgEx(aHdc
, &vpOrg
)) {
275 RECT
newWRect(*aWidgetRect
);
276 newWRect
.left
= bitmap
.bmWidth
- (aWidgetRect
->right
+ 2 * vpOrg
.x
);
277 newWRect
.right
= bitmap
.bmWidth
- (aWidgetRect
->left
+ 2 * vpOrg
.x
);
280 RECT
* newCRectPtr
= nullptr;
283 newCRect
.top
= aClipRect
->top
;
284 newCRect
.bottom
= aClipRect
->bottom
;
285 newCRect
.left
= bitmap
.bmWidth
- (aClipRect
->right
+ 2 * vpOrg
.x
);
286 newCRect
.right
= bitmap
.bmWidth
- (aClipRect
->left
+ 2 * vpOrg
.x
);
287 newCRectPtr
= &newCRect
;
290 SetLayout(aHdc
, LAYOUT_RTL
);
291 HRESULT hr
= DrawThemeBackground(aTheme
, aHdc
, aPart
, aState
, &newWRect
,
298 return DrawThemeBackground(aTheme
, aHdc
, aPart
, aState
, aWidgetRect
,
303 * Caption button padding data - 'hot' button padding.
304 * These areas are considered hot, in that they activate
305 * a button when hovered or clicked. The button graphic
306 * is drawn inside the padding border. Unrecognized themes
307 * are treated as their recognized counterparts for now.
308 * left top right bottom
309 * classic min 1 2 0 1
310 * classic max 0 2 1 1
311 * classic close 1 2 2 1
313 * aero basic min 1 2 0 2
314 * aero basic max 0 2 1 2
315 * aero basic close 1 2 1 2
317 * 'cold' button padding - generic button padding, should
319 * left top right bottom
320 * classic min 0 0 0 0
321 * classic max 0 0 0 0
322 * classic close 0 0 0 0
324 * aero basic min 0 0 1 0
325 * aero basic max 1 0 0 0
326 * aero basic close 0 0 0 0
329 enum CaptionDesktopTheme
{
335 CAPTIONBUTTON_MINIMIZE
= 0,
336 CAPTIONBUTTON_RESTORE
,
340 struct CaptionButtonPadding
{
344 // RECT: left, top, right, bottom
345 static CaptionButtonPadding buttonData
[3] = {
346 {{{1, 2, 0, 1}, {0, 2, 1, 1}, {1, 2, 2, 1}}},
347 {{{1, 2, 0, 2}, {0, 2, 1, 2}, {1, 2, 2, 2}}},
348 {{{0, 2, 0, 2}, {0, 2, 1, 2}, {1, 2, 2, 2}}}};
350 // Adds "hot" caption button padding to minimum widget size.
351 static void AddPaddingRect(LayoutDeviceIntSize
* aSize
, CaptionButton button
) {
353 RECT offset
= buttonData
[CAPTION_BASIC
].hotPadding
[button
];
354 aSize
->width
+= offset
.left
+ offset
.right
;
355 aSize
->height
+= offset
.top
+ offset
.bottom
;
358 // If we've added padding to the minimum widget size, offset
359 // the area we draw into to compensate.
360 static void OffsetBackgroundRect(RECT
& rect
, CaptionButton button
) {
361 RECT offset
= buttonData
[CAPTION_BASIC
].hotPadding
[button
];
362 rect
.left
+= offset
.left
;
363 rect
.top
+= offset
.top
;
364 rect
.right
-= offset
.right
;
365 rect
.bottom
-= offset
.bottom
;
369 * Notes on progress track and meter part constants:
371 * PP_BAR(_VERT) - base progress track
372 * PP_TRANSPARENTBAR(_VERT) - transparent progress track. this only works if
373 * the underlying surface supports alpha. otherwise
374 * theme lib's DrawThemeBackground falls back on
375 * opaque PP_BAR. we currently don't use this.
376 * PP_CHUNK(_VERT) - xp progress meter. this does not draw an xp style
377 * progress w/chunks, it draws fill using the chunk
380 * PP_FILL(_VERT) - progress meter. these have four states/colors.
381 * PP_PULSEOVERLAY(_VERT) - white reflection - an overlay, not sure what this
383 * PP_MOVEOVERLAY(_VERT) - green pulse - the pulse effect overlay on
384 * determined progress bars. we also use this for
385 * indeterminate chunk.
387 * Notes on state constants:
388 * PBBS_NORMAL - green progress
389 * PBBVS_PARTIAL/PBFVS_ERROR - red error progress
390 * PBFS_PAUSED - yellow paused progress
392 * There is no common controls style indeterminate part on vista and up.
396 * Progress bar related constants. These values are found by experimenting and
397 * comparing against native widgets used by the system. They are very unlikely
398 * exact but try to not be too wrong.
400 // The amount of time we animate progress meters parts across the frame.
401 static const double kProgressDeterminateTimeSpan
= 3.0;
402 static const double kProgressIndeterminateTimeSpan
= 5.0;
403 // The width of the overlay used to animate the horizontal progress bar (Vista
405 static const int32_t kProgressHorizontalOverlaySize
= 120;
406 // The height of the overlay used to animate the vertical progress bar (Vista
408 static const int32_t kProgressVerticalOverlaySize
= 45;
409 // The height of the overlay used for the vertical indeterminate progress bar
410 // (Vista and later).
411 static const int32_t kProgressVerticalIndeterminateOverlaySize
= 60;
412 // The width of the overlay used to animate the indeterminate progress bar
413 // (Windows Classic).
414 static const int32_t kProgressClassicOverlaySize
= 40;
417 * GetProgressOverlayStyle - returns the proper overlay part for themed
418 * progress bars based on os and orientation.
420 static int32_t GetProgressOverlayStyle(bool aIsVertical
) {
421 return aIsVertical
? PP_MOVEOVERLAYVERT
: PP_MOVEOVERLAY
;
425 * GetProgressOverlaySize - returns the minimum width or height for themed
426 * progress bar overlays. This includes the width of indeterminate chunks
427 * and vista pulse overlays.
429 static int32_t GetProgressOverlaySize(bool aIsVertical
, bool aIsIndeterminate
) {
431 return aIsIndeterminate
? kProgressVerticalIndeterminateOverlaySize
432 : kProgressVerticalOverlaySize
;
434 return kProgressHorizontalOverlaySize
;
438 * IsProgressMeterFilled - Determines if a progress meter is at 100% fill based
439 * on a comparison of the current value and maximum.
441 static bool IsProgressMeterFilled(nsIFrame
* aFrame
) {
442 NS_ENSURE_TRUE(aFrame
, false);
443 nsIFrame
* parentFrame
= aFrame
->GetParent();
444 NS_ENSURE_TRUE(parentFrame
, false);
445 return nsNativeTheme::GetProgressValue(parentFrame
) ==
446 nsNativeTheme::GetProgressMaxValue(parentFrame
);
450 * CalculateProgressOverlayRect - returns the padded overlay animation rect
451 * used in rendering progress bars. Resulting rects are used in rendering
452 * vista+ pulse overlays and indeterminate progress meters. Graphics should
453 * be rendered at the origin.
455 RECT
nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame
* aFrame
,
458 bool aIsIndeterminate
,
460 NS_ASSERTION(aFrame
, "bad frame pointer");
461 NS_ASSERTION(aWidgetRect
, "bad rect pointer");
463 int32_t frameSize
= aIsVertical
? aWidgetRect
->bottom
- aWidgetRect
->top
464 : aWidgetRect
->right
- aWidgetRect
->left
;
466 // Recycle a set of progress pulse timers - these timers control the position
467 // of all progress overlays and indeterminate chunks that get rendered.
468 double span
= aIsIndeterminate
? kProgressIndeterminateTimeSpan
469 : kProgressDeterminateTimeSpan
;
471 if (!aIsIndeterminate
) {
472 if (TimeStamp::Now() >
473 (mProgressDeterminateTimeStamp
+ TimeDuration::FromSeconds(span
))) {
474 mProgressDeterminateTimeStamp
= TimeStamp::Now();
476 period
= TimeStamp::Now() - mProgressDeterminateTimeStamp
;
478 if (TimeStamp::Now() >
479 (mProgressIndeterminateTimeStamp
+ TimeDuration::FromSeconds(span
))) {
480 mProgressIndeterminateTimeStamp
= TimeStamp::Now();
482 period
= TimeStamp::Now() - mProgressIndeterminateTimeStamp
;
485 double percent
= period
/ TimeDuration::FromSeconds(span
);
487 if (!aIsVertical
&& IsFrameRTL(aFrame
)) percent
= 1 - percent
;
489 RECT overlayRect
= *aWidgetRect
;
492 overlaySize
= GetProgressOverlaySize(aIsVertical
, aIsIndeterminate
);
494 overlaySize
= kProgressClassicOverlaySize
;
497 // Calculate a bounds that is larger than the meters frame such that the
498 // overlay starts and ends completely off the edge of the frame:
499 // [overlay][frame][overlay]
500 // This also yields a nice delay on rotation. Use overlaySize as the minimum
501 // size for [overlay] based on the graphics dims. If [frame] is larger, use
502 // the frame size instead.
503 int trackWidth
= frameSize
> overlaySize
? frameSize
: overlaySize
;
505 int xPos
= aWidgetRect
->left
- trackWidth
;
506 xPos
+= (int)ceil(((double)(trackWidth
* 2) * percent
));
507 overlayRect
.left
= xPos
;
508 overlayRect
.right
= xPos
+ overlaySize
;
510 int yPos
= aWidgetRect
->bottom
+ trackWidth
;
511 yPos
-= (int)ceil(((double)(trackWidth
* 2) * percent
));
512 overlayRect
.bottom
= yPos
;
513 overlayRect
.top
= yPos
- overlaySize
;
519 * DrawProgressMeter - render an appropriate progress meter based on progress
520 * meter style, orientation, and os. Note, this does not render the underlying
523 * @param aFrame the widget frame
524 * @param aAppearance type of widget
525 * @param aTheme progress theme handle
526 * @param aHdc hdc returned by gfxWindowsNativeDrawing
527 * @param aPart the PP_X progress part
528 * @param aState the theme state
529 * @param aWidgetRect bounding rect for the widget
530 * @param aClipRect dirty rect that needs drawing.
531 * @param aAppUnits app units per device pixel
533 void nsNativeThemeWin::DrawThemedProgressMeter(
534 nsIFrame
* aFrame
, StyleAppearance aAppearance
, HANDLE aTheme
, HDC aHdc
,
535 int aPart
, int aState
, RECT
* aWidgetRect
, RECT
* aClipRect
) {
536 if (!aFrame
|| !aTheme
|| !aHdc
) return;
538 NS_ASSERTION(aWidgetRect
, "bad rect pointer");
539 NS_ASSERTION(aClipRect
, "bad clip rect pointer");
541 RECT adjWidgetRect
, adjClipRect
;
542 adjWidgetRect
= *aWidgetRect
;
543 adjClipRect
= *aClipRect
;
545 nsIFrame
* parentFrame
= aFrame
->GetParent();
547 // We have no parent to work with, just bail.
548 NS_WARNING("No parent frame for progress rendering. Can't paint.");
552 ElementState elementState
= GetContentState(parentFrame
, aAppearance
);
553 bool vertical
= IsVerticalProgress(parentFrame
);
554 bool indeterminate
= elementState
.HasState(ElementState::INDETERMINATE
);
555 bool animate
= indeterminate
;
557 // Vista and up progress meter is fill style, rendered here. We render
558 // the pulse overlay in the follow up section below.
559 DrawThemeBackground(aTheme
, aHdc
, aPart
, aState
, &adjWidgetRect
,
561 if (!IsProgressMeterFilled(aFrame
)) {
566 // Indeterminate rendering
567 int32_t overlayPart
= GetProgressOverlayStyle(vertical
);
568 RECT overlayRect
= CalculateProgressOverlayRect(
569 aFrame
, &adjWidgetRect
, vertical
, indeterminate
, false);
570 DrawThemeBackground(aTheme
, aHdc
, overlayPart
, aState
, &overlayRect
,
573 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 60)) {
574 NS_WARNING("unable to animate progress widget!");
579 LayoutDeviceIntMargin
nsNativeThemeWin::GetCachedWidgetBorder(
580 HTHEME aTheme
, nsUXThemeClass aThemeClass
, StyleAppearance aAppearance
,
581 int32_t aPart
, int32_t aState
) {
582 int32_t cacheIndex
= aThemeClass
* THEME_PART_DISTINCT_VALUE_COUNT
+ aPart
;
583 int32_t cacheBitIndex
= cacheIndex
/ 8;
584 uint8_t cacheBit
= 1u << (cacheIndex
% 8);
586 if (mBorderCacheValid
[cacheBitIndex
] & cacheBit
) {
587 return mBorderCache
[cacheIndex
];
591 RECT outerRect
; // Create a fake outer rect.
592 outerRect
.top
= outerRect
.left
= 100;
593 outerRect
.right
= outerRect
.bottom
= 200;
594 RECT
contentRect(outerRect
);
595 HRESULT res
= GetThemeBackgroundContentRect(aTheme
, nullptr, aPart
, aState
,
596 &outerRect
, &contentRect
);
599 return LayoutDeviceIntMargin();
602 // Now compute the delta in each direction and place it in our
603 // nsIntMargin struct.
604 LayoutDeviceIntMargin result
;
605 result
.top
= contentRect
.top
- outerRect
.top
;
606 result
.bottom
= outerRect
.bottom
- contentRect
.bottom
;
607 result
.left
= contentRect
.left
- outerRect
.left
;
608 result
.right
= outerRect
.right
- contentRect
.right
;
610 mBorderCacheValid
[cacheBitIndex
] |= cacheBit
;
611 mBorderCache
[cacheIndex
] = result
;
616 nsresult
nsNativeThemeWin::GetCachedMinimumWidgetSize(
617 nsIFrame
* aFrame
, HANDLE aTheme
, nsUXThemeClass aThemeClass
,
618 StyleAppearance aAppearance
, int32_t aPart
, int32_t aState
,
619 THEMESIZE aSizeReq
, mozilla::LayoutDeviceIntSize
* aResult
) {
620 int32_t cachePart
= aPart
;
622 if (aAppearance
== StyleAppearance::Button
&& aSizeReq
== TS_MIN
) {
623 // In practice, StyleAppearance::Button is the only widget type which has an
624 // aSizeReq that varies for us, and it can only be TS_MIN or TS_TRUE. Just
625 // stuff that extra bit into the aPart part of the cache, since BP_Count is
626 // well below THEME_PART_DISTINCT_VALUE_COUNT anyway.
627 cachePart
= BP_Count
;
630 MOZ_ASSERT(aPart
< THEME_PART_DISTINCT_VALUE_COUNT
);
632 aThemeClass
* THEME_PART_DISTINCT_VALUE_COUNT
+ cachePart
;
633 int32_t cacheBitIndex
= cacheIndex
/ 8;
634 uint8_t cacheBit
= 1u << (cacheIndex
% 8);
636 if (mMinimumWidgetSizeCacheValid
[cacheBitIndex
] & cacheBit
) {
637 *aResult
= mMinimumWidgetSizeCache
[cacheIndex
];
641 HDC hdc
= ::GetDC(NULL
);
643 return NS_ERROR_FAILURE
;
647 GetThemePartSize(aTheme
, hdc
, aPart
, aState
, nullptr, aSizeReq
, &sz
);
648 aResult
->width
= sz
.cx
;
649 aResult
->height
= sz
.cy
;
651 switch (aAppearance
) {
652 case StyleAppearance::SpinnerUpbutton
:
653 case StyleAppearance::SpinnerDownbutton
:
655 aResult
->height
= aResult
->height
/ 2 + 1;
658 case StyleAppearance::Menuseparator
: {
659 SIZE
gutterSize(GetGutterSize(aTheme
, hdc
));
660 aResult
->width
+= gutterSize
.cx
;
664 case StyleAppearance::Menuarrow
:
665 // Use the width of the arrow glyph as padding. See the drawing
674 ::ReleaseDC(nullptr, hdc
);
676 mMinimumWidgetSizeCacheValid
[cacheBitIndex
] |= cacheBit
;
677 mMinimumWidgetSizeCache
[cacheIndex
] = *aResult
;
682 mozilla::Maybe
<nsUXThemeClass
> nsNativeThemeWin::GetThemeClass(
683 StyleAppearance aAppearance
) {
684 switch (aAppearance
) {
685 case StyleAppearance::Button
:
686 case StyleAppearance::Radio
:
687 case StyleAppearance::Checkbox
:
688 case StyleAppearance::Groupbox
:
689 return Some(eUXButton
);
690 case StyleAppearance::NumberInput
:
691 case StyleAppearance::Textfield
:
692 case StyleAppearance::Textarea
:
693 return Some(eUXEdit
);
694 case StyleAppearance::Toolbox
:
695 return Some(eUXRebar
);
696 case StyleAppearance::MozWinMediaToolbox
:
697 return Some(eUXMediaRebar
);
698 case StyleAppearance::MozWinCommunicationsToolbox
:
699 return Some(eUXCommunicationsRebar
);
700 case StyleAppearance::MozWinBrowsertabbarToolbox
:
701 return Some(eUXBrowserTabBarRebar
);
702 case StyleAppearance::Toolbar
:
703 case StyleAppearance::Toolbarbutton
:
704 case StyleAppearance::Separator
:
705 return Some(eUXToolbar
);
706 case StyleAppearance::ProgressBar
:
707 case StyleAppearance::Progresschunk
:
708 return Some(eUXProgress
);
709 case StyleAppearance::Tab
:
710 case StyleAppearance::Tabpanel
:
711 case StyleAppearance::Tabpanels
:
713 case StyleAppearance::Range
:
714 case StyleAppearance::RangeThumb
:
715 return Some(eUXTrackbar
);
716 case StyleAppearance::SpinnerUpbutton
:
717 case StyleAppearance::SpinnerDownbutton
:
718 return Some(eUXSpin
);
719 case StyleAppearance::Menulist
:
720 case StyleAppearance::MenulistButton
:
721 case StyleAppearance::MozMenulistArrowButton
:
722 return Some(eUXCombobox
);
723 case StyleAppearance::Treeheadercell
:
724 case StyleAppearance::Treeheadersortarrow
:
725 return Some(eUXHeader
);
726 case StyleAppearance::Listbox
:
727 case StyleAppearance::Treeview
:
728 case StyleAppearance::Treetwistyopen
:
729 case StyleAppearance::Treeitem
:
730 return Some(eUXListview
);
731 case StyleAppearance::Menubar
:
732 case StyleAppearance::Menupopup
:
733 case StyleAppearance::Menuitem
:
734 case StyleAppearance::Checkmenuitem
:
735 case StyleAppearance::Radiomenuitem
:
736 case StyleAppearance::Menucheckbox
:
737 case StyleAppearance::Menuradio
:
738 case StyleAppearance::Menuseparator
:
739 case StyleAppearance::Menuarrow
:
740 case StyleAppearance::Menuimage
:
741 case StyleAppearance::Menuitemtext
:
742 return Some(eUXMenu
);
743 case StyleAppearance::MozWindowTitlebar
:
744 case StyleAppearance::MozWindowTitlebarMaximized
:
745 case StyleAppearance::MozWindowButtonClose
:
746 case StyleAppearance::MozWindowButtonMinimize
:
747 case StyleAppearance::MozWindowButtonMaximize
:
748 case StyleAppearance::MozWindowButtonRestore
:
749 return Some(eUXWindowFrame
);
756 nsNativeThemeWin::GetTheme(StyleAppearance aAppearance
) {
757 mozilla::Maybe
<nsUXThemeClass
> themeClass
= GetThemeClass(aAppearance
);
758 if (themeClass
.isNothing()) {
761 return nsUXThemeData::GetTheme(themeClass
.value());
764 int32_t nsNativeThemeWin::StandardGetState(nsIFrame
* aFrame
,
765 StyleAppearance aAppearance
,
767 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
768 if (elementState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
)) {
771 if (elementState
.HasState(ElementState::HOVER
)) {
775 if (elementState
.HasState(ElementState::FOCUSRING
)) {
778 // On Windows, focused buttons are always drawn as such by the native
779 // theme, that's why we check ElementState::FOCUS instead of
780 // ElementState::FOCUSRING.
781 if (aAppearance
== StyleAppearance::Button
&&
782 elementState
.HasState(ElementState::FOCUS
)) {
790 bool nsNativeThemeWin::IsMenuActive(nsIFrame
* aFrame
,
791 StyleAppearance aAppearance
) {
792 nsIContent
* content
= aFrame
->GetContent();
793 if (content
->IsXULElement() &&
794 content
->NodeInfo()->Equals(nsGkAtoms::richlistitem
))
795 return CheckBooleanAttr(aFrame
, nsGkAtoms::selected
);
797 return CheckBooleanAttr(aFrame
, nsGkAtoms::menuactive
);
801 * aPart is filled in with the UXTheme part code. On return, values > 0
802 * are the actual UXTheme part code; -1 means the widget will be drawn by
803 * us; 0 means that we should use part code 0, which isn't a real part code
804 * but elicits some kind of default behaviour from UXTheme when drawing
805 * (but isThemeBackgroundPartiallyTransparent may not work).
807 nsresult
nsNativeThemeWin::GetThemePartAndState(nsIFrame
* aFrame
,
808 StyleAppearance aAppearance
,
811 switch (aAppearance
) {
812 case StyleAppearance::Button
: {
819 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
820 if (elementState
.HasState(ElementState::DISABLED
)) {
821 aState
= TS_DISABLED
;
824 if (IsOpenButton(aFrame
) || IsCheckedButton(aFrame
)) {
829 aState
= StandardGetState(aFrame
, aAppearance
, true);
831 // Check for default dialog buttons. These buttons should always look
833 if (aState
== TS_NORMAL
&& IsDefaultButton(aFrame
)) aState
= TS_FOCUSED
;
836 case StyleAppearance::Checkbox
:
837 case StyleAppearance::Radio
: {
838 bool isCheckbox
= (aAppearance
== StyleAppearance::Checkbox
);
839 aPart
= isCheckbox
? BP_CHECKBOX
: BP_RADIO
;
841 enum InputState
{ UNCHECKED
= 0, CHECKED
, INDETERMINATE
};
842 InputState inputState
= UNCHECKED
;
847 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
848 if (elementState
.HasState(ElementState::CHECKED
)) {
849 inputState
= CHECKED
;
851 if (isCheckbox
&& elementState
.HasState(ElementState::INDETERMINATE
)) {
852 inputState
= INDETERMINATE
;
855 if (elementState
.HasState(ElementState::DISABLED
)) {
856 aState
= TS_DISABLED
;
858 aState
= StandardGetState(aFrame
, aAppearance
, false);
862 // 4 unchecked states, 4 checked states, 4 indeterminate states.
863 aState
+= inputState
* 4;
866 case StyleAppearance::Groupbox
: {
869 // Since we don't support groupbox disabled and GBS_DISABLED looks the
870 // same as GBS_NORMAL don't bother supporting GBS_DISABLED.
873 case StyleAppearance::NumberInput
:
874 case StyleAppearance::Textfield
:
875 case StyleAppearance::Textarea
: {
876 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
878 /* Note: the NOSCROLL type has a rounded corner in each corner. The more
879 * specific HSCROLL, VSCROLL, HVSCROLL types have side and/or top/bottom
880 * edges rendered as straight horizontal lines with sharp corners to
881 * accommodate a scrollbar. However, the scrollbar gets rendered on top
882 * of this for us, so we don't care, and can just use NOSCROLL here.
884 aPart
= TFP_EDITBORDER_NOSCROLL
;
887 aState
= TFS_EDITBORDER_NORMAL
;
888 } else if (elementState
.HasState(ElementState::DISABLED
)) {
889 aState
= TFS_EDITBORDER_DISABLED
;
890 } else if (IsReadOnly(aFrame
)) {
891 /* no special read-only state */
892 aState
= TFS_EDITBORDER_NORMAL
;
893 } else if (elementState
.HasAtLeastOneOfStates(ElementState::ACTIVE
|
894 ElementState::FOCUSRING
)) {
895 aState
= TFS_EDITBORDER_FOCUSED
;
896 } else if (elementState
.HasState(ElementState::HOVER
)) {
897 aState
= TFS_EDITBORDER_HOVER
;
899 aState
= TFS_EDITBORDER_NORMAL
;
904 case StyleAppearance::ProgressBar
: {
905 bool vertical
= IsVerticalProgress(aFrame
);
906 aPart
= vertical
? PP_BARVERT
: PP_BAR
;
907 aState
= PBBS_NORMAL
;
910 case StyleAppearance::Progresschunk
: {
911 nsIFrame
* parentFrame
= aFrame
->GetParent();
912 if (IsVerticalProgress(parentFrame
)) {
918 aState
= PBBVS_NORMAL
;
921 case StyleAppearance::Toolbarbutton
: {
928 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
929 if (elementState
.HasState(ElementState::DISABLED
)) {
930 aState
= TS_DISABLED
;
933 if (IsOpenButton(aFrame
)) {
938 if (elementState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
))
940 else if (elementState
.HasState(ElementState::HOVER
)) {
941 if (IsCheckedButton(aFrame
))
942 aState
= TB_HOVER_CHECKED
;
946 if (IsCheckedButton(aFrame
))
954 case StyleAppearance::Separator
: {
955 aPart
= TP_SEPARATOR
;
959 case StyleAppearance::Range
: {
960 if (IsRangeHorizontal(aFrame
)) {
964 aPart
= TKP_TRACKVERT
;
965 aState
= TRVS_NORMAL
;
969 case StyleAppearance::RangeThumb
: {
970 if (IsRangeHorizontal(aFrame
)) {
971 aPart
= TKP_THUMBBOTTOM
;
973 aPart
= IsFrameRTL(aFrame
) ? TKP_THUMBLEFT
: TKP_THUMBRIGHT
;
975 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
978 } else if (elementState
.HasState(ElementState::DISABLED
)) {
979 aState
= TKP_DISABLED
;
981 if (elementState
.HasState(
982 ElementState::ACTIVE
)) // Hover is not also a requirement for
983 // the thumb, since the drag is not
984 // canceled when you move outside the
987 else if (elementState
.HasState(ElementState::FOCUSRING
))
988 aState
= TKP_FOCUSED
;
989 else if (elementState
.HasState(ElementState::HOVER
))
996 case StyleAppearance::SpinnerUpbutton
:
997 case StyleAppearance::SpinnerDownbutton
: {
998 aPart
= (aAppearance
== StyleAppearance::SpinnerUpbutton
) ? SPNP_UP
1000 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1003 } else if (elementState
.HasState(ElementState::DISABLED
)) {
1004 aState
= TS_DISABLED
;
1006 aState
= StandardGetState(aFrame
, aAppearance
, false);
1010 case StyleAppearance::Toolbox
:
1011 case StyleAppearance::MozWinMediaToolbox
:
1012 case StyleAppearance::MozWinCommunicationsToolbox
:
1013 case StyleAppearance::MozWinBrowsertabbarToolbox
: {
1015 aPart
= RP_BACKGROUND
;
1018 case StyleAppearance::Toolbar
: {
1019 // Use -1 to indicate we don't wish to have the theme background drawn
1020 // for this item. We will pass any nessessary information via aState,
1021 // and will render the item using separate code.
1025 nsIContent
* content
= aFrame
->GetContent();
1026 nsIContent
* parent
= content
->GetParent();
1027 // XXXzeniko hiding the first toolbar will result in an unwanted margin
1028 if (parent
&& parent
->GetFirstChild() == content
) {
1034 case StyleAppearance::Treeview
:
1035 case StyleAppearance::Listbox
: {
1036 aPart
= TREEVIEW_BODY
;
1040 case StyleAppearance::Tabpanels
: {
1041 aPart
= TABP_PANELS
;
1045 case StyleAppearance::Tabpanel
: {
1050 case StyleAppearance::Tab
: {
1057 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1058 if (elementState
.HasState(ElementState::DISABLED
)) {
1059 aState
= TS_DISABLED
;
1063 if (IsSelectedTab(aFrame
)) {
1064 aPart
= TABP_TAB_SELECTED
;
1065 aState
= TS_ACTIVE
; // The selected tab is always "pressed".
1067 aState
= StandardGetState(aFrame
, aAppearance
, true);
1071 case StyleAppearance::Treeheadersortarrow
: {
1072 // XXX Probably will never work due to a bug in the Luna theme.
1077 case StyleAppearance::Treeheadercell
: {
1084 aState
= StandardGetState(aFrame
, aAppearance
, true);
1088 case StyleAppearance::MenulistButton
:
1089 case StyleAppearance::Menulist
: {
1090 nsIContent
* content
= aFrame
->GetContent();
1091 bool useDropBorder
= content
&& content
->IsHTMLElement();
1092 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1094 /* On Vista/Win7, we use CBP_DROPBORDER instead of DROPFRAME for HTML
1095 * content or for editable menulists; this gives us the thin outline,
1096 * instead of the gradient-filled background */
1098 aPart
= CBP_DROPBORDER
;
1100 aPart
= CBP_DROPFRAME
;
1102 if (elementState
.HasState(ElementState::DISABLED
)) {
1103 aState
= TS_DISABLED
;
1104 } else if (IsReadOnly(aFrame
)) {
1106 } else if (IsOpenButton(aFrame
)) {
1108 } else if (useDropBorder
&&
1109 elementState
.HasState(ElementState::FOCUSRING
)) {
1111 } else if (elementState
.HasAllStates(ElementState::HOVER
|
1112 ElementState::ACTIVE
)) {
1114 } else if (elementState
.HasState(ElementState::HOVER
)) {
1122 case StyleAppearance::MozMenulistArrowButton
: {
1123 bool isOpen
= false;
1125 // HTML select and XUL menulist dropdown buttons get state from the
1127 nsIFrame
* parentFrame
= aFrame
->GetParent();
1128 aFrame
= parentFrame
;
1130 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1131 aPart
= CBP_DROPMARKER_VISTA
;
1133 // For HTML controls with author styling, we should fall
1134 // back to the old dropmarker style to avoid clashes with
1135 // author-specified backgrounds and borders (bug #441034)
1136 if (IsWidgetStyled(aFrame
->PresContext(), aFrame
,
1137 StyleAppearance::Menulist
)) {
1138 aPart
= CBP_DROPMARKER
;
1141 if (elementState
.HasState(ElementState::DISABLED
)) {
1142 aState
= TS_DISABLED
;
1146 if (nsComboboxControlFrame
* ccf
= do_QueryFrame(aFrame
)) {
1147 isOpen
= ccf
->IsDroppedDown();
1149 /* Hover is propagated, but we need to know whether we're hovering
1150 * just the combobox frame, not the dropdown frame. But, we can't get
1151 * that information, since hover is on the content node, and they
1152 * share the same content node. So, instead, we cheat -- if the
1153 * dropdown is open, we always show the hover state. This looks fine
1160 /* The dropdown indicator on a menulist button in chrome is not given a
1161 * hover effect. When the frame isn't isn't HTML content, we cheat and
1162 * force the dropdown state to be normal. (Bug 430434)
1164 isOpen
= IsOpenButton(aFrame
);
1171 // Dropdown button active state doesn't need :hover.
1172 if (elementState
.HasState(ElementState::ACTIVE
)) {
1174 // XXX Button should look active until the mouse is released, but
1175 // without making it look active when the popup is clicked.
1179 } else if (elementState
.HasState(ElementState::HOVER
)) {
1180 // No hover effect for XUL menulists and autocomplete dropdown buttons
1181 // while the dropdown menu is open.
1183 // XXX HTML select dropdown buttons should have the hover effect when
1184 // hovering the combobox frame, but not the popup frame.
1191 case StyleAppearance::Menupopup
: {
1192 aPart
= MENU_POPUPBACKGROUND
;
1196 case StyleAppearance::Menuitem
:
1197 case StyleAppearance::Checkmenuitem
:
1198 case StyleAppearance::Radiomenuitem
: {
1199 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1201 auto* menu
= dom::XULButtonElement::FromNodeOrNull(aFrame
->GetContent());
1203 const bool isTopLevel
= IsTopLevelMenu(aFrame
);
1204 const bool isOpen
= menu
&& menu
->IsMenuPopupOpen();
1205 const bool isHover
= IsMenuActive(aFrame
, aAppearance
);
1208 aPart
= MENU_BARITEM
;
1211 aState
= MBI_PUSHED
;
1215 aState
= MBI_NORMAL
;
1217 // the disabled states are offset by 3
1218 if (elementState
.HasState(ElementState::DISABLED
)) {
1222 aPart
= MENU_POPUPITEM
;
1227 aState
= MPI_NORMAL
;
1229 // the disabled states are offset by 2
1230 if (elementState
.HasState(ElementState::DISABLED
)) {
1237 case StyleAppearance::Menuseparator
:
1238 aPart
= MENU_POPUPSEPARATOR
;
1241 case StyleAppearance::Menuarrow
: {
1242 aPart
= MENU_POPUPSUBMENU
;
1243 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1244 aState
= elementState
.HasState(ElementState::DISABLED
) ? MSM_DISABLED
1248 case StyleAppearance::Menucheckbox
:
1249 case StyleAppearance::Menuradio
: {
1250 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1252 aPart
= MENU_POPUPCHECK
;
1253 aState
= MC_CHECKMARKNORMAL
;
1255 // Radio states are offset by 2
1256 if (aAppearance
== StyleAppearance::Menuradio
) aState
+= 2;
1258 // the disabled states are offset by 1
1259 if (elementState
.HasState(ElementState::DISABLED
)) {
1265 case StyleAppearance::Menuitemtext
:
1266 case StyleAppearance::Menuimage
:
1271 case StyleAppearance::MozWindowTitlebar
:
1272 aPart
= mozilla::widget::themeconst::WP_CAPTION
;
1273 aState
= GetTopLevelWindowActiveState(aFrame
);
1275 case StyleAppearance::MozWindowTitlebarMaximized
:
1276 aPart
= mozilla::widget::themeconst::WP_MAXCAPTION
;
1277 aState
= GetTopLevelWindowActiveState(aFrame
);
1279 case StyleAppearance::MozWindowButtonClose
:
1280 aPart
= mozilla::widget::themeconst::WP_CLOSEBUTTON
;
1281 aState
= GetWindowFrameButtonState(aFrame
,
1282 GetContentState(aFrame
, aAppearance
));
1284 case StyleAppearance::MozWindowButtonMinimize
:
1285 aPart
= mozilla::widget::themeconst::WP_MINBUTTON
;
1286 aState
= GetWindowFrameButtonState(aFrame
,
1287 GetContentState(aFrame
, aAppearance
));
1289 case StyleAppearance::MozWindowButtonMaximize
:
1290 aPart
= mozilla::widget::themeconst::WP_MAXBUTTON
;
1291 aState
= GetWindowFrameButtonState(aFrame
,
1292 GetContentState(aFrame
, aAppearance
));
1294 case StyleAppearance::MozWindowButtonRestore
:
1295 aPart
= mozilla::widget::themeconst::WP_RESTOREBUTTON
;
1296 aState
= GetWindowFrameButtonState(aFrame
,
1297 GetContentState(aFrame
, aAppearance
));
1302 return NS_ERROR_FAILURE
;
1306 static bool AssumeThemePartAndStateAreTransparent(int32_t aPart
,
1308 if (!(IsWin8Point1OrLater() && nsUXThemeData::IsHighContrastOn()) &&
1309 aPart
== MENU_POPUPITEM
&& aState
== MBI_NORMAL
) {
1315 // When running with per-monitor DPI (on Win8.1+), and rendering on a display
1316 // with a different DPI setting from the system's default scaling, we need to
1317 // apply scaling to native-themed elements as the Windows theme APIs assume
1318 // the system default resolution.
1319 static inline double GetThemeDpiScaleFactor(nsPresContext
* aPresContext
) {
1320 if (WinUtils::IsPerMonitorDPIAware() ||
1321 StaticPrefs::layout_css_devPixelsPerPx() > 0.0) {
1322 nsCOMPtr
<nsIWidget
> rootWidget
= aPresContext
->GetRootWidget();
1324 double systemScale
= WinUtils::SystemScaleFactor();
1325 return rootWidget
->GetDefaultScale().scale
/ systemScale
;
1331 static inline double GetThemeDpiScaleFactor(nsIFrame
* aFrame
) {
1332 return GetThemeDpiScaleFactor(aFrame
->PresContext());
1336 nsNativeThemeWin::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
1337 StyleAppearance aAppearance
,
1338 const nsRect
& aRect
,
1339 const nsRect
& aDirtyRect
,
1340 DrawOverflow aDrawOverflow
) {
1341 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1342 return Theme::DrawWidgetBackground(aContext
, aFrame
, aAppearance
, aRect
,
1343 aDirtyRect
, aDrawOverflow
);
1346 HANDLE theme
= GetTheme(aAppearance
);
1348 return ClassicDrawWidgetBackground(aContext
, aFrame
, aAppearance
, aRect
,
1351 // ^^ without the right sdk, assume xp theming and fall through.
1352 switch (aAppearance
) {
1353 case StyleAppearance::MozWindowTitlebar
:
1354 case StyleAppearance::MozWindowTitlebarMaximized
:
1355 // Nothing to draw, these areas are glass. Minimum dimensions
1356 // should be set, so xul content should be laid out correctly.
1358 case StyleAppearance::MozWindowButtonClose
:
1359 case StyleAppearance::MozWindowButtonMinimize
:
1360 case StyleAppearance::MozWindowButtonMaximize
:
1361 case StyleAppearance::MozWindowButtonRestore
:
1362 // Not conventional bitmaps, can't be retrieved. If we fall
1363 // through here and call the theme library we'll get aero
1370 int32_t part
, state
;
1371 nsresult rv
= GetThemePartAndState(aFrame
, aAppearance
, part
, state
);
1372 if (NS_FAILED(rv
)) return rv
;
1374 if (AssumeThemePartAndStateAreTransparent(part
, state
)) {
1378 gfxContextMatrixAutoSaveRestore
save(aContext
);
1380 double themeScale
= GetThemeDpiScaleFactor(aFrame
);
1381 if (themeScale
!= 1.0) {
1382 aContext
->SetMatrix(
1383 aContext
->CurrentMatrix().PreScale(themeScale
, themeScale
));
1386 gfxFloat p2a
= gfxFloat(aFrame
->PresContext()->AppUnitsPerDevPixel());
1389 gfxRect
tr(aRect
.X(), aRect
.Y(), aRect
.Width(), aRect
.Height()),
1390 dr(aDirtyRect
.X(), aDirtyRect
.Y(), aDirtyRect
.Width(),
1391 aDirtyRect
.Height());
1393 tr
.Scale(1.0 / (p2a
* themeScale
));
1394 dr
.Scale(1.0 / (p2a
* themeScale
));
1396 gfxWindowsNativeDrawing
nativeDrawing(
1397 aContext
, dr
, GetWidgetNativeDrawingFlags(aAppearance
));
1401 HDC hdc
= nativeDrawing
.BeginNativeDrawing();
1402 if (!hdc
) return NS_ERROR_FAILURE
;
1404 nativeDrawing
.TransformToNativeRect(tr
, widgetRect
);
1405 nativeDrawing
.TransformToNativeRect(dr
, clipRect
);
1409 MOZ_LOG(gWindowsLog
, LogLevel::Error
,
1410 (stderr
, "xform: %f %f %f %f [%f %f]\n", m
._11
, m
._21
, m
._12
, m
._22
,
1412 MOZ_LOG(gWindowsLog
, LogLevel::Error
,
1413 (stderr
, "tr: [%d %d %d %d]\ndr: [%d %d %d %d]\noff: [%f %f]\n",
1414 tr
.x
, tr
.y
, tr
.width
, tr
.height
, dr
.x
, dr
.y
, dr
.width
, dr
.height
,
1415 offset
.x
, offset
.y
));
1419 if (aAppearance
== StyleAppearance::MozWindowTitlebar
) {
1420 // Clip out the left and right corners of the frame, all we want in
1421 // is the middle section.
1422 widgetRect
.left
-= GetSystemMetrics(SM_CXFRAME
);
1423 widgetRect
.right
+= GetSystemMetrics(SM_CXFRAME
);
1424 } else if (aAppearance
== StyleAppearance::MozWindowTitlebarMaximized
) {
1425 // The origin of the window is off screen when maximized and windows
1426 // doesn't compensate for this in rendering the background. Push the
1427 // top of the bitmap down by SM_CYFRAME so we get the full graphic.
1428 widgetRect
.top
+= GetSystemMetrics(SM_CYFRAME
);
1429 } else if (aAppearance
== StyleAppearance::Tab
) {
1430 // For left edge and right edge tabs, we need to adjust the widget
1431 // rects and clip rects so that the edges don't get drawn.
1432 bool isLeft
= IsLeftToSelectedTab(aFrame
);
1433 bool isRight
= !isLeft
&& IsRightToSelectedTab(aFrame
);
1435 if (isLeft
|| isRight
) {
1436 // HACK ALERT: There appears to be no way to really obtain this value, so
1437 // we're forced to just use the default value for Luna (which also happens
1438 // to be correct for all the other skins I've tried).
1439 int32_t edgeSize
= 2;
1441 // Armed with the size of the edge, we now need to either shift to the
1442 // left or to the right. The clip rect won't include this extra area, so
1443 // we know that we're effectively shifting the edge out of view (such that
1444 // it won't be painted).
1446 // The right edge should not be drawn. Extend our rect by the edge
1448 widgetRect
.right
+= edgeSize
;
1450 // The left edge should not be drawn. Move the widget rect's left coord
1452 widgetRect
.left
-= edgeSize
;
1454 } else if (aAppearance
== StyleAppearance::MozWindowButtonMinimize
) {
1455 OffsetBackgroundRect(widgetRect
, CAPTIONBUTTON_MINIMIZE
);
1456 } else if (aAppearance
== StyleAppearance::MozWindowButtonMaximize
||
1457 aAppearance
== StyleAppearance::MozWindowButtonRestore
) {
1458 OffsetBackgroundRect(widgetRect
, CAPTIONBUTTON_RESTORE
);
1459 } else if (aAppearance
== StyleAppearance::MozWindowButtonClose
) {
1460 OffsetBackgroundRect(widgetRect
, CAPTIONBUTTON_CLOSE
);
1463 // widgetRect is the bounding box for a widget, yet the scale track is only
1464 // a small portion of this size, so the edges of the scale need to be
1465 // adjusted to the real size of the track.
1466 if (aAppearance
== StyleAppearance::Range
) {
1468 GetThemeBackgroundContentRect(theme
, hdc
, part
, state
, &widgetRect
,
1472 GetThemePartSize(theme
, hdc
, part
, state
, &widgetRect
, TS_TRUE
, &siz
);
1474 // When rounding is necessary, we round the position of the track
1475 // away from the chevron of the thumb to make it look better.
1476 if (IsRangeHorizontal(aFrame
)) {
1477 contentRect
.top
+= (contentRect
.bottom
- contentRect
.top
- siz
.cy
) / 2;
1478 contentRect
.bottom
= contentRect
.top
+ siz
.cy
;
1480 if (!IsFrameRTL(aFrame
)) {
1481 contentRect
.left
+= (contentRect
.right
- contentRect
.left
- siz
.cx
) / 2;
1482 contentRect
.right
= contentRect
.left
+ siz
.cx
;
1484 contentRect
.right
-=
1485 (contentRect
.right
- contentRect
.left
- siz
.cx
) / 2;
1486 contentRect
.left
= contentRect
.right
- siz
.cx
;
1490 DrawThemeBackground(theme
, hdc
, part
, state
, &contentRect
, &clipRect
);
1491 } else if (aAppearance
== StyleAppearance::Menucheckbox
||
1492 aAppearance
== StyleAppearance::Menuradio
) {
1493 bool isChecked
= false;
1494 isChecked
= CheckBooleanAttr(aFrame
, nsGkAtoms::checked
);
1497 int bgState
= MCB_NORMAL
;
1498 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1500 // the disabled states are offset by 1
1501 if (elementState
.HasState(ElementState::DISABLED
)) {
1505 SIZE
checkboxBGSize(GetCheckboxBGSize(theme
, hdc
));
1507 RECT checkBGRect
= widgetRect
;
1508 if (IsFrameRTL(aFrame
)) {
1509 checkBGRect
.left
= checkBGRect
.right
- checkboxBGSize
.cx
;
1511 checkBGRect
.right
= checkBGRect
.left
+ checkboxBGSize
.cx
;
1514 // Center the checkbox background vertically in the menuitem
1516 (checkBGRect
.bottom
- checkBGRect
.top
) / 2 - checkboxBGSize
.cy
/ 2;
1517 checkBGRect
.bottom
= checkBGRect
.top
+ checkboxBGSize
.cy
;
1519 DrawThemeBackground(theme
, hdc
, MENU_POPUPCHECKBACKGROUND
, bgState
,
1520 &checkBGRect
, &clipRect
);
1522 MARGINS checkMargins
= GetCheckboxMargins(theme
, hdc
);
1523 RECT checkRect
= checkBGRect
;
1524 checkRect
.left
+= checkMargins
.cxLeftWidth
;
1525 checkRect
.right
-= checkMargins
.cxRightWidth
;
1526 checkRect
.top
+= checkMargins
.cyTopHeight
;
1527 checkRect
.bottom
-= checkMargins
.cyBottomHeight
;
1528 DrawThemeBackground(theme
, hdc
, MENU_POPUPCHECK
, state
, &checkRect
,
1531 } else if (aAppearance
== StyleAppearance::Menupopup
) {
1532 DrawThemeBackground(theme
, hdc
, MENU_POPUPBORDERS
, /* state */ 0,
1533 &widgetRect
, &clipRect
);
1535 GetThemePartSize(theme
, hdc
, MENU_POPUPBORDERS
, 0, nullptr, TS_TRUE
,
1538 RECT bgRect
= widgetRect
;
1539 bgRect
.top
+= borderSize
.cy
;
1540 bgRect
.bottom
-= borderSize
.cy
;
1541 bgRect
.left
+= borderSize
.cx
;
1542 bgRect
.right
-= borderSize
.cx
;
1544 DrawThemeBackground(theme
, hdc
, MENU_POPUPBACKGROUND
, /* state */ 0,
1545 &bgRect
, &clipRect
);
1547 SIZE
gutterSize(GetGutterSize(theme
, hdc
));
1550 gutterRect
.top
= bgRect
.top
;
1551 gutterRect
.bottom
= bgRect
.bottom
;
1552 if (IsFrameRTL(aFrame
)) {
1553 gutterRect
.right
= bgRect
.right
;
1554 gutterRect
.left
= gutterRect
.right
- gutterSize
.cx
;
1556 gutterRect
.left
= bgRect
.left
;
1557 gutterRect
.right
= gutterRect
.left
+ gutterSize
.cx
;
1560 DrawThemeBGRTLAware(theme
, hdc
, MENU_POPUPGUTTER
, /* state */ 0,
1561 &gutterRect
, &clipRect
, IsFrameRTL(aFrame
));
1562 } else if (aAppearance
== StyleAppearance::Menuseparator
) {
1563 SIZE
gutterSize(GetGutterSize(theme
, hdc
));
1565 RECT sepRect
= widgetRect
;
1566 if (IsFrameRTL(aFrame
))
1567 sepRect
.right
-= gutterSize
.cx
;
1569 sepRect
.left
+= gutterSize
.cx
;
1571 DrawThemeBackground(theme
, hdc
, MENU_POPUPSEPARATOR
, /* state */ 0,
1572 &sepRect
, &clipRect
);
1573 } else if (aAppearance
== StyleAppearance::Menuarrow
) {
1574 // We're dpi aware and as such on systems that have dpi > 96 set, the
1575 // theme library expects us to do proper positioning and scaling of glyphs.
1576 // For StyleAppearance::Menuarrow, layout may hand us a widget rect larger
1577 // than the glyph rect we request in GetMinimumWidgetSize. To prevent
1578 // distortion we have to position and scale what we draw.
1581 GetThemePartSize(theme
, hdc
, part
, state
, nullptr, TS_TRUE
, &glyphSize
);
1583 int32_t widgetHeight
= widgetRect
.bottom
- widgetRect
.top
;
1585 RECT renderRect
= widgetRect
;
1587 // We request (glyph width * 2, glyph height) in GetMinimumWidgetSize. In
1588 // Firefox some menu items provide the full height of the item to us, in
1589 // others our widget rect is the exact dims of our arrow glyph. Adjust the
1590 // vertical position by the added space, if any exists.
1591 renderRect
.top
+= ((widgetHeight
- glyphSize
.cy
) / 2);
1592 renderRect
.bottom
= renderRect
.top
+ glyphSize
.cy
;
1593 // I'm using the width of the arrow glyph for the arrow-side padding.
1594 // AFAICT there doesn't appear to be a theme constant we can query
1595 // for this value. Generally this looks correct, and has the added
1596 // benefit of being a dpi adjusted value.
1597 if (!IsFrameRTL(aFrame
)) {
1598 renderRect
.right
= widgetRect
.right
- glyphSize
.cx
;
1599 renderRect
.left
= renderRect
.right
- glyphSize
.cx
;
1601 renderRect
.left
= glyphSize
.cx
;
1602 renderRect
.right
= renderRect
.left
+ glyphSize
.cx
;
1604 DrawThemeBGRTLAware(theme
, hdc
, part
, state
, &renderRect
, &clipRect
,
1605 IsFrameRTL(aFrame
));
1607 // The following widgets need to be RTL-aware
1608 else if (aAppearance
== StyleAppearance::MozMenulistArrowButton
) {
1609 DrawThemeBGRTLAware(theme
, hdc
, part
, state
, &widgetRect
, &clipRect
,
1610 IsFrameRTL(aFrame
));
1611 } else if (aAppearance
== StyleAppearance::NumberInput
||
1612 aAppearance
== StyleAppearance::Textfield
||
1613 aAppearance
== StyleAppearance::Textarea
) {
1614 DrawThemeBackground(theme
, hdc
, part
, state
, &widgetRect
, &clipRect
);
1616 if (state
== TFS_EDITBORDER_DISABLED
) {
1617 InflateRect(&widgetRect
, -1, -1);
1618 ::FillRect(hdc
, &widgetRect
, reinterpret_cast<HBRUSH
>(COLOR_BTNFACE
+ 1));
1620 } else if (aAppearance
== StyleAppearance::ProgressBar
) {
1621 // DrawThemeBackground renders each corner with a solid white pixel.
1622 // Restore these pixels to the underlying color. Tracks are rendered
1623 // using alpha recovery, so this makes the corners transparent.
1625 color
= GetPixel(hdc
, widgetRect
.left
, widgetRect
.top
);
1626 DrawThemeBackground(theme
, hdc
, part
, state
, &widgetRect
, &clipRect
);
1627 SetPixel(hdc
, widgetRect
.left
, widgetRect
.top
, color
);
1628 SetPixel(hdc
, widgetRect
.right
- 1, widgetRect
.top
, color
);
1629 SetPixel(hdc
, widgetRect
.right
- 1, widgetRect
.bottom
- 1, color
);
1630 SetPixel(hdc
, widgetRect
.left
, widgetRect
.bottom
- 1, color
);
1631 } else if (aAppearance
== StyleAppearance::Progresschunk
) {
1632 DrawThemedProgressMeter(aFrame
, aAppearance
, theme
, hdc
, part
, state
,
1633 &widgetRect
, &clipRect
);
1635 // If part is negative, the element wishes us to not render a themed
1636 // background, instead opting to be drawn specially below.
1637 else if (part
>= 0) {
1638 DrawThemeBackground(theme
, hdc
, part
, state
, &widgetRect
, &clipRect
);
1641 // Draw focus rectangles for range elements
1642 // XXX it'd be nice to draw these outside of the frame
1643 if (aAppearance
== StyleAppearance::Range
) {
1644 ElementState contentState
= GetContentState(aFrame
, aAppearance
);
1646 if (contentState
.HasState(ElementState::FOCUSRING
)) {
1648 HPEN hPen
= nullptr;
1650 uint8_t id
= SaveDC(hdc
);
1652 ::SelectClipRgn(hdc
, nullptr);
1653 ::GetViewportOrgEx(hdc
, &vpOrg
);
1654 ::SetBrushOrgEx(hdc
, vpOrg
.x
+ widgetRect
.left
, vpOrg
.y
+ widgetRect
.top
,
1656 ::SetTextColor(hdc
, 0);
1657 ::DrawFocusRect(hdc
, &widgetRect
);
1658 ::RestoreDC(hdc
, id
);
1660 ::DeleteObject(hPen
);
1663 } else if (aAppearance
== StyleAppearance::Toolbar
&& state
== 0) {
1664 // Draw toolbar separator lines above all toolbars except the first one.
1665 // The lines are part of the Rebar theme, which is loaded for
1666 // StyleAppearance::Toolbox.
1667 theme
= GetTheme(StyleAppearance::Toolbox
);
1668 if (!theme
) return NS_ERROR_FAILURE
;
1670 widgetRect
.bottom
= widgetRect
.top
+ TB_SEPARATOR_HEIGHT
;
1671 DrawThemeEdge(theme
, hdc
, RP_BAND
, 0, &widgetRect
, EDGE_ETCHED
, BF_TOP
,
1675 nativeDrawing
.EndNativeDrawing();
1677 if (nativeDrawing
.ShouldRenderAgain()) goto RENDER_AGAIN
;
1679 nativeDrawing
.PaintToContext();
1684 bool nsNativeThemeWin::CreateWebRenderCommandsForWidget(
1685 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
1686 const layers::StackingContextHelper
& aSc
,
1687 layers::RenderRootStateManager
* aManager
, nsIFrame
* aFrame
,
1688 StyleAppearance aAppearance
, const nsRect
& aRect
) {
1689 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1690 return Theme::CreateWebRenderCommandsForWidget(
1691 aBuilder
, aResources
, aSc
, aManager
, aFrame
, aAppearance
, aRect
);
1696 static void ScaleForFrameDPI(LayoutDeviceIntMargin
* aMargin
, nsIFrame
* aFrame
) {
1697 double themeScale
= GetThemeDpiScaleFactor(aFrame
);
1698 if (themeScale
!= 1.0) {
1699 aMargin
->top
= NSToIntRound(aMargin
->top
* themeScale
);
1700 aMargin
->left
= NSToIntRound(aMargin
->left
* themeScale
);
1701 aMargin
->bottom
= NSToIntRound(aMargin
->bottom
* themeScale
);
1702 aMargin
->right
= NSToIntRound(aMargin
->right
* themeScale
);
1706 static void ScaleForFrameDPI(LayoutDeviceIntSize
* aSize
, nsIFrame
* aFrame
) {
1707 double themeScale
= GetThemeDpiScaleFactor(aFrame
);
1708 if (themeScale
!= 1.0) {
1709 aSize
->width
= NSToIntRound(aSize
->width
* themeScale
);
1710 aSize
->height
= NSToIntRound(aSize
->height
* themeScale
);
1714 LayoutDeviceIntMargin
nsNativeThemeWin::GetWidgetBorder(
1715 nsDeviceContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1716 LayoutDeviceIntMargin result
;
1717 mozilla::Maybe
<nsUXThemeClass
> themeClass
= GetThemeClass(aAppearance
);
1718 HTHEME theme
= NULL
;
1719 if (!themeClass
.isNothing()) {
1720 theme
= nsUXThemeData::GetTheme(themeClass
.value());
1723 result
= ClassicGetWidgetBorder(aContext
, aFrame
, aAppearance
);
1724 ScaleForFrameDPI(&result
, aFrame
);
1728 if (!WidgetIsContainer(aAppearance
) ||
1729 aAppearance
== StyleAppearance::Toolbox
||
1730 aAppearance
== StyleAppearance::MozWinMediaToolbox
||
1731 aAppearance
== StyleAppearance::MozWinCommunicationsToolbox
||
1732 aAppearance
== StyleAppearance::MozWinBrowsertabbarToolbox
||
1733 aAppearance
== StyleAppearance::Tabpanel
||
1734 aAppearance
== StyleAppearance::Menuitem
||
1735 aAppearance
== StyleAppearance::Checkmenuitem
||
1736 aAppearance
== StyleAppearance::Radiomenuitem
||
1737 aAppearance
== StyleAppearance::Menupopup
||
1738 aAppearance
== StyleAppearance::Menuimage
||
1739 aAppearance
== StyleAppearance::Menuitemtext
||
1740 aAppearance
== StyleAppearance::Separator
||
1741 aAppearance
== StyleAppearance::MozWindowTitlebar
||
1742 aAppearance
== StyleAppearance::MozWindowTitlebarMaximized
)
1743 return result
; // Don't worry about it.
1745 int32_t part
, state
;
1746 nsresult rv
= GetThemePartAndState(aFrame
, aAppearance
, part
, state
);
1747 if (NS_FAILED(rv
)) return result
;
1749 if (aAppearance
== StyleAppearance::Toolbar
) {
1750 // make space for the separator line above all toolbars but the first
1751 if (state
== 0) result
.top
= TB_SEPARATOR_HEIGHT
;
1755 result
= GetCachedWidgetBorder(theme
, themeClass
.value(), aAppearance
, part
,
1758 // Remove the edges for tabs that are before or after the selected tab,
1759 if (aAppearance
== StyleAppearance::Tab
) {
1760 if (IsLeftToSelectedTab(aFrame
))
1761 // Remove the right edge, since we won't be drawing it.
1763 else if (IsRightToSelectedTab(aFrame
))
1764 // Remove the left edge, since we won't be drawing it.
1768 if (aFrame
&& (aAppearance
== StyleAppearance::NumberInput
||
1769 aAppearance
== StyleAppearance::Textfield
||
1770 aAppearance
== StyleAppearance::Textarea
)) {
1771 nsIContent
* content
= aFrame
->GetContent();
1772 if (content
&& content
->IsHTMLElement()) {
1773 // We need to pad textfields by 1 pixel, since the caret will draw
1774 // flush against the edge by default if we don't.
1776 result
.left
.value
++;
1777 result
.bottom
.value
++;
1778 result
.right
.value
++;
1782 ScaleForFrameDPI(&result
, aFrame
);
1786 bool nsNativeThemeWin::GetWidgetPadding(nsDeviceContext
* aContext
,
1788 StyleAppearance aAppearance
,
1789 LayoutDeviceIntMargin
* aResult
) {
1790 switch (aAppearance
) {
1791 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1792 // and have a meaningful baseline, so they can't have
1793 // author-specified padding.
1794 case StyleAppearance::Checkbox
:
1795 case StyleAppearance::Radio
:
1796 aResult
->SizeTo(0, 0, 0, 0);
1805 if (aAppearance
== StyleAppearance::MozWindowTitlebar
||
1806 aAppearance
== StyleAppearance::MozWindowTitlebarMaximized
) {
1807 aResult
->SizeTo(0, 0, 0, 0);
1808 // Prior to Windows 10, a bug in DwmDefWindowProc would cause window
1809 // button presses/mouseovers to be missed. This bug is circumvented by
1810 // adding padding to the top of the window that is the size of the caption
1811 // area and then "removing" it when calculating the client area for
1812 // WM_NCCALCSIZE. See bug 618353,
1813 if (!IsWin10OrLater() &&
1814 aAppearance
== StyleAppearance::MozWindowTitlebarMaximized
) {
1815 nsCOMPtr
<nsIWidget
> rootWidget
;
1816 if (WinUtils::HasSystemMetricsForDpi()) {
1817 rootWidget
= aFrame
->PresContext()->GetRootWidget();
1820 double dpi
= rootWidget
->GetDPI();
1821 aResult
->top
= WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
1822 WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
);
1825 GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CXPADDEDBORDER
);
1831 HANDLE theme
= GetTheme(aAppearance
);
1833 ok
= ClassicGetWidgetPadding(aContext
, aFrame
, aAppearance
, aResult
);
1834 ScaleForFrameDPI(aResult
, aFrame
);
1838 if (aAppearance
== StyleAppearance::Menupopup
) {
1840 GetThemePartSize(theme
, nullptr, MENU_POPUPBORDERS
, /* state */ 0, nullptr,
1841 TS_TRUE
, &popupSize
);
1842 aResult
->top
= aResult
->bottom
= popupSize
.cy
;
1843 aResult
->left
= aResult
->right
= popupSize
.cx
;
1844 ScaleForFrameDPI(aResult
, aFrame
);
1848 /* textfields need extra pixels on all sides, otherwise they wrap their
1849 * content too tightly. The actual border is drawn 1px inside the specified
1850 * rectangle, so Gecko will end up making the contents look too small.
1851 * Instead, we add 2px padding for the contents and fix this. (Used to be 1px
1852 * added, see bug 430212)
1854 if (aAppearance
== StyleAppearance::NumberInput
||
1855 aAppearance
== StyleAppearance::Textfield
||
1856 aAppearance
== StyleAppearance::Textarea
) {
1857 aResult
->top
= aResult
->bottom
= 2;
1858 aResult
->left
= aResult
->right
= 2;
1859 ScaleForFrameDPI(aResult
, aFrame
);
1861 } else if (IsHTMLContent(aFrame
) &&
1862 (aAppearance
== StyleAppearance::Menulist
||
1863 aAppearance
== StyleAppearance::MenulistButton
)) {
1864 /* For content menulist controls, we need an extra pixel so that we have
1865 * room to draw our focus rectangle stuff. Otherwise, the focus rect might
1866 * overlap the control's border.
1868 aResult
->top
= aResult
->bottom
= 1;
1869 aResult
->left
= aResult
->right
= 1;
1870 ScaleForFrameDPI(aResult
, aFrame
);
1874 int32_t right
, left
, top
, bottom
;
1875 right
= left
= top
= bottom
= 0;
1876 switch (aAppearance
) {
1877 case StyleAppearance::Menuimage
:
1881 case StyleAppearance::Menucheckbox
:
1882 case StyleAppearance::Menuradio
:
1886 case StyleAppearance::Menuitemtext
:
1887 // There seem to be exactly 4 pixels from the edge
1888 // of the gutter to the text: 2px margin (CSS) + 2px padding (here)
1890 SIZE
size(GetGutterSize(theme
, nullptr));
1894 case StyleAppearance::Menuseparator
: {
1895 SIZE
size(GetGutterSize(theme
, nullptr));
1898 case StyleAppearance::Button
:
1899 if (aFrame
->GetContent()->IsXULElement()) {
1909 if (IsFrameRTL(aFrame
)) {
1910 aResult
->right
= left
;
1911 aResult
->left
= right
;
1913 aResult
->right
= right
;
1914 aResult
->left
= left
;
1917 aResult
->bottom
= bottom
;
1919 ScaleForFrameDPI(aResult
, aFrame
);
1923 bool nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext
* aContext
,
1925 StyleAppearance aAppearance
,
1926 nsRect
* aOverflowRect
) {
1927 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1928 return Theme::GetWidgetOverflow(aContext
, aFrame
, aAppearance
,
1932 /* This is disabled for now, because it causes invalidation problems --
1933 * see bug 420381. The effect of not updating the overflow area is that
1934 * for dropdown buttons in content areas, there is a 1px border on 3 sides
1935 * where, if invalidated, the dropdown control probably won't be repainted.
1936 * This is fairly minor, as by default there is nothing in that area, and
1937 * a border only shows up if the widget is being hovered.
1939 * TODO(jwatt): Figure out what do to about
1940 * StyleAppearance::MozMenulistArrowButton too.
1943 /* We explicitly draw dropdown buttons in HTML content 1px bigger up, right,
1944 * and bottom so that they overlap the dropdown's border like they're
1947 if (aAppearance
== StyleAppearance::MenulistButton
&&
1948 IsHTMLContent(aFrame
) &&
1949 !IsWidgetStyled(aFrame
->GetParent()->PresContext(),
1950 aFrame
->GetParent(),
1951 StyleAppearance::Menulist
))
1953 int32_t p2a
= aContext
->AppUnitsPerDevPixel();
1954 /* Note: no overflow on the left */
1955 nsMargin
m(p2a
, p2a
, p2a
, 0);
1956 aOverflowRect
->Inflate (m
);
1964 LayoutDeviceIntSize
nsNativeThemeWin::GetMinimumWidgetSize(
1965 nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
1966 StyleAppearance aAppearance
) {
1967 if (IsWidgetNonNative(aFrame
, aAppearance
) == NonNative::Always
) {
1968 return Theme::GetMinimumWidgetSize(aPresContext
, aFrame
, aAppearance
);
1971 mozilla::Maybe
<nsUXThemeClass
> themeClass
= GetThemeClass(aAppearance
);
1972 HTHEME theme
= NULL
;
1973 if (!themeClass
.isNothing()) {
1974 theme
= nsUXThemeData::GetTheme(themeClass
.value());
1977 auto result
= ClassicGetMinimumWidgetSize(aFrame
, aAppearance
);
1978 ScaleForFrameDPI(&result
, aFrame
);
1982 switch (aAppearance
) {
1983 case StyleAppearance::Groupbox
:
1984 case StyleAppearance::NumberInput
:
1985 case StyleAppearance::Textfield
:
1986 case StyleAppearance::Toolbox
:
1987 case StyleAppearance::MozWinMediaToolbox
:
1988 case StyleAppearance::MozWinCommunicationsToolbox
:
1989 case StyleAppearance::MozWinBrowsertabbarToolbox
:
1990 case StyleAppearance::Toolbar
:
1991 case StyleAppearance::Progresschunk
:
1992 case StyleAppearance::Tabpanels
:
1993 case StyleAppearance::Tabpanel
:
1994 case StyleAppearance::Listbox
:
1995 case StyleAppearance::Treeview
:
1996 case StyleAppearance::Menuitemtext
:
1997 return {}; // Don't worry about it.
2002 if (aAppearance
== StyleAppearance::Menuitem
&& IsTopLevelMenu(aFrame
)) {
2003 return {}; // Don't worry about it for top level menus
2006 // Call GetSystemMetrics to determine size for WinXP scrollbars
2007 // (GetThemeSysSize API returns the optimal size for the theme, but
2008 // Windows appears to always use metrics when drawing standard scrollbars)
2009 THEMESIZE sizeReq
= TS_TRUE
; // Best-fit size
2010 switch (aAppearance
) {
2011 case StyleAppearance::MozMenulistArrowButton
: {
2012 auto result
= ClassicGetMinimumWidgetSize(aFrame
, aAppearance
);
2013 ScaleForFrameDPI(&result
, aFrame
);
2016 case StyleAppearance::Menuitem
:
2017 case StyleAppearance::Checkmenuitem
:
2018 case StyleAppearance::Radiomenuitem
:
2019 if (!IsTopLevelMenu(aFrame
)) {
2020 SIZE
gutterSize(GetCachedGutterSize(theme
));
2021 LayoutDeviceIntSize
result(gutterSize
.cx
, gutterSize
.cy
);
2022 ScaleForFrameDPI(&result
, aFrame
);
2027 case StyleAppearance::Menuimage
:
2028 case StyleAppearance::Menucheckbox
:
2029 case StyleAppearance::Menuradio
: {
2030 SIZE
boxSize(GetCachedGutterSize(theme
));
2031 LayoutDeviceIntSize
result(boxSize
.cx
+ 2, boxSize
.cy
);
2032 ScaleForFrameDPI(&result
, aFrame
);
2036 case StyleAppearance::Menuitemtext
:
2039 case StyleAppearance::ProgressBar
:
2040 // Best-fit size for progress meters is too large for most
2041 // themes. We want these widgets to be able to really shrink
2042 // down, so use the min-size request value (of 0).
2046 case StyleAppearance::RangeThumb
: {
2047 LayoutDeviceIntSize
result(12, 20);
2048 if (!IsRangeHorizontal(aFrame
)) {
2049 std::swap(result
.width
, result
.height
);
2051 ScaleForFrameDPI(&result
, aFrame
);
2055 case StyleAppearance::Separator
: {
2056 // that's 2px left margin, 2px right margin and 2px separator
2057 // (the margin is drawn as part of the separator, though)
2058 LayoutDeviceIntSize
result(6, 0);
2059 ScaleForFrameDPI(&result
, aFrame
);
2063 case StyleAppearance::Button
:
2064 // We should let HTML buttons shrink to their min size.
2065 // FIXME bug 403934: We should probably really separate
2066 // GetPreferredWidgetSize from GetMinimumWidgetSize, so callers can
2067 // use the one they want.
2068 if (aFrame
->GetContent()->IsHTMLElement()) {
2073 case StyleAppearance::MozWindowTitlebar
:
2074 case StyleAppearance::MozWindowTitlebarMaximized
: {
2075 LayoutDeviceIntSize result
;
2076 result
.height
= GetSystemMetrics(SM_CYCAPTION
);
2077 result
.height
+= GetSystemMetrics(SM_CYFRAME
);
2078 result
.height
+= GetSystemMetrics(SM_CXPADDEDBORDER
);
2079 ScaleForFrameDPI(&result
, aFrame
);
2087 int32_t part
, state
;
2088 nsresult rv
= GetThemePartAndState(aFrame
, aAppearance
, part
, state
);
2089 if (NS_FAILED(rv
)) {
2093 LayoutDeviceIntSize result
;
2094 rv
= GetCachedMinimumWidgetSize(aFrame
, theme
, themeClass
.value(),
2095 aAppearance
, part
, state
, sizeReq
, &result
);
2096 ScaleForFrameDPI(&result
, aFrame
);
2101 nsNativeThemeWin::WidgetStateChanged(nsIFrame
* aFrame
,
2102 StyleAppearance aAppearance
,
2103 nsAtom
* aAttribute
, bool* aShouldRepaint
,
2104 const nsAttrValue
* aOldValue
) {
2105 // Some widget types just never change state.
2106 if (aAppearance
== StyleAppearance::Toolbox
||
2107 aAppearance
== StyleAppearance::MozWinMediaToolbox
||
2108 aAppearance
== StyleAppearance::MozWinCommunicationsToolbox
||
2109 aAppearance
== StyleAppearance::MozWinBrowsertabbarToolbox
||
2110 aAppearance
== StyleAppearance::Toolbar
||
2111 aAppearance
== StyleAppearance::Progresschunk
||
2112 aAppearance
== StyleAppearance::ProgressBar
||
2113 aAppearance
== StyleAppearance::Tabpanels
||
2114 aAppearance
== StyleAppearance::Tabpanel
||
2115 aAppearance
== StyleAppearance::Separator
) {
2116 *aShouldRepaint
= false;
2120 if (aAppearance
== StyleAppearance::MozWindowTitlebar
||
2121 aAppearance
== StyleAppearance::MozWindowTitlebarMaximized
||
2122 aAppearance
== StyleAppearance::MozWindowButtonClose
||
2123 aAppearance
== StyleAppearance::MozWindowButtonMinimize
||
2124 aAppearance
== StyleAppearance::MozWindowButtonMaximize
||
2125 aAppearance
== StyleAppearance::MozWindowButtonRestore
) {
2126 *aShouldRepaint
= true;
2130 // We need to repaint the dropdown arrow in vista HTML combobox controls when
2131 // the control is closed to get rid of the hover effect.
2132 if ((aAppearance
== StyleAppearance::Menulist
||
2133 aAppearance
== StyleAppearance::MenulistButton
||
2134 aAppearance
== StyleAppearance::MozMenulistArrowButton
) &&
2135 nsNativeTheme::IsHTMLContent(aFrame
)) {
2136 *aShouldRepaint
= true;
2140 // XXXdwh Not sure what can really be done here. Can at least guess for
2141 // specific widgets that they're highly unlikely to have certain states.
2142 // For example, a toolbar doesn't care about any states.
2144 // Hover/focus/active changed. Always repaint.
2145 *aShouldRepaint
= true;
2147 // Check the attribute to see if it's relevant.
2148 // disabled, checked, dlgtype, default, etc.
2149 *aShouldRepaint
= false;
2150 if (aAttribute
== nsGkAtoms::disabled
|| aAttribute
== nsGkAtoms::checked
||
2151 aAttribute
== nsGkAtoms::selected
||
2152 aAttribute
== nsGkAtoms::visuallyselected
||
2153 aAttribute
== nsGkAtoms::readonly
|| aAttribute
== nsGkAtoms::open
||
2154 aAttribute
== nsGkAtoms::menuactive
|| aAttribute
== nsGkAtoms::focused
)
2155 *aShouldRepaint
= true;
2162 nsNativeThemeWin::ThemeChanged() {
2163 nsUXThemeData::Invalidate();
2164 memset(mBorderCacheValid
, 0, sizeof(mBorderCacheValid
));
2165 memset(mMinimumWidgetSizeCacheValid
, 0, sizeof(mMinimumWidgetSizeCacheValid
));
2166 mGutterSizeCacheValid
= false;
2170 bool nsNativeThemeWin::ThemeSupportsWidget(nsPresContext
* aPresContext
,
2172 StyleAppearance aAppearance
) {
2173 // XXXdwh We can go even further and call the API to ask if support exists for
2174 // specific widgets.
2176 if (IsWidgetNonNative(aFrame
, aAppearance
) == NonNative::Always
) {
2177 return Theme::ThemeSupportsWidget(aPresContext
, aFrame
, aAppearance
);
2180 HANDLE theme
= nullptr;
2181 if (aAppearance
== StyleAppearance::CheckboxContainer
)
2182 theme
= GetTheme(StyleAppearance::Checkbox
);
2183 else if (aAppearance
== StyleAppearance::RadioContainer
)
2184 theme
= GetTheme(StyleAppearance::Radio
);
2186 theme
= GetTheme(aAppearance
);
2188 if (theme
|| ClassicThemeSupportsWidget(aFrame
, aAppearance
))
2189 // turn off theming for some HTML widgets styled by the page
2190 return (!IsWidgetStyled(aPresContext
, aFrame
, aAppearance
));
2195 bool nsNativeThemeWin::WidgetIsContainer(StyleAppearance aAppearance
) {
2196 // XXXdwh At some point flesh all of this out.
2197 if (aAppearance
== StyleAppearance::MozMenulistArrowButton
||
2198 aAppearance
== StyleAppearance::Radio
||
2199 aAppearance
== StyleAppearance::Checkbox
)
2204 bool nsNativeThemeWin::ThemeDrawsFocusForWidget(nsIFrame
* aFrame
,
2205 StyleAppearance aAppearance
) {
2206 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
2207 return Theme::ThemeDrawsFocusForWidget(aFrame
, aAppearance
);
2209 switch (aAppearance
) {
2210 case StyleAppearance::Menulist
:
2211 case StyleAppearance::MenulistButton
:
2212 case StyleAppearance::Textarea
:
2213 case StyleAppearance::Textfield
:
2214 case StyleAppearance::NumberInput
:
2221 bool nsNativeThemeWin::ThemeNeedsComboboxDropmarker() { return true; }
2223 bool nsNativeThemeWin::WidgetAppearanceDependsOnWindowFocus(
2224 StyleAppearance aAppearance
) {
2225 switch (aAppearance
) {
2226 case StyleAppearance::MozWindowTitlebar
:
2227 case StyleAppearance::MozWindowTitlebarMaximized
:
2228 case StyleAppearance::MozWindowButtonClose
:
2229 case StyleAppearance::MozWindowButtonMinimize
:
2230 case StyleAppearance::MozWindowButtonMaximize
:
2231 case StyleAppearance::MozWindowButtonRestore
:
2238 nsITheme::Transparency
nsNativeThemeWin::GetWidgetTransparency(
2239 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
2240 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
2241 return Theme::GetWidgetTransparency(aFrame
, aAppearance
);
2244 switch (aAppearance
) {
2245 case StyleAppearance::ProgressBar
:
2246 case StyleAppearance::Progresschunk
:
2247 case StyleAppearance::Range
:
2248 return eTransparent
;
2253 HANDLE theme
= GetTheme(aAppearance
);
2254 // For the classic theme we don't really have a way of knowing
2256 // menu backgrounds which can't be themed are opaque
2257 if (aAppearance
== StyleAppearance::Menupopup
) {
2260 return eUnknownTransparency
;
2263 int32_t part
, state
;
2264 nsresult rv
= GetThemePartAndState(aFrame
, aAppearance
, part
, state
);
2265 // Fail conservatively
2266 NS_ENSURE_SUCCESS(rv
, eUnknownTransparency
);
2269 // Not a real part code, so IsThemeBackgroundPartiallyTransparent may
2270 // not work, so don't call it.
2271 return eUnknownTransparency
;
2274 if (IsThemeBackgroundPartiallyTransparent(theme
, part
, state
))
2275 return eTransparent
;
2279 /* Windows 9x/NT/2000/Classic XP Theme Support */
2281 bool nsNativeThemeWin::ClassicThemeSupportsWidget(nsIFrame
* aFrame
,
2282 StyleAppearance aAppearance
) {
2283 switch (aAppearance
) {
2284 case StyleAppearance::Menubar
:
2285 case StyleAppearance::Menupopup
:
2286 case StyleAppearance::Button
:
2287 case StyleAppearance::NumberInput
:
2288 case StyleAppearance::Textfield
:
2289 case StyleAppearance::Textarea
:
2290 case StyleAppearance::Checkbox
:
2291 case StyleAppearance::Radio
:
2292 case StyleAppearance::Range
:
2293 case StyleAppearance::RangeThumb
:
2294 case StyleAppearance::Groupbox
:
2295 case StyleAppearance::Menulist
:
2296 case StyleAppearance::MenulistButton
:
2297 case StyleAppearance::MozMenulistArrowButton
:
2298 case StyleAppearance::SpinnerUpbutton
:
2299 case StyleAppearance::SpinnerDownbutton
:
2300 case StyleAppearance::Listbox
:
2301 case StyleAppearance::Treeview
:
2302 case StyleAppearance::ProgressBar
:
2303 case StyleAppearance::Progresschunk
:
2304 case StyleAppearance::Tab
:
2305 case StyleAppearance::Tabpanel
:
2306 case StyleAppearance::Tabpanels
:
2307 case StyleAppearance::Menuitem
:
2308 case StyleAppearance::Checkmenuitem
:
2309 case StyleAppearance::Radiomenuitem
:
2310 case StyleAppearance::Menucheckbox
:
2311 case StyleAppearance::Menuradio
:
2312 case StyleAppearance::Menuarrow
:
2313 case StyleAppearance::Menuseparator
:
2314 case StyleAppearance::Menuitemtext
:
2315 case StyleAppearance::MozWindowTitlebar
:
2316 case StyleAppearance::MozWindowTitlebarMaximized
:
2317 case StyleAppearance::MozWindowButtonClose
:
2318 case StyleAppearance::MozWindowButtonMinimize
:
2319 case StyleAppearance::MozWindowButtonMaximize
:
2320 case StyleAppearance::MozWindowButtonRestore
:
2327 LayoutDeviceIntMargin
nsNativeThemeWin::ClassicGetWidgetBorder(
2328 nsDeviceContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
2329 LayoutDeviceIntMargin result
;
2330 switch (aAppearance
) {
2331 case StyleAppearance::Groupbox
:
2332 case StyleAppearance::Button
:
2333 result
.top
= result
.left
= result
.bottom
= result
.right
= 2;
2335 case StyleAppearance::Listbox
:
2336 case StyleAppearance::Treeview
:
2337 case StyleAppearance::Menulist
:
2338 case StyleAppearance::MenulistButton
:
2339 case StyleAppearance::Tab
:
2340 case StyleAppearance::NumberInput
:
2341 case StyleAppearance::Textfield
:
2342 case StyleAppearance::Textarea
:
2343 result
.top
= result
.left
= result
.bottom
= result
.right
= 2;
2345 case StyleAppearance::ProgressBar
:
2346 result
.top
= result
.left
= result
.bottom
= result
.right
= 1;
2348 case StyleAppearance::Menubar
:
2349 result
.top
= result
.left
= result
.bottom
= result
.right
= 0;
2351 case StyleAppearance::Menupopup
:
2352 result
.top
= result
.left
= result
.bottom
= result
.right
= 3;
2355 result
.top
= result
.bottom
= result
.left
= result
.right
= 0;
2361 bool nsNativeThemeWin::ClassicGetWidgetPadding(nsDeviceContext
* aContext
,
2363 StyleAppearance aAppearance
,
2364 LayoutDeviceIntMargin
* aResult
) {
2365 switch (aAppearance
) {
2366 case StyleAppearance::Menuitem
:
2367 case StyleAppearance::Checkmenuitem
:
2368 case StyleAppearance::Radiomenuitem
: {
2369 int32_t part
, state
;
2372 if (NS_FAILED(ClassicGetThemePartAndState(aFrame
, aAppearance
, part
,
2376 if (part
== 1) { // top-level menu
2377 (*aResult
).top
= (*aResult
).bottom
= (*aResult
).left
=
2378 (*aResult
).right
= 2;
2381 (*aResult
).bottom
= (*aResult
).left
= (*aResult
).right
= 2;
2385 case StyleAppearance::ProgressBar
:
2386 (*aResult
).top
= (*aResult
).left
= (*aResult
).bottom
= (*aResult
).right
=
2394 LayoutDeviceIntSize
nsNativeThemeWin::ClassicGetMinimumWidgetSize(
2395 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
2396 LayoutDeviceIntSize result
;
2397 switch (aAppearance
) {
2398 case StyleAppearance::Radio
:
2399 case StyleAppearance::Checkbox
:
2400 result
.width
= result
.height
= 13;
2402 case StyleAppearance::Menucheckbox
:
2403 case StyleAppearance::Menuradio
:
2404 case StyleAppearance::Menuarrow
:
2405 result
.width
= ::GetSystemMetrics(SM_CXMENUCHECK
);
2406 result
.height
= ::GetSystemMetrics(SM_CYMENUCHECK
);
2408 case StyleAppearance::SpinnerUpbutton
:
2409 case StyleAppearance::SpinnerDownbutton
:
2410 result
.width
= ::GetSystemMetrics(SM_CXVSCROLL
);
2411 result
.height
= 8; // No good metrics available for this
2413 case StyleAppearance::RangeThumb
: {
2414 if (IsRangeHorizontal(aFrame
)) {
2423 case StyleAppearance::MozMenulistArrowButton
:
2424 result
.width
= ::GetSystemMetrics(SM_CXVSCROLL
);
2426 case StyleAppearance::Menulist
:
2427 case StyleAppearance::MenulistButton
:
2428 case StyleAppearance::Button
:
2429 case StyleAppearance::Groupbox
:
2430 case StyleAppearance::Listbox
:
2431 case StyleAppearance::Treeview
:
2432 case StyleAppearance::NumberInput
:
2433 case StyleAppearance::Textfield
:
2434 case StyleAppearance::Textarea
:
2435 case StyleAppearance::Progresschunk
:
2436 case StyleAppearance::ProgressBar
:
2437 case StyleAppearance::Tab
:
2438 case StyleAppearance::Tabpanel
:
2439 case StyleAppearance::Tabpanels
:
2440 // no minimum widget size
2442 case StyleAppearance::Menuseparator
: {
2448 case StyleAppearance::MozWindowTitlebarMaximized
:
2449 case StyleAppearance::MozWindowTitlebar
:
2451 GetSystemMetrics(SM_CYCAPTION
) + GetSystemMetrics(SM_CYFRAME
);
2453 case StyleAppearance::MozWindowButtonClose
:
2454 case StyleAppearance::MozWindowButtonMinimize
:
2455 case StyleAppearance::MozWindowButtonMaximize
:
2456 case StyleAppearance::MozWindowButtonRestore
:
2457 result
.width
= GetSystemMetrics(SM_CXSIZE
);
2458 result
.height
= GetSystemMetrics(SM_CYSIZE
);
2459 // XXX I have no idea why these caption metrics are always off,
2463 if (aAppearance
== StyleAppearance::MozWindowButtonMinimize
) {
2464 AddPaddingRect(&result
, CAPTIONBUTTON_MINIMIZE
);
2465 } else if (aAppearance
== StyleAppearance::MozWindowButtonMaximize
||
2466 aAppearance
== StyleAppearance::MozWindowButtonRestore
) {
2467 AddPaddingRect(&result
, CAPTIONBUTTON_RESTORE
);
2468 } else if (aAppearance
== StyleAppearance::MozWindowButtonClose
) {
2469 AddPaddingRect(&result
, CAPTIONBUTTON_CLOSE
);
2479 nsresult
nsNativeThemeWin::ClassicGetThemePartAndState(
2480 nsIFrame
* aFrame
, StyleAppearance aAppearance
, int32_t& aPart
,
2481 int32_t& aState
, bool& aFocused
) {
2483 switch (aAppearance
) {
2484 case StyleAppearance::Button
: {
2486 aState
= DFCS_BUTTONPUSH
;
2489 ElementState contentState
= GetContentState(aFrame
, aAppearance
);
2490 if (contentState
.HasState(ElementState::DISABLED
)) {
2491 aState
|= DFCS_INACTIVE
;
2492 } else if (IsOpenButton(aFrame
)) {
2493 aState
|= DFCS_PUSHED
;
2494 } else if (IsCheckedButton(aFrame
)) {
2495 aState
|= DFCS_CHECKED
;
2497 if (contentState
.HasAllStates(ElementState::ACTIVE
|
2498 ElementState::HOVER
)) {
2499 aState
|= DFCS_PUSHED
;
2500 // The down state is flat if the button is focusable
2501 if (aFrame
->StyleUI()->UserFocus() == StyleUserFocus::Normal
) {
2502 if (!aFrame
->GetContent()->IsHTMLElement()) aState
|= DFCS_FLAT
;
2507 // On Windows, focused buttons are always drawn as such by the native
2508 // theme, that's why we check ElementState::FOCUS instead of
2509 // ElementState::FOCUSRING.
2510 if (contentState
.HasState(ElementState::FOCUS
) ||
2511 (aState
== DFCS_BUTTONPUSH
&& IsDefaultButton(aFrame
))) {
2518 case StyleAppearance::Checkbox
:
2519 case StyleAppearance::Radio
: {
2520 ElementState contentState
= GetContentState(aFrame
, aAppearance
);
2525 nsIContent
* content
= aFrame
->GetContent();
2526 bool isCheckbox
= (aAppearance
== StyleAppearance::Checkbox
);
2527 bool isChecked
= contentState
.HasState(ElementState::CHECKED
);
2528 bool isIndeterminate
= contentState
.HasState(ElementState::INDETERMINATE
);
2531 // indeterminate state takes precedence over checkedness.
2532 if (isIndeterminate
) {
2533 aState
= DFCS_BUTTON3STATE
| DFCS_CHECKED
;
2535 aState
= DFCS_BUTTONCHECK
;
2538 aState
= DFCS_BUTTONRADIO
;
2541 aState
|= DFCS_CHECKED
;
2544 if (!content
->IsXULElement() &&
2545 contentState
.HasState(ElementState::FOCUSRING
)) {
2549 if (contentState
.HasState(ElementState::DISABLED
)) {
2550 aState
|= DFCS_INACTIVE
;
2551 } else if (contentState
.HasAllStates(ElementState::ACTIVE
|
2552 ElementState::HOVER
)) {
2553 aState
|= DFCS_PUSHED
;
2558 case StyleAppearance::Menuitem
:
2559 case StyleAppearance::Checkmenuitem
:
2560 case StyleAppearance::Radiomenuitem
: {
2561 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
2563 auto* menu
= dom::XULButtonElement::FromNodeOrNull(aFrame
->GetContent());
2565 const bool isTopLevel
= IsTopLevelMenu(aFrame
);
2566 const bool isOpen
= menu
&& menu
->IsMenuPopupOpen();
2568 // We indicate top-level-ness using aPart. 0 is a normal menu item,
2569 // 1 is a top-level menu item. The state of the item is composed of
2570 // DFCS_* flags only.
2574 if (elementState
.HasState(ElementState::DISABLED
)) {
2575 aState
|= DFCS_INACTIVE
;
2581 aState
|= DFCS_PUSHED
;
2585 if (IsMenuActive(aFrame
, aAppearance
)) {
2591 case StyleAppearance::Menucheckbox
:
2592 case StyleAppearance::Menuradio
:
2593 case StyleAppearance::Menuarrow
: {
2595 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
2597 if (elementState
.HasState(ElementState::DISABLED
)) {
2598 aState
|= DFCS_INACTIVE
;
2600 if (IsMenuActive(aFrame
, aAppearance
)) aState
|= DFCS_HOT
;
2602 if (aAppearance
== StyleAppearance::Menucheckbox
||
2603 aAppearance
== StyleAppearance::Menuradio
) {
2604 if (IsCheckedButton(aFrame
)) aState
|= DFCS_CHECKED
;
2605 } else if (IsFrameRTL(aFrame
)) {
2610 case StyleAppearance::Listbox
:
2611 case StyleAppearance::Treeview
:
2612 case StyleAppearance::NumberInput
:
2613 case StyleAppearance::Textfield
:
2614 case StyleAppearance::Textarea
:
2615 case StyleAppearance::Menulist
:
2616 case StyleAppearance::MenulistButton
:
2617 case StyleAppearance::Range
:
2618 case StyleAppearance::RangeThumb
:
2619 case StyleAppearance::Progresschunk
:
2620 case StyleAppearance::ProgressBar
:
2621 case StyleAppearance::Tab
:
2622 case StyleAppearance::Tabpanel
:
2623 case StyleAppearance::Tabpanels
:
2624 case StyleAppearance::Menubar
:
2625 case StyleAppearance::Menupopup
:
2626 case StyleAppearance::Groupbox
:
2627 // these don't use DrawFrameControl
2629 case StyleAppearance::MozMenulistArrowButton
: {
2631 aState
= DFCS_SCROLLCOMBOBOX
;
2633 nsIFrame
* parentFrame
= aFrame
->GetParent();
2634 // HTML select and XUL menulist dropdown buttons get state from the
2636 aFrame
= parentFrame
;
2638 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
2640 if (elementState
.HasState(ElementState::DISABLED
)) {
2641 aState
|= DFCS_INACTIVE
;
2645 bool isOpen
= false;
2646 if (nsComboboxControlFrame
* ccf
= do_QueryFrame(aFrame
)) {
2647 isOpen
= ccf
->IsDroppedDown();
2649 isOpen
= IsOpenButton(aFrame
);
2652 // XXX Button should look active until the mouse is released, but
2653 // without making it look active when the popup is clicked.
2658 // Dropdown button active state doesn't need :hover.
2659 if (elementState
.HasState(ElementState::ACTIVE
))
2660 aState
|= DFCS_PUSHED
| DFCS_FLAT
;
2664 case StyleAppearance::SpinnerUpbutton
:
2665 case StyleAppearance::SpinnerDownbutton
: {
2666 ElementState contentState
= GetContentState(aFrame
, aAppearance
);
2669 switch (aAppearance
) {
2670 case StyleAppearance::SpinnerUpbutton
:
2671 aState
= DFCS_SCROLLUP
;
2673 case StyleAppearance::SpinnerDownbutton
:
2674 aState
= DFCS_SCROLLDOWN
;
2680 if (contentState
.HasState(ElementState::DISABLED
)) {
2681 aState
|= DFCS_INACTIVE
;
2683 if (contentState
.HasAllStates(ElementState::HOVER
|
2684 ElementState::ACTIVE
))
2685 aState
|= DFCS_PUSHED
;
2690 case StyleAppearance::Menuseparator
:
2694 case StyleAppearance::MozWindowTitlebar
:
2695 aPart
= mozilla::widget::themeconst::WP_CAPTION
;
2696 aState
= GetTopLevelWindowActiveState(aFrame
);
2698 case StyleAppearance::MozWindowTitlebarMaximized
:
2699 aPart
= mozilla::widget::themeconst::WP_MAXCAPTION
;
2700 aState
= GetTopLevelWindowActiveState(aFrame
);
2702 case StyleAppearance::MozWindowButtonClose
:
2703 aPart
= DFC_CAPTION
;
2704 aState
= DFCS_CAPTIONCLOSE
| GetClassicWindowFrameButtonState(
2705 GetContentState(aFrame
, aAppearance
));
2707 case StyleAppearance::MozWindowButtonMinimize
:
2708 aPart
= DFC_CAPTION
;
2709 aState
= DFCS_CAPTIONMIN
| GetClassicWindowFrameButtonState(
2710 GetContentState(aFrame
, aAppearance
));
2712 case StyleAppearance::MozWindowButtonMaximize
:
2713 aPart
= DFC_CAPTION
;
2714 aState
= DFCS_CAPTIONMAX
| GetClassicWindowFrameButtonState(
2715 GetContentState(aFrame
, aAppearance
));
2717 case StyleAppearance::MozWindowButtonRestore
:
2718 aPart
= DFC_CAPTION
;
2719 aState
= DFCS_CAPTIONRESTORE
| GetClassicWindowFrameButtonState(
2720 GetContentState(aFrame
, aAppearance
));
2723 return NS_ERROR_FAILURE
;
2727 // Draw classic Windows tab
2728 // (no system API for this, but DrawEdge can draw all the parts of a tab)
2729 static void DrawTab(HDC hdc
, const RECT
& R
, int32_t aPosition
, bool aSelected
,
2730 bool aDrawLeft
, bool aDrawRight
) {
2731 int32_t leftFlag
, topFlag
, rightFlag
, lightFlag
, shadeFlag
;
2732 RECT topRect
, sideRect
, bottomRect
, lightRect
, shadeRect
;
2733 int32_t selectedOffset
, lOffset
, rOffset
;
2735 selectedOffset
= aSelected
? 1 : 0;
2736 lOffset
= aDrawLeft
? 2 : 0;
2737 rOffset
= aDrawRight
? 2 : 0;
2739 // Get info for tab orientation/position (Left, Top, Right, Bottom)
2740 switch (aPosition
) {
2744 rightFlag
= BF_BOTTOM
;
2745 lightFlag
= BF_DIAGONAL_ENDTOPRIGHT
;
2746 shadeFlag
= BF_DIAGONAL_ENDBOTTOMRIGHT
;
2748 ::SetRect(&topRect
, R
.left
, R
.top
+ lOffset
, R
.right
, R
.bottom
- rOffset
);
2749 ::SetRect(&sideRect
, R
.left
+ 2, R
.top
, R
.right
- 2 + selectedOffset
,
2751 ::SetRect(&bottomRect
, R
.right
- 2, R
.top
, R
.right
, R
.bottom
);
2752 ::SetRect(&lightRect
, R
.left
, R
.top
, R
.left
+ 3, R
.top
+ 3);
2753 ::SetRect(&shadeRect
, R
.left
+ 1, R
.bottom
- 2, R
.left
+ 2, R
.bottom
- 1);
2758 rightFlag
= BF_RIGHT
;
2759 lightFlag
= BF_DIAGONAL_ENDTOPRIGHT
;
2760 shadeFlag
= BF_DIAGONAL_ENDBOTTOMRIGHT
;
2762 ::SetRect(&topRect
, R
.left
+ lOffset
, R
.top
, R
.right
- rOffset
, R
.bottom
);
2763 ::SetRect(&sideRect
, R
.left
, R
.top
+ 2, R
.right
,
2764 R
.bottom
- 1 + selectedOffset
);
2765 ::SetRect(&bottomRect
, R
.left
, R
.bottom
- 1, R
.right
, R
.bottom
);
2766 ::SetRect(&lightRect
, R
.left
, R
.top
, R
.left
+ 3, R
.top
+ 3);
2767 ::SetRect(&shadeRect
, R
.right
- 2, R
.top
+ 1, R
.right
- 1, R
.top
+ 2);
2772 rightFlag
= BF_BOTTOM
;
2773 lightFlag
= BF_DIAGONAL_ENDTOPLEFT
;
2774 shadeFlag
= BF_DIAGONAL_ENDBOTTOMLEFT
;
2776 ::SetRect(&topRect
, R
.left
, R
.top
+ lOffset
, R
.right
, R
.bottom
- rOffset
);
2777 ::SetRect(&sideRect
, R
.left
+ 2 - selectedOffset
, R
.top
, R
.right
- 2,
2779 ::SetRect(&bottomRect
, R
.left
, R
.top
, R
.left
+ 2, R
.bottom
);
2780 ::SetRect(&lightRect
, R
.right
- 3, R
.top
, R
.right
- 1, R
.top
+ 2);
2781 ::SetRect(&shadeRect
, R
.right
- 2, R
.bottom
- 3, R
.right
, R
.bottom
- 1);
2785 topFlag
= BF_BOTTOM
;
2786 rightFlag
= BF_RIGHT
;
2787 lightFlag
= BF_DIAGONAL_ENDTOPLEFT
;
2788 shadeFlag
= BF_DIAGONAL_ENDBOTTOMLEFT
;
2790 ::SetRect(&topRect
, R
.left
+ lOffset
, R
.top
, R
.right
- rOffset
, R
.bottom
);
2791 ::SetRect(&sideRect
, R
.left
, R
.top
+ 2 - selectedOffset
, R
.right
,
2793 ::SetRect(&bottomRect
, R
.left
, R
.top
, R
.right
, R
.top
+ 2);
2794 ::SetRect(&lightRect
, R
.left
, R
.bottom
- 3, R
.left
+ 2, R
.bottom
- 1);
2795 ::SetRect(&shadeRect
, R
.right
- 2, R
.bottom
- 3, R
.right
, R
.bottom
- 1);
2802 ::FillRect(hdc
, &R
, (HBRUSH
)(COLOR_3DFACE
+ 1));
2805 ::DrawEdge(hdc
, &topRect
, EDGE_RAISED
, BF_SOFT
| topFlag
);
2808 if (!aSelected
) ::DrawEdge(hdc
, &bottomRect
, EDGE_RAISED
, BF_SOFT
| topFlag
);
2811 if (!aDrawLeft
) leftFlag
= 0;
2812 if (!aDrawRight
) rightFlag
= 0;
2813 ::DrawEdge(hdc
, &sideRect
, EDGE_RAISED
, BF_SOFT
| leftFlag
| rightFlag
);
2815 // Tab Diagonal Corners
2816 if (aDrawLeft
) ::DrawEdge(hdc
, &lightRect
, EDGE_RAISED
, BF_SOFT
| lightFlag
);
2818 if (aDrawRight
) ::DrawEdge(hdc
, &shadeRect
, EDGE_RAISED
, BF_SOFT
| shadeFlag
);
2821 void nsNativeThemeWin::DrawCheckedRect(HDC hdc
, const RECT
& rc
, int32_t fore
,
2822 int32_t back
, HBRUSH defaultBack
) {
2823 static WORD patBits
[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55};
2825 HBITMAP patBmp
= ::CreateBitmap(8, 8, 1, 1, patBits
);
2827 HBRUSH brush
= (HBRUSH
)::CreatePatternBrush(patBmp
);
2829 COLORREF oldForeColor
= ::SetTextColor(hdc
, ::GetSysColor(fore
));
2830 COLORREF oldBackColor
= ::SetBkColor(hdc
, ::GetSysColor(back
));
2833 ::UnrealizeObject(brush
);
2834 ::GetViewportOrgEx(hdc
, &vpOrg
);
2835 ::SetBrushOrgEx(hdc
, vpOrg
.x
+ rc
.left
, vpOrg
.y
+ rc
.top
, nullptr);
2836 HBRUSH oldBrush
= (HBRUSH
)::SelectObject(hdc
, brush
);
2837 ::FillRect(hdc
, &rc
, brush
);
2838 ::SetTextColor(hdc
, oldForeColor
);
2839 ::SetBkColor(hdc
, oldBackColor
);
2840 ::SelectObject(hdc
, oldBrush
);
2841 ::DeleteObject(brush
);
2843 ::FillRect(hdc
, &rc
, defaultBack
);
2845 ::DeleteObject(patBmp
);
2849 nsresult
nsNativeThemeWin::ClassicDrawWidgetBackground(
2850 gfxContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
,
2851 const nsRect
& aRect
, const nsRect
& aDirtyRect
) {
2852 int32_t part
, state
;
2855 rv
= ClassicGetThemePartAndState(aFrame
, aAppearance
, part
, state
, focused
);
2856 if (NS_FAILED(rv
)) return rv
;
2858 if (AssumeThemePartAndStateAreTransparent(part
, state
)) {
2862 gfxFloat p2a
= gfxFloat(aFrame
->PresContext()->AppUnitsPerDevPixel());
2864 gfxRect
tr(aRect
.X(), aRect
.Y(), aRect
.Width(), aRect
.Height()),
2865 dr(aDirtyRect
.X(), aDirtyRect
.Y(), aDirtyRect
.Width(),
2866 aDirtyRect
.Height());
2868 tr
.Scale(1.0 / p2a
);
2869 dr
.Scale(1.0 / p2a
);
2871 gfxWindowsNativeDrawing
nativeDrawing(
2872 aContext
, dr
, GetWidgetNativeDrawingFlags(aAppearance
));
2876 HDC hdc
= nativeDrawing
.BeginNativeDrawing();
2877 if (!hdc
) return NS_ERROR_FAILURE
;
2879 nativeDrawing
.TransformToNativeRect(tr
, widgetRect
);
2882 switch (aAppearance
) {
2884 case StyleAppearance::Button
: {
2886 // draw dark button focus border first
2888 brush
= ::GetSysColorBrush(COLOR_3DDKSHADOW
);
2889 if (brush
) ::FrameRect(hdc
, &widgetRect
, brush
);
2890 InflateRect(&widgetRect
, -1, -1);
2894 // Draw controls supported by DrawFrameControl
2895 case StyleAppearance::Checkbox
:
2896 case StyleAppearance::Radio
:
2897 case StyleAppearance::SpinnerUpbutton
:
2898 case StyleAppearance::SpinnerDownbutton
:
2899 case StyleAppearance::MozMenulistArrowButton
: {
2901 // setup DC to make DrawFrameControl draw correctly
2902 oldTA
= ::SetTextAlign(hdc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
2903 ::DrawFrameControl(hdc
, &widgetRect
, part
, state
);
2904 ::SetTextAlign(hdc
, oldTA
);
2907 // Draw controls with 2px 3D inset border
2908 case StyleAppearance::NumberInput
:
2909 case StyleAppearance::Textfield
:
2910 case StyleAppearance::Textarea
:
2911 case StyleAppearance::Listbox
:
2912 case StyleAppearance::Menulist
:
2913 case StyleAppearance::MenulistButton
: {
2915 ::DrawEdge(hdc
, &widgetRect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
2917 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
2919 // Fill in background
2921 if (elementState
.HasState(ElementState::DISABLED
) ||
2922 (aFrame
->GetContent()->IsXULElement() && IsReadOnly(aFrame
)))
2923 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_BTNFACE
+ 1));
2925 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_WINDOW
+ 1));
2929 case StyleAppearance::Treeview
: {
2931 ::DrawEdge(hdc
, &widgetRect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
2933 // Fill in window color background
2934 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_WINDOW
+ 1));
2938 case StyleAppearance::Groupbox
:
2939 ::DrawEdge(hdc
, &widgetRect
, EDGE_ETCHED
, BF_RECT
| BF_ADJUST
);
2940 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_BTNFACE
+ 1));
2942 // Draw 3D face background controls
2943 case StyleAppearance::ProgressBar
:
2945 ::DrawEdge(hdc
, &widgetRect
, BDR_SUNKENOUTER
, BF_RECT
| BF_MIDDLE
);
2946 InflateRect(&widgetRect
, -1, -1);
2948 case StyleAppearance::Tabpanel
: {
2949 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_BTNFACE
+ 1));
2952 case StyleAppearance::RangeThumb
: {
2953 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
2955 ::DrawEdge(hdc
, &widgetRect
, EDGE_RAISED
,
2956 BF_RECT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
2957 if (elementState
.HasState(ElementState::DISABLED
)) {
2958 DrawCheckedRect(hdc
, widgetRect
, COLOR_3DFACE
, COLOR_3DHILIGHT
,
2959 (HBRUSH
)COLOR_3DHILIGHT
);
2964 // Draw scale track background
2965 case StyleAppearance::Range
: {
2966 const int32_t trackWidth
= 4;
2967 // When rounding is necessary, we round the position of the track
2968 // away from the chevron of the thumb to make it look better.
2969 if (IsRangeHorizontal(aFrame
)) {
2970 widgetRect
.top
+= (widgetRect
.bottom
- widgetRect
.top
- trackWidth
) / 2;
2971 widgetRect
.bottom
= widgetRect
.top
+ trackWidth
;
2973 if (!IsFrameRTL(aFrame
)) {
2975 (widgetRect
.right
- widgetRect
.left
- trackWidth
) / 2;
2976 widgetRect
.right
= widgetRect
.left
+ trackWidth
;
2979 (widgetRect
.right
- widgetRect
.left
- trackWidth
) / 2;
2980 widgetRect
.left
= widgetRect
.right
- trackWidth
;
2984 ::DrawEdge(hdc
, &widgetRect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
2985 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)GetStockObject(GRAY_BRUSH
));
2989 case StyleAppearance::Progresschunk
: {
2990 nsIFrame
* stateFrame
= aFrame
->GetParent();
2991 ElementState elementState
= GetContentState(stateFrame
, aAppearance
);
2993 const bool indeterminate
=
2994 elementState
.HasState(ElementState::INDETERMINATE
);
2995 bool vertical
= IsVerticalProgress(stateFrame
);
2997 nsIContent
* content
= aFrame
->GetContent();
2998 if (!indeterminate
|| !content
) {
2999 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_HIGHLIGHT
+ 1));
3003 RECT overlayRect
= CalculateProgressOverlayRect(
3004 aFrame
, &widgetRect
, vertical
, indeterminate
, true);
3006 ::FillRect(hdc
, &overlayRect
, (HBRUSH
)(COLOR_HIGHLIGHT
+ 1));
3008 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 30)) {
3009 NS_WARNING("unable to animate progress widget!");
3015 case StyleAppearance::Tab
: {
3016 DrawTab(hdc
, widgetRect
, IsBottomTab(aFrame
) ? BF_BOTTOM
: BF_TOP
,
3017 IsSelectedTab(aFrame
), !IsRightToSelectedTab(aFrame
),
3018 !IsLeftToSelectedTab(aFrame
));
3022 case StyleAppearance::Tabpanels
:
3023 ::DrawEdge(hdc
, &widgetRect
, EDGE_RAISED
,
3024 BF_SOFT
| BF_MIDDLE
| BF_LEFT
| BF_RIGHT
| BF_BOTTOM
);
3027 case StyleAppearance::Menubar
:
3029 case StyleAppearance::Menuseparator
: {
3030 // separators are offset by a bit (see menu.css)
3034 // This magic number is brought to you by the value in menu.css
3035 widgetRect
.top
+= 4;
3036 // Our rectangles are 1 pixel high (see border size in menu.css)
3037 widgetRect
.bottom
= widgetRect
.top
+ 1;
3038 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_3DSHADOW
+ 1));
3040 widgetRect
.bottom
++;
3041 ::FillRect(hdc
, &widgetRect
, (HBRUSH
)(COLOR_3DHILIGHT
+ 1));
3045 case StyleAppearance::MozWindowTitlebar
:
3046 case StyleAppearance::MozWindowTitlebarMaximized
: {
3047 RECT rect
= widgetRect
;
3048 int32_t offset
= GetSystemMetrics(SM_CXFRAME
);
3050 // first fill the area to the color of the window background
3051 ::FillRect(hdc
, &rect
, (HBRUSH
)(COLOR_3DFACE
+ 1));
3053 // inset the caption area so it doesn't overflow.
3055 // if enabled, draw a gradient titlebar background, otherwise
3056 // fill with a solid color.
3058 SystemParametersInfo(SPI_GETGRADIENTCAPTIONS
, 0, &bFlag
, 0);
3060 if (state
== mozilla::widget::themeconst::FS_ACTIVE
)
3061 ::FillRect(hdc
, &rect
, (HBRUSH
)(COLOR_ACTIVECAPTION
+ 1));
3063 ::FillRect(hdc
, &rect
, (HBRUSH
)(COLOR_INACTIVECAPTION
+ 1));
3065 DWORD startColor
, endColor
;
3066 if (state
== mozilla::widget::themeconst::FS_ACTIVE
) {
3067 startColor
= GetSysColor(COLOR_ACTIVECAPTION
);
3068 endColor
= GetSysColor(COLOR_GRADIENTACTIVECAPTION
);
3070 startColor
= GetSysColor(COLOR_INACTIVECAPTION
);
3071 endColor
= GetSysColor(COLOR_GRADIENTINACTIVECAPTION
);
3074 TRIVERTEX vertex
[2];
3075 vertex
[0].x
= rect
.left
;
3076 vertex
[0].y
= rect
.top
;
3077 vertex
[0].Red
= GetRValue(startColor
) << 8;
3078 vertex
[0].Green
= GetGValue(startColor
) << 8;
3079 vertex
[0].Blue
= GetBValue(startColor
) << 8;
3080 vertex
[0].Alpha
= 0;
3082 vertex
[1].x
= rect
.right
;
3083 vertex
[1].y
= rect
.bottom
;
3084 vertex
[1].Red
= GetRValue(endColor
) << 8;
3085 vertex
[1].Green
= GetGValue(endColor
) << 8;
3086 vertex
[1].Blue
= GetBValue(endColor
) << 8;
3087 vertex
[1].Alpha
= 0;
3089 GRADIENT_RECT gRect
;
3090 gRect
.UpperLeft
= 0;
3091 gRect
.LowerRight
= 1;
3092 // available on win2k & up
3093 GradientFill(hdc
, vertex
, 2, &gRect
, 1, GRADIENT_FILL_RECT_H
);
3096 if (aAppearance
== StyleAppearance::MozWindowTitlebar
) {
3097 // frame things up with a top raised border.
3098 DrawEdge(hdc
, &widgetRect
, EDGE_RAISED
, BF_TOP
);
3103 case StyleAppearance::MozWindowButtonClose
:
3104 case StyleAppearance::MozWindowButtonMinimize
:
3105 case StyleAppearance::MozWindowButtonMaximize
:
3106 case StyleAppearance::MozWindowButtonRestore
: {
3107 if (aAppearance
== StyleAppearance::MozWindowButtonMinimize
) {
3108 OffsetBackgroundRect(widgetRect
, CAPTIONBUTTON_MINIMIZE
);
3109 } else if (aAppearance
== StyleAppearance::MozWindowButtonMaximize
||
3110 aAppearance
== StyleAppearance::MozWindowButtonRestore
) {
3111 OffsetBackgroundRect(widgetRect
, CAPTIONBUTTON_RESTORE
);
3112 } else if (aAppearance
== StyleAppearance::MozWindowButtonClose
) {
3113 OffsetBackgroundRect(widgetRect
, CAPTIONBUTTON_CLOSE
);
3115 int32_t oldTA
= SetTextAlign(hdc
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
3116 DrawFrameControl(hdc
, &widgetRect
, part
, state
);
3117 SetTextAlign(hdc
, oldTA
);
3122 rv
= NS_ERROR_FAILURE
;
3126 nativeDrawing
.EndNativeDrawing();
3128 if (NS_FAILED(rv
)) return rv
;
3130 if (nativeDrawing
.ShouldRenderAgain()) goto RENDER_AGAIN
;
3132 nativeDrawing
.PaintToContext();
3137 uint32_t nsNativeThemeWin::GetWidgetNativeDrawingFlags(
3138 StyleAppearance aAppearance
) {
3139 switch (aAppearance
) {
3140 case StyleAppearance::Button
:
3141 case StyleAppearance::NumberInput
:
3142 case StyleAppearance::Textfield
:
3143 case StyleAppearance::Textarea
:
3144 case StyleAppearance::Menulist
:
3145 case StyleAppearance::MenulistButton
:
3146 return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA
|
3147 gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE
|
3148 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM
;
3150 // the dropdown button /almost/ renders correctly with scaling,
3151 // except that the graphic in the dropdown button (the downward arrow)
3152 // doesn't get scaled up.
3153 case StyleAppearance::MozMenulistArrowButton
:
3154 // these are definitely no; they're all graphics that don't get scaled up
3155 case StyleAppearance::Checkbox
:
3156 case StyleAppearance::Radio
:
3157 case StyleAppearance::Groupbox
:
3158 case StyleAppearance::Checkmenuitem
:
3159 case StyleAppearance::Radiomenuitem
:
3160 case StyleAppearance::Menucheckbox
:
3161 case StyleAppearance::Menuradio
:
3162 case StyleAppearance::Menuarrow
:
3163 return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA
|
3164 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE
|
3165 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM
;
3167 // need to check these others
3169 return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA
|
3170 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE
|
3171 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM
;
3175 } // namespace mozilla::widget
3177 ///////////////////////////////////////////
3179 ///////////////////////////////////////////
3181 already_AddRefed
<Theme
> do_CreateNativeThemeDoNotUseDirectly() {
3182 return do_AddRef(new nsNativeThemeWin());