widget/windows/nsNativeThemeWin.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial