Backed out changeset 8517afe50156 (bug 540456) for reftest failures.
[gecko.git] / widget / windows / nsNativeThemeWin.cpp
blobe3104531edd81590da51a64e1f7a1d258adecf86
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 <windows.h>
7 #include "nsNativeThemeWin.h"
8 #include "nsRenderingContext.h"
9 #include "nsRect.h"
10 #include "nsSize.h"
11 #include "nsTransform2D.h"
12 #include "nsThemeConstants.h"
13 #include "nsIPresShell.h"
14 #include "nsPresContext.h"
15 #include "nsIContent.h"
16 #include "nsIFrame.h"
17 #include "nsEventStates.h"
18 #include "nsINameSpaceManager.h"
19 #include "nsIDOMHTMLInputElement.h"
20 #include "nsMenuFrame.h"
21 #include "nsGkAtoms.h"
22 #include <malloc.h>
23 #include "nsWindow.h"
24 #include "nsIComboboxControlFrame.h"
25 #include "prinrval.h"
26 #include "WinUtils.h"
28 #include "gfxPlatform.h"
29 #include "gfxContext.h"
30 #include "gfxMatrix.h"
31 #include "gfxWindowsPlatform.h"
32 #include "gfxWindowsSurface.h"
33 #include "gfxWindowsNativeDrawing.h"
35 #include "nsUXThemeData.h"
36 #include "nsUXThemeConstants.h"
37 #include <algorithm>
39 using namespace mozilla::widget;
41 #ifdef PR_LOGGING
42 extern PRLogModuleInfo* gWindowsLog;
43 #endif
45 NS_IMPL_ISUPPORTS_INHERITED1(nsNativeThemeWin, nsNativeTheme, nsITheme)
47 nsNativeThemeWin::nsNativeThemeWin() :
48 mProgressDeterminateTimeStamp(TimeStamp::Now()),
49 mProgressIndeterminateTimeStamp(TimeStamp::Now())
51 // If there is a relevant change in forms.css for windows platform,
52 // static widget style variables (e.g. sButtonBorderSize) should be
53 // reinitialized here.
56 nsNativeThemeWin::~nsNativeThemeWin()
58 nsUXThemeData::Invalidate();
61 static int32_t
62 GetTopLevelWindowActiveState(nsIFrame *aFrame)
64 // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
65 // until it finds a real window.
66 nsIWidget* widget = aFrame->GetNearestWidget();
67 nsWindowBase * window = static_cast<nsWindowBase*>(widget);
68 if (!window)
69 return mozilla::widget::themeconst::FS_INACTIVE;
70 if (widget && !window->IsTopLevelWidget() &&
71 !(window = window->GetParentWindowBase(false)))
72 return mozilla::widget::themeconst::FS_INACTIVE;
74 if (window->GetWindowHandle() == ::GetActiveWindow())
75 return mozilla::widget::themeconst::FS_ACTIVE;
76 return mozilla::widget::themeconst::FS_INACTIVE;
79 static int32_t
80 GetWindowFrameButtonState(nsIFrame *aFrame, nsEventStates eventState)
82 if (GetTopLevelWindowActiveState(aFrame) ==
83 mozilla::widget::themeconst::FS_INACTIVE) {
84 if (eventState.HasState(NS_EVENT_STATE_HOVER))
85 return mozilla::widget::themeconst::BS_HOT;
86 return mozilla::widget::themeconst::BS_INACTIVE;
89 if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
90 if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
91 return mozilla::widget::themeconst::BS_PUSHED;
92 return mozilla::widget::themeconst::BS_HOT;
94 return mozilla::widget::themeconst::BS_NORMAL;
97 static int32_t
98 GetClassicWindowFrameButtonState(nsEventStates eventState)
100 if (eventState.HasState(NS_EVENT_STATE_ACTIVE) &&
101 eventState.HasState(NS_EVENT_STATE_HOVER))
102 return DFCS_BUTTONPUSH|DFCS_PUSHED;
103 return DFCS_BUTTONPUSH;
106 static bool
107 IsTopLevelMenu(nsIFrame *aFrame)
109 bool isTopLevel(false);
110 nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
111 if (menuFrame) {
112 isTopLevel = menuFrame->IsOnMenuBar();
114 return isTopLevel;
117 static MARGINS
118 GetCheckboxMargins(HANDLE theme, HDC hdc)
120 MARGINS checkboxContent = {0};
121 GetThemeMargins(theme, hdc, MENU_POPUPCHECK, MCB_NORMAL,
122 TMT_CONTENTMARGINS, nullptr, &checkboxContent);
123 return checkboxContent;
126 static SIZE
127 GetCheckboxBGSize(HANDLE theme, HDC hdc)
129 SIZE checkboxSize;
130 GetThemePartSize(theme, hdc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL,
131 nullptr, TS_TRUE, &checkboxSize);
133 MARGINS checkboxMargins = GetCheckboxMargins(theme, hdc);
135 int leftMargin = checkboxMargins.cxLeftWidth;
136 int rightMargin = checkboxMargins.cxRightWidth;
137 int topMargin = checkboxMargins.cyTopHeight;
138 int bottomMargin = checkboxMargins.cyBottomHeight;
140 int width = leftMargin + checkboxSize.cx + rightMargin;
141 int height = topMargin + checkboxSize.cy + bottomMargin;
142 SIZE ret;
143 ret.cx = width;
144 ret.cy = height;
145 return ret;
148 static SIZE
149 GetCheckboxBGBounds(HANDLE theme, HDC hdc)
151 MARGINS checkboxBGSizing = {0};
152 MARGINS checkboxBGContent = {0};
153 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
154 TMT_SIZINGMARGINS, nullptr, &checkboxBGSizing);
155 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
156 TMT_CONTENTMARGINS, nullptr, &checkboxBGContent);
158 #define posdx(d) ((d) > 0 ? d : 0)
160 int dx = posdx(checkboxBGContent.cxRightWidth -
161 checkboxBGSizing.cxRightWidth) +
162 posdx(checkboxBGContent.cxLeftWidth -
163 checkboxBGSizing.cxLeftWidth);
164 int dy = posdx(checkboxBGContent.cyTopHeight -
165 checkboxBGSizing.cyTopHeight) +
166 posdx(checkboxBGContent.cyBottomHeight -
167 checkboxBGSizing.cyBottomHeight);
169 #undef posdx
171 SIZE ret(GetCheckboxBGSize(theme, hdc));
172 ret.cx += dx;
173 ret.cy += dy;
174 return ret;
177 static SIZE
178 GetGutterSize(HANDLE theme, HDC hdc)
180 SIZE gutterSize;
181 GetThemePartSize(theme, hdc, MENU_POPUPGUTTER, 0, nullptr, TS_TRUE, &gutterSize);
183 SIZE checkboxBGSize(GetCheckboxBGBounds(theme, hdc));
185 SIZE itemSize;
186 GetThemePartSize(theme, hdc, MENU_POPUPITEM, MPI_NORMAL, nullptr, TS_TRUE, &itemSize);
188 // Figure out how big the menuitem's icon will be (if present) at current DPI
189 double scaleFactor = nsIWidget::DefaultScaleOverride();
190 if (scaleFactor <= 0.0) {
191 scaleFactor = gfxWindowsPlatform::GetPlatform()->GetDPIScale();
193 int iconDevicePixels = NSToIntRound(16 * scaleFactor);
194 SIZE iconSize = {
195 iconDevicePixels, iconDevicePixels
197 // Not really sure what margins should be used here, but this seems to work in practice...
198 MARGINS margins = {0};
199 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
200 TMT_CONTENTMARGINS, nullptr, &margins);
201 iconSize.cx += margins.cxLeftWidth + margins.cxRightWidth;
202 iconSize.cy += margins.cyTopHeight + margins.cyBottomHeight;
204 int width = std::max(itemSize.cx, std::max(iconSize.cx, checkboxBGSize.cx) + gutterSize.cx);
205 int height = std::max(itemSize.cy, std::max(iconSize.cy, checkboxBGSize.cy));
207 SIZE ret;
208 ret.cx = width;
209 ret.cy = height;
210 return ret;
213 /* DrawThemeBGRTLAware - render a theme part based on rtl state.
214 * Some widgets are not direction-neutral and need to be drawn reversed for
215 * RTL. Windows provides a way to do this with SetLayout, but this reverses
216 * the entire drawing area of a given device context, which means that its
217 * use will also affect the positioning of the widget. There are two ways
218 * to work around this:
220 * Option 1: Alter the position of the rect that we send so that we cancel
221 * out the positioning effects of SetLayout
222 * Option 2: Create a memory DC with the widgetRect's dimensions, draw onto
223 * that, and then transfer the results back to our DC
225 * This function tries to implement option 1, under the assumption that the
226 * correct way to reverse the effects of SetLayout is to translate the rect
227 * such that the offset from the DC bitmap's left edge to the old rect's
228 * left edge is equal to the offset from the DC bitmap's right edge to the
229 * new rect's right edge. In other words,
230 * (oldRect.left + vpOrg.x) == ((dcBMP.width - vpOrg.x) - newRect.right)
232 static HRESULT
233 DrawThemeBGRTLAware(HANDLE aTheme, HDC aHdc, int aPart, int aState,
234 const RECT *aWidgetRect, const RECT *aClipRect,
235 bool aIsRtl)
237 NS_ASSERTION(aTheme, "Bad theme handle.");
238 NS_ASSERTION(aHdc, "Bad hdc.");
239 NS_ASSERTION(aWidgetRect, "Bad rect.");
240 NS_ASSERTION(aClipRect, "Bad clip rect.");
242 if (!aIsRtl) {
243 return DrawThemeBackground(aTheme, aHdc, aPart, aState,
244 aWidgetRect, aClipRect);
247 HGDIOBJ hObj = GetCurrentObject(aHdc, OBJ_BITMAP);
248 BITMAP bitmap;
249 POINT vpOrg;
251 if (hObj && GetObject(hObj, sizeof(bitmap), &bitmap) &&
252 GetViewportOrgEx(aHdc, &vpOrg)) {
253 RECT newWRect(*aWidgetRect);
254 newWRect.left = bitmap.bmWidth - (aWidgetRect->right + 2*vpOrg.x);
255 newWRect.right = bitmap.bmWidth - (aWidgetRect->left + 2*vpOrg.x);
257 RECT newCRect;
258 RECT *newCRectPtr = nullptr;
260 if (aClipRect) {
261 newCRect.top = aClipRect->top;
262 newCRect.bottom = aClipRect->bottom;
263 newCRect.left = bitmap.bmWidth - (aClipRect->right + 2*vpOrg.x);
264 newCRect.right = bitmap.bmWidth - (aClipRect->left + 2*vpOrg.x);
265 newCRectPtr = &newCRect;
268 SetLayout(aHdc, LAYOUT_RTL);
269 HRESULT hr = DrawThemeBackground(aTheme, aHdc, aPart, aState, &newWRect,
270 newCRectPtr);
271 SetLayout(aHdc, 0);
272 if (SUCCEEDED(hr)) {
273 return hr;
276 return DrawThemeBackground(aTheme, aHdc, aPart, aState,
277 aWidgetRect, aClipRect);
281 * Caption button padding data - 'hot' button padding.
282 * These areas are considered hot, in that they activate
283 * a button when hovered or clicked. The button graphic
284 * is drawn inside the padding border. Unrecognized themes
285 * are treated as their recognized counterparts for now.
286 * left top right bottom
287 * classic min 1 2 0 1
288 * classic max 0 2 1 1
289 * classic close 1 2 2 1
291 * aero basic min 1 2 0 2
292 * aero basic max 0 2 1 2
293 * aero basic close 1 2 1 2
295 * xp theme min 0 2 0 2
296 * xp theme max 0 2 1 2
297 * xp theme close 1 2 2 2
299 * 'cold' button padding - generic button padding, should
300 * be handled in css.
301 * left top right bottom
302 * classic min 0 0 0 0
303 * classic max 0 0 0 0
304 * classic close 0 0 0 0
306 * aero basic min 0 0 1 0
307 * aero basic max 1 0 0 0
308 * aero basic close 0 0 0 0
310 * xp theme min 0 0 1 0
311 * xp theme max 1 0 0 0
312 * xp theme close 0 0 0 0
315 enum CaptionDesktopTheme {
316 CAPTION_CLASSIC = 0,
317 CAPTION_BASIC,
318 CAPTION_XPTHEME,
321 enum CaptionButton {
322 CAPTIONBUTTON_MINIMIZE = 0,
323 CAPTIONBUTTON_RESTORE,
324 CAPTIONBUTTON_CLOSE,
327 struct CaptionButtonPadding {
328 RECT hotPadding[3];
331 // RECT: left, top, right, bottom
332 static CaptionButtonPadding buttonData[3] = {
334 { { 1, 2, 0, 1 }, { 0, 2, 1, 1 }, { 1, 2, 2, 1 } }
337 { { 1, 2, 0, 2 }, { 0, 2, 1, 2 }, { 1, 2, 2, 2 } }
340 { { 0, 2, 0, 2 }, { 0, 2, 1, 2 }, { 1, 2, 2, 2 } }
344 // Adds "hot" caption button padding to minimum widget size.
345 static void
346 AddPaddingRect(nsIntSize* aSize, CaptionButton button) {
347 if (!aSize)
348 return;
349 RECT offset;
350 if (!IsAppThemed())
351 offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
352 else if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION)
353 offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
354 else
355 offset = buttonData[CAPTION_BASIC].hotPadding[button];
356 aSize->width += offset.left + offset.right;
357 aSize->height += offset.top + offset.bottom;
360 // If we've added padding to the minimum widget size, offset
361 // the area we draw into to compensate.
362 static void
363 OffsetBackgroundRect(RECT& rect, CaptionButton button) {
364 RECT offset;
365 if (!IsAppThemed())
366 offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
367 else if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION)
368 offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
369 else
370 offset = buttonData[CAPTION_BASIC].hotPadding[button];
371 rect.left += offset.left;
372 rect.top += offset.top;
373 rect.right -= offset.right;
374 rect.bottom -= offset.bottom;
378 * Notes on progress track and meter part constants:
379 * xp and up:
380 * PP_BAR(_VERT) - base progress track
381 * PP_TRANSPARENTBAR(_VERT) - transparent progress track. this only works if
382 * the underlying surface supports alpha. otherwise
383 * theme lib's DrawThemeBackground falls back on
384 * opaque PP_BAR. we currently don't use this.
385 * PP_CHUNK(_VERT) - xp progress meter. this does not draw an xp style
386 * progress w/chunks, it draws fill using the chunk
387 * graphic.
388 * vista and up:
389 * PP_FILL(_VERT) - progress meter. these have four states/colors.
390 * PP_PULSEOVERLAY(_VERT) - white reflection - an overlay, not sure what this
391 * is used for.
392 * PP_MOVEOVERLAY(_VERT) - green pulse - the pulse effect overlay on
393 * determined progress bars. we also use this for
394 * indeterminate chunk.
396 * Notes on state constants:
397 * PBBS_NORMAL - green progress
398 * PBBVS_PARTIAL/PBFVS_ERROR - red error progress
399 * PBFS_PAUSED - yellow paused progress
401 * There is no common controls style indeterminate part on vista and up.
405 * Progress bar related constants. These values are found by experimenting and
406 * comparing against native widgets used by the system. They are very unlikely
407 * exact but try to not be too wrong.
409 // The amount of time we animate progress meters parts across the frame.
410 static const double kProgressDeterminateTimeSpan = 3.0;
411 static const double kProgressIndeterminateTimeSpan = 5.0;
412 // The width of the overlay used to animate the horizontal progress bar (Vista and later).
413 static const int32_t kProgressHorizontalVistaOverlaySize = 120;
414 // The width of the overlay used for the horizontal indeterminate progress bars on XP.
415 static const int32_t kProgressHorizontalXPOverlaySize = 55;
416 // The height of the overlay used to animate the vertical progress bar (Vista and later).
417 static const int32_t kProgressVerticalOverlaySize = 45;
418 // The height of the overlay used for the vertical indeterminate progress bar (Vista and later).
419 static const int32_t kProgressVerticalIndeterminateOverlaySize = 60;
420 // The width of the overlay used to animate the indeterminate progress bar (Windows Classic).
421 static const int32_t kProgressClassicOverlaySize = 40;
424 * GetProgressOverlayStyle - returns the proper overlay part for themed
425 * progress bars based on os and orientation.
427 static int32_t
428 GetProgressOverlayStyle(bool aIsVertical)
430 if (aIsVertical) {
431 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
432 return PP_MOVEOVERLAYVERT;
434 return PP_CHUNKVERT;
435 } else {
436 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
437 return PP_MOVEOVERLAY;
439 return PP_CHUNK;
444 * GetProgressOverlaySize - returns the minimum width or height for themed
445 * progress bar overlays. This includes the width of indeterminate chunks
446 * and vista pulse overlays.
448 static int32_t
449 GetProgressOverlaySize(bool aIsVertical, bool aIsIndeterminate)
451 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
452 if (aIsVertical) {
453 return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize
454 : kProgressVerticalOverlaySize;
456 return kProgressHorizontalVistaOverlaySize;
458 return kProgressHorizontalXPOverlaySize;
462 * IsProgressMeterFilled - Determines if a progress meter is at 100% fill based
463 * on a comparison of the current value and maximum.
465 static bool
466 IsProgressMeterFilled(nsIFrame* aFrame)
468 NS_ENSURE_TRUE(aFrame, false);
469 nsIFrame* parentFrame = aFrame->GetParent();
470 NS_ENSURE_TRUE(parentFrame, false);
471 return nsNativeTheme::GetProgressValue(parentFrame) ==
472 nsNativeTheme::GetProgressMaxValue(parentFrame);
476 * CalculateProgressOverlayRect - returns the padded overlay animation rect
477 * used in rendering progress bars. Resulting rects are used in rendering
478 * vista+ pulse overlays and indeterminate progress meters. Graphics should
479 * be rendered at the origin.
481 RECT
482 nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame,
483 RECT* aWidgetRect,
484 bool aIsVertical,
485 bool aIsIndeterminate,
486 bool aIsClassic)
488 NS_ASSERTION(aFrame, "bad frame pointer");
489 NS_ASSERTION(aWidgetRect, "bad rect pointer");
491 int32_t frameSize = aIsVertical ? aWidgetRect->bottom - aWidgetRect->top
492 : aWidgetRect->right - aWidgetRect->left;
494 // Recycle a set of progress pulse timers - these timers control the position
495 // of all progress overlays and indeterminate chunks that get rendered.
496 double span = aIsIndeterminate ? kProgressIndeterminateTimeSpan
497 : kProgressDeterminateTimeSpan;
498 TimeDuration period;
499 if (!aIsIndeterminate) {
500 if (TimeStamp::Now() > (mProgressDeterminateTimeStamp +
501 TimeDuration::FromSeconds(span))) {
502 mProgressDeterminateTimeStamp = TimeStamp::Now();
504 period = TimeStamp::Now() - mProgressDeterminateTimeStamp;
505 } else {
506 if (TimeStamp::Now() > (mProgressIndeterminateTimeStamp +
507 TimeDuration::FromSeconds(span))) {
508 mProgressIndeterminateTimeStamp = TimeStamp::Now();
510 period = TimeStamp::Now() - mProgressIndeterminateTimeStamp;
513 double percent = period / TimeDuration::FromSeconds(span);
515 if (!aIsVertical && IsFrameRTL(aFrame))
516 percent = 1 - percent;
518 RECT overlayRect = *aWidgetRect;
519 int32_t overlaySize;
520 if (!aIsClassic) {
521 overlaySize = GetProgressOverlaySize(aIsVertical, aIsIndeterminate);
522 } else {
523 overlaySize = kProgressClassicOverlaySize;
526 // Calculate a bounds that is larger than the meters frame such that the
527 // overlay starts and ends completely off the edge of the frame:
528 // [overlay][frame][overlay]
529 // This also yields a nice delay on rotation. Use overlaySize as the minimum
530 // size for [overlay] based on the graphics dims. If [frame] is larger, use
531 // the frame size instead.
532 int trackWidth = frameSize > overlaySize ? frameSize : overlaySize;
533 if (!aIsVertical) {
534 int xPos = aWidgetRect->left - trackWidth;
535 xPos += (int)ceil(((double)(trackWidth*2) * percent));
536 overlayRect.left = xPos;
537 overlayRect.right = xPos + overlaySize;
538 } else {
539 int yPos = aWidgetRect->bottom + trackWidth;
540 yPos -= (int)ceil(((double)(trackWidth*2) * percent));
541 overlayRect.bottom = yPos;
542 overlayRect.top = yPos - overlaySize;
544 return overlayRect;
548 * DrawChunkProgressMeter - renders an xp style chunked progress meter. Called
549 * by DrawProgressMeter.
551 * @param aTheme progress theme handle
552 * @param aHdc hdc returned by gfxWindowsNativeDrawing
553 * @param aPart the PP_X progress part
554 * @param aState the theme state
555 * @param aFrame the elements frame
556 * @param aWidgetRect bounding rect for the widget
557 * @param aClipRect dirty rect that needs drawing.
558 * @param aAppUnits app units per device pixel
559 * @param aIsIndeterm is an indeterminate progress?
560 * @param aIsVertical render a vertical progress?
561 * @param aIsRtl direction is rtl
563 static void
564 DrawChunkProgressMeter(HTHEME aTheme, HDC aHdc, int aPart,
565 int aState, nsIFrame* aFrame, RECT* aWidgetRect,
566 RECT* aClipRect, gfxFloat aAppUnits, bool aIsIndeterm,
567 bool aIsVertical, bool aIsRtl)
569 NS_ASSERTION(aTheme, "Bad theme.");
570 NS_ASSERTION(aHdc, "Bad hdc.");
571 NS_ASSERTION(aWidgetRect, "Bad rect.");
572 NS_ASSERTION(aClipRect, "Bad clip rect.");
573 NS_ASSERTION(aFrame, "Bad frame.");
575 // For horizontal meters, the theme lib paints the right graphic but doesn't
576 // paint the chunks, so we do that manually. For vertical meters, the theme
577 // library draws everything correctly.
578 if (aIsVertical) {
579 DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
580 return;
583 // query for the proper chunk metrics
584 int chunkSize, spaceSize;
585 if (FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
586 TMT_PROGRESSCHUNKSIZE, &chunkSize)) ||
587 FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
588 TMT_PROGRESSSPACESIZE, &spaceSize))) {
589 DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
590 return;
593 // render chunks
594 if (!aIsRtl || aIsIndeterm) {
595 for (int chunk = aWidgetRect->left; chunk <= aWidgetRect->right;
596 chunk += (chunkSize+spaceSize)) {
597 if (!aIsIndeterm && ((chunk + chunkSize) > aWidgetRect->right)) {
598 // aWidgetRect->right represents the end of the meter. Partial blocks
599 // don't get rendered with one exception, so exit here if we don't have
600 // a full chunk to draw.
601 // The above is true *except* when the meter is at 100% fill, in which
602 // case Windows renders any remaining partial block. Query the parent
603 // frame to find out if we're at 100%.
604 if (!IsProgressMeterFilled(aFrame)) {
605 break;
608 RECT bounds =
609 { chunk, aWidgetRect->top, chunk + chunkSize, aWidgetRect->bottom };
610 DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
612 } else {
613 // rtl needs to grow in the opposite direction to look right.
614 for (int chunk = aWidgetRect->right; chunk >= aWidgetRect->left;
615 chunk -= (chunkSize+spaceSize)) {
616 if ((chunk - chunkSize) < aWidgetRect->left) {
617 if (!IsProgressMeterFilled(aFrame)) {
618 break;
621 RECT bounds =
622 { chunk - chunkSize, aWidgetRect->top, chunk, aWidgetRect->bottom };
623 DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
629 * DrawProgressMeter - render an appropriate progress meter based on progress
630 * meter style, orientation, and os. Note, this does not render the underlying
631 * progress track.
633 * @param aFrame the widget frame
634 * @param aWidgetType type of widget
635 * @param aTheme progress theme handle
636 * @param aHdc hdc returned by gfxWindowsNativeDrawing
637 * @param aPart the PP_X progress part
638 * @param aState the theme state
639 * @param aWidgetRect bounding rect for the widget
640 * @param aClipRect dirty rect that needs drawing.
641 * @param aAppUnits app units per device pixel
643 void
644 nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
645 HANDLE aTheme, HDC aHdc,
646 int aPart, int aState,
647 RECT* aWidgetRect, RECT* aClipRect,
648 gfxFloat aAppUnits)
650 if (!aFrame || !aTheme || !aHdc)
651 return;
653 NS_ASSERTION(aWidgetRect, "bad rect pointer");
654 NS_ASSERTION(aClipRect, "bad clip rect pointer");
656 RECT adjWidgetRect, adjClipRect;
657 adjWidgetRect = *aWidgetRect;
658 adjClipRect = *aClipRect;
659 if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) {
660 // Adjust clipping out by one pixel. XP progress meters are inset,
661 // Vista+ are not.
662 InflateRect(&adjWidgetRect, 1, 1);
663 InflateRect(&adjClipRect, 1, 1);
666 nsIFrame* parentFrame = aFrame->GetParent();
667 if (!parentFrame) {
668 // We have no parent to work with, just bail.
669 NS_WARNING("No parent frame for progress rendering. Can't paint.");
670 return;
673 nsEventStates eventStates = GetContentState(parentFrame, aWidgetType);
674 bool vertical = IsVerticalProgress(parentFrame) ||
675 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL;
676 bool indeterminate = IsIndeterminateProgress(parentFrame, eventStates);
677 bool animate = indeterminate;
679 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
680 // Vista and up progress meter is fill style, rendered here. We render
681 // the pulse overlay in the follow up section below.
682 DrawThemeBackground(aTheme, aHdc, aPart, aState,
683 &adjWidgetRect, &adjClipRect);
684 if (!IsProgressMeterFilled(aFrame)) {
685 animate = true;
687 } else if (!indeterminate) {
688 // XP progress meters are 'chunk' style.
689 DrawChunkProgressMeter(aTheme, aHdc, aPart, aState, aFrame,
690 &adjWidgetRect, &adjClipRect, aAppUnits,
691 indeterminate, vertical, IsFrameRTL(aFrame));
694 if (animate) {
695 // Indeterminate rendering
696 int32_t overlayPart = GetProgressOverlayStyle(vertical);
697 RECT overlayRect =
698 CalculateProgressOverlayRect(aFrame, &adjWidgetRect, vertical,
699 indeterminate, false);
700 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
701 DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect,
702 &adjClipRect);
703 } else {
704 DrawChunkProgressMeter(aTheme, aHdc, overlayPart, aState, aFrame,
705 &overlayRect, &adjClipRect, aAppUnits,
706 indeterminate, vertical, IsFrameRTL(aFrame));
709 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
710 NS_WARNING("unable to animate progress widget!");
715 HANDLE
716 nsNativeThemeWin::GetTheme(uint8_t aWidgetType)
718 if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) {
719 // On XP or earlier, render dropdowns as textfields;
720 // doing it the right way works fine with the MS themes,
721 // but breaks on a lot of custom themes (presumably because MS
722 // apps do the textfield border business as well).
723 if (aWidgetType == NS_THEME_DROPDOWN)
724 aWidgetType = NS_THEME_TEXTFIELD;
727 switch (aWidgetType) {
728 case NS_THEME_BUTTON:
729 case NS_THEME_RADIO:
730 case NS_THEME_CHECKBOX:
731 case NS_THEME_GROUPBOX:
732 return nsUXThemeData::GetTheme(eUXButton);
733 case NS_THEME_TEXTFIELD:
734 case NS_THEME_TEXTFIELD_MULTILINE:
735 return nsUXThemeData::GetTheme(eUXEdit);
736 case NS_THEME_TOOLTIP:
737 // XP/2K3 should force a classic treatment of tooltips
738 return WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION ?
739 nullptr : nsUXThemeData::GetTheme(eUXTooltip);
740 case NS_THEME_TOOLBOX:
741 return nsUXThemeData::GetTheme(eUXRebar);
742 case NS_THEME_WIN_MEDIA_TOOLBOX:
743 return nsUXThemeData::GetTheme(eUXMediaRebar);
744 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
745 return nsUXThemeData::GetTheme(eUXCommunicationsRebar);
746 case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
747 return nsUXThemeData::GetTheme(eUXBrowserTabBarRebar);
748 case NS_THEME_TOOLBAR:
749 case NS_THEME_TOOLBAR_BUTTON:
750 case NS_THEME_TOOLBAR_SEPARATOR:
751 return nsUXThemeData::GetTheme(eUXToolbar);
752 case NS_THEME_PROGRESSBAR:
753 case NS_THEME_PROGRESSBAR_VERTICAL:
754 case NS_THEME_PROGRESSBAR_CHUNK:
755 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
756 return nsUXThemeData::GetTheme(eUXProgress);
757 case NS_THEME_TAB:
758 case NS_THEME_TAB_PANEL:
759 case NS_THEME_TAB_PANELS:
760 return nsUXThemeData::GetTheme(eUXTab);
761 case NS_THEME_SCROLLBAR:
762 case NS_THEME_SCROLLBAR_SMALL:
763 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
764 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
765 case NS_THEME_SCROLLBAR_BUTTON_UP:
766 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
767 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
768 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
769 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
770 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
771 return nsUXThemeData::GetTheme(eUXScrollbar);
772 case NS_THEME_RANGE:
773 case NS_THEME_RANGE_THUMB:
774 case NS_THEME_SCALE_HORIZONTAL:
775 case NS_THEME_SCALE_VERTICAL:
776 case NS_THEME_SCALE_THUMB_HORIZONTAL:
777 case NS_THEME_SCALE_THUMB_VERTICAL:
778 return nsUXThemeData::GetTheme(eUXTrackbar);
779 case NS_THEME_SPINNER_UP_BUTTON:
780 case NS_THEME_SPINNER_DOWN_BUTTON:
781 return nsUXThemeData::GetTheme(eUXSpin);
782 case NS_THEME_STATUSBAR:
783 case NS_THEME_STATUSBAR_PANEL:
784 case NS_THEME_STATUSBAR_RESIZER_PANEL:
785 case NS_THEME_RESIZER:
786 return nsUXThemeData::GetTheme(eUXStatus);
787 case NS_THEME_DROPDOWN:
788 case NS_THEME_DROPDOWN_BUTTON:
789 return nsUXThemeData::GetTheme(eUXCombobox);
790 case NS_THEME_TREEVIEW_HEADER_CELL:
791 case NS_THEME_TREEVIEW_HEADER_SORTARROW:
792 return nsUXThemeData::GetTheme(eUXHeader);
793 case NS_THEME_LISTBOX:
794 case NS_THEME_LISTBOX_LISTITEM:
795 case NS_THEME_TREEVIEW:
796 case NS_THEME_TREEVIEW_TWISTY_OPEN:
797 case NS_THEME_TREEVIEW_TREEITEM:
798 return nsUXThemeData::GetTheme(eUXListview);
799 case NS_THEME_MENUBAR:
800 case NS_THEME_MENUPOPUP:
801 case NS_THEME_MENUITEM:
802 case NS_THEME_CHECKMENUITEM:
803 case NS_THEME_RADIOMENUITEM:
804 case NS_THEME_MENUCHECKBOX:
805 case NS_THEME_MENURADIO:
806 case NS_THEME_MENUSEPARATOR:
807 case NS_THEME_MENUARROW:
808 case NS_THEME_MENUIMAGE:
809 case NS_THEME_MENUITEMTEXT:
810 return nsUXThemeData::GetTheme(eUXMenu);
811 case NS_THEME_WINDOW_TITLEBAR:
812 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
813 case NS_THEME_WINDOW_FRAME_LEFT:
814 case NS_THEME_WINDOW_FRAME_RIGHT:
815 case NS_THEME_WINDOW_FRAME_BOTTOM:
816 case NS_THEME_WINDOW_BUTTON_CLOSE:
817 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
818 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
819 case NS_THEME_WINDOW_BUTTON_RESTORE:
820 case NS_THEME_WINDOW_BUTTON_BOX:
821 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
822 case NS_THEME_WIN_GLASS:
823 case NS_THEME_WIN_BORDERLESS_GLASS:
824 return nsUXThemeData::GetTheme(eUXWindowFrame);
826 return nullptr;
829 int32_t
830 nsNativeThemeWin::StandardGetState(nsIFrame* aFrame, uint8_t aWidgetType,
831 bool wantFocused)
833 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
834 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
835 return TS_ACTIVE;
836 if (eventState.HasState(NS_EVENT_STATE_HOVER))
837 return TS_HOVER;
838 if (wantFocused && eventState.HasState(NS_EVENT_STATE_FOCUS))
839 return TS_FOCUSED;
841 return TS_NORMAL;
844 bool
845 nsNativeThemeWin::IsMenuActive(nsIFrame *aFrame, uint8_t aWidgetType)
847 nsIContent* content = aFrame->GetContent();
848 if (content->IsXUL() &&
849 content->NodeInfo()->Equals(nsGkAtoms::richlistitem))
850 return CheckBooleanAttr(aFrame, nsGkAtoms::selected);
852 return CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
856 * aPart is filled in with the UXTheme part code. On return, values > 0
857 * are the actual UXTheme part code; -1 means the widget will be drawn by
858 * us; 0 means that we should use part code 0, which isn't a real part code
859 * but elicits some kind of default behaviour from UXTheme when drawing
860 * (but isThemeBackgroundPartiallyTransparent may not work).
862 nsresult
863 nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
864 int32_t& aPart, int32_t& aState)
866 if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) {
867 // See GetTheme
868 if (aWidgetType == NS_THEME_DROPDOWN)
869 aWidgetType = NS_THEME_TEXTFIELD;
872 switch (aWidgetType) {
873 case NS_THEME_BUTTON: {
874 aPart = BP_BUTTON;
875 if (!aFrame) {
876 aState = TS_NORMAL;
877 return NS_OK;
880 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
881 if (IsDisabled(aFrame, eventState)) {
882 aState = TS_DISABLED;
883 return NS_OK;
884 } else if (IsOpenButton(aFrame) ||
885 IsCheckedButton(aFrame)) {
886 aState = TS_ACTIVE;
887 return NS_OK;
890 aState = StandardGetState(aFrame, aWidgetType, true);
892 // Check for default dialog buttons. These buttons should always look
893 // focused.
894 if (aState == TS_NORMAL && IsDefaultButton(aFrame))
895 aState = TS_FOCUSED;
896 return NS_OK;
898 case NS_THEME_CHECKBOX:
899 case NS_THEME_RADIO: {
900 bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
901 aPart = isCheckbox ? BP_CHECKBOX : BP_RADIO;
903 enum InputState {
904 UNCHECKED = 0, CHECKED, INDETERMINATE
906 InputState inputState = UNCHECKED;
907 bool isXULCheckboxRadio = false;
909 if (!aFrame) {
910 aState = TS_NORMAL;
911 } else {
912 if (GetCheckedOrSelected(aFrame, !isCheckbox)) {
913 inputState = CHECKED;
914 } if (isCheckbox && GetIndeterminate(aFrame)) {
915 inputState = INDETERMINATE;
918 nsEventStates eventState = GetContentState(isXULCheckboxRadio ? aFrame->GetParent()
919 : aFrame,
920 aWidgetType);
921 if (IsDisabled(aFrame, eventState)) {
922 aState = TS_DISABLED;
923 } else {
924 aState = StandardGetState(aFrame, aWidgetType, false);
928 // 4 unchecked states, 4 checked states, 4 indeterminate states.
929 aState += inputState * 4;
930 return NS_OK;
932 case NS_THEME_GROUPBOX: {
933 aPart = BP_GROUPBOX;
934 aState = TS_NORMAL;
935 // Since we don't support groupbox disabled and GBS_DISABLED looks the
936 // same as GBS_NORMAL don't bother supporting GBS_DISABLED.
937 return NS_OK;
939 case NS_THEME_TEXTFIELD:
940 case NS_THEME_TEXTFIELD_MULTILINE: {
941 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
943 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
944 /* Note: the NOSCROLL type has a rounded corner in each
945 * corner. The more specific HSCROLL, VSCROLL, HVSCROLL types
946 * have side and/or top/bottom edges rendered as straight
947 * horizontal lines with sharp corners to accommodate a
948 * scrollbar. However, the scrollbar gets rendered on top of
949 * this for us, so we don't care, and can just use NOSCROLL
950 * here.
952 aPart = TFP_EDITBORDER_NOSCROLL;
954 if (!aFrame) {
955 aState = TFS_EDITBORDER_NORMAL;
956 } else if (IsDisabled(aFrame, eventState)) {
957 aState = TFS_EDITBORDER_DISABLED;
958 } else if (IsReadOnly(aFrame)) {
959 /* no special read-only state */
960 aState = TFS_EDITBORDER_NORMAL;
961 } else {
962 nsIContent* content = aFrame->GetContent();
964 /* XUL textboxes don't get focused themselves, because they have child
965 * html:input.. but we can check the XUL focused attributes on them
967 if (content && content->IsXUL() && IsFocused(aFrame))
968 aState = TFS_EDITBORDER_FOCUSED;
969 else if (eventState.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS))
970 aState = TFS_EDITBORDER_FOCUSED;
971 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
972 aState = TFS_EDITBORDER_HOVER;
973 else
974 aState = TFS_EDITBORDER_NORMAL;
976 } else {
977 aPart = TFP_TEXTFIELD;
979 if (!aFrame)
980 aState = TS_NORMAL;
981 else if (IsDisabled(aFrame, eventState))
982 aState = TS_DISABLED;
983 else if (IsReadOnly(aFrame))
984 aState = TFS_READONLY;
985 else
986 aState = StandardGetState(aFrame, aWidgetType, true);
989 return NS_OK;
991 case NS_THEME_TOOLTIP: {
992 aPart = TTP_STANDARD;
993 aState = TS_NORMAL;
994 return NS_OK;
996 case NS_THEME_PROGRESSBAR:
997 case NS_THEME_PROGRESSBAR_VERTICAL: {
998 // Note IsVerticalProgress only tests for orient css attrribute,
999 // NS_THEME_PROGRESSBAR_VERTICAL is dedicated to -moz-appearance:
1000 // progressbar-vertical.
1001 bool vertical = IsVerticalProgress(aFrame) ||
1002 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL;
1003 aPart = vertical ? PP_BARVERT : PP_BAR;
1004 aState = PBBS_NORMAL;
1005 return NS_OK;
1007 case NS_THEME_PROGRESSBAR_CHUNK:
1008 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: {
1009 nsIFrame* parentFrame = aFrame->GetParent();
1010 nsEventStates eventStates = GetContentState(parentFrame, aWidgetType);
1011 if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
1012 IsVerticalProgress(parentFrame)) {
1013 aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ?
1014 PP_FILLVERT : PP_CHUNKVERT;
1015 } else {
1016 aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ?
1017 PP_FILL : PP_CHUNK;
1020 aState = PBBVS_NORMAL;
1021 return NS_OK;
1023 case NS_THEME_TOOLBAR_BUTTON: {
1024 aPart = BP_BUTTON;
1025 if (!aFrame) {
1026 aState = TS_NORMAL;
1027 return NS_OK;
1030 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1031 if (IsDisabled(aFrame, eventState)) {
1032 aState = TS_DISABLED;
1033 return NS_OK;
1035 if (IsOpenButton(aFrame)) {
1036 aState = TS_ACTIVE;
1037 return NS_OK;
1040 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
1041 aState = TS_ACTIVE;
1042 else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
1043 if (IsCheckedButton(aFrame))
1044 aState = TB_HOVER_CHECKED;
1045 else
1046 aState = TS_HOVER;
1048 else {
1049 if (IsCheckedButton(aFrame))
1050 aState = TB_CHECKED;
1051 else
1052 aState = TS_NORMAL;
1055 return NS_OK;
1057 case NS_THEME_TOOLBAR_SEPARATOR: {
1058 aPart = TP_SEPARATOR;
1059 aState = TS_NORMAL;
1060 return NS_OK;
1062 case NS_THEME_SCROLLBAR_BUTTON_UP:
1063 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
1064 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
1065 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: {
1066 aPart = SP_BUTTON;
1067 aState = (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP)*4;
1068 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1069 if (!aFrame)
1070 aState += TS_NORMAL;
1071 else if (IsDisabled(aFrame, eventState))
1072 aState += TS_DISABLED;
1073 else {
1074 nsIFrame *parent = aFrame->GetParent();
1075 nsEventStates parentState = GetContentState(parent, parent->StyleDisplay()->mAppearance);
1076 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
1077 aState += TS_ACTIVE;
1078 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1079 aState += TS_HOVER;
1080 else if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
1081 parentState.HasState(NS_EVENT_STATE_HOVER))
1082 aState = (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP) + SP_BUTTON_IMPLICIT_HOVER_BASE;
1083 else
1084 aState += TS_NORMAL;
1086 return NS_OK;
1088 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
1089 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: {
1090 aPart = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL) ?
1091 SP_TRACKSTARTHOR : SP_TRACKSTARTVERT;
1092 aState = TS_NORMAL;
1093 return NS_OK;
1095 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
1096 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: {
1097 aPart = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) ?
1098 SP_THUMBHOR : SP_THUMBVERT;
1099 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1100 if (!aFrame)
1101 aState = TS_NORMAL;
1102 else if (IsDisabled(aFrame, eventState))
1103 aState = TS_DISABLED;
1104 else {
1105 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) // Hover is not also a requirement for
1106 // the thumb, since the drag is not canceled
1107 // when you move outside the thumb.
1108 aState = TS_ACTIVE;
1109 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1110 aState = TS_HOVER;
1111 else
1112 aState = TS_NORMAL;
1114 return NS_OK;
1116 case NS_THEME_RANGE:
1117 case NS_THEME_SCALE_HORIZONTAL:
1118 case NS_THEME_SCALE_VERTICAL: {
1119 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1120 (aWidgetType == NS_THEME_RANGE &&
1121 IsRangeHorizontal(aFrame))) {
1122 aPart = TKP_TRACK;
1123 aState = TRS_NORMAL;
1124 } else {
1125 aPart = TKP_TRACKVERT;
1126 aState = TRVS_NORMAL;
1128 return NS_OK;
1130 case NS_THEME_RANGE_THUMB:
1131 case NS_THEME_SCALE_THUMB_HORIZONTAL:
1132 case NS_THEME_SCALE_THUMB_VERTICAL: {
1133 if (aWidgetType == NS_THEME_RANGE_THUMB) {
1134 if (IsRangeHorizontal(aFrame)) {
1135 aPart = TKP_THUMBBOTTOM;
1136 } else {
1137 aPart = IsFrameRTL(aFrame) ? TKP_THUMBLEFT : TKP_THUMBRIGHT;
1139 } else {
1140 aPart = (aWidgetType == NS_THEME_SCALE_THUMB_HORIZONTAL) ?
1141 TKP_THUMB : TKP_THUMBVERT;
1143 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1144 if (!aFrame)
1145 aState = TS_NORMAL;
1146 else if (IsDisabled(aFrame, eventState)) {
1147 aState = TKP_DISABLED;
1149 else {
1150 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) // Hover is not also a requirement for
1151 // the thumb, since the drag is not canceled
1152 // when you move outside the thumb.
1153 aState = TS_ACTIVE;
1154 else if (eventState.HasState(NS_EVENT_STATE_FOCUS))
1155 aState = TKP_FOCUSED;
1156 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1157 aState = TS_HOVER;
1158 else
1159 aState = TS_NORMAL;
1161 return NS_OK;
1163 case NS_THEME_SPINNER_UP_BUTTON:
1164 case NS_THEME_SPINNER_DOWN_BUTTON: {
1165 aPart = (aWidgetType == NS_THEME_SPINNER_UP_BUTTON) ?
1166 SPNP_UP : SPNP_DOWN;
1167 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1168 if (!aFrame)
1169 aState = TS_NORMAL;
1170 else if (IsDisabled(aFrame, eventState))
1171 aState = TS_DISABLED;
1172 else
1173 aState = StandardGetState(aFrame, aWidgetType, false);
1174 return NS_OK;
1176 case NS_THEME_TOOLBOX:
1177 case NS_THEME_WIN_MEDIA_TOOLBOX:
1178 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
1179 case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
1180 case NS_THEME_STATUSBAR:
1181 case NS_THEME_SCROLLBAR:
1182 case NS_THEME_SCROLLBAR_SMALL: {
1183 aState = 0;
1184 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
1185 // On vista, they have a part
1186 aPart = RP_BACKGROUND;
1187 } else {
1188 // Otherwise, they don't. (But I bet
1189 // RP_BACKGROUND would work here, too);
1190 aPart = 0;
1192 return NS_OK;
1194 case NS_THEME_TOOLBAR: {
1195 // Use -1 to indicate we don't wish to have the theme background drawn
1196 // for this item. We will pass any nessessary information via aState,
1197 // and will render the item using separate code.
1198 aPart = -1;
1199 aState = 0;
1200 if (aFrame) {
1201 nsIContent* content = aFrame->GetContent();
1202 nsIContent* parent = content->GetParent();
1203 // XXXzeniko hiding the first toolbar will result in an unwanted margin
1204 if (parent && parent->GetFirstChild() == content) {
1205 aState = 1;
1208 return NS_OK;
1210 case NS_THEME_STATUSBAR_PANEL:
1211 case NS_THEME_STATUSBAR_RESIZER_PANEL:
1212 case NS_THEME_RESIZER: {
1213 aPart = (aWidgetType - NS_THEME_STATUSBAR_PANEL) + 1;
1214 aState = TS_NORMAL;
1215 return NS_OK;
1217 case NS_THEME_TREEVIEW:
1218 case NS_THEME_LISTBOX: {
1219 aPart = TREEVIEW_BODY;
1220 aState = TS_NORMAL;
1221 return NS_OK;
1223 case NS_THEME_TAB_PANELS: {
1224 aPart = TABP_PANELS;
1225 aState = TS_NORMAL;
1226 return NS_OK;
1228 case NS_THEME_TAB_PANEL: {
1229 aPart = TABP_PANEL;
1230 aState = TS_NORMAL;
1231 return NS_OK;
1233 case NS_THEME_TAB: {
1234 aPart = TABP_TAB;
1235 if (!aFrame) {
1236 aState = TS_NORMAL;
1237 return NS_OK;
1240 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1241 if (IsDisabled(aFrame, eventState)) {
1242 aState = TS_DISABLED;
1243 return NS_OK;
1246 if (IsSelectedTab(aFrame)) {
1247 aPart = TABP_TAB_SELECTED;
1248 aState = TS_ACTIVE; // The selected tab is always "pressed".
1250 else
1251 aState = StandardGetState(aFrame, aWidgetType, true);
1253 return NS_OK;
1255 case NS_THEME_TREEVIEW_HEADER_SORTARROW: {
1256 // XXX Probably will never work due to a bug in the Luna theme.
1257 aPart = 4;
1258 aState = 1;
1259 return NS_OK;
1261 case NS_THEME_TREEVIEW_HEADER_CELL: {
1262 aPart = 1;
1263 if (!aFrame) {
1264 aState = TS_NORMAL;
1265 return NS_OK;
1268 aState = StandardGetState(aFrame, aWidgetType, true);
1270 return NS_OK;
1272 case NS_THEME_DROPDOWN: {
1273 nsIContent* content = aFrame->GetContent();
1274 bool isHTML = content && content->IsHTML();
1275 bool useDropBorder = isHTML || IsMenuListEditable(aFrame);
1276 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1278 /* On Vista/Win7, we use CBP_DROPBORDER instead of DROPFRAME for HTML
1279 * content or for editable menulists; this gives us the thin outline,
1280 * instead of the gradient-filled background */
1281 if (useDropBorder)
1282 aPart = CBP_DROPBORDER;
1283 else
1284 aPart = CBP_DROPFRAME;
1286 if (IsDisabled(aFrame, eventState)) {
1287 aState = TS_DISABLED;
1288 } else if (IsReadOnly(aFrame)) {
1289 aState = TS_NORMAL;
1290 } else if (IsOpenButton(aFrame)) {
1291 aState = TS_ACTIVE;
1292 } else {
1293 if (useDropBorder && (eventState.HasState(NS_EVENT_STATE_FOCUS) || IsFocused(aFrame)))
1294 aState = TS_ACTIVE;
1295 else if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
1296 aState = TS_ACTIVE;
1297 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1298 aState = TS_HOVER;
1299 else
1300 aState = TS_NORMAL;
1303 return NS_OK;
1305 case NS_THEME_DROPDOWN_BUTTON: {
1306 bool isHTML = IsHTMLContent(aFrame);
1307 nsIFrame* parentFrame = aFrame->GetParent();
1308 bool isMenulist = !isHTML && parentFrame->GetType() == nsGkAtoms::menuFrame;
1309 bool isOpen = false;
1311 // HTML select and XUL menulist dropdown buttons get state from the parent.
1312 if (isHTML || isMenulist)
1313 aFrame = parentFrame;
1315 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1316 aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ?
1317 CBP_DROPMARKER_VISTA : CBP_DROPMARKER;
1319 // For HTML controls with author styling, we should fall
1320 // back to the old dropmarker style to avoid clashes with
1321 // author-specified backgrounds and borders (bug #441034)
1322 if (isHTML && IsWidgetStyled(aFrame->PresContext(), aFrame, NS_THEME_DROPDOWN))
1323 aPart = CBP_DROPMARKER;
1325 if (IsDisabled(aFrame, eventState)) {
1326 aState = TS_DISABLED;
1327 return NS_OK;
1330 if (isHTML) {
1331 nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
1332 isOpen = (ccf && ccf->IsDroppedDown());
1334 else
1335 isOpen = IsOpenButton(aFrame);
1337 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
1338 if (isHTML || IsMenuListEditable(aFrame)) {
1339 if (isOpen) {
1340 /* Hover is propagated, but we need to know whether we're
1341 * hovering just the combobox frame, not the dropdown frame.
1342 * But, we can't get that information, since hover is on the
1343 * content node, and they share the same content node. So,
1344 * instead, we cheat -- if the dropdown is open, we always
1345 * show the hover state. This looks fine in practice.
1347 aState = TS_HOVER;
1348 return NS_OK;
1350 } else {
1351 /* On Vista, the dropdown indicator on a menulist button in
1352 * chrome is not given a hover effect. When the frame isn't
1353 * isn't HTML content, we cheat and force the dropdown state
1354 * to be normal. (Bug 430434)
1356 aState = TS_NORMAL;
1357 return NS_OK;
1361 aState = TS_NORMAL;
1363 // Dropdown button active state doesn't need :hover.
1364 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) {
1365 if (isOpen && (isHTML || isMenulist)) {
1366 // XXX Button should look active until the mouse is released, but
1367 // without making it look active when the popup is clicked.
1368 return NS_OK;
1370 aState = TS_ACTIVE;
1372 else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
1373 // No hover effect for XUL menulists and autocomplete dropdown buttons
1374 // while the dropdown menu is open.
1375 if (isOpen) {
1376 // XXX HTML select dropdown buttons should have the hover effect when
1377 // hovering the combobox frame, but not the popup frame.
1378 return NS_OK;
1380 aState = TS_HOVER;
1382 return NS_OK;
1384 case NS_THEME_MENUPOPUP: {
1385 aPart = MENU_POPUPBACKGROUND;
1386 aState = MB_ACTIVE;
1387 return NS_OK;
1389 case NS_THEME_MENUITEM:
1390 case NS_THEME_CHECKMENUITEM:
1391 case NS_THEME_RADIOMENUITEM: {
1392 bool isTopLevel = false;
1393 bool isOpen = false;
1394 bool isHover = false;
1395 nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
1396 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1398 isTopLevel = IsTopLevelMenu(aFrame);
1400 if (menuFrame)
1401 isOpen = menuFrame->IsOpen();
1403 isHover = IsMenuActive(aFrame, aWidgetType);
1405 if (isTopLevel) {
1406 aPart = MENU_BARITEM;
1408 if (isOpen)
1409 aState = MBI_PUSHED;
1410 else if (isHover)
1411 aState = MBI_HOT;
1412 else
1413 aState = MBI_NORMAL;
1415 // the disabled states are offset by 3
1416 if (IsDisabled(aFrame, eventState))
1417 aState += 3;
1418 } else {
1419 aPart = MENU_POPUPITEM;
1421 if (isHover)
1422 aState = MPI_HOT;
1423 else
1424 aState = MPI_NORMAL;
1426 // the disabled states are offset by 2
1427 if (IsDisabled(aFrame, eventState))
1428 aState += 2;
1431 return NS_OK;
1433 case NS_THEME_MENUSEPARATOR:
1434 aPart = MENU_POPUPSEPARATOR;
1435 aState = 0;
1436 return NS_OK;
1437 case NS_THEME_MENUARROW:
1439 aPart = MENU_POPUPSUBMENU;
1440 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1441 aState = IsDisabled(aFrame, eventState) ? MSM_DISABLED : MSM_NORMAL;
1442 return NS_OK;
1444 case NS_THEME_MENUCHECKBOX:
1445 case NS_THEME_MENURADIO:
1447 bool isChecked;
1448 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1450 // NOTE: we can probably use NS_EVENT_STATE_CHECKED
1451 isChecked = CheckBooleanAttr(aFrame, nsGkAtoms::checked);
1453 aPart = MENU_POPUPCHECK;
1454 aState = MC_CHECKMARKNORMAL;
1456 // Radio states are offset by 2
1457 if (aWidgetType == NS_THEME_MENURADIO)
1458 aState += 2;
1460 // the disabled states are offset by 1
1461 if (IsDisabled(aFrame, eventState))
1462 aState += 1;
1464 return NS_OK;
1466 case NS_THEME_MENUITEMTEXT:
1467 case NS_THEME_MENUIMAGE:
1468 aPart = -1;
1469 aState = 0;
1470 return NS_OK;
1472 case NS_THEME_WINDOW_TITLEBAR:
1473 aPart = mozilla::widget::themeconst::WP_CAPTION;
1474 aState = GetTopLevelWindowActiveState(aFrame);
1475 return NS_OK;
1476 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
1477 aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
1478 aState = GetTopLevelWindowActiveState(aFrame);
1479 return NS_OK;
1480 case NS_THEME_WINDOW_FRAME_LEFT:
1481 aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
1482 aState = GetTopLevelWindowActiveState(aFrame);
1483 return NS_OK;
1484 case NS_THEME_WINDOW_FRAME_RIGHT:
1485 aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
1486 aState = GetTopLevelWindowActiveState(aFrame);
1487 return NS_OK;
1488 case NS_THEME_WINDOW_FRAME_BOTTOM:
1489 aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
1490 aState = GetTopLevelWindowActiveState(aFrame);
1491 return NS_OK;
1492 case NS_THEME_WINDOW_BUTTON_CLOSE:
1493 aPart = mozilla::widget::themeconst::WP_CLOSEBUTTON;
1494 aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
1495 return NS_OK;
1496 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
1497 aPart = mozilla::widget::themeconst::WP_MINBUTTON;
1498 aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
1499 return NS_OK;
1500 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
1501 aPart = mozilla::widget::themeconst::WP_MAXBUTTON;
1502 aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
1503 return NS_OK;
1504 case NS_THEME_WINDOW_BUTTON_RESTORE:
1505 aPart = mozilla::widget::themeconst::WP_RESTOREBUTTON;
1506 aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
1507 return NS_OK;
1508 case NS_THEME_WINDOW_BUTTON_BOX:
1509 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
1510 case NS_THEME_WIN_GLASS:
1511 case NS_THEME_WIN_BORDERLESS_GLASS:
1512 aPart = -1;
1513 aState = 0;
1514 return NS_OK;
1517 aPart = 0;
1518 aState = 0;
1519 return NS_ERROR_FAILURE;
1522 static bool
1523 AssumeThemePartAndStateAreTransparent(int32_t aPart, int32_t aState)
1525 if (aPart == MENU_POPUPITEM && aState == MBI_NORMAL) {
1526 return true;
1528 return false;
1531 NS_IMETHODIMP
1532 nsNativeThemeWin::DrawWidgetBackground(nsRenderingContext* aContext,
1533 nsIFrame* aFrame,
1534 uint8_t aWidgetType,
1535 const nsRect& aRect,
1536 const nsRect& aDirtyRect)
1538 HANDLE theme = GetTheme(aWidgetType);
1539 if (!theme)
1540 return ClassicDrawWidgetBackground(aContext, aFrame, aWidgetType, aRect, aDirtyRect);
1542 // ^^ without the right sdk, assume xp theming and fall through.
1543 if (nsUXThemeData::CheckForCompositor()) {
1544 switch (aWidgetType) {
1545 case NS_THEME_WINDOW_TITLEBAR:
1546 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
1547 case NS_THEME_WINDOW_FRAME_LEFT:
1548 case NS_THEME_WINDOW_FRAME_RIGHT:
1549 case NS_THEME_WINDOW_FRAME_BOTTOM:
1550 // Nothing to draw, these areas are glass. Minimum dimensions
1551 // should be set, so xul content should be layed out correctly.
1552 return NS_OK;
1553 break;
1554 case NS_THEME_WINDOW_BUTTON_CLOSE:
1555 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
1556 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
1557 case NS_THEME_WINDOW_BUTTON_RESTORE:
1558 // Not conventional bitmaps, can't be retrieved. If we fall
1559 // through here and call the theme library we'll get aero
1560 // basic bitmaps.
1561 return NS_OK;
1562 break;
1563 case NS_THEME_WIN_GLASS:
1564 case NS_THEME_WIN_BORDERLESS_GLASS:
1565 // Nothing to draw, this is the glass background.
1566 return NS_OK;
1567 break;
1571 int32_t part, state;
1572 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
1573 if (NS_FAILED(rv))
1574 return rv;
1576 if (AssumeThemePartAndStateAreTransparent(part, state)) {
1577 return NS_OK;
1580 gfxFloat p2a = gfxFloat(aContext->AppUnitsPerDevPixel());
1581 RECT widgetRect;
1582 RECT clipRect;
1583 gfxRect tr(aRect.x, aRect.y, aRect.width, aRect.height),
1584 dr(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
1586 tr.ScaleInverse(p2a);
1587 dr.ScaleInverse(p2a);
1589 /* See GetWidgetOverflow */
1590 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON &&
1591 part == CBP_DROPMARKER_VISTA && IsHTMLContent(aFrame))
1593 tr.y -= 1.0;
1594 tr.width += 1.0;
1595 tr.height += 2.0;
1597 dr.y -= 1.0;
1598 dr.width += 1.0;
1599 dr.height += 2.0;
1601 if (IsFrameRTL(aFrame)) {
1602 tr.x -= 1.0;
1603 dr.x -= 1.0;
1607 nsRefPtr<gfxContext> ctx = aContext->ThebesContext();
1609 gfxWindowsNativeDrawing nativeDrawing(ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
1611 RENDER_AGAIN:
1613 HDC hdc = nativeDrawing.BeginNativeDrawing();
1614 if (!hdc)
1615 return NS_ERROR_FAILURE;
1617 nativeDrawing.TransformToNativeRect(tr, widgetRect);
1618 nativeDrawing.TransformToNativeRect(dr, clipRect);
1620 #if 0
1622 PR_LOG(gWindowsLog, PR_LOG_ERROR,
1623 (stderr, "xform: %f %f %f %f [%f %f]\n", m.xx, m.yx, m.xy, m.yy,
1624 m.x0, m.y0));
1625 PR_LOG(gWindowsLog, PR_LOG_ERROR,
1626 (stderr, "tr: [%d %d %d %d]\ndr: [%d %d %d %d]\noff: [%f %f]\n",
1627 tr.x, tr.y, tr.width, tr.height, dr.x, dr.y, dr.width, dr.height,
1628 offset.x, offset.y));
1630 #endif
1632 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
1633 // Clip out the left and right corners of the frame, all we want in
1634 // is the middle section.
1635 widgetRect.left -= GetSystemMetrics(SM_CXFRAME);
1636 widgetRect.right += GetSystemMetrics(SM_CXFRAME);
1637 } else if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
1638 // The origin of the window is off screen when maximized and windows
1639 // doesn't compensate for this in rendering the background. Push the
1640 // top of the bitmap down by SM_CYFRAME so we get the full graphic.
1641 widgetRect.top += GetSystemMetrics(SM_CYFRAME);
1642 } else if (aWidgetType == NS_THEME_TAB) {
1643 // For left edge and right edge tabs, we need to adjust the widget
1644 // rects and clip rects so that the edges don't get drawn.
1645 bool isLeft = IsLeftToSelectedTab(aFrame);
1646 bool isRight = !isLeft && IsRightToSelectedTab(aFrame);
1648 if (isLeft || isRight) {
1649 // HACK ALERT: There appears to be no way to really obtain this value, so we're forced
1650 // to just use the default value for Luna (which also happens to be correct for
1651 // all the other skins I've tried).
1652 int32_t edgeSize = 2;
1654 // Armed with the size of the edge, we now need to either shift to the left or to the
1655 // right. The clip rect won't include this extra area, so we know that we're
1656 // effectively shifting the edge out of view (such that it won't be painted).
1657 if (isLeft)
1658 // The right edge should not be drawn. Extend our rect by the edge size.
1659 widgetRect.right += edgeSize;
1660 else
1661 // The left edge should not be drawn. Move the widget rect's left coord back.
1662 widgetRect.left -= edgeSize;
1665 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
1666 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
1668 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
1669 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
1670 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
1672 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
1673 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
1676 // widgetRect is the bounding box for a widget, yet the scale track is only
1677 // a small portion of this size, so the edges of the scale need to be
1678 // adjusted to the real size of the track.
1679 if (aWidgetType == NS_THEME_RANGE ||
1680 aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1681 aWidgetType == NS_THEME_SCALE_VERTICAL) {
1682 RECT contentRect;
1683 GetThemeBackgroundContentRect(theme, hdc, part, state, &widgetRect, &contentRect);
1685 SIZE siz;
1686 GetThemePartSize(theme, hdc, part, state, &widgetRect, TS_TRUE, &siz);
1688 // When rounding is necessary, we round the position of the track
1689 // away from the chevron of the thumb to make it look better.
1690 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1691 (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
1692 contentRect.top += (contentRect.bottom - contentRect.top - siz.cy) / 2;
1693 contentRect.bottom = contentRect.top + siz.cy;
1695 else {
1696 if (!IsFrameRTL(aFrame)) {
1697 contentRect.left += (contentRect.right - contentRect.left - siz.cx) / 2;
1698 contentRect.right = contentRect.left + siz.cx;
1699 } else {
1700 contentRect.right -= (contentRect.right - contentRect.left - siz.cx) / 2;
1701 contentRect.left = contentRect.right - siz.cx;
1705 DrawThemeBackground(theme, hdc, part, state, &contentRect, &clipRect);
1707 else if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO)
1709 bool isChecked = false;
1710 isChecked = CheckBooleanAttr(aFrame, nsGkAtoms::checked);
1712 if (isChecked)
1714 int bgState = MCB_NORMAL;
1715 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1717 // the disabled states are offset by 1
1718 if (IsDisabled(aFrame, eventState))
1719 bgState += 1;
1721 SIZE checkboxBGSize(GetCheckboxBGSize(theme, hdc));
1723 RECT checkBGRect = widgetRect;
1724 if (IsFrameRTL(aFrame)) {
1725 checkBGRect.left = checkBGRect.right-checkboxBGSize.cx;
1726 } else {
1727 checkBGRect.right = checkBGRect.left+checkboxBGSize.cx;
1730 // Center the checkbox background vertically in the menuitem
1731 checkBGRect.top += (checkBGRect.bottom - checkBGRect.top)/2 - checkboxBGSize.cy/2;
1732 checkBGRect.bottom = checkBGRect.top + checkboxBGSize.cy;
1734 DrawThemeBackground(theme, hdc, MENU_POPUPCHECKBACKGROUND, bgState, &checkBGRect, &clipRect);
1736 MARGINS checkMargins = GetCheckboxMargins(theme, hdc);
1737 RECT checkRect = checkBGRect;
1738 checkRect.left += checkMargins.cxLeftWidth;
1739 checkRect.right -= checkMargins.cxRightWidth;
1740 checkRect.top += checkMargins.cyTopHeight;
1741 checkRect.bottom -= checkMargins.cyBottomHeight;
1742 DrawThemeBackground(theme, hdc, MENU_POPUPCHECK, state, &checkRect, &clipRect);
1745 else if (aWidgetType == NS_THEME_MENUPOPUP)
1747 DrawThemeBackground(theme, hdc, MENU_POPUPBORDERS, /* state */ 0, &widgetRect, &clipRect);
1748 SIZE borderSize;
1749 GetThemePartSize(theme, hdc, MENU_POPUPBORDERS, 0, nullptr, TS_TRUE, &borderSize);
1751 RECT bgRect = widgetRect;
1752 bgRect.top += borderSize.cy;
1753 bgRect.bottom -= borderSize.cy;
1754 bgRect.left += borderSize.cx;
1755 bgRect.right -= borderSize.cx;
1757 DrawThemeBackground(theme, hdc, MENU_POPUPBACKGROUND, /* state */ 0, &bgRect, &clipRect);
1759 SIZE gutterSize(GetGutterSize(theme, hdc));
1761 RECT gutterRect;
1762 gutterRect.top = bgRect.top;
1763 gutterRect.bottom = bgRect.bottom;
1764 if (IsFrameRTL(aFrame)) {
1765 gutterRect.right = bgRect.right;
1766 gutterRect.left = gutterRect.right-gutterSize.cx;
1767 } else {
1768 gutterRect.left = bgRect.left;
1769 gutterRect.right = gutterRect.left+gutterSize.cx;
1772 DrawThemeBGRTLAware(theme, hdc, MENU_POPUPGUTTER, /* state */ 0,
1773 &gutterRect, &clipRect, IsFrameRTL(aFrame));
1775 else if (aWidgetType == NS_THEME_MENUSEPARATOR)
1777 SIZE gutterSize(GetGutterSize(theme,hdc));
1779 RECT sepRect = widgetRect;
1780 if (IsFrameRTL(aFrame))
1781 sepRect.right -= gutterSize.cx;
1782 else
1783 sepRect.left += gutterSize.cx;
1785 DrawThemeBackground(theme, hdc, MENU_POPUPSEPARATOR, /* state */ 0, &sepRect, &clipRect);
1787 // The following widgets need to be RTL-aware
1788 else if (aWidgetType == NS_THEME_MENUARROW ||
1789 aWidgetType == NS_THEME_RESIZER ||
1790 aWidgetType == NS_THEME_DROPDOWN_BUTTON)
1792 DrawThemeBGRTLAware(theme, hdc, part, state,
1793 &widgetRect, &clipRect, IsFrameRTL(aFrame));
1795 else if (aWidgetType == NS_THEME_PROGRESSBAR ||
1796 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL) {
1797 // DrawThemeBackground renders each corner with a solid white pixel.
1798 // Restore these pixels to the underlying color. Tracks are rendered
1799 // using alpha recovery, so this makes the corners transparent.
1800 COLORREF color;
1801 color = GetPixel(hdc, widgetRect.left, widgetRect.top);
1802 DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
1803 SetPixel(hdc, widgetRect.left, widgetRect.top, color);
1804 SetPixel(hdc, widgetRect.right-1, widgetRect.top, color);
1805 SetPixel(hdc, widgetRect.right-1, widgetRect.bottom-1, color);
1806 SetPixel(hdc, widgetRect.left, widgetRect.bottom-1, color);
1808 else if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
1809 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL) {
1810 DrawThemedProgressMeter(aFrame, aWidgetType, theme, hdc, part, state,
1811 &widgetRect, &clipRect, p2a);
1813 // If part is negative, the element wishes us to not render a themed
1814 // background, instead opting to be drawn specially below.
1815 else if (part >= 0) {
1816 DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
1819 // Draw focus rectangles for XP HTML checkboxes and radio buttons
1820 // XXX it'd be nice to draw these outside of the frame
1821 if (((aWidgetType == NS_THEME_CHECKBOX || aWidgetType == NS_THEME_RADIO) &&
1822 aFrame->GetContent()->IsHTML()) ||
1823 aWidgetType == NS_THEME_RANGE ||
1824 aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1825 aWidgetType == NS_THEME_SCALE_VERTICAL) {
1826 nsEventStates contentState = GetContentState(aFrame, aWidgetType);
1828 if (contentState.HasState(NS_EVENT_STATE_FOCUS)) {
1829 POINT vpOrg;
1830 HPEN hPen = nullptr;
1832 uint8_t id = SaveDC(hdc);
1834 ::SelectClipRgn(hdc, nullptr);
1835 ::GetViewportOrgEx(hdc, &vpOrg);
1836 ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top, nullptr);
1838 // On vista, choose our own colors and draw an XP style half focus rect
1839 // for focused checkboxes and a full rect when active.
1840 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
1841 aWidgetType == NS_THEME_CHECKBOX) {
1842 LOGBRUSH lb;
1843 lb.lbStyle = BS_SOLID;
1844 lb.lbColor = RGB(255,255,255);
1845 lb.lbHatch = 0;
1847 hPen = ::ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, nullptr);
1848 ::SelectObject(hdc, hPen);
1850 // If pressed, draw the upper left corner of the dotted rect.
1851 if (contentState.HasState(NS_EVENT_STATE_ACTIVE)) {
1852 ::MoveToEx(hdc, widgetRect.left, widgetRect.bottom-1, nullptr);
1853 ::LineTo(hdc, widgetRect.left, widgetRect.top);
1854 ::LineTo(hdc, widgetRect.right-1, widgetRect.top);
1857 // Draw the lower right corner of the dotted rect.
1858 ::MoveToEx(hdc, widgetRect.right-1, widgetRect.top, nullptr);
1859 ::LineTo(hdc, widgetRect.right-1, widgetRect.bottom-1);
1860 ::LineTo(hdc, widgetRect.left, widgetRect.bottom-1);
1861 } else {
1862 ::SetTextColor(hdc, 0);
1863 ::DrawFocusRect(hdc, &widgetRect);
1865 ::RestoreDC(hdc, id);
1866 if (hPen) {
1867 ::DeleteObject(hPen);
1871 else if (aWidgetType == NS_THEME_TOOLBAR && state == 0) {
1872 // Draw toolbar separator lines above all toolbars except the first one.
1873 // The lines are part of the Rebar theme, which is loaded for NS_THEME_TOOLBOX.
1874 theme = GetTheme(NS_THEME_TOOLBOX);
1875 if (!theme)
1876 return NS_ERROR_FAILURE;
1878 widgetRect.bottom = widgetRect.top + TB_SEPARATOR_HEIGHT;
1879 DrawThemeEdge(theme, hdc, RP_BAND, 0, &widgetRect, EDGE_ETCHED, BF_TOP, nullptr);
1881 else if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL ||
1882 aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL)
1884 // Draw the decorative gripper for the scrollbar thumb button, if it fits
1886 SIZE gripSize;
1887 MARGINS thumbMgns;
1888 int gripPart = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) ?
1889 SP_GRIPPERHOR : SP_GRIPPERVERT;
1891 if (GetThemePartSize(theme, hdc, gripPart, state, nullptr, TS_TRUE, &gripSize) == S_OK &&
1892 GetThemeMargins(theme, hdc, part, state, TMT_CONTENTMARGINS, nullptr, &thumbMgns) == S_OK &&
1893 gripSize.cx + thumbMgns.cxLeftWidth + thumbMgns.cxRightWidth <= widgetRect.right - widgetRect.left &&
1894 gripSize.cy + thumbMgns.cyTopHeight + thumbMgns.cyBottomHeight <= widgetRect.bottom - widgetRect.top)
1896 DrawThemeBackground(theme, hdc, gripPart, state, &widgetRect, &clipRect);
1899 else if ((aWidgetType == NS_THEME_WINDOW_BUTTON_BOX ||
1900 aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) &&
1901 nsUXThemeData::CheckForCompositor())
1903 // The caption buttons are drawn by the DWM, we just need to clear the area where they
1904 // are because we might have drawn something above them (like a background-image).
1905 ctx->Save();
1906 ctx->ResetClip();
1907 ctx->Translate(dr.TopLeft());
1909 // Create a rounded rectangle to follow the buttons' look.
1910 gfxRect buttonbox1(0.0, 0.0, dr.Width(), dr.Height() - 2.0);
1911 gfxRect buttonbox2(1.0, dr.Height() - 2.0, dr.Width() - 1.0, 1.0);
1912 gfxRect buttonbox3(2.0, dr.Height() - 1.0, dr.Width() - 3.0, 1.0);
1914 gfxContext::GraphicsOperator currentOp = ctx->CurrentOperator();
1915 ctx->SetOperator(gfxContext::OPERATOR_CLEAR);
1917 // Each rectangle is drawn individually because OPERATOR_CLEAR takes
1918 // the fallback path to cairo_d2d_acquire_dest if the area to fill
1919 // is a complex region.
1920 ctx->NewPath();
1921 ctx->Rectangle(buttonbox1, true);
1922 ctx->Fill();
1924 ctx->NewPath();
1925 ctx->Rectangle(buttonbox2, true);
1926 ctx->Fill();
1928 ctx->NewPath();
1929 ctx->Rectangle(buttonbox3, true);
1930 ctx->Fill();
1932 ctx->Restore();
1933 ctx->SetOperator(currentOp);
1936 nativeDrawing.EndNativeDrawing();
1938 if (nativeDrawing.ShouldRenderAgain())
1939 goto RENDER_AGAIN;
1941 nativeDrawing.PaintToContext();
1943 return NS_OK;
1946 NS_IMETHODIMP
1947 nsNativeThemeWin::GetWidgetBorder(nsDeviceContext* aContext,
1948 nsIFrame* aFrame,
1949 uint8_t aWidgetType,
1950 nsIntMargin* aResult)
1952 HANDLE theme = GetTheme(aWidgetType);
1953 if (!theme)
1954 return ClassicGetWidgetBorder(aContext, aFrame, aWidgetType, aResult);
1956 (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
1958 if (!WidgetIsContainer(aWidgetType) ||
1959 aWidgetType == NS_THEME_TOOLBOX ||
1960 aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
1961 aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
1962 aWidgetType == NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX ||
1963 aWidgetType == NS_THEME_STATUSBAR ||
1964 aWidgetType == NS_THEME_RESIZER || aWidgetType == NS_THEME_TAB_PANEL ||
1965 aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL ||
1966 aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL ||
1967 aWidgetType == NS_THEME_MENUITEM || aWidgetType == NS_THEME_CHECKMENUITEM ||
1968 aWidgetType == NS_THEME_RADIOMENUITEM || aWidgetType == NS_THEME_MENUPOPUP ||
1969 aWidgetType == NS_THEME_MENUIMAGE || aWidgetType == NS_THEME_MENUITEMTEXT ||
1970 aWidgetType == NS_THEME_TOOLBAR_SEPARATOR ||
1971 aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
1972 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
1973 aWidgetType == NS_THEME_WIN_GLASS || aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS)
1974 return NS_OK; // Don't worry about it.
1976 int32_t part, state;
1977 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
1978 if (NS_FAILED(rv))
1979 return rv;
1981 if (aWidgetType == NS_THEME_TOOLBAR) {
1982 // make space for the separator line above all toolbars but the first
1983 if (state == 0)
1984 aResult->top = TB_SEPARATOR_HEIGHT;
1985 return NS_OK;
1988 // Get our info.
1989 RECT outerRect; // Create a fake outer rect.
1990 outerRect.top = outerRect.left = 100;
1991 outerRect.right = outerRect.bottom = 200;
1992 RECT contentRect(outerRect);
1993 HRESULT res = GetThemeBackgroundContentRect(theme, nullptr, part, state, &outerRect, &contentRect);
1995 if (FAILED(res))
1996 return NS_ERROR_FAILURE;
1998 // Now compute the delta in each direction and place it in our
1999 // nsIntMargin struct.
2000 aResult->top = contentRect.top - outerRect.top;
2001 aResult->bottom = outerRect.bottom - contentRect.bottom;
2002 aResult->left = contentRect.left - outerRect.left;
2003 aResult->right = outerRect.right - contentRect.right;
2005 // Remove the edges for tabs that are before or after the selected tab,
2006 if (aWidgetType == NS_THEME_TAB) {
2007 if (IsLeftToSelectedTab(aFrame))
2008 // Remove the right edge, since we won't be drawing it.
2009 aResult->right = 0;
2010 else if (IsRightToSelectedTab(aFrame))
2011 // Remove the left edge, since we won't be drawing it.
2012 aResult->left = 0;
2015 if (aFrame && (aWidgetType == NS_THEME_TEXTFIELD || aWidgetType == NS_THEME_TEXTFIELD_MULTILINE)) {
2016 nsIContent* content = aFrame->GetContent();
2017 if (content && content->IsHTML()) {
2018 // We need to pad textfields by 1 pixel, since the caret will draw
2019 // flush against the edge by default if we don't.
2020 aResult->top++;
2021 aResult->left++;
2022 aResult->bottom++;
2023 aResult->right++;
2027 return NS_OK;
2030 bool
2031 nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext,
2032 nsIFrame* aFrame,
2033 uint8_t aWidgetType,
2034 nsIntMargin* aResult)
2036 switch (aWidgetType) {
2037 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
2038 // and have a meaningful baseline, so they can't have
2039 // author-specified padding.
2040 case NS_THEME_CHECKBOX:
2041 case NS_THEME_RADIO:
2042 aResult->SizeTo(0, 0, 0, 0);
2043 return true;
2046 HANDLE theme = GetTheme(aWidgetType);
2048 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX ||
2049 aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
2050 aResult->SizeTo(0, 0, 0, 0);
2052 // aero glass doesn't display custom buttons
2053 if (nsUXThemeData::CheckForCompositor())
2054 return true;
2056 // button padding for standard windows
2057 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX) {
2058 aResult->top = GetSystemMetrics(SM_CXFRAME);
2060 return true;
2063 // Content padding
2064 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
2065 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
2066 aResult->SizeTo(0, 0, 0, 0);
2067 // XXX Maximized windows have an offscreen offset equal to
2068 // the border padding. This should be addressed in nsWindow,
2069 // but currently can't be, see UpdateNonClientMargins.
2070 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED)
2071 aResult->top = GetSystemMetrics(SM_CXFRAME)
2072 + GetSystemMetrics(SM_CXPADDEDBORDER);
2073 return true;
2076 if (!theme)
2077 return ClassicGetWidgetPadding(aContext, aFrame, aWidgetType, aResult);
2079 if (aWidgetType == NS_THEME_MENUPOPUP)
2081 SIZE popupSize;
2082 GetThemePartSize(theme, nullptr, MENU_POPUPBORDERS, /* state */ 0, nullptr, TS_TRUE, &popupSize);
2083 aResult->top = aResult->bottom = popupSize.cy;
2084 aResult->left = aResult->right = popupSize.cx;
2085 return true;
2088 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
2089 if (aWidgetType == NS_THEME_TEXTFIELD ||
2090 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
2091 aWidgetType == NS_THEME_DROPDOWN)
2093 /* If we have author-specified padding for these elements, don't do the fixups below */
2094 if (aFrame->PresContext()->HasAuthorSpecifiedRules(aFrame, NS_AUTHOR_SPECIFIED_PADDING))
2095 return false;
2098 /* textfields need extra pixels on all sides, otherwise they
2099 * wrap their content too tightly. The actual border is drawn 1px
2100 * inside the specified rectangle, so Gecko will end up making the
2101 * contents look too small. Instead, we add 2px padding for the
2102 * contents and fix this. (Used to be 1px added, see bug 430212)
2104 if (aWidgetType == NS_THEME_TEXTFIELD || aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
2105 aResult->top = aResult->bottom = 2;
2106 aResult->left = aResult->right = 2;
2107 return true;
2108 } else if (IsHTMLContent(aFrame) && aWidgetType == NS_THEME_DROPDOWN) {
2109 /* For content menulist controls, we need an extra pixel so
2110 * that we have room to draw our focus rectangle stuff.
2111 * Otherwise, the focus rect might overlap the control's
2112 * border.
2114 aResult->top = aResult->bottom = 1;
2115 aResult->left = aResult->right = 1;
2116 return true;
2120 int32_t right, left, top, bottom;
2121 right = left = top = bottom = 0;
2122 switch (aWidgetType)
2124 case NS_THEME_MENUIMAGE:
2125 right = 8;
2126 left = 3;
2127 break;
2128 case NS_THEME_MENUCHECKBOX:
2129 case NS_THEME_MENURADIO:
2130 right = 8;
2131 left = 0;
2132 break;
2133 case NS_THEME_MENUITEMTEXT:
2134 // There seem to be exactly 4 pixels from the edge
2135 // of the gutter to the text: 2px margin (CSS) + 2px padding (here)
2137 SIZE size(GetGutterSize(theme, nullptr));
2138 left = size.cx + 2;
2140 break;
2141 case NS_THEME_MENUSEPARATOR:
2143 SIZE size(GetGutterSize(theme, nullptr));
2144 left = size.cx + 5;
2145 top = 10;
2146 bottom = 7;
2148 break;
2149 default:
2150 return false;
2153 if (IsFrameRTL(aFrame))
2155 aResult->right = left;
2156 aResult->left = right;
2158 else
2160 aResult->right = right;
2161 aResult->left = left;
2164 return true;
2167 bool
2168 nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext,
2169 nsIFrame* aFrame,
2170 uint8_t aOverflowRect,
2171 nsRect* aResult)
2173 /* This is disabled for now, because it causes invalidation problems --
2174 * see bug 420381. The effect of not updating the overflow area is that
2175 * for dropdown buttons in content areas, there is a 1px border on 3 sides
2176 * where, if invalidated, the dropdown control probably won't be repainted.
2177 * This is fairly minor, as by default there is nothing in that area, and
2178 * a border only shows up if the widget is being hovered.
2180 #if 0
2181 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
2182 /* We explicitly draw dropdown buttons in HTML content 1px bigger
2183 * up, right, and bottom so that they overlap the dropdown's border
2184 * like they're supposed to.
2186 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON &&
2187 IsHTMLContent(aFrame) &&
2188 !IsWidgetStyled(aFrame->GetParent()->PresContext(),
2189 aFrame->GetParent(),
2190 NS_THEME_DROPDOWN))
2192 int32_t p2a = aContext->AppUnitsPerDevPixel();
2193 /* Note: no overflow on the left */
2194 nsMargin m(p2a, p2a, p2a, 0);
2195 aOverflowRect->Inflate (m);
2196 return true;
2199 #endif
2201 return false;
2204 NS_IMETHODIMP
2205 nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* aFrame,
2206 uint8_t aWidgetType,
2207 nsIntSize* aResult, bool* aIsOverridable)
2209 (*aResult).width = (*aResult).height = 0;
2210 *aIsOverridable = true;
2212 HANDLE theme = GetTheme(aWidgetType);
2213 if (!theme)
2214 return ClassicGetMinimumWidgetSize(aContext, aFrame, aWidgetType, aResult, aIsOverridable);
2216 switch (aWidgetType) {
2217 case NS_THEME_GROUPBOX:
2218 case NS_THEME_TEXTFIELD:
2219 case NS_THEME_TOOLBOX:
2220 case NS_THEME_WIN_MEDIA_TOOLBOX:
2221 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
2222 case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
2223 case NS_THEME_TOOLBAR:
2224 case NS_THEME_STATUSBAR:
2225 case NS_THEME_PROGRESSBAR_CHUNK:
2226 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2227 case NS_THEME_TAB_PANELS:
2228 case NS_THEME_TAB_PANEL:
2229 case NS_THEME_LISTBOX:
2230 case NS_THEME_TREEVIEW:
2231 case NS_THEME_MENUITEMTEXT:
2232 case NS_THEME_WIN_GLASS:
2233 case NS_THEME_WIN_BORDERLESS_GLASS:
2234 return NS_OK; // Don't worry about it.
2237 if (aWidgetType == NS_THEME_MENUITEM && IsTopLevelMenu(aFrame))
2238 return NS_OK; // Don't worry about it for top level menus
2240 // Call GetSystemMetrics to determine size for WinXP scrollbars
2241 // (GetThemeSysSize API returns the optimal size for the theme, but
2242 // Windows appears to always use metrics when drawing standard scrollbars)
2243 THEMESIZE sizeReq = TS_TRUE; // Best-fit size
2244 switch (aWidgetType) {
2245 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2246 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2247 case NS_THEME_SCROLLBAR_BUTTON_UP:
2248 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2249 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2250 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2251 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2252 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2253 case NS_THEME_DROPDOWN_BUTTON:
2254 return ClassicGetMinimumWidgetSize(aContext, aFrame, aWidgetType, aResult, aIsOverridable);
2256 case NS_THEME_MENUITEM:
2257 case NS_THEME_CHECKMENUITEM:
2258 case NS_THEME_RADIOMENUITEM:
2259 if(!IsTopLevelMenu(aFrame))
2261 SIZE gutterSize(GetGutterSize(theme, nullptr));
2262 aResult->width = gutterSize.cx;
2263 aResult->height = gutterSize.cy;
2264 return NS_OK;
2266 break;
2268 case NS_THEME_MENUIMAGE:
2269 case NS_THEME_MENUCHECKBOX:
2270 case NS_THEME_MENURADIO:
2272 SIZE boxSize(GetGutterSize(theme, nullptr));
2273 aResult->width = boxSize.cx+2;
2274 aResult->height = boxSize.cy;
2275 *aIsOverridable = false;
2278 case NS_THEME_MENUITEMTEXT:
2279 return NS_OK;
2281 case NS_THEME_MENUARROW:
2282 aResult->width = 26;
2283 aResult->height = 16;
2284 return NS_OK;
2286 case NS_THEME_PROGRESSBAR:
2287 case NS_THEME_PROGRESSBAR_VERTICAL:
2288 // Best-fit size for progress meters is too large for most
2289 // themes. We want these widgets to be able to really shrink
2290 // down, so use the min-size request value (of 0).
2291 sizeReq = TS_MIN;
2292 break;
2294 case NS_THEME_RESIZER:
2295 *aIsOverridable = false;
2296 break;
2298 case NS_THEME_RANGE_THUMB:
2299 case NS_THEME_SCALE_THUMB_HORIZONTAL:
2300 case NS_THEME_SCALE_THUMB_VERTICAL:
2302 *aIsOverridable = false;
2303 // on Vista, GetThemePartAndState returns odd values for
2304 // scale thumbs, so use a hardcoded size instead.
2305 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
2306 if (aWidgetType == NS_THEME_SCALE_THUMB_HORIZONTAL ||
2307 (aWidgetType == NS_THEME_RANGE_THUMB && IsRangeHorizontal(aFrame))) {
2308 aResult->width = 12;
2309 aResult->height = 20;
2311 else {
2312 aResult->width = 20;
2313 aResult->height = 12;
2315 return NS_OK;
2317 break;
2320 case NS_THEME_TOOLBAR_SEPARATOR:
2321 // that's 2px left margin, 2px right margin and 2px separator
2322 // (the margin is drawn as part of the separator, though)
2323 aResult->width = 6;
2324 return NS_OK;
2326 case NS_THEME_BUTTON:
2327 // We should let HTML buttons shrink to their min size.
2328 // FIXME bug 403934: We should probably really separate
2329 // GetPreferredWidgetSize from GetMinimumWidgetSize, so callers can
2330 // use the one they want.
2331 if (aFrame->GetContent()->IsHTML()) {
2332 sizeReq = TS_MIN;
2334 break;
2336 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2337 case NS_THEME_WINDOW_BUTTON_RESTORE:
2338 // The only way to get accurate titlebar button info is to query a
2339 // window w/buttons when it's visible. nsWindow takes care of this and
2340 // stores that info in nsUXThemeData.
2341 aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cx;
2342 aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cy;
2343 // For XP, subtract 4 from system metrics dimensions.
2344 if (WinUtils::GetWindowsVersion() == WinUtils::WINXP_VERSION) {
2345 aResult->width -= 4;
2346 aResult->height -= 4;
2348 AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
2349 *aIsOverridable = false;
2350 return NS_OK;
2352 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2353 aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cx;
2354 aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cy;
2355 if (WinUtils::GetWindowsVersion() == WinUtils::WINXP_VERSION) {
2356 aResult->width -= 4;
2357 aResult->height -= 4;
2359 AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
2360 *aIsOverridable = false;
2361 return NS_OK;
2363 case NS_THEME_WINDOW_BUTTON_CLOSE:
2364 aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cx;
2365 aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cy;
2366 if (WinUtils::GetWindowsVersion() == WinUtils::WINXP_VERSION) {
2367 aResult->width -= 4;
2368 aResult->height -= 4;
2370 AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
2371 *aIsOverridable = false;
2372 return NS_OK;
2374 case NS_THEME_WINDOW_TITLEBAR:
2375 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2376 aResult->height = GetSystemMetrics(SM_CYCAPTION);
2377 aResult->height += GetSystemMetrics(SM_CYFRAME);
2378 aResult->height += GetSystemMetrics(SM_CXPADDEDBORDER);
2379 *aIsOverridable = false;
2380 return NS_OK;
2382 case NS_THEME_WINDOW_BUTTON_BOX:
2383 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2384 if (nsUXThemeData::CheckForCompositor()) {
2385 aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx;
2386 aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy
2387 - GetSystemMetrics(SM_CYFRAME)
2388 - GetSystemMetrics(SM_CXPADDEDBORDER);
2389 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
2390 aResult->width += 1;
2391 aResult->height -= 2;
2393 *aIsOverridable = false;
2394 return NS_OK;
2396 break;
2398 case NS_THEME_WINDOW_FRAME_LEFT:
2399 case NS_THEME_WINDOW_FRAME_RIGHT:
2400 case NS_THEME_WINDOW_FRAME_BOTTOM:
2401 aResult->width = GetSystemMetrics(SM_CXFRAME);
2402 aResult->height = GetSystemMetrics(SM_CYFRAME);
2403 *aIsOverridable = false;
2404 return NS_OK;
2407 int32_t part, state;
2408 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
2409 if (NS_FAILED(rv))
2410 return rv;
2412 HDC hdc = gfxWindowsPlatform::GetPlatform()->GetScreenDC();
2413 if (!hdc)
2414 return NS_ERROR_FAILURE;
2416 SIZE sz;
2417 GetThemePartSize(theme, hdc, part, state, nullptr, sizeReq, &sz);
2418 aResult->width = sz.cx;
2419 aResult->height = sz.cy;
2421 switch(aWidgetType) {
2422 case NS_THEME_SPINNER_UP_BUTTON:
2423 case NS_THEME_SPINNER_DOWN_BUTTON:
2424 aResult->width++;
2425 aResult->height = aResult->height / 2 + 1;
2426 break;
2428 case NS_THEME_MENUSEPARATOR:
2430 SIZE gutterSize(GetGutterSize(theme,hdc));
2431 aResult->width += gutterSize.cx;
2432 break;
2436 return NS_OK;
2439 NS_IMETHODIMP
2440 nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
2441 nsIAtom* aAttribute, bool* aShouldRepaint)
2443 // Some widget types just never change state.
2444 if (aWidgetType == NS_THEME_TOOLBOX ||
2445 aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
2446 aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
2447 aWidgetType == NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX ||
2448 aWidgetType == NS_THEME_TOOLBAR ||
2449 aWidgetType == NS_THEME_STATUSBAR || aWidgetType == NS_THEME_STATUSBAR_PANEL ||
2450 aWidgetType == NS_THEME_STATUSBAR_RESIZER_PANEL ||
2451 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
2452 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
2453 aWidgetType == NS_THEME_PROGRESSBAR ||
2454 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
2455 aWidgetType == NS_THEME_TOOLTIP ||
2456 aWidgetType == NS_THEME_TAB_PANELS ||
2457 aWidgetType == NS_THEME_TAB_PANEL ||
2458 aWidgetType == NS_THEME_TOOLBAR_SEPARATOR ||
2459 aWidgetType == NS_THEME_WIN_GLASS ||
2460 aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS) {
2461 *aShouldRepaint = false;
2462 return NS_OK;
2465 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
2466 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
2467 aWidgetType == NS_THEME_WINDOW_FRAME_LEFT ||
2468 aWidgetType == NS_THEME_WINDOW_FRAME_RIGHT ||
2469 aWidgetType == NS_THEME_WINDOW_FRAME_BOTTOM ||
2470 aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE ||
2471 aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||
2472 aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||
2473 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
2474 *aShouldRepaint = true;
2475 return NS_OK;
2478 // On Vista, the scrollbar buttons need to change state when the track has/doesn't have hover
2479 if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION &&
2480 (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL ||
2481 aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL)) {
2482 *aShouldRepaint = false;
2483 return NS_OK;
2486 // We need to repaint the dropdown arrow in vista HTML combobox controls when
2487 // the control is closed to get rid of the hover effect.
2488 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
2489 (aWidgetType == NS_THEME_DROPDOWN || aWidgetType == NS_THEME_DROPDOWN_BUTTON) &&
2490 IsHTMLContent(aFrame))
2492 *aShouldRepaint = true;
2493 return NS_OK;
2496 // XXXdwh Not sure what can really be done here. Can at least guess for
2497 // specific widgets that they're highly unlikely to have certain states.
2498 // For example, a toolbar doesn't care about any states.
2499 if (!aAttribute) {
2500 // Hover/focus/active changed. Always repaint.
2501 *aShouldRepaint = true;
2503 else {
2504 // Check the attribute to see if it's relevant.
2505 // disabled, checked, dlgtype, default, etc.
2506 *aShouldRepaint = false;
2507 if (aAttribute == nsGkAtoms::disabled ||
2508 aAttribute == nsGkAtoms::checked ||
2509 aAttribute == nsGkAtoms::selected ||
2510 aAttribute == nsGkAtoms::readonly ||
2511 aAttribute == nsGkAtoms::open ||
2512 aAttribute == nsGkAtoms::menuactive ||
2513 aAttribute == nsGkAtoms::focused)
2514 *aShouldRepaint = true;
2517 return NS_OK;
2520 NS_IMETHODIMP
2521 nsNativeThemeWin::ThemeChanged()
2523 nsUXThemeData::Invalidate();
2524 return NS_OK;
2527 bool
2528 nsNativeThemeWin::ThemeSupportsWidget(nsPresContext* aPresContext,
2529 nsIFrame* aFrame,
2530 uint8_t aWidgetType)
2532 // XXXdwh We can go even further and call the API to ask if support exists for
2533 // specific widgets.
2535 if (aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled())
2536 return false;
2538 HANDLE theme = nullptr;
2539 if (aWidgetType == NS_THEME_CHECKBOX_CONTAINER)
2540 theme = GetTheme(NS_THEME_CHECKBOX);
2541 else if (aWidgetType == NS_THEME_RADIO_CONTAINER)
2542 theme = GetTheme(NS_THEME_RADIO);
2543 else
2544 theme = GetTheme(aWidgetType);
2546 if ((theme) || (!theme && ClassicThemeSupportsWidget(aPresContext, aFrame, aWidgetType)))
2547 // turn off theming for some HTML widgets styled by the page
2548 return (!IsWidgetStyled(aPresContext, aFrame, aWidgetType));
2550 return false;
2553 bool
2554 nsNativeThemeWin::WidgetIsContainer(uint8_t aWidgetType)
2556 // XXXdwh At some point flesh all of this out.
2557 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON ||
2558 aWidgetType == NS_THEME_RADIO ||
2559 aWidgetType == NS_THEME_CHECKBOX)
2560 return false;
2561 return true;
2564 bool
2565 nsNativeThemeWin::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
2567 return false;
2570 bool
2571 nsNativeThemeWin::ThemeNeedsComboboxDropmarker()
2573 return true;
2576 bool
2577 nsNativeThemeWin::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)
2579 switch (aWidgetType) {
2580 case NS_THEME_WINDOW_TITLEBAR:
2581 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2582 case NS_THEME_WINDOW_FRAME_LEFT:
2583 case NS_THEME_WINDOW_FRAME_RIGHT:
2584 case NS_THEME_WINDOW_FRAME_BOTTOM:
2585 case NS_THEME_WINDOW_BUTTON_CLOSE:
2586 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2587 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2588 case NS_THEME_WINDOW_BUTTON_RESTORE:
2589 return true;
2590 default:
2591 return false;
2595 nsITheme::Transparency
2596 nsNativeThemeWin::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
2598 switch (aWidgetType) {
2599 case NS_THEME_SCROLLBAR_SMALL:
2600 case NS_THEME_SCROLLBAR:
2601 case NS_THEME_STATUSBAR:
2602 // Knowing that scrollbars and statusbars are opaque improves
2603 // performance, because we create layers for them. This better be
2604 // true across all Windows themes! If it's not true, we should
2605 // paint an opaque background for them to make it true!
2606 return eOpaque;
2607 case NS_THEME_WIN_GLASS:
2608 case NS_THEME_WIN_BORDERLESS_GLASS:
2609 case NS_THEME_SCALE_HORIZONTAL:
2610 case NS_THEME_SCALE_VERTICAL:
2611 case NS_THEME_PROGRESSBAR:
2612 case NS_THEME_PROGRESSBAR_VERTICAL:
2613 case NS_THEME_PROGRESSBAR_CHUNK:
2614 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2615 case NS_THEME_RANGE:
2616 return eTransparent;
2619 HANDLE theme = GetTheme(aWidgetType);
2620 // For the classic theme we don't really have a way of knowing
2621 if (!theme) {
2622 // menu backgrounds and tooltips which can't be themed are opaque
2623 if (aWidgetType == NS_THEME_MENUPOPUP || aWidgetType == NS_THEME_TOOLTIP) {
2624 return eOpaque;
2626 return eUnknownTransparency;
2629 int32_t part, state;
2630 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
2631 // Fail conservatively
2632 NS_ENSURE_SUCCESS(rv, eUnknownTransparency);
2634 if (part <= 0) {
2635 // Not a real part code, so IsThemeBackgroundPartiallyTransparent may
2636 // not work, so don't call it.
2637 return eUnknownTransparency;
2640 if (IsThemeBackgroundPartiallyTransparent(theme, part, state))
2641 return eTransparent;
2642 return eOpaque;
2645 /* Windows 9x/NT/2000/Classic XP Theme Support */
2647 bool
2648 nsNativeThemeWin::ClassicThemeSupportsWidget(nsPresContext* aPresContext,
2649 nsIFrame* aFrame,
2650 uint8_t aWidgetType)
2652 switch (aWidgetType) {
2653 case NS_THEME_RESIZER:
2655 // The classic native resizer has an opaque grey background which doesn't
2656 // match the usually white background of the scrollable container, so
2657 // only support the native resizer if not in a scrollframe.
2658 nsIFrame* parentFrame = aFrame->GetParent();
2659 return (!parentFrame || parentFrame->GetType() != nsGkAtoms::scrollFrame);
2661 case NS_THEME_MENUBAR:
2662 case NS_THEME_MENUPOPUP:
2663 // Classic non-flat menus are handled almost entirely through CSS.
2664 if (!nsUXThemeData::sFlatMenus)
2665 return false;
2666 case NS_THEME_BUTTON:
2667 case NS_THEME_TEXTFIELD:
2668 case NS_THEME_TEXTFIELD_MULTILINE:
2669 case NS_THEME_CHECKBOX:
2670 case NS_THEME_RADIO:
2671 case NS_THEME_RANGE:
2672 case NS_THEME_RANGE_THUMB:
2673 case NS_THEME_GROUPBOX:
2674 case NS_THEME_SCROLLBAR_BUTTON_UP:
2675 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2676 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2677 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2678 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2679 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2680 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2681 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2682 case NS_THEME_SCALE_HORIZONTAL:
2683 case NS_THEME_SCALE_VERTICAL:
2684 case NS_THEME_SCALE_THUMB_HORIZONTAL:
2685 case NS_THEME_SCALE_THUMB_VERTICAL:
2686 case NS_THEME_DROPDOWN_BUTTON:
2687 case NS_THEME_SPINNER_UP_BUTTON:
2688 case NS_THEME_SPINNER_DOWN_BUTTON:
2689 case NS_THEME_LISTBOX:
2690 case NS_THEME_TREEVIEW:
2691 case NS_THEME_DROPDOWN_TEXTFIELD:
2692 case NS_THEME_DROPDOWN:
2693 case NS_THEME_TOOLTIP:
2694 case NS_THEME_STATUSBAR:
2695 case NS_THEME_STATUSBAR_PANEL:
2696 case NS_THEME_STATUSBAR_RESIZER_PANEL:
2697 case NS_THEME_PROGRESSBAR:
2698 case NS_THEME_PROGRESSBAR_VERTICAL:
2699 case NS_THEME_PROGRESSBAR_CHUNK:
2700 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2701 case NS_THEME_TAB:
2702 case NS_THEME_TAB_PANEL:
2703 case NS_THEME_TAB_PANELS:
2704 case NS_THEME_MENUITEM:
2705 case NS_THEME_CHECKMENUITEM:
2706 case NS_THEME_RADIOMENUITEM:
2707 case NS_THEME_MENUCHECKBOX:
2708 case NS_THEME_MENURADIO:
2709 case NS_THEME_MENUARROW:
2710 case NS_THEME_MENUSEPARATOR:
2711 case NS_THEME_MENUITEMTEXT:
2712 case NS_THEME_WINDOW_TITLEBAR:
2713 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2714 case NS_THEME_WINDOW_FRAME_LEFT:
2715 case NS_THEME_WINDOW_FRAME_RIGHT:
2716 case NS_THEME_WINDOW_FRAME_BOTTOM:
2717 case NS_THEME_WINDOW_BUTTON_CLOSE:
2718 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2719 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2720 case NS_THEME_WINDOW_BUTTON_RESTORE:
2721 case NS_THEME_WINDOW_BUTTON_BOX:
2722 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2723 return true;
2725 return false;
2728 nsresult
2729 nsNativeThemeWin::ClassicGetWidgetBorder(nsDeviceContext* aContext,
2730 nsIFrame* aFrame,
2731 uint8_t aWidgetType,
2732 nsIntMargin* aResult)
2734 switch (aWidgetType) {
2735 case NS_THEME_GROUPBOX:
2736 case NS_THEME_BUTTON:
2737 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
2738 break;
2739 case NS_THEME_STATUSBAR:
2740 (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
2741 (*aResult).top = 2;
2742 break;
2743 case NS_THEME_LISTBOX:
2744 case NS_THEME_TREEVIEW:
2745 case NS_THEME_DROPDOWN:
2746 case NS_THEME_DROPDOWN_TEXTFIELD:
2747 case NS_THEME_TAB:
2748 case NS_THEME_TEXTFIELD:
2749 case NS_THEME_TEXTFIELD_MULTILINE:
2750 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
2751 break;
2752 case NS_THEME_STATUSBAR_PANEL:
2753 case NS_THEME_STATUSBAR_RESIZER_PANEL: {
2754 (*aResult).top = 1;
2755 (*aResult).left = 1;
2756 (*aResult).bottom = 1;
2757 (*aResult).right = aFrame->GetNextSibling() ? 3 : 1;
2758 break;
2760 case NS_THEME_TOOLTIP:
2761 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
2762 break;
2763 case NS_THEME_PROGRESSBAR:
2764 case NS_THEME_PROGRESSBAR_VERTICAL:
2765 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
2766 break;
2767 case NS_THEME_MENUBAR:
2768 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 0;
2769 break;
2770 case NS_THEME_MENUPOPUP:
2771 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 3;
2772 break;
2773 default:
2774 (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
2775 break;
2777 return NS_OK;
2780 bool
2781 nsNativeThemeWin::ClassicGetWidgetPadding(nsDeviceContext* aContext,
2782 nsIFrame* aFrame,
2783 uint8_t aWidgetType,
2784 nsIntMargin* aResult)
2786 switch (aWidgetType) {
2787 case NS_THEME_MENUITEM:
2788 case NS_THEME_CHECKMENUITEM:
2789 case NS_THEME_RADIOMENUITEM: {
2790 int32_t part, state;
2791 bool focused;
2793 if (NS_FAILED(ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused)))
2794 return false;
2796 if (part == 1) { // top-level menu
2797 if (nsUXThemeData::sFlatMenus || !(state & DFCS_PUSHED)) {
2798 (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
2800 else {
2801 // make top-level menus look sunken when pushed in the Classic look
2802 (*aResult).top = (*aResult).left = 3;
2803 (*aResult).bottom = (*aResult).right = 1;
2806 else {
2807 (*aResult).top = 0;
2808 (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
2810 return true;
2812 case NS_THEME_PROGRESSBAR:
2813 case NS_THEME_PROGRESSBAR_VERTICAL:
2814 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
2815 return true;
2816 default:
2817 return false;
2821 nsresult
2822 nsNativeThemeWin::ClassicGetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* aFrame,
2823 uint8_t aWidgetType,
2824 nsIntSize* aResult, bool* aIsOverridable)
2826 (*aResult).width = (*aResult).height = 0;
2827 *aIsOverridable = true;
2828 switch (aWidgetType) {
2829 case NS_THEME_RADIO:
2830 case NS_THEME_CHECKBOX:
2831 (*aResult).width = (*aResult).height = 13;
2832 break;
2833 case NS_THEME_MENUCHECKBOX:
2834 case NS_THEME_MENURADIO:
2835 case NS_THEME_MENUARROW:
2836 (*aResult).width = ::GetSystemMetrics(SM_CXMENUCHECK);
2837 (*aResult).height = ::GetSystemMetrics(SM_CYMENUCHECK);
2838 break;
2839 case NS_THEME_SCROLLBAR_BUTTON_UP:
2840 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2841 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2842 (*aResult).height = ::GetSystemMetrics(SM_CYVSCROLL);
2843 *aIsOverridable = false;
2844 break;
2845 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2846 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2847 (*aResult).width = ::GetSystemMetrics(SM_CXHSCROLL);
2848 (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
2849 *aIsOverridable = false;
2850 break;
2851 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2852 // XXX HACK We should be able to have a minimum height for the scrollbar
2853 // track. However, this causes problems when uncollapsing a scrollbar
2854 // inside a tree. See bug 201379 for details.
2856 // (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB) << 1;
2857 break;
2858 case NS_THEME_RANGE_THUMB: {
2859 if (IsRangeHorizontal(aFrame)) {
2860 (*aResult).width = 12;
2861 (*aResult).height = 20;
2862 } else {
2863 (*aResult).width = 20;
2864 (*aResult).height = 12;
2866 *aIsOverridable = false;
2867 break;
2869 case NS_THEME_SCALE_THUMB_HORIZONTAL:
2870 (*aResult).width = 12;
2871 (*aResult).height = 20;
2872 *aIsOverridable = false;
2873 break;
2874 case NS_THEME_SCALE_THUMB_VERTICAL:
2875 (*aResult).width = 20;
2876 (*aResult).height = 12;
2877 *aIsOverridable = false;
2878 break;
2879 case NS_THEME_DROPDOWN_BUTTON:
2880 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2881 break;
2882 case NS_THEME_DROPDOWN:
2883 case NS_THEME_BUTTON:
2884 case NS_THEME_GROUPBOX:
2885 case NS_THEME_LISTBOX:
2886 case NS_THEME_TREEVIEW:
2887 case NS_THEME_TEXTFIELD:
2888 case NS_THEME_TEXTFIELD_MULTILINE:
2889 case NS_THEME_DROPDOWN_TEXTFIELD:
2890 case NS_THEME_STATUSBAR:
2891 case NS_THEME_STATUSBAR_PANEL:
2892 case NS_THEME_STATUSBAR_RESIZER_PANEL:
2893 case NS_THEME_PROGRESSBAR_CHUNK:
2894 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2895 case NS_THEME_TOOLTIP:
2896 case NS_THEME_PROGRESSBAR:
2897 case NS_THEME_PROGRESSBAR_VERTICAL:
2898 case NS_THEME_TAB:
2899 case NS_THEME_TAB_PANEL:
2900 case NS_THEME_TAB_PANELS:
2901 // no minimum widget size
2902 break;
2903 case NS_THEME_RESIZER: {
2904 NONCLIENTMETRICS nc;
2905 nc.cbSize = sizeof(nc);
2906 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nc), &nc, 0))
2907 (*aResult).width = (*aResult).height = abs(nc.lfStatusFont.lfHeight) + 4;
2908 else
2909 (*aResult).width = (*aResult).height = 15;
2910 *aIsOverridable = false;
2911 break;
2912 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2913 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2914 (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB);
2915 // Without theming, divide the thumb size by two in order to look more
2916 // native
2917 if (!GetTheme(aWidgetType))
2918 (*aResult).height >>= 1;
2919 *aIsOverridable = false;
2920 break;
2921 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2922 (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB);
2923 (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
2924 // Without theming, divide the thumb size by two in order to look more
2925 // native
2926 if (!GetTheme(aWidgetType))
2927 (*aResult).width >>= 1;
2928 *aIsOverridable = false;
2929 break;
2930 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2931 (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB) << 1;
2932 break;
2934 case NS_THEME_MENUSEPARATOR:
2936 aResult->width = 0;
2937 aResult->height = 10;
2938 break;
2941 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2942 case NS_THEME_WINDOW_TITLEBAR:
2943 aResult->height = GetSystemMetrics(SM_CYCAPTION);
2944 aResult->height += GetSystemMetrics(SM_CYFRAME);
2945 aResult->width = 0;
2946 break;
2947 case NS_THEME_WINDOW_FRAME_LEFT:
2948 case NS_THEME_WINDOW_FRAME_RIGHT:
2949 aResult->width = GetSystemMetrics(SM_CXFRAME);
2950 aResult->height = 0;
2951 break;
2953 case NS_THEME_WINDOW_FRAME_BOTTOM:
2954 aResult->height = GetSystemMetrics(SM_CYFRAME);
2955 aResult->width = 0;
2956 break;
2958 case NS_THEME_WINDOW_BUTTON_CLOSE:
2959 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2960 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2961 case NS_THEME_WINDOW_BUTTON_RESTORE:
2962 aResult->width = GetSystemMetrics(SM_CXSIZE);
2963 aResult->height = GetSystemMetrics(SM_CYSIZE);
2964 // XXX I have no idea why these caption metrics are always off,
2965 // but they are.
2966 aResult->width -= 2;
2967 aResult->height -= 4;
2968 if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
2969 AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
2971 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
2972 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
2973 AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
2975 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
2976 AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
2978 break;
2980 default:
2981 return NS_ERROR_FAILURE;
2983 return NS_OK;
2987 nsresult nsNativeThemeWin::ClassicGetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
2988 int32_t& aPart, int32_t& aState, bool& aFocused)
2990 aFocused = false;
2991 switch (aWidgetType) {
2992 case NS_THEME_BUTTON: {
2993 nsEventStates contentState;
2995 aPart = DFC_BUTTON;
2996 aState = DFCS_BUTTONPUSH;
2997 aFocused = false;
2999 contentState = GetContentState(aFrame, aWidgetType);
3000 if (IsDisabled(aFrame, contentState))
3001 aState |= DFCS_INACTIVE;
3002 else if (IsOpenButton(aFrame))
3003 aState |= DFCS_PUSHED;
3004 else if (IsCheckedButton(aFrame))
3005 aState |= DFCS_CHECKED;
3006 else {
3007 if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) {
3008 aState |= DFCS_PUSHED;
3009 const nsStyleUserInterface *uiData = aFrame->StyleUserInterface();
3010 // The down state is flat if the button is focusable
3011 if (uiData->mUserFocus == NS_STYLE_USER_FOCUS_NORMAL) {
3012 if (!aFrame->GetContent()->IsHTML())
3013 aState |= DFCS_FLAT;
3015 aFocused = true;
3018 if (contentState.HasState(NS_EVENT_STATE_FOCUS) ||
3019 (aState == DFCS_BUTTONPUSH && IsDefaultButton(aFrame))) {
3020 aFocused = true;
3025 return NS_OK;
3027 case NS_THEME_CHECKBOX:
3028 case NS_THEME_RADIO: {
3029 nsEventStates contentState;
3030 aFocused = false;
3032 aPart = DFC_BUTTON;
3033 aState = 0;
3034 nsIContent* content = aFrame->GetContent();
3035 bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
3036 bool isChecked = GetCheckedOrSelected(aFrame, !isCheckbox);
3037 bool isIndeterminate = isCheckbox && GetIndeterminate(aFrame);
3039 if (isCheckbox) {
3040 // indeterminate state takes precedence over checkedness.
3041 if (isIndeterminate) {
3042 aState = DFCS_BUTTON3STATE | DFCS_CHECKED;
3043 } else {
3044 aState = DFCS_BUTTONCHECK;
3046 } else {
3047 aState = DFCS_BUTTONRADIO;
3049 if (isChecked) {
3050 aState |= DFCS_CHECKED;
3053 contentState = GetContentState(aFrame, aWidgetType);
3054 if (!content->IsXUL() &&
3055 contentState.HasState(NS_EVENT_STATE_FOCUS)) {
3056 aFocused = true;
3059 if (IsDisabled(aFrame, contentState)) {
3060 aState |= DFCS_INACTIVE;
3061 } else if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE |
3062 NS_EVENT_STATE_HOVER)) {
3063 aState |= DFCS_PUSHED;
3066 return NS_OK;
3068 case NS_THEME_MENUITEM:
3069 case NS_THEME_CHECKMENUITEM:
3070 case NS_THEME_RADIOMENUITEM: {
3071 bool isTopLevel = false;
3072 bool isOpen = false;
3073 bool isContainer = false;
3074 nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
3075 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
3077 // We indicate top-level-ness using aPart. 0 is a normal menu item,
3078 // 1 is a top-level menu item. The state of the item is composed of
3079 // DFCS_* flags only.
3080 aPart = 0;
3081 aState = 0;
3083 if (menuFrame) {
3084 // If this is a real menu item, we should check if it is part of the
3085 // main menu bar or not, and if it is a container, as these affect
3086 // rendering.
3087 isTopLevel = menuFrame->IsOnMenuBar();
3088 isOpen = menuFrame->IsOpen();
3089 isContainer = menuFrame->IsMenu();
3092 if (IsDisabled(aFrame, eventState))
3093 aState |= DFCS_INACTIVE;
3095 if (isTopLevel) {
3096 aPart = 1;
3097 if (isOpen)
3098 aState |= DFCS_PUSHED;
3101 if (IsMenuActive(aFrame, aWidgetType))
3102 aState |= DFCS_HOT;
3104 return NS_OK;
3106 case NS_THEME_MENUCHECKBOX:
3107 case NS_THEME_MENURADIO:
3108 case NS_THEME_MENUARROW: {
3109 aState = 0;
3110 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
3112 if (IsDisabled(aFrame, eventState))
3113 aState |= DFCS_INACTIVE;
3114 if (IsMenuActive(aFrame, aWidgetType))
3115 aState |= DFCS_HOT;
3117 if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO) {
3118 if (IsCheckedButton(aFrame))
3119 aState |= DFCS_CHECKED;
3120 } else if (IsFrameRTL(aFrame)) {
3121 aState |= DFCS_RTL;
3123 return NS_OK;
3125 case NS_THEME_LISTBOX:
3126 case NS_THEME_TREEVIEW:
3127 case NS_THEME_TEXTFIELD:
3128 case NS_THEME_TEXTFIELD_MULTILINE:
3129 case NS_THEME_DROPDOWN:
3130 case NS_THEME_DROPDOWN_TEXTFIELD:
3131 case NS_THEME_RANGE:
3132 case NS_THEME_RANGE_THUMB:
3133 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
3134 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
3135 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
3136 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
3137 case NS_THEME_SCALE_HORIZONTAL:
3138 case NS_THEME_SCALE_VERTICAL:
3139 case NS_THEME_SCALE_THUMB_HORIZONTAL:
3140 case NS_THEME_SCALE_THUMB_VERTICAL:
3141 case NS_THEME_STATUSBAR:
3142 case NS_THEME_STATUSBAR_PANEL:
3143 case NS_THEME_STATUSBAR_RESIZER_PANEL:
3144 case NS_THEME_PROGRESSBAR_CHUNK:
3145 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
3146 case NS_THEME_TOOLTIP:
3147 case NS_THEME_PROGRESSBAR:
3148 case NS_THEME_PROGRESSBAR_VERTICAL:
3149 case NS_THEME_TAB:
3150 case NS_THEME_TAB_PANEL:
3151 case NS_THEME_TAB_PANELS:
3152 case NS_THEME_MENUBAR:
3153 case NS_THEME_MENUPOPUP:
3154 case NS_THEME_GROUPBOX:
3155 // these don't use DrawFrameControl
3156 return NS_OK;
3157 case NS_THEME_DROPDOWN_BUTTON: {
3159 aPart = DFC_SCROLL;
3160 aState = DFCS_SCROLLCOMBOBOX;
3162 nsIFrame* parentFrame = aFrame->GetParent();
3163 bool isHTML = IsHTMLContent(aFrame);
3164 bool isMenulist = !isHTML && parentFrame->GetType() == nsGkAtoms::menuFrame;
3165 bool isOpen = false;
3167 // HTML select and XUL menulist dropdown buttons get state from the parent.
3168 if (isHTML || isMenulist)
3169 aFrame = parentFrame;
3171 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
3173 if (IsDisabled(aFrame, eventState)) {
3174 aState |= DFCS_INACTIVE;
3175 return NS_OK;
3178 if (isHTML) {
3179 nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
3180 isOpen = (ccf && ccf->IsDroppedDown());
3182 else
3183 isOpen = IsOpenButton(aFrame);
3185 // XXX Button should look active until the mouse is released, but
3186 // without making it look active when the popup is clicked.
3187 if (isOpen && (isHTML || isMenulist))
3188 return NS_OK;
3190 // Dropdown button active state doesn't need :hover.
3191 if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
3192 aState |= DFCS_PUSHED | DFCS_FLAT;
3194 return NS_OK;
3196 case NS_THEME_SCROLLBAR_BUTTON_UP:
3197 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
3198 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
3199 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: {
3200 nsEventStates contentState = GetContentState(aFrame, aWidgetType);
3202 aPart = DFC_SCROLL;
3203 switch (aWidgetType) {
3204 case NS_THEME_SCROLLBAR_BUTTON_UP:
3205 aState = DFCS_SCROLLUP;
3206 break;
3207 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
3208 aState = DFCS_SCROLLDOWN;
3209 break;
3210 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
3211 aState = DFCS_SCROLLLEFT;
3212 break;
3213 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
3214 aState = DFCS_SCROLLRIGHT;
3215 break;
3218 if (IsDisabled(aFrame, contentState))
3219 aState |= DFCS_INACTIVE;
3220 else {
3221 if (contentState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
3222 aState |= DFCS_PUSHED | DFCS_FLAT;
3225 return NS_OK;
3227 case NS_THEME_SPINNER_UP_BUTTON:
3228 case NS_THEME_SPINNER_DOWN_BUTTON: {
3229 nsEventStates contentState = GetContentState(aFrame, aWidgetType);
3231 aPart = DFC_SCROLL;
3232 switch (aWidgetType) {
3233 case NS_THEME_SPINNER_UP_BUTTON:
3234 aState = DFCS_SCROLLUP;
3235 break;
3236 case NS_THEME_SPINNER_DOWN_BUTTON:
3237 aState = DFCS_SCROLLDOWN;
3238 break;
3241 if (IsDisabled(aFrame, contentState))
3242 aState |= DFCS_INACTIVE;
3243 else {
3244 if (contentState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
3245 aState |= DFCS_PUSHED;
3248 return NS_OK;
3250 case NS_THEME_RESIZER:
3251 aPart = DFC_SCROLL;
3252 aState = (IsFrameRTL(aFrame)) ?
3253 DFCS_SCROLLSIZEGRIPRIGHT : DFCS_SCROLLSIZEGRIP;
3254 return NS_OK;
3255 case NS_THEME_MENUSEPARATOR:
3256 aPart = 0;
3257 aState = 0;
3258 return NS_OK;
3259 case NS_THEME_WINDOW_TITLEBAR:
3260 aPart = mozilla::widget::themeconst::WP_CAPTION;
3261 aState = GetTopLevelWindowActiveState(aFrame);
3262 return NS_OK;
3263 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
3264 aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
3265 aState = GetTopLevelWindowActiveState(aFrame);
3266 return NS_OK;
3267 case NS_THEME_WINDOW_FRAME_LEFT:
3268 aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
3269 aState = GetTopLevelWindowActiveState(aFrame);
3270 return NS_OK;
3271 case NS_THEME_WINDOW_FRAME_RIGHT:
3272 aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
3273 aState = GetTopLevelWindowActiveState(aFrame);
3274 return NS_OK;
3275 case NS_THEME_WINDOW_FRAME_BOTTOM:
3276 aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
3277 aState = GetTopLevelWindowActiveState(aFrame);
3278 return NS_OK;
3279 case NS_THEME_WINDOW_BUTTON_CLOSE:
3280 aPart = DFC_CAPTION;
3281 aState = DFCS_CAPTIONCLOSE |
3282 GetClassicWindowFrameButtonState(GetContentState(aFrame,
3283 aWidgetType));
3284 return NS_OK;
3285 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
3286 aPart = DFC_CAPTION;
3287 aState = DFCS_CAPTIONMIN |
3288 GetClassicWindowFrameButtonState(GetContentState(aFrame,
3289 aWidgetType));
3290 return NS_OK;
3291 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
3292 aPart = DFC_CAPTION;
3293 aState = DFCS_CAPTIONMAX |
3294 GetClassicWindowFrameButtonState(GetContentState(aFrame,
3295 aWidgetType));
3296 return NS_OK;
3297 case NS_THEME_WINDOW_BUTTON_RESTORE:
3298 aPart = DFC_CAPTION;
3299 aState = DFCS_CAPTIONRESTORE |
3300 GetClassicWindowFrameButtonState(GetContentState(aFrame,
3301 aWidgetType));
3302 return NS_OK;
3304 return NS_ERROR_FAILURE;
3307 // Draw classic Windows tab
3308 // (no system API for this, but DrawEdge can draw all the parts of a tab)
3309 static void DrawTab(HDC hdc, const RECT& R, int32_t aPosition, bool aSelected,
3310 bool aDrawLeft, bool aDrawRight)
3312 int32_t leftFlag, topFlag, rightFlag, lightFlag, shadeFlag;
3313 RECT topRect, sideRect, bottomRect, lightRect, shadeRect;
3314 int32_t selectedOffset, lOffset, rOffset;
3316 selectedOffset = aSelected ? 1 : 0;
3317 lOffset = aDrawLeft ? 2 : 0;
3318 rOffset = aDrawRight ? 2 : 0;
3320 // Get info for tab orientation/position (Left, Top, Right, Bottom)
3321 switch (aPosition) {
3322 case BF_LEFT:
3323 leftFlag = BF_TOP; topFlag = BF_LEFT;
3324 rightFlag = BF_BOTTOM;
3325 lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
3326 shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
3328 ::SetRect(&topRect, R.left, R.top+lOffset, R.right, R.bottom-rOffset);
3329 ::SetRect(&sideRect, R.left+2, R.top, R.right-2+selectedOffset, R.bottom);
3330 ::SetRect(&bottomRect, R.right-2, R.top, R.right, R.bottom);
3331 ::SetRect(&lightRect, R.left, R.top, R.left+3, R.top+3);
3332 ::SetRect(&shadeRect, R.left+1, R.bottom-2, R.left+2, R.bottom-1);
3333 break;
3334 case BF_TOP:
3335 leftFlag = BF_LEFT; topFlag = BF_TOP;
3336 rightFlag = BF_RIGHT;
3337 lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
3338 shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
3340 ::SetRect(&topRect, R.left+lOffset, R.top, R.right-rOffset, R.bottom);
3341 ::SetRect(&sideRect, R.left, R.top+2, R.right, R.bottom-1+selectedOffset);
3342 ::SetRect(&bottomRect, R.left, R.bottom-1, R.right, R.bottom);
3343 ::SetRect(&lightRect, R.left, R.top, R.left+3, R.top+3);
3344 ::SetRect(&shadeRect, R.right-2, R.top+1, R.right-1, R.top+2);
3345 break;
3346 case BF_RIGHT:
3347 leftFlag = BF_TOP; topFlag = BF_RIGHT;
3348 rightFlag = BF_BOTTOM;
3349 lightFlag = BF_DIAGONAL_ENDTOPLEFT;
3350 shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
3352 ::SetRect(&topRect, R.left, R.top+lOffset, R.right, R.bottom-rOffset);
3353 ::SetRect(&sideRect, R.left+2-selectedOffset, R.top, R.right-2, R.bottom);
3354 ::SetRect(&bottomRect, R.left, R.top, R.left+2, R.bottom);
3355 ::SetRect(&lightRect, R.right-3, R.top, R.right-1, R.top+2);
3356 ::SetRect(&shadeRect, R.right-2, R.bottom-3, R.right, R.bottom-1);
3357 break;
3358 case BF_BOTTOM:
3359 leftFlag = BF_LEFT; topFlag = BF_BOTTOM;
3360 rightFlag = BF_RIGHT;
3361 lightFlag = BF_DIAGONAL_ENDTOPLEFT;
3362 shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
3364 ::SetRect(&topRect, R.left+lOffset, R.top, R.right-rOffset, R.bottom);
3365 ::SetRect(&sideRect, R.left, R.top+2-selectedOffset, R.right, R.bottom-2);
3366 ::SetRect(&bottomRect, R.left, R.top, R.right, R.top+2);
3367 ::SetRect(&lightRect, R.left, R.bottom-3, R.left+2, R.bottom-1);
3368 ::SetRect(&shadeRect, R.right-2, R.bottom-3, R.right, R.bottom-1);
3369 break;
3372 // Background
3373 ::FillRect(hdc, &R, (HBRUSH) (COLOR_3DFACE+1) );
3375 // Tab "Top"
3376 ::DrawEdge(hdc, &topRect, EDGE_RAISED, BF_SOFT | topFlag);
3378 // Tab "Bottom"
3379 if (!aSelected)
3380 ::DrawEdge(hdc, &bottomRect, EDGE_RAISED, BF_SOFT | topFlag);
3382 // Tab "Sides"
3383 if (!aDrawLeft)
3384 leftFlag = 0;
3385 if (!aDrawRight)
3386 rightFlag = 0;
3387 ::DrawEdge(hdc, &sideRect, EDGE_RAISED, BF_SOFT | leftFlag | rightFlag);
3389 // Tab Diagonal Corners
3390 if (aDrawLeft)
3391 ::DrawEdge(hdc, &lightRect, EDGE_RAISED, BF_SOFT | lightFlag);
3393 if (aDrawRight)
3394 ::DrawEdge(hdc, &shadeRect, EDGE_RAISED, BF_SOFT | shadeFlag);
3397 static void DrawMenuImage(HDC hdc, const RECT& rc, int32_t aComponent, uint32_t aColor)
3399 // This procedure creates a memory bitmap to contain the check mark, draws
3400 // it into the bitmap (it is a mask image), then composes it onto the menu
3401 // item in appropriate colors.
3402 HDC hMemoryDC = ::CreateCompatibleDC(hdc);
3403 if (hMemoryDC) {
3404 // XXXjgr We should ideally be caching these, but we wont be notified when
3405 // they change currently, so we can't do so easily. Same for the bitmap.
3406 int checkW = ::GetSystemMetrics(SM_CXMENUCHECK);
3407 int checkH = ::GetSystemMetrics(SM_CYMENUCHECK);
3409 HBITMAP hMonoBitmap = ::CreateBitmap(checkW, checkH, 1, 1, nullptr);
3410 if (hMonoBitmap) {
3412 HBITMAP hPrevBitmap = (HBITMAP) ::SelectObject(hMemoryDC, hMonoBitmap);
3413 if (hPrevBitmap) {
3415 // XXXjgr This will go pear-shaped if the image is bigger than the
3416 // provided rect. What should we do?
3417 RECT imgRect = { 0, 0, checkW, checkH };
3418 POINT imgPos = {
3419 rc.left + (rc.right - rc.left - checkW) / 2,
3420 rc.top + (rc.bottom - rc.top - checkH) / 2
3423 // XXXzeniko Windows renders these 1px lower than you'd expect
3424 if (aComponent == DFCS_MENUCHECK || aComponent == DFCS_MENUBULLET)
3425 imgPos.y++;
3427 ::DrawFrameControl(hMemoryDC, &imgRect, DFC_MENU, aComponent);
3428 COLORREF oldTextCol = ::SetTextColor(hdc, 0x00000000);
3429 COLORREF oldBackCol = ::SetBkColor(hdc, 0x00FFFFFF);
3430 ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCAND);
3431 ::SetTextColor(hdc, ::GetSysColor(aColor));
3432 ::SetBkColor(hdc, 0x00000000);
3433 ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCPAINT);
3434 ::SetTextColor(hdc, oldTextCol);
3435 ::SetBkColor(hdc, oldBackCol);
3436 ::SelectObject(hMemoryDC, hPrevBitmap);
3438 ::DeleteObject(hMonoBitmap);
3440 ::DeleteDC(hMemoryDC);
3444 void nsNativeThemeWin::DrawCheckedRect(HDC hdc, const RECT& rc, int32_t fore, int32_t back,
3445 HBRUSH defaultBack)
3447 static WORD patBits[8] = {
3448 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
3451 HBITMAP patBmp = ::CreateBitmap(8, 8, 1, 1, patBits);
3452 if (patBmp) {
3453 HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patBmp);
3454 if (brush) {
3455 COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(fore));
3456 COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(back));
3457 POINT vpOrg;
3459 ::UnrealizeObject(brush);
3460 ::GetViewportOrgEx(hdc, &vpOrg);
3461 ::SetBrushOrgEx(hdc, vpOrg.x + rc.left, vpOrg.y + rc.top, nullptr);
3462 HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush);
3463 ::FillRect(hdc, &rc, brush);
3464 ::SetTextColor(hdc, oldForeColor);
3465 ::SetBkColor(hdc, oldBackColor);
3466 ::SelectObject(hdc, oldBrush);
3467 ::DeleteObject(brush);
3469 else
3470 ::FillRect(hdc, &rc, defaultBack);
3472 ::DeleteObject(patBmp);
3476 nsresult nsNativeThemeWin::ClassicDrawWidgetBackground(nsRenderingContext* aContext,
3477 nsIFrame* aFrame,
3478 uint8_t aWidgetType,
3479 const nsRect& aRect,
3480 const nsRect& aDirtyRect)
3482 int32_t part, state;
3483 bool focused;
3484 nsresult rv;
3485 rv = ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused);
3486 if (NS_FAILED(rv))
3487 return rv;
3489 if (AssumeThemePartAndStateAreTransparent(part, state)) {
3490 return NS_OK;
3493 gfxFloat p2a = gfxFloat(aContext->AppUnitsPerDevPixel());
3494 RECT widgetRect;
3495 gfxRect tr(aRect.x, aRect.y, aRect.width, aRect.height),
3496 dr(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
3498 tr.ScaleInverse(p2a);
3499 dr.ScaleInverse(p2a);
3501 nsRefPtr<gfxContext> ctx = aContext->ThebesContext();
3503 gfxWindowsNativeDrawing nativeDrawing(ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
3505 RENDER_AGAIN:
3507 HDC hdc = nativeDrawing.BeginNativeDrawing();
3508 if (!hdc)
3509 return NS_ERROR_FAILURE;
3511 nativeDrawing.TransformToNativeRect(tr, widgetRect);
3513 rv = NS_OK;
3514 switch (aWidgetType) {
3515 // Draw button
3516 case NS_THEME_BUTTON: {
3517 if (focused) {
3518 // draw dark button focus border first
3519 HBRUSH brush;
3520 brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
3521 if (brush)
3522 ::FrameRect(hdc, &widgetRect, brush);
3523 InflateRect(&widgetRect, -1, -1);
3525 // fall-through...
3527 // Draw controls supported by DrawFrameControl
3528 case NS_THEME_CHECKBOX:
3529 case NS_THEME_RADIO:
3530 case NS_THEME_SCROLLBAR_BUTTON_UP:
3531 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
3532 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
3533 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
3534 case NS_THEME_SPINNER_UP_BUTTON:
3535 case NS_THEME_SPINNER_DOWN_BUTTON:
3536 case NS_THEME_DROPDOWN_BUTTON:
3537 case NS_THEME_RESIZER: {
3538 int32_t oldTA;
3539 // setup DC to make DrawFrameControl draw correctly
3540 oldTA = ::SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
3541 ::DrawFrameControl(hdc, &widgetRect, part, state);
3542 ::SetTextAlign(hdc, oldTA);
3544 // Draw focus rectangles for HTML checkboxes and radio buttons
3545 // XXX it'd be nice to draw these outside of the frame
3546 if (focused && (aWidgetType == NS_THEME_CHECKBOX || aWidgetType == NS_THEME_RADIO)) {
3547 // setup DC to make DrawFocusRect draw correctly
3548 POINT vpOrg;
3549 ::GetViewportOrgEx(hdc, &vpOrg);
3550 ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top, nullptr);
3551 int32_t oldColor;
3552 oldColor = ::SetTextColor(hdc, 0);
3553 // draw focus rectangle
3554 ::DrawFocusRect(hdc, &widgetRect);
3555 ::SetTextColor(hdc, oldColor);
3557 break;
3559 // Draw controls with 2px 3D inset border
3560 case NS_THEME_TEXTFIELD:
3561 case NS_THEME_TEXTFIELD_MULTILINE:
3562 case NS_THEME_LISTBOX:
3563 case NS_THEME_DROPDOWN:
3564 case NS_THEME_DROPDOWN_TEXTFIELD: {
3565 // Draw inset edge
3566 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3567 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
3569 // Fill in background
3570 if (IsDisabled(aFrame, eventState) ||
3571 (aFrame->GetContent()->IsXUL() &&
3572 IsReadOnly(aFrame)))
3573 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
3574 else
3575 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_WINDOW+1));
3577 break;
3579 case NS_THEME_TREEVIEW: {
3580 // Draw inset edge
3581 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3583 // Fill in window color background
3584 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_WINDOW+1));
3586 break;
3588 // Draw ToolTip background
3589 case NS_THEME_TOOLTIP:
3590 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_WINDOWFRAME));
3591 InflateRect(&widgetRect, -1, -1);
3592 ::FillRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_INFOBK));
3594 break;
3595 case NS_THEME_GROUPBOX:
3596 ::DrawEdge(hdc, &widgetRect, EDGE_ETCHED, BF_RECT | BF_ADJUST);
3597 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
3598 break;
3599 // Draw 3D face background controls
3600 case NS_THEME_PROGRESSBAR:
3601 case NS_THEME_PROGRESSBAR_VERTICAL:
3602 // Draw 3D border
3603 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
3604 InflateRect(&widgetRect, -1, -1);
3605 // fall through
3606 case NS_THEME_TAB_PANEL:
3607 case NS_THEME_STATUSBAR:
3608 case NS_THEME_STATUSBAR_RESIZER_PANEL: {
3609 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
3611 break;
3613 // Draw 3D inset statusbar panel
3614 case NS_THEME_STATUSBAR_PANEL: {
3615 if (aFrame->GetNextSibling())
3616 widgetRect.right -= 2; // space between sibling status panels
3618 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
3620 break;
3622 // Draw scrollbar thumb
3623 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
3624 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
3625 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
3627 break;
3628 case NS_THEME_RANGE_THUMB:
3629 case NS_THEME_SCALE_THUMB_VERTICAL:
3630 case NS_THEME_SCALE_THUMB_HORIZONTAL: {
3631 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
3633 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
3634 if (IsDisabled(aFrame, eventState)) {
3635 DrawCheckedRect(hdc, widgetRect, COLOR_3DFACE, COLOR_3DHILIGHT,
3636 (HBRUSH) COLOR_3DHILIGHT);
3639 break;
3641 // Draw scrollbar track background
3642 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
3643 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: {
3645 // Windows fills in the scrollbar track differently
3646 // depending on whether these are equal
3647 DWORD color3D, colorScrollbar, colorWindow;
3649 color3D = ::GetSysColor(COLOR_3DFACE);
3650 colorWindow = ::GetSysColor(COLOR_WINDOW);
3651 colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR);
3653 if ((color3D != colorScrollbar) && (colorWindow != colorScrollbar))
3654 // Use solid brush
3655 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_SCROLLBAR+1));
3656 else
3658 DrawCheckedRect(hdc, widgetRect, COLOR_3DHILIGHT, COLOR_3DFACE,
3659 (HBRUSH) COLOR_SCROLLBAR+1);
3661 // XXX should invert the part of the track being clicked here
3662 // but the track is never :active
3664 break;
3666 // Draw scale track background
3667 case NS_THEME_RANGE:
3668 case NS_THEME_SCALE_VERTICAL:
3669 case NS_THEME_SCALE_HORIZONTAL: {
3670 const int32_t trackWidth = 4;
3671 // When rounding is necessary, we round the position of the track
3672 // away from the chevron of the thumb to make it look better.
3673 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
3674 (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
3675 widgetRect.top += (widgetRect.bottom - widgetRect.top - trackWidth) / 2;
3676 widgetRect.bottom = widgetRect.top + trackWidth;
3678 else {
3679 if (!IsFrameRTL(aFrame)) {
3680 widgetRect.left += (widgetRect.right - widgetRect.left - trackWidth) / 2;
3681 widgetRect.right = widgetRect.left + trackWidth;
3682 } else {
3683 widgetRect.right -= (widgetRect.right - widgetRect.left - trackWidth) / 2;
3684 widgetRect.left = widgetRect.right - trackWidth;
3688 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3689 ::FillRect(hdc, &widgetRect, (HBRUSH) GetStockObject(GRAY_BRUSH));
3691 break;
3693 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
3694 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
3695 break;
3697 case NS_THEME_PROGRESSBAR_CHUNK: {
3698 nsIFrame* stateFrame = aFrame->GetParent();
3699 nsEventStates eventStates = GetContentState(stateFrame, aWidgetType);
3701 bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates);
3702 bool vertical = IsVerticalProgress(stateFrame) ||
3703 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL;
3704 int32_t overlayPart = GetProgressOverlayStyle(vertical);
3706 nsIContent* content = aFrame->GetContent();
3707 if (!indeterminate || !content) {
3708 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
3709 break;
3712 RECT overlayRect =
3713 CalculateProgressOverlayRect(aFrame, &widgetRect, vertical,
3714 indeterminate, true);
3716 ::FillRect(hdc, &overlayRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
3718 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
3719 NS_WARNING("unable to animate progress widget!");
3721 break;
3724 // Draw Tab
3725 case NS_THEME_TAB: {
3726 DrawTab(hdc, widgetRect,
3727 IsBottomTab(aFrame) ? BF_BOTTOM : BF_TOP,
3728 IsSelectedTab(aFrame),
3729 !IsRightToSelectedTab(aFrame),
3730 !IsLeftToSelectedTab(aFrame));
3732 break;
3734 case NS_THEME_TAB_PANELS:
3735 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_SOFT | BF_MIDDLE |
3736 BF_LEFT | BF_RIGHT | BF_BOTTOM);
3738 break;
3739 case NS_THEME_MENUBAR:
3740 break;
3741 case NS_THEME_MENUPOPUP:
3742 NS_ASSERTION(nsUXThemeData::sFlatMenus, "Classic menus are styled entirely through CSS");
3743 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENU+1));
3744 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
3745 break;
3746 case NS_THEME_MENUITEM:
3747 case NS_THEME_CHECKMENUITEM:
3748 case NS_THEME_RADIOMENUITEM:
3749 // part == 0 for normal items
3750 // part == 1 for top-level menu items
3751 if (nsUXThemeData::sFlatMenus) {
3752 // Not disabled and hot/pushed.
3753 if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
3754 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENUHILIGHT+1));
3755 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_HIGHLIGHT));
3757 } else {
3758 if (part == 1) {
3759 if ((state & DFCS_INACTIVE) == 0) {
3760 if ((state & DFCS_PUSHED) != 0) {
3761 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT);
3762 } else if ((state & DFCS_HOT) != 0) {
3763 ::DrawEdge(hdc, &widgetRect, BDR_RAISEDINNER, BF_RECT);
3766 } else {
3767 if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
3768 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
3772 break;
3773 case NS_THEME_MENUCHECKBOX:
3774 case NS_THEME_MENURADIO:
3775 if (!(state & DFCS_CHECKED))
3776 break; // nothin' to do
3777 case NS_THEME_MENUARROW: {
3778 uint32_t color = COLOR_MENUTEXT;
3779 if ((state & DFCS_INACTIVE))
3780 color = COLOR_GRAYTEXT;
3781 else if ((state & DFCS_HOT))
3782 color = COLOR_HIGHLIGHTTEXT;
3784 if (aWidgetType == NS_THEME_MENUCHECKBOX)
3785 DrawMenuImage(hdc, widgetRect, DFCS_MENUCHECK, color);
3786 else if (aWidgetType == NS_THEME_MENURADIO)
3787 DrawMenuImage(hdc, widgetRect, DFCS_MENUBULLET, color);
3788 else if (aWidgetType == NS_THEME_MENUARROW)
3789 DrawMenuImage(hdc, widgetRect,
3790 (state & DFCS_RTL) ? DFCS_MENUARROWRIGHT : DFCS_MENUARROW,
3791 color);
3792 break;
3794 case NS_THEME_MENUSEPARATOR: {
3795 // separators are offset by a bit (see menu.css)
3796 widgetRect.left++;
3797 widgetRect.right--;
3799 // This magic number is brought to you by the value in menu.css
3800 widgetRect.top += 4;
3801 // Our rectangles are 1 pixel high (see border size in menu.css)
3802 widgetRect.bottom = widgetRect.top+1;
3803 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DSHADOW+1));
3804 widgetRect.top++;
3805 widgetRect.bottom++;
3806 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DHILIGHT+1));
3807 break;
3810 case NS_THEME_WINDOW_TITLEBAR:
3811 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
3813 RECT rect = widgetRect;
3814 int32_t offset = GetSystemMetrics(SM_CXFRAME);
3815 rect.bottom -= 1;
3817 // first fill the area to the color of the window background
3818 FillRect(hdc, &rect, (HBRUSH)(COLOR_3DFACE+1));
3820 // inset the caption area so it doesn't overflow.
3821 rect.top += offset;
3822 // if enabled, draw a gradient titlebar background, otherwise
3823 // fill with a solid color.
3824 BOOL bFlag = TRUE;
3825 SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &bFlag, 0);
3826 if (!bFlag) {
3827 if (state == mozilla::widget::themeconst::FS_ACTIVE)
3828 FillRect(hdc, &rect, (HBRUSH)(COLOR_ACTIVECAPTION+1));
3829 else
3830 FillRect(hdc, &rect, (HBRUSH)(COLOR_INACTIVECAPTION+1));
3831 } else {
3832 DWORD startColor, endColor;
3833 if (state == mozilla::widget::themeconst::FS_ACTIVE) {
3834 startColor = GetSysColor(COLOR_ACTIVECAPTION);
3835 endColor = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
3836 } else {
3837 startColor = GetSysColor(COLOR_INACTIVECAPTION);
3838 endColor = GetSysColor(COLOR_GRADIENTINACTIVECAPTION);
3841 TRIVERTEX vertex[2];
3842 vertex[0].x = rect.left;
3843 vertex[0].y = rect.top;
3844 vertex[0].Red = GetRValue(startColor) << 8;
3845 vertex[0].Green = GetGValue(startColor) << 8;
3846 vertex[0].Blue = GetBValue(startColor) << 8;
3847 vertex[0].Alpha = 0;
3849 vertex[1].x = rect.right;
3850 vertex[1].y = rect.bottom;
3851 vertex[1].Red = GetRValue(endColor) << 8;
3852 vertex[1].Green = GetGValue(endColor) << 8;
3853 vertex[1].Blue = GetBValue(endColor) << 8;
3854 vertex[1].Alpha = 0;
3856 GRADIENT_RECT gRect;
3857 gRect.UpperLeft = 0;
3858 gRect.LowerRight = 1;
3859 // available on win2k & up
3860 GradientFill(hdc, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H);
3863 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
3864 // frame things up with a top raised border.
3865 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_TOP);
3867 break;
3870 case NS_THEME_WINDOW_FRAME_LEFT:
3871 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_LEFT);
3872 break;
3874 case NS_THEME_WINDOW_FRAME_RIGHT:
3875 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RIGHT);
3876 break;
3878 case NS_THEME_WINDOW_FRAME_BOTTOM:
3879 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_BOTTOM);
3880 break;
3882 case NS_THEME_WINDOW_BUTTON_CLOSE:
3883 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
3884 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
3885 case NS_THEME_WINDOW_BUTTON_RESTORE:
3887 if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
3888 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
3890 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
3891 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
3892 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
3894 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
3895 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
3897 int32_t oldTA = SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
3898 DrawFrameControl(hdc, &widgetRect, part, state);
3899 SetTextAlign(hdc, oldTA);
3900 break;
3903 default:
3904 rv = NS_ERROR_FAILURE;
3905 break;
3908 nativeDrawing.EndNativeDrawing();
3910 if (NS_FAILED(rv))
3911 return rv;
3913 if (nativeDrawing.ShouldRenderAgain())
3914 goto RENDER_AGAIN;
3916 nativeDrawing.PaintToContext();
3918 return rv;
3921 uint32_t
3922 nsNativeThemeWin::GetWidgetNativeDrawingFlags(uint8_t aWidgetType)
3924 switch (aWidgetType) {
3925 case NS_THEME_BUTTON:
3926 case NS_THEME_TEXTFIELD:
3927 case NS_THEME_TEXTFIELD_MULTILINE:
3929 case NS_THEME_DROPDOWN:
3930 case NS_THEME_DROPDOWN_TEXTFIELD:
3931 return
3932 gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
3933 gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE |
3934 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
3936 // need to check these others
3937 case NS_THEME_RANGE:
3938 case NS_THEME_RANGE_THUMB:
3939 case NS_THEME_SCROLLBAR_BUTTON_UP:
3940 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
3941 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
3942 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
3943 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
3944 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
3945 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
3946 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
3947 case NS_THEME_SCALE_HORIZONTAL:
3948 case NS_THEME_SCALE_VERTICAL:
3949 case NS_THEME_SCALE_THUMB_HORIZONTAL:
3950 case NS_THEME_SCALE_THUMB_VERTICAL:
3951 case NS_THEME_SPINNER_UP_BUTTON:
3952 case NS_THEME_SPINNER_DOWN_BUTTON:
3953 case NS_THEME_LISTBOX:
3954 case NS_THEME_TREEVIEW:
3955 case NS_THEME_TOOLTIP:
3956 case NS_THEME_STATUSBAR:
3957 case NS_THEME_STATUSBAR_PANEL:
3958 case NS_THEME_STATUSBAR_RESIZER_PANEL:
3959 case NS_THEME_RESIZER:
3960 case NS_THEME_PROGRESSBAR:
3961 case NS_THEME_PROGRESSBAR_VERTICAL:
3962 case NS_THEME_PROGRESSBAR_CHUNK:
3963 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
3964 case NS_THEME_TAB:
3965 case NS_THEME_TAB_PANEL:
3966 case NS_THEME_TAB_PANELS:
3967 case NS_THEME_MENUBAR:
3968 case NS_THEME_MENUPOPUP:
3969 case NS_THEME_MENUITEM:
3970 break;
3972 // the dropdown button /almost/ renders correctly with scaling,
3973 // except that the graphic in the dropdown button (the downward arrow)
3974 // doesn't get scaled up.
3975 case NS_THEME_DROPDOWN_BUTTON:
3976 // these are definitely no; they're all graphics that don't get scaled up
3977 case NS_THEME_CHECKBOX:
3978 case NS_THEME_RADIO:
3979 case NS_THEME_GROUPBOX:
3980 case NS_THEME_CHECKMENUITEM:
3981 case NS_THEME_RADIOMENUITEM:
3982 case NS_THEME_MENUCHECKBOX:
3983 case NS_THEME_MENURADIO:
3984 case NS_THEME_MENUARROW:
3985 return
3986 gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
3987 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
3988 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
3991 return
3992 gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
3993 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
3994 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
3997 ///////////////////////////////////////////
3998 // Creation Routine
3999 ///////////////////////////////////////////
4001 // from nsWindow.cpp
4002 extern bool gDisableNativeTheme;
4004 nsresult NS_NewNativeTheme(nsISupports *aOuter, REFNSIID aIID, void **aResult)
4006 if (gDisableNativeTheme)
4007 return NS_ERROR_NO_INTERFACE;
4009 if (aOuter)
4010 return NS_ERROR_NO_AGGREGATION;
4012 nsNativeThemeWin* theme = new nsNativeThemeWin();
4013 if (!theme)
4014 return NS_ERROR_OUT_OF_MEMORY;
4015 return theme->QueryInterface(aIID, aResult);