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