|
1 /* -*- Mode: C++; tab-width: 2; 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 "nsNativeThemeCocoa.h" |
|
7 #include "nsObjCExceptions.h" |
|
8 #include "nsNumberControlFrame.h" |
|
9 #include "nsRangeFrame.h" |
|
10 #include "nsRenderingContext.h" |
|
11 #include "nsRect.h" |
|
12 #include "nsSize.h" |
|
13 #include "nsThemeConstants.h" |
|
14 #include "nsIPresShell.h" |
|
15 #include "nsPresContext.h" |
|
16 #include "nsIContent.h" |
|
17 #include "nsIDocument.h" |
|
18 #include "nsIFrame.h" |
|
19 #include "nsIAtom.h" |
|
20 #include "nsNameSpaceManager.h" |
|
21 #include "nsPresContext.h" |
|
22 #include "nsGkAtoms.h" |
|
23 #include "nsCocoaFeatures.h" |
|
24 #include "nsCocoaWindow.h" |
|
25 #include "nsNativeThemeColors.h" |
|
26 #include "nsIScrollableFrame.h" |
|
27 #include "mozilla/EventStates.h" |
|
28 #include "mozilla/dom/Element.h" |
|
29 #include "mozilla/dom/HTMLMeterElement.h" |
|
30 #include "nsLookAndFeel.h" |
|
31 |
|
32 #include "gfxContext.h" |
|
33 #include "gfxQuartzSurface.h" |
|
34 #include "gfxQuartzNativeDrawing.h" |
|
35 #include <algorithm> |
|
36 |
|
37 using namespace mozilla; |
|
38 using namespace mozilla::gfx; |
|
39 using mozilla::dom::HTMLMeterElement; |
|
40 |
|
41 #define DRAW_IN_FRAME_DEBUG 0 |
|
42 #define SCROLLBARS_VISUAL_DEBUG 0 |
|
43 |
|
44 // private Quartz routines needed here |
|
45 extern "C" { |
|
46 CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform); |
|
47 } |
|
48 |
|
49 // Workaround for NSCell control tint drawing |
|
50 // Without this workaround, NSCells are always drawn with the clear control tint |
|
51 // as long as they're not attached to an NSControl which is a subview of an active window. |
|
52 // XXXmstange Why doesn't Webkit need this? |
|
53 @implementation NSCell (ControlTintWorkaround) |
|
54 - (int)_realControlTint { return [self controlTint]; } |
|
55 @end |
|
56 |
|
57 // The purpose of this class is to provide objects that can be used when drawing |
|
58 // NSCells using drawWithFrame:inView: without causing any harm. The only |
|
59 // messages that will be sent to such an object are "isFlipped" and |
|
60 // "currentEditor": isFlipped needs to return YES in order to avoid drawing bugs |
|
61 // on 10.4 (see bug 465069); currentEditor (which isn't even a method of |
|
62 // NSView) will be called when drawing search fields, and we only provide it in |
|
63 // order to prevent "unrecognized selector" exceptions. |
|
64 // There's no need to pass the actual NSView that we're drawing into to |
|
65 // drawWithFrame:inView:. What's more, doing so even causes unnecessary |
|
66 // invalidations as soon as we draw a focusring! |
|
67 @interface CellDrawView : NSView |
|
68 |
|
69 @end; |
|
70 |
|
71 @implementation CellDrawView |
|
72 |
|
73 - (BOOL)isFlipped |
|
74 { |
|
75 return YES; |
|
76 } |
|
77 |
|
78 - (NSText*)currentEditor |
|
79 { |
|
80 return nil; |
|
81 } |
|
82 |
|
83 @end |
|
84 |
|
85 static void |
|
86 DrawCellIncludingFocusRing(NSCell* aCell, NSRect aWithFrame, NSView* aInView) |
|
87 { |
|
88 [aCell drawWithFrame:aWithFrame inView:aInView]; |
|
89 |
|
90 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 |
|
91 // When building with the 10.8 SDK or higher, focus rings don't draw as part |
|
92 // of -[NSCell drawWithFrame:inView:] and must be drawn by a separate call |
|
93 // to -[NSCell drawFocusRingMaskWithFrame:inView:]; . |
|
94 // See the NSButtonCell section under |
|
95 // https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_8Notes |
|
96 if ([aCell showsFirstResponder]) { |
|
97 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; |
|
98 CGContextSaveGState(cgContext); |
|
99 |
|
100 // It's important to set the focus ring style before we enter the |
|
101 // transparency layer so that the transparency layer only contains |
|
102 // the normal button mask without the focus ring, and the conversion |
|
103 // to the focus ring shape happens only when the transparency layer is |
|
104 // ended. |
|
105 NSSetFocusRingStyle(NSFocusRingOnly); |
|
106 |
|
107 // We need to draw the whole button into a transparency layer because |
|
108 // many button types are composed of multiple parts, and if these parts |
|
109 // were drawn while the focus ring style was active, each individual part |
|
110 // would produce a focus ring for itself. But we only want one focus ring |
|
111 // for the whole button. The transparency layer is a way to merge the |
|
112 // individual button parts together before the focus ring shape is |
|
113 // calculated. |
|
114 CGContextBeginTransparencyLayerWithRect(cgContext, NSRectToCGRect(aWithFrame), 0); |
|
115 [aCell drawFocusRingMaskWithFrame:aWithFrame inView:aInView]; |
|
116 CGContextEndTransparencyLayer(cgContext); |
|
117 |
|
118 CGContextRestoreGState(cgContext); |
|
119 } |
|
120 #endif |
|
121 } |
|
122 |
|
123 /** |
|
124 * NSProgressBarCell is used to draw progress bars of any size. |
|
125 */ |
|
126 @interface NSProgressBarCell : NSCell |
|
127 { |
|
128 /*All instance variables are private*/ |
|
129 double mValue; |
|
130 double mMax; |
|
131 bool mIsIndeterminate; |
|
132 bool mIsHorizontal; |
|
133 } |
|
134 |
|
135 - (void)setValue:(double)value; |
|
136 - (double)value; |
|
137 - (void)setMax:(double)max; |
|
138 - (double)max; |
|
139 - (void)setIndeterminate:(bool)aIndeterminate; |
|
140 - (bool)isIndeterminate; |
|
141 - (void)setHorizontal:(bool)aIsHorizontal; |
|
142 - (bool)isHorizontal; |
|
143 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView; |
|
144 @end |
|
145 |
|
146 @implementation NSProgressBarCell |
|
147 |
|
148 - (void)setMax:(double)aMax |
|
149 { |
|
150 mMax = aMax; |
|
151 } |
|
152 |
|
153 - (double)max |
|
154 { |
|
155 return mMax; |
|
156 } |
|
157 |
|
158 - (void)setValue:(double)aValue |
|
159 { |
|
160 mValue = aValue; |
|
161 } |
|
162 |
|
163 - (double)value |
|
164 { |
|
165 return mValue; |
|
166 } |
|
167 |
|
168 - (void)setIndeterminate:(bool)aIndeterminate |
|
169 { |
|
170 mIsIndeterminate = aIndeterminate; |
|
171 } |
|
172 |
|
173 - (bool)isIndeterminate |
|
174 { |
|
175 return mIsIndeterminate; |
|
176 } |
|
177 |
|
178 - (void)setHorizontal:(bool)aIsHorizontal |
|
179 { |
|
180 mIsHorizontal = aIsHorizontal; |
|
181 } |
|
182 |
|
183 - (bool)isHorizontal |
|
184 { |
|
185 return mIsHorizontal; |
|
186 } |
|
187 |
|
188 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView |
|
189 { |
|
190 CGContext* cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; |
|
191 |
|
192 HIThemeTrackDrawInfo tdi; |
|
193 |
|
194 tdi.version = 0; |
|
195 tdi.min = 0; |
|
196 |
|
197 tdi.value = INT32_MAX * (mValue / mMax); |
|
198 tdi.max = INT32_MAX; |
|
199 tdi.bounds = NSRectToCGRect(cellFrame); |
|
200 tdi.attributes = mIsHorizontal ? kThemeTrackHorizontal : 0; |
|
201 tdi.enableState = [self controlTint] == NSClearControlTint ? kThemeTrackInactive |
|
202 : kThemeTrackActive; |
|
203 |
|
204 NSControlSize size = [self controlSize]; |
|
205 if (size == NSRegularControlSize) { |
|
206 tdi.kind = mIsIndeterminate ? kThemeLargeIndeterminateBar |
|
207 : kThemeLargeProgressBar; |
|
208 } else { |
|
209 NS_ASSERTION(size == NSSmallControlSize, |
|
210 "We shouldn't have another size than small and regular for the moment"); |
|
211 tdi.kind = mIsIndeterminate ? kThemeMediumIndeterminateBar |
|
212 : kThemeMediumProgressBar; |
|
213 } |
|
214 |
|
215 int32_t stepsPerSecond = mIsIndeterminate ? 60 : 30; |
|
216 int32_t milliSecondsPerStep = 1000 / stepsPerSecond; |
|
217 tdi.trackInfo.progress.phase = uint8_t(PR_IntervalToMilliseconds(PR_IntervalNow()) / |
|
218 milliSecondsPerStep); |
|
219 |
|
220 HIThemeDrawTrack(&tdi, NULL, cgContext, kHIThemeOrientationNormal); |
|
221 } |
|
222 |
|
223 @end |
|
224 |
|
225 @interface ContextAwareSearchFieldCell : NSSearchFieldCell |
|
226 { |
|
227 nsIFrame* mContext; |
|
228 } |
|
229 |
|
230 // setContext: stores the searchfield nsIFrame so that it can be consulted |
|
231 // during painting. Please reset this by calling setContext:nullptr as soon as |
|
232 // you're done with painting because we don't want to keep a dangling pointer. |
|
233 - (void)setContext:(nsIFrame*)aContext; |
|
234 @end |
|
235 |
|
236 @implementation ContextAwareSearchFieldCell |
|
237 |
|
238 - (id)initTextCell:(NSString*)aString |
|
239 { |
|
240 if ((self = [super initTextCell:aString])) { |
|
241 mContext = nullptr; |
|
242 } |
|
243 return self; |
|
244 } |
|
245 |
|
246 - (void)setContext:(nsIFrame*)aContext |
|
247 { |
|
248 mContext = aContext; |
|
249 } |
|
250 |
|
251 static BOOL IsToolbarStyleContainer(nsIFrame* aFrame) |
|
252 { |
|
253 nsIContent* content = aFrame->GetContent(); |
|
254 if (!content) |
|
255 return NO; |
|
256 |
|
257 if (content->Tag() == nsGkAtoms::toolbar || |
|
258 content->Tag() == nsGkAtoms::toolbox || |
|
259 content->Tag() == nsGkAtoms::statusbar) |
|
260 return YES; |
|
261 |
|
262 switch (aFrame->StyleDisplay()->mAppearance) { |
|
263 case NS_THEME_TOOLBAR: |
|
264 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: |
|
265 case NS_THEME_STATUSBAR: |
|
266 return YES; |
|
267 default: |
|
268 return NO; |
|
269 } |
|
270 } |
|
271 |
|
272 - (BOOL)_isToolbarMode |
|
273 { |
|
274 // On 10.7, searchfields have two different styles, depending on whether |
|
275 // the searchfield is on top of of window chrome. This function is called on |
|
276 // 10.7 during drawing in order to determine which style to use. |
|
277 for (nsIFrame* frame = mContext; frame; frame = frame->GetParent()) { |
|
278 if (IsToolbarStyleContainer(frame)) { |
|
279 return YES; |
|
280 } |
|
281 } |
|
282 return NO; |
|
283 } |
|
284 |
|
285 @end |
|
286 |
|
287 // Workaround for Bug 542048 |
|
288 // On 64-bit, NSSearchFieldCells don't draw focus rings. |
|
289 #if defined(__x86_64__) |
|
290 |
|
291 static void DrawFocusRing(NSRect rect, float radius) |
|
292 { |
|
293 NSSetFocusRingStyle(NSFocusRingOnly); |
|
294 NSBezierPath* path = [NSBezierPath bezierPath]; |
|
295 rect = NSInsetRect(rect, radius, radius); |
|
296 [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0]; |
|
297 [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0]; |
|
298 [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle: 0.0 endAngle: 90.0]; |
|
299 [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle: 90.0 endAngle:180.0]; |
|
300 [path closePath]; |
|
301 [path fill]; |
|
302 } |
|
303 |
|
304 @interface SearchFieldCellWithFocusRing : ContextAwareSearchFieldCell {} @end |
|
305 |
|
306 @implementation SearchFieldCellWithFocusRing |
|
307 |
|
308 - (void)drawWithFrame:(NSRect)rect inView:(NSView*)controlView |
|
309 { |
|
310 [super drawWithFrame:rect inView:controlView]; |
|
311 if ([self showsFirstResponder]) { |
|
312 DrawFocusRing(rect, NSHeight(rect) / 2); |
|
313 } |
|
314 } |
|
315 |
|
316 @end |
|
317 |
|
318 #endif |
|
319 |
|
320 // Copied from nsLookAndFeel.h |
|
321 // Apple hasn't defined a constant for scollbars with two arrows on each end, so we'll use this one. |
|
322 static const int kThemeScrollBarArrowsBoth = 2; |
|
323 |
|
324 #define HITHEME_ORIENTATION kHIThemeOrientationNormal |
|
325 #define MAX_FOCUS_RING_WIDTH 4 |
|
326 |
|
327 // These enums are for indexing into the margin array. |
|
328 enum { |
|
329 leopardOS = 0 |
|
330 }; |
|
331 |
|
332 enum { |
|
333 miniControlSize, |
|
334 smallControlSize, |
|
335 regularControlSize |
|
336 }; |
|
337 |
|
338 enum { |
|
339 leftMargin, |
|
340 topMargin, |
|
341 rightMargin, |
|
342 bottomMargin |
|
343 }; |
|
344 |
|
345 static int EnumSizeForCocoaSize(NSControlSize cocoaControlSize) { |
|
346 if (cocoaControlSize == NSMiniControlSize) |
|
347 return miniControlSize; |
|
348 else if (cocoaControlSize == NSSmallControlSize) |
|
349 return smallControlSize; |
|
350 else |
|
351 return regularControlSize; |
|
352 } |
|
353 |
|
354 static NSControlSize CocoaSizeForEnum(int32_t enumControlSize) { |
|
355 if (enumControlSize == miniControlSize) |
|
356 return NSMiniControlSize; |
|
357 else if (enumControlSize == smallControlSize) |
|
358 return NSSmallControlSize; |
|
359 else |
|
360 return NSRegularControlSize; |
|
361 } |
|
362 |
|
363 static NSString* CUIControlSizeForCocoaSize(NSControlSize aControlSize) |
|
364 { |
|
365 if (aControlSize == NSRegularControlSize) |
|
366 return @"regular"; |
|
367 else if (aControlSize == NSSmallControlSize) |
|
368 return @"small"; |
|
369 else |
|
370 return @"mini"; |
|
371 } |
|
372 |
|
373 static void InflateControlRect(NSRect* rect, NSControlSize cocoaControlSize, const float marginSet[][3][4]) |
|
374 { |
|
375 if (!marginSet) |
|
376 return; |
|
377 |
|
378 static int osIndex = leopardOS; |
|
379 int controlSize = EnumSizeForCocoaSize(cocoaControlSize); |
|
380 const float* buttonMargins = marginSet[osIndex][controlSize]; |
|
381 rect->origin.x -= buttonMargins[leftMargin]; |
|
382 rect->origin.y -= buttonMargins[bottomMargin]; |
|
383 rect->size.width += buttonMargins[leftMargin] + buttonMargins[rightMargin]; |
|
384 rect->size.height += buttonMargins[bottomMargin] + buttonMargins[topMargin]; |
|
385 } |
|
386 |
|
387 static NSWindow* NativeWindowForFrame(nsIFrame* aFrame, |
|
388 nsIWidget** aTopLevelWidget = NULL) |
|
389 { |
|
390 if (!aFrame) |
|
391 return nil; |
|
392 |
|
393 nsIWidget* widget = aFrame->GetNearestWidget(); |
|
394 if (!widget) |
|
395 return nil; |
|
396 |
|
397 nsIWidget* topLevelWidget = widget->GetTopLevelWidget(); |
|
398 if (aTopLevelWidget) |
|
399 *aTopLevelWidget = topLevelWidget; |
|
400 |
|
401 return (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW); |
|
402 } |
|
403 |
|
404 static NSSize |
|
405 WindowButtonsSize(nsIFrame* aFrame) |
|
406 { |
|
407 NSWindow* window = NativeWindowForFrame(aFrame); |
|
408 if (!window) { |
|
409 // Return fallback values. |
|
410 if (!nsCocoaFeatures::OnLionOrLater()) |
|
411 return NSMakeSize(57, 16); |
|
412 return NSMakeSize(54, 16); |
|
413 } |
|
414 |
|
415 NSRect buttonBox = NSZeroRect; |
|
416 NSButton* closeButton = [window standardWindowButton:NSWindowCloseButton]; |
|
417 if (closeButton) { |
|
418 buttonBox = NSUnionRect(buttonBox, [closeButton frame]); |
|
419 } |
|
420 NSButton* minimizeButton = [window standardWindowButton:NSWindowMiniaturizeButton]; |
|
421 if (minimizeButton) { |
|
422 buttonBox = NSUnionRect(buttonBox, [minimizeButton frame]); |
|
423 } |
|
424 NSButton* zoomButton = [window standardWindowButton:NSWindowZoomButton]; |
|
425 if (zoomButton) { |
|
426 buttonBox = NSUnionRect(buttonBox, [zoomButton frame]); |
|
427 } |
|
428 return buttonBox.size; |
|
429 } |
|
430 |
|
431 static BOOL FrameIsInActiveWindow(nsIFrame* aFrame) |
|
432 { |
|
433 nsIWidget* topLevelWidget = NULL; |
|
434 NSWindow* win = NativeWindowForFrame(aFrame, &topLevelWidget); |
|
435 if (!topLevelWidget || !win) |
|
436 return YES; |
|
437 |
|
438 // XUL popups, e.g. the toolbar customization popup, can't become key windows, |
|
439 // but controls in these windows should still get the active look. |
|
440 if (topLevelWidget->WindowType() == eWindowType_popup) |
|
441 return YES; |
|
442 if ([win isSheet]) |
|
443 return [win isKeyWindow]; |
|
444 return [win isMainWindow] && ![win attachedSheet]; |
|
445 } |
|
446 |
|
447 // Toolbar controls and content controls respond to different window |
|
448 // activeness states. |
|
449 static BOOL IsActive(nsIFrame* aFrame, BOOL aIsToolbarControl) |
|
450 { |
|
451 if (aIsToolbarControl) |
|
452 return [NativeWindowForFrame(aFrame) isMainWindow]; |
|
453 return FrameIsInActiveWindow(aFrame); |
|
454 } |
|
455 |
|
456 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeCocoa, nsNativeTheme, nsITheme) |
|
457 |
|
458 |
|
459 nsNativeThemeCocoa::nsNativeThemeCocoa() |
|
460 { |
|
461 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
462 |
|
463 // provide a local autorelease pool, as this is called during startup |
|
464 // before the main event-loop pool is in place |
|
465 nsAutoreleasePool pool; |
|
466 |
|
467 mHelpButtonCell = [[NSButtonCell alloc] initTextCell:nil]; |
|
468 [mHelpButtonCell setBezelStyle:NSHelpButtonBezelStyle]; |
|
469 [mHelpButtonCell setButtonType:NSMomentaryPushInButton]; |
|
470 [mHelpButtonCell setHighlightsBy:NSPushInCellMask]; |
|
471 |
|
472 mPushButtonCell = [[NSButtonCell alloc] initTextCell:nil]; |
|
473 [mPushButtonCell setButtonType:NSMomentaryPushInButton]; |
|
474 [mPushButtonCell setHighlightsBy:NSPushInCellMask]; |
|
475 |
|
476 mRadioButtonCell = [[NSButtonCell alloc] initTextCell:nil]; |
|
477 [mRadioButtonCell setButtonType:NSRadioButton]; |
|
478 |
|
479 mCheckboxCell = [[NSButtonCell alloc] initTextCell:nil]; |
|
480 [mCheckboxCell setButtonType:NSSwitchButton]; |
|
481 [mCheckboxCell setAllowsMixedState:YES]; |
|
482 |
|
483 #if defined(__x86_64__) |
|
484 mSearchFieldCell = [[SearchFieldCellWithFocusRing alloc] initTextCell:@""]; |
|
485 #else |
|
486 mSearchFieldCell = [[ContextAwareSearchFieldCell alloc] initTextCell:@""]; |
|
487 #endif |
|
488 [mSearchFieldCell setBezelStyle:NSTextFieldRoundedBezel]; |
|
489 [mSearchFieldCell setBezeled:YES]; |
|
490 [mSearchFieldCell setEditable:YES]; |
|
491 [mSearchFieldCell setFocusRingType:NSFocusRingTypeExterior]; |
|
492 |
|
493 mDropdownCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]; |
|
494 |
|
495 mComboBoxCell = [[NSComboBoxCell alloc] initTextCell:@""]; |
|
496 [mComboBoxCell setBezeled:YES]; |
|
497 [mComboBoxCell setEditable:YES]; |
|
498 [mComboBoxCell setFocusRingType:NSFocusRingTypeExterior]; |
|
499 |
|
500 mProgressBarCell = [[NSProgressBarCell alloc] init]; |
|
501 |
|
502 mMeterBarCell = [[NSLevelIndicatorCell alloc] |
|
503 initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]; |
|
504 |
|
505 mCellDrawView = [[CellDrawView alloc] init]; |
|
506 |
|
507 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
508 } |
|
509 |
|
510 nsNativeThemeCocoa::~nsNativeThemeCocoa() |
|
511 { |
|
512 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
513 |
|
514 [mMeterBarCell release]; |
|
515 [mProgressBarCell release]; |
|
516 [mHelpButtonCell release]; |
|
517 [mPushButtonCell release]; |
|
518 [mRadioButtonCell release]; |
|
519 [mCheckboxCell release]; |
|
520 [mSearchFieldCell release]; |
|
521 [mDropdownCell release]; |
|
522 [mComboBoxCell release]; |
|
523 [mCellDrawView release]; |
|
524 |
|
525 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
526 } |
|
527 |
|
528 // Limit on the area of the target rect (in pixels^2) in |
|
529 // DrawCellWithScaling(), DrawButton() and DrawScrollbar(), above which we |
|
530 // don't draw the object into a bitmap buffer. This is to avoid crashes in |
|
531 // [NSGraphicsContext graphicsContextWithGraphicsPort:flipped:] and |
|
532 // CGContextDrawImage(), and also to avoid very poor drawing performance in |
|
533 // CGContextDrawImage() when it scales the bitmap (particularly if xscale or |
|
534 // yscale is less than but near 1 -- e.g. 0.9). This value was determined |
|
535 // by trial and error, on OS X 10.4.11 and 10.5.4, and on systems with |
|
536 // different amounts of RAM. |
|
537 #define BITMAP_MAX_AREA 500000 |
|
538 |
|
539 static int |
|
540 GetBackingScaleFactorForRendering(CGContextRef cgContext) |
|
541 { |
|
542 CGAffineTransform ctm = CGContextGetUserSpaceToDeviceSpaceTransform(cgContext); |
|
543 CGRect transformedUserSpacePixel = CGRectApplyAffineTransform(CGRectMake(0, 0, 1, 1), ctm); |
|
544 float maxScale = std::max(fabs(transformedUserSpacePixel.size.width), |
|
545 fabs(transformedUserSpacePixel.size.height)); |
|
546 return maxScale > 1.0 ? 2 : 1; |
|
547 } |
|
548 |
|
549 /* |
|
550 * Draw the given NSCell into the given cgContext. |
|
551 * |
|
552 * destRect - the size and position of the resulting control rectangle |
|
553 * controlSize - the NSControlSize which will be given to the NSCell before |
|
554 * asking it to render |
|
555 * naturalSize - The natural dimensions of this control. |
|
556 * If the control rect size is not equal to either of these, a scale |
|
557 * will be applied to the context so that rendering the control at the |
|
558 * natural size will result in it filling the destRect space. |
|
559 * If a control has no natural dimensions in either/both axes, pass 0.0f. |
|
560 * minimumSize - The minimum dimensions of this control. |
|
561 * If the control rect size is less than the minimum for a given axis, |
|
562 * a scale will be applied to the context so that the minimum is used |
|
563 * for drawing. If a control has no minimum dimensions in either/both |
|
564 * axes, pass 0.0f. |
|
565 * marginSet - an array of margins; a multidimensional array of [2][3][4], |
|
566 * with the first dimension being the OS version (Tiger or Leopard), |
|
567 * the second being the control size (mini, small, regular), and the third |
|
568 * being the 4 margin values (left, top, right, bottom). |
|
569 * view - The NSView that we're drawing into. As far as I can tell, it doesn't |
|
570 * matter if this is really the right view; it just has to return YES when |
|
571 * asked for isFlipped. Otherwise we'll get drawing bugs on 10.4. |
|
572 * mirrorHorizontal - whether to mirror the cell horizontally |
|
573 */ |
|
574 static void DrawCellWithScaling(NSCell *cell, |
|
575 CGContextRef cgContext, |
|
576 const HIRect& destRect, |
|
577 NSControlSize controlSize, |
|
578 NSSize naturalSize, |
|
579 NSSize minimumSize, |
|
580 const float marginSet[][3][4], |
|
581 NSView* view, |
|
582 BOOL mirrorHorizontal) |
|
583 { |
|
584 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
585 |
|
586 NSRect drawRect = NSMakeRect(destRect.origin.x, destRect.origin.y, destRect.size.width, destRect.size.height); |
|
587 |
|
588 if (naturalSize.width != 0.0f) |
|
589 drawRect.size.width = naturalSize.width; |
|
590 if (naturalSize.height != 0.0f) |
|
591 drawRect.size.height = naturalSize.height; |
|
592 |
|
593 // Keep aspect ratio when scaling if one dimension is free. |
|
594 if (naturalSize.width == 0.0f && naturalSize.height != 0.0f) |
|
595 drawRect.size.width = destRect.size.width * naturalSize.height / destRect.size.height; |
|
596 if (naturalSize.height == 0.0f && naturalSize.width != 0.0f) |
|
597 drawRect.size.height = destRect.size.height * naturalSize.width / destRect.size.width; |
|
598 |
|
599 // Honor minimum sizes. |
|
600 if (drawRect.size.width < minimumSize.width) |
|
601 drawRect.size.width = minimumSize.width; |
|
602 if (drawRect.size.height < minimumSize.height) |
|
603 drawRect.size.height = minimumSize.height; |
|
604 |
|
605 [NSGraphicsContext saveGraphicsState]; |
|
606 |
|
607 // Only skip the buffer if the area of our cell (in pixels^2) is too large. |
|
608 if (drawRect.size.width * drawRect.size.height > BITMAP_MAX_AREA) { |
|
609 // Inflate the rect Gecko gave us by the margin for the control. |
|
610 InflateControlRect(&drawRect, controlSize, marginSet); |
|
611 |
|
612 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; |
|
613 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]]; |
|
614 |
|
615 DrawCellIncludingFocusRing(cell, drawRect, view); |
|
616 |
|
617 [NSGraphicsContext setCurrentContext:savedContext]; |
|
618 } |
|
619 else { |
|
620 float w = ceil(drawRect.size.width); |
|
621 float h = ceil(drawRect.size.height); |
|
622 NSRect tmpRect = NSMakeRect(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, w, h); |
|
623 |
|
624 // inflate to figure out the frame we need to tell NSCell to draw in, to get something that's 0,0,w,h |
|
625 InflateControlRect(&tmpRect, controlSize, marginSet); |
|
626 |
|
627 // and then, expand by MAX_FOCUS_RING_WIDTH size to make sure we can capture any focus ring |
|
628 w += MAX_FOCUS_RING_WIDTH * 2.0; |
|
629 h += MAX_FOCUS_RING_WIDTH * 2.0; |
|
630 |
|
631 int backingScaleFactor = GetBackingScaleFactorForRendering(cgContext); |
|
632 CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); |
|
633 CGContextRef ctx = CGBitmapContextCreate(NULL, |
|
634 (int) w * backingScaleFactor, (int) h * backingScaleFactor, |
|
635 8, (int) w * backingScaleFactor * 4, |
|
636 rgb, kCGImageAlphaPremultipliedFirst); |
|
637 CGColorSpaceRelease(rgb); |
|
638 |
|
639 // We need to flip the image twice in order to avoid drawing bugs on 10.4, see bug 465069. |
|
640 // This is the first flip transform, applied to cgContext. |
|
641 CGContextScaleCTM(cgContext, 1.0f, -1.0f); |
|
642 CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height)); |
|
643 if (mirrorHorizontal) { |
|
644 CGContextScaleCTM(cgContext, -1.0f, 1.0f); |
|
645 CGContextTranslateCTM(cgContext, -(2.0 * destRect.origin.x + destRect.size.width), 0.0f); |
|
646 } |
|
647 |
|
648 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; |
|
649 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]]; |
|
650 |
|
651 CGContextScaleCTM(ctx, backingScaleFactor, backingScaleFactor); |
|
652 |
|
653 // This is the second flip transform, applied to ctx. |
|
654 CGContextScaleCTM(ctx, 1.0f, -1.0f); |
|
655 CGContextTranslateCTM(ctx, 0.0f, -(2.0 * tmpRect.origin.y + tmpRect.size.height)); |
|
656 |
|
657 DrawCellIncludingFocusRing(cell, tmpRect, view); |
|
658 |
|
659 [NSGraphicsContext setCurrentContext:savedContext]; |
|
660 |
|
661 CGImageRef img = CGBitmapContextCreateImage(ctx); |
|
662 |
|
663 // Drop the image into the original destination rectangle, scaling to fit |
|
664 // Only scale MAX_FOCUS_RING_WIDTH by xscale/yscale when the resulting rect |
|
665 // doesn't extend beyond the overflow rect |
|
666 float xscale = destRect.size.width / drawRect.size.width; |
|
667 float yscale = destRect.size.height / drawRect.size.height; |
|
668 float scaledFocusRingX = xscale < 1.0f ? MAX_FOCUS_RING_WIDTH * xscale : MAX_FOCUS_RING_WIDTH; |
|
669 float scaledFocusRingY = yscale < 1.0f ? MAX_FOCUS_RING_WIDTH * yscale : MAX_FOCUS_RING_WIDTH; |
|
670 CGContextDrawImage(cgContext, CGRectMake(destRect.origin.x - scaledFocusRingX, |
|
671 destRect.origin.y - scaledFocusRingY, |
|
672 destRect.size.width + scaledFocusRingX * 2, |
|
673 destRect.size.height + scaledFocusRingY * 2), |
|
674 img); |
|
675 |
|
676 CGImageRelease(img); |
|
677 CGContextRelease(ctx); |
|
678 } |
|
679 |
|
680 [NSGraphicsContext restoreGraphicsState]; |
|
681 |
|
682 #if DRAW_IN_FRAME_DEBUG |
|
683 CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); |
|
684 CGContextFillRect(cgContext, destRect); |
|
685 #endif |
|
686 |
|
687 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
688 } |
|
689 |
|
690 struct CellRenderSettings { |
|
691 // The natural dimensions of the control. |
|
692 // If a control has no natural dimensions in either/both axes, set to 0.0f. |
|
693 NSSize naturalSizes[3]; |
|
694 |
|
695 // The minimum dimensions of the control. |
|
696 // If a control has no minimum dimensions in either/both axes, set to 0.0f. |
|
697 NSSize minimumSizes[3]; |
|
698 |
|
699 // A three-dimensional array, |
|
700 // with the first dimension being the OS version (only Leopard for the moment), |
|
701 // the second being the control size (mini, small, regular), and the third |
|
702 // being the 4 margin values (left, top, right, bottom). |
|
703 float margins[1][3][4]; |
|
704 }; |
|
705 |
|
706 /* |
|
707 * This is a helper method that returns the required NSControlSize given a size |
|
708 * and the size of the three controls plus a tolerance. |
|
709 * size - The width or the height of the element to draw. |
|
710 * sizes - An array with the all the width/height of the element for its |
|
711 * different sizes. |
|
712 * tolerance - The tolerance as passed to DrawCellWithSnapping. |
|
713 * NOTE: returns NSRegularControlSize if all values in 'sizes' are zero. |
|
714 */ |
|
715 static NSControlSize FindControlSize(CGFloat size, const CGFloat* sizes, CGFloat tolerance) |
|
716 { |
|
717 for (uint32_t i = miniControlSize; i <= regularControlSize; ++i) { |
|
718 if (sizes[i] == 0) { |
|
719 continue; |
|
720 } |
|
721 |
|
722 CGFloat next = 0; |
|
723 // Find next value. |
|
724 for (uint32_t j = i+1; j <= regularControlSize; ++j) { |
|
725 if (sizes[j] != 0) { |
|
726 next = sizes[j]; |
|
727 break; |
|
728 } |
|
729 } |
|
730 |
|
731 // If it's the latest value, we pick it. |
|
732 if (next == 0) { |
|
733 return CocoaSizeForEnum(i); |
|
734 } |
|
735 |
|
736 if (size <= sizes[i] + tolerance && size < next) { |
|
737 return CocoaSizeForEnum(i); |
|
738 } |
|
739 } |
|
740 |
|
741 // If we are here, that means sizes[] was an array with only empty values |
|
742 // or the algorithm above is wrong. |
|
743 // The former can happen but the later would be wrong. |
|
744 NS_ASSERTION(sizes[0] == 0 && sizes[1] == 0 && sizes[2] == 0, |
|
745 "We found no control! We shouldn't be there!"); |
|
746 return CocoaSizeForEnum(regularControlSize); |
|
747 } |
|
748 |
|
749 /* |
|
750 * Draw the given NSCell into the given cgContext with a nice control size. |
|
751 * |
|
752 * This function is similar to DrawCellWithScaling, but it decides what |
|
753 * control size to use based on the destRect's size. |
|
754 * Scaling is only applied when the difference between the destRect's size |
|
755 * and the next smaller natural size is greater than snapTolerance. Otherwise |
|
756 * it snaps to the next smaller control size without scaling because unscaled |
|
757 * controls look nicer. |
|
758 */ |
|
759 static void DrawCellWithSnapping(NSCell *cell, |
|
760 CGContextRef cgContext, |
|
761 const HIRect& destRect, |
|
762 const CellRenderSettings settings, |
|
763 float verticalAlignFactor, |
|
764 NSView* view, |
|
765 BOOL mirrorHorizontal, |
|
766 float snapTolerance = 2.0f) |
|
767 { |
|
768 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
769 |
|
770 const float rectWidth = destRect.size.width, rectHeight = destRect.size.height; |
|
771 const NSSize *sizes = settings.naturalSizes; |
|
772 const NSSize miniSize = sizes[EnumSizeForCocoaSize(NSMiniControlSize)]; |
|
773 const NSSize smallSize = sizes[EnumSizeForCocoaSize(NSSmallControlSize)]; |
|
774 const NSSize regularSize = sizes[EnumSizeForCocoaSize(NSRegularControlSize)]; |
|
775 |
|
776 HIRect drawRect = destRect; |
|
777 |
|
778 CGFloat controlWidths[3] = { miniSize.width, smallSize.width, regularSize.width }; |
|
779 NSControlSize controlSizeX = FindControlSize(rectWidth, controlWidths, snapTolerance); |
|
780 CGFloat controlHeights[3] = { miniSize.height, smallSize.height, regularSize.height }; |
|
781 NSControlSize controlSizeY = FindControlSize(rectHeight, controlHeights, snapTolerance); |
|
782 |
|
783 NSControlSize controlSize = NSRegularControlSize; |
|
784 int sizeIndex = 0; |
|
785 |
|
786 // At some sizes, don't scale but snap. |
|
787 const NSControlSize smallerControlSize = |
|
788 EnumSizeForCocoaSize(controlSizeX) < EnumSizeForCocoaSize(controlSizeY) ? |
|
789 controlSizeX : controlSizeY; |
|
790 const int smallerControlSizeIndex = EnumSizeForCocoaSize(smallerControlSize); |
|
791 const NSSize size = sizes[smallerControlSizeIndex]; |
|
792 float diffWidth = size.width ? rectWidth - size.width : 0.0f; |
|
793 float diffHeight = size.height ? rectHeight - size.height : 0.0f; |
|
794 if (diffWidth >= 0.0f && diffHeight >= 0.0f && |
|
795 diffWidth <= snapTolerance && diffHeight <= snapTolerance) { |
|
796 // Snap to the smaller control size. |
|
797 controlSize = smallerControlSize; |
|
798 sizeIndex = smallerControlSizeIndex; |
|
799 // Resize and center the drawRect. |
|
800 if (sizes[sizeIndex].width) { |
|
801 drawRect.origin.x += ceil((destRect.size.width - sizes[sizeIndex].width) / 2); |
|
802 drawRect.size.width = sizes[sizeIndex].width; |
|
803 } |
|
804 if (sizes[sizeIndex].height) { |
|
805 drawRect.origin.y += floor((destRect.size.height - sizes[sizeIndex].height) * verticalAlignFactor); |
|
806 drawRect.size.height = sizes[sizeIndex].height; |
|
807 } |
|
808 } else { |
|
809 // Use the larger control size. |
|
810 controlSize = EnumSizeForCocoaSize(controlSizeX) > EnumSizeForCocoaSize(controlSizeY) ? |
|
811 controlSizeX : controlSizeY; |
|
812 sizeIndex = EnumSizeForCocoaSize(controlSize); |
|
813 } |
|
814 |
|
815 [cell setControlSize:controlSize]; |
|
816 |
|
817 NSSize minimumSize = settings.minimumSizes ? settings.minimumSizes[sizeIndex] : NSZeroSize; |
|
818 DrawCellWithScaling(cell, cgContext, drawRect, controlSize, sizes[sizeIndex], |
|
819 minimumSize, settings.margins, view, mirrorHorizontal); |
|
820 |
|
821 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
822 } |
|
823 |
|
824 static float VerticalAlignFactor(nsIFrame *aFrame) |
|
825 { |
|
826 if (!aFrame) |
|
827 return 0.5f; // default: center |
|
828 |
|
829 const nsStyleCoord& va = aFrame->StyleTextReset()->mVerticalAlign; |
|
830 uint8_t intval = (va.GetUnit() == eStyleUnit_Enumerated) |
|
831 ? va.GetIntValue() |
|
832 : NS_STYLE_VERTICAL_ALIGN_MIDDLE; |
|
833 switch (intval) { |
|
834 case NS_STYLE_VERTICAL_ALIGN_TOP: |
|
835 case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP: |
|
836 return 0.0f; |
|
837 |
|
838 case NS_STYLE_VERTICAL_ALIGN_SUB: |
|
839 case NS_STYLE_VERTICAL_ALIGN_SUPER: |
|
840 case NS_STYLE_VERTICAL_ALIGN_MIDDLE: |
|
841 case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE: |
|
842 return 0.5f; |
|
843 |
|
844 case NS_STYLE_VERTICAL_ALIGN_BASELINE: |
|
845 case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM: |
|
846 case NS_STYLE_VERTICAL_ALIGN_BOTTOM: |
|
847 return 1.0f; |
|
848 |
|
849 default: |
|
850 NS_NOTREACHED("invalid vertical-align"); |
|
851 return 0.5f; |
|
852 } |
|
853 } |
|
854 |
|
855 // These are the sizes that Gecko needs to request to draw if it wants |
|
856 // to get a standard-sized Aqua radio button drawn. Note that the rects |
|
857 // that draw these are actually a little bigger. |
|
858 static const CellRenderSettings radioSettings = { |
|
859 { |
|
860 NSMakeSize(11, 11), // mini |
|
861 NSMakeSize(13, 13), // small |
|
862 NSMakeSize(16, 16) // regular |
|
863 }, |
|
864 { |
|
865 NSZeroSize, NSZeroSize, NSZeroSize |
|
866 }, |
|
867 { |
|
868 { // Leopard |
|
869 {0, 0, 0, 0}, // mini |
|
870 {0, 1, 1, 1}, // small |
|
871 {0, 0, 0, 0} // regular |
|
872 } |
|
873 } |
|
874 }; |
|
875 |
|
876 static const CellRenderSettings checkboxSettings = { |
|
877 { |
|
878 NSMakeSize(11, 11), // mini |
|
879 NSMakeSize(13, 13), // small |
|
880 NSMakeSize(16, 16) // regular |
|
881 }, |
|
882 { |
|
883 NSZeroSize, NSZeroSize, NSZeroSize |
|
884 }, |
|
885 { |
|
886 { // Leopard |
|
887 {0, 1, 0, 0}, // mini |
|
888 {0, 1, 0, 1}, // small |
|
889 {0, 1, 0, 1} // regular |
|
890 } |
|
891 } |
|
892 }; |
|
893 |
|
894 void |
|
895 nsNativeThemeCocoa::DrawCheckboxOrRadio(CGContextRef cgContext, bool inCheckbox, |
|
896 const HIRect& inBoxRect, bool inSelected, |
|
897 EventStates inState, nsIFrame* aFrame) |
|
898 { |
|
899 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
900 |
|
901 NSButtonCell *cell = inCheckbox ? mCheckboxCell : mRadioButtonCell; |
|
902 NSCellStateValue state = inSelected ? NSOnState : NSOffState; |
|
903 |
|
904 // Check if we have an indeterminate checkbox |
|
905 if (inCheckbox && GetIndeterminate(aFrame)) |
|
906 state = NSMixedState; |
|
907 |
|
908 [cell setEnabled:!IsDisabled(aFrame, inState)]; |
|
909 [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS)]; |
|
910 [cell setState:state]; |
|
911 [cell setHighlighted:inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)]; |
|
912 [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)]; |
|
913 |
|
914 // Ensure that the control is square. |
|
915 float length = std::min(inBoxRect.size.width, inBoxRect.size.height); |
|
916 HIRect drawRect = CGRectMake(inBoxRect.origin.x + (int)((inBoxRect.size.width - length) / 2.0f), |
|
917 inBoxRect.origin.y + (int)((inBoxRect.size.height - length) / 2.0f), |
|
918 length, length); |
|
919 |
|
920 DrawCellWithSnapping(cell, cgContext, drawRect, |
|
921 inCheckbox ? checkboxSettings : radioSettings, |
|
922 VerticalAlignFactor(aFrame), mCellDrawView, NO); |
|
923 |
|
924 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
925 } |
|
926 |
|
927 static const CellRenderSettings searchFieldSettings = { |
|
928 { |
|
929 NSMakeSize(0, 16), // mini |
|
930 NSMakeSize(0, 19), // small |
|
931 NSMakeSize(0, 22) // regular |
|
932 }, |
|
933 { |
|
934 NSMakeSize(32, 0), // mini |
|
935 NSMakeSize(38, 0), // small |
|
936 NSMakeSize(44, 0) // regular |
|
937 }, |
|
938 { |
|
939 { // Leopard |
|
940 {0, 0, 0, 0}, // mini |
|
941 {0, 0, 0, 0}, // small |
|
942 {0, 0, 0, 0} // regular |
|
943 } |
|
944 } |
|
945 }; |
|
946 |
|
947 void |
|
948 nsNativeThemeCocoa::DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect, |
|
949 nsIFrame* aFrame, EventStates inState) |
|
950 { |
|
951 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
952 |
|
953 ContextAwareSearchFieldCell* cell = mSearchFieldCell; |
|
954 [cell setContext:aFrame]; |
|
955 [cell setEnabled:!IsDisabled(aFrame, inState)]; |
|
956 // NOTE: this could probably use inState |
|
957 [cell setShowsFirstResponder:IsFocused(aFrame)]; |
|
958 |
|
959 DrawCellWithSnapping(cell, cgContext, inBoxRect, searchFieldSettings, |
|
960 VerticalAlignFactor(aFrame), mCellDrawView, |
|
961 IsFrameRTL(aFrame)); |
|
962 |
|
963 [cell setContext:nullptr]; |
|
964 |
|
965 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
966 } |
|
967 |
|
968 static const NSSize kHelpButtonSize = NSMakeSize(20, 20); |
|
969 |
|
970 static const CellRenderSettings pushButtonSettings = { |
|
971 { |
|
972 NSMakeSize(0, 16), // mini |
|
973 NSMakeSize(0, 19), // small |
|
974 NSMakeSize(0, 22) // regular |
|
975 }, |
|
976 { |
|
977 NSMakeSize(18, 0), // mini |
|
978 NSMakeSize(26, 0), // small |
|
979 NSMakeSize(30, 0) // regular |
|
980 }, |
|
981 { |
|
982 { // Leopard |
|
983 {0, 0, 0, 0}, // mini |
|
984 {4, 0, 4, 1}, // small |
|
985 {5, 0, 5, 2} // regular |
|
986 } |
|
987 } |
|
988 }; |
|
989 |
|
990 // The height at which we start doing square buttons instead of rounded buttons |
|
991 // Rounded buttons look bad if drawn at a height greater than 26, so at that point |
|
992 // we switch over to doing square buttons which looks fine at any size. |
|
993 #define DO_SQUARE_BUTTON_HEIGHT 26 |
|
994 |
|
995 void |
|
996 nsNativeThemeCocoa::DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRect, |
|
997 EventStates inState, uint8_t aWidgetType, |
|
998 nsIFrame* aFrame) |
|
999 { |
|
1000 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1001 |
|
1002 BOOL isActive = FrameIsInActiveWindow(aFrame); |
|
1003 BOOL isDisabled = IsDisabled(aFrame, inState); |
|
1004 |
|
1005 NSButtonCell* cell = (aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON) ? mHelpButtonCell : mPushButtonCell; |
|
1006 [cell setEnabled:!isDisabled]; |
|
1007 [cell setHighlighted:isActive && |
|
1008 inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)]; |
|
1009 [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS) && !isDisabled && isActive]; |
|
1010 |
|
1011 if (aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON) { |
|
1012 DrawCellWithScaling(cell, cgContext, inBoxRect, NSRegularControlSize, |
|
1013 NSZeroSize, kHelpButtonSize, NULL, mCellDrawView, |
|
1014 false); // Don't mirror icon in RTL. |
|
1015 } else { |
|
1016 // If the button is tall enough, draw the square button style so that |
|
1017 // buttons with non-standard content look good. Otherwise draw normal |
|
1018 // rounded aqua buttons. |
|
1019 if (inBoxRect.size.height > DO_SQUARE_BUTTON_HEIGHT) { |
|
1020 [cell setBezelStyle:NSShadowlessSquareBezelStyle]; |
|
1021 DrawCellWithScaling(cell, cgContext, inBoxRect, NSRegularControlSize, |
|
1022 NSZeroSize, NSMakeSize(14, 0), NULL, mCellDrawView, |
|
1023 IsFrameRTL(aFrame)); |
|
1024 } else { |
|
1025 [cell setBezelStyle:NSRoundedBezelStyle]; |
|
1026 DrawCellWithSnapping(cell, cgContext, inBoxRect, pushButtonSettings, 0.5f, |
|
1027 mCellDrawView, IsFrameRTL(aFrame), 1.0f); |
|
1028 } |
|
1029 } |
|
1030 |
|
1031 #if DRAW_IN_FRAME_DEBUG |
|
1032 CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); |
|
1033 CGContextFillRect(cgContext, inBoxRect); |
|
1034 #endif |
|
1035 |
|
1036 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1037 } |
|
1038 |
|
1039 typedef void (*RenderHIThemeControlFunction)(CGContextRef cgContext, const HIRect& aRenderRect, void* aData); |
|
1040 |
|
1041 static void |
|
1042 RenderTransformedHIThemeControl(CGContextRef aCGContext, const HIRect& aRect, |
|
1043 RenderHIThemeControlFunction aFunc, void* aData, |
|
1044 BOOL mirrorHorizontally = NO) |
|
1045 { |
|
1046 CGAffineTransform savedCTM = CGContextGetCTM(aCGContext); |
|
1047 CGContextTranslateCTM(aCGContext, aRect.origin.x, aRect.origin.y); |
|
1048 |
|
1049 bool drawDirect; |
|
1050 HIRect drawRect = aRect; |
|
1051 drawRect.origin = CGPointZero; |
|
1052 |
|
1053 if (!mirrorHorizontally && savedCTM.a == 1.0f && savedCTM.b == 0.0f && |
|
1054 savedCTM.c == 0.0f && (savedCTM.d == 1.0f || savedCTM.d == -1.0f)) { |
|
1055 drawDirect = TRUE; |
|
1056 } else { |
|
1057 drawDirect = FALSE; |
|
1058 } |
|
1059 |
|
1060 // Fall back to no bitmap buffer if the area of our control (in pixels^2) |
|
1061 // is too large. |
|
1062 if (drawDirect || (aRect.size.width * aRect.size.height > BITMAP_MAX_AREA)) { |
|
1063 aFunc(aCGContext, drawRect, aData); |
|
1064 } else { |
|
1065 // Inflate the buffer to capture focus rings. |
|
1066 int w = ceil(drawRect.size.width) + 2 * MAX_FOCUS_RING_WIDTH; |
|
1067 int h = ceil(drawRect.size.height) + 2 * MAX_FOCUS_RING_WIDTH; |
|
1068 |
|
1069 int backingScaleFactor = GetBackingScaleFactorForRendering(aCGContext); |
|
1070 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); |
|
1071 CGContextRef bitmapctx = CGBitmapContextCreate(NULL, |
|
1072 w * backingScaleFactor, |
|
1073 h * backingScaleFactor, |
|
1074 8, |
|
1075 w * backingScaleFactor * 4, |
|
1076 colorSpace, |
|
1077 kCGImageAlphaPremultipliedFirst); |
|
1078 CGColorSpaceRelease(colorSpace); |
|
1079 |
|
1080 CGContextScaleCTM(bitmapctx, backingScaleFactor, backingScaleFactor); |
|
1081 CGContextTranslateCTM(bitmapctx, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH); |
|
1082 |
|
1083 // HITheme always wants to draw into a flipped context, or things |
|
1084 // get confused. |
|
1085 CGContextTranslateCTM(bitmapctx, 0.0f, aRect.size.height); |
|
1086 CGContextScaleCTM(bitmapctx, 1.0f, -1.0f); |
|
1087 |
|
1088 aFunc(bitmapctx, drawRect, aData); |
|
1089 |
|
1090 CGImageRef bitmap = CGBitmapContextCreateImage(bitmapctx); |
|
1091 |
|
1092 CGAffineTransform ctm = CGContextGetCTM(aCGContext); |
|
1093 |
|
1094 // We need to unflip, so that we can do a DrawImage without getting a flipped image. |
|
1095 CGContextTranslateCTM(aCGContext, 0.0f, aRect.size.height); |
|
1096 CGContextScaleCTM(aCGContext, 1.0f, -1.0f); |
|
1097 |
|
1098 if (mirrorHorizontally) { |
|
1099 CGContextTranslateCTM(aCGContext, aRect.size.width, 0); |
|
1100 CGContextScaleCTM(aCGContext, -1.0f, 1.0f); |
|
1101 } |
|
1102 |
|
1103 HIRect inflatedDrawRect = CGRectMake(-MAX_FOCUS_RING_WIDTH, -MAX_FOCUS_RING_WIDTH, w, h); |
|
1104 CGContextDrawImage(aCGContext, inflatedDrawRect, bitmap); |
|
1105 |
|
1106 CGContextSetCTM(aCGContext, ctm); |
|
1107 |
|
1108 CGImageRelease(bitmap); |
|
1109 CGContextRelease(bitmapctx); |
|
1110 } |
|
1111 |
|
1112 CGContextSetCTM(aCGContext, savedCTM); |
|
1113 } |
|
1114 |
|
1115 static void |
|
1116 RenderButton(CGContextRef cgContext, const HIRect& aRenderRect, void* aData) |
|
1117 { |
|
1118 HIThemeButtonDrawInfo* bdi = (HIThemeButtonDrawInfo*)aData; |
|
1119 HIThemeDrawButton(&aRenderRect, bdi, cgContext, kHIThemeOrientationNormal, NULL); |
|
1120 } |
|
1121 |
|
1122 void |
|
1123 nsNativeThemeCocoa::DrawButton(CGContextRef cgContext, ThemeButtonKind inKind, |
|
1124 const HIRect& inBoxRect, bool inIsDefault, |
|
1125 ThemeButtonValue inValue, ThemeButtonAdornment inAdornment, |
|
1126 EventStates inState, nsIFrame* aFrame) |
|
1127 { |
|
1128 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1129 |
|
1130 BOOL isActive = FrameIsInActiveWindow(aFrame); |
|
1131 BOOL isDisabled = IsDisabled(aFrame, inState); |
|
1132 |
|
1133 HIThemeButtonDrawInfo bdi; |
|
1134 bdi.version = 0; |
|
1135 bdi.kind = inKind; |
|
1136 bdi.value = inValue; |
|
1137 bdi.adornment = inAdornment; |
|
1138 |
|
1139 if (isDisabled) { |
|
1140 bdi.state = kThemeStateUnavailable; |
|
1141 } |
|
1142 else if (inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) { |
|
1143 bdi.state = kThemeStatePressed; |
|
1144 } |
|
1145 else { |
|
1146 if (inKind == kThemeArrowButton) |
|
1147 bdi.state = kThemeStateUnavailable; // these are always drawn as unavailable |
|
1148 else if (!isActive && inKind == kThemeListHeaderButton) |
|
1149 bdi.state = kThemeStateInactive; |
|
1150 else |
|
1151 bdi.state = kThemeStateActive; |
|
1152 } |
|
1153 |
|
1154 if (inState.HasState(NS_EVENT_STATE_FOCUS) && isActive) |
|
1155 bdi.adornment |= kThemeAdornmentFocus; |
|
1156 |
|
1157 if (inIsDefault && !isDisabled && isActive && |
|
1158 !inState.HasState(NS_EVENT_STATE_ACTIVE)) { |
|
1159 bdi.adornment |= kThemeAdornmentDefault; |
|
1160 bdi.animation.time.start = 0; |
|
1161 bdi.animation.time.current = CFAbsoluteTimeGetCurrent(); |
|
1162 } |
|
1163 |
|
1164 HIRect drawFrame = inBoxRect; |
|
1165 |
|
1166 if (inKind == kThemePushButton) { |
|
1167 drawFrame.size.height -= 2; |
|
1168 if (inBoxRect.size.height < pushButtonSettings.naturalSizes[smallControlSize].height) { |
|
1169 bdi.kind = kThemePushButtonMini; |
|
1170 } |
|
1171 else if (inBoxRect.size.height < pushButtonSettings.naturalSizes[regularControlSize].height) { |
|
1172 bdi.kind = kThemePushButtonSmall; |
|
1173 drawFrame.origin.y -= 1; |
|
1174 drawFrame.origin.x += 1; |
|
1175 drawFrame.size.width -= 2; |
|
1176 } |
|
1177 } |
|
1178 else if (inKind == kThemeListHeaderButton) { |
|
1179 CGContextClipToRect(cgContext, inBoxRect); |
|
1180 // Always remove the top border. |
|
1181 drawFrame.origin.y -= 1; |
|
1182 drawFrame.size.height += 1; |
|
1183 // Remove the left border in LTR mode and the right border in RTL mode. |
|
1184 drawFrame.size.width += 1; |
|
1185 bool isLast = IsLastTreeHeaderCell(aFrame); |
|
1186 if (isLast) |
|
1187 drawFrame.size.width += 1; // Also remove the other border. |
|
1188 if (!IsFrameRTL(aFrame) || isLast) |
|
1189 drawFrame.origin.x -= 1; |
|
1190 } |
|
1191 |
|
1192 RenderTransformedHIThemeControl(cgContext, drawFrame, RenderButton, &bdi, |
|
1193 IsFrameRTL(aFrame)); |
|
1194 |
|
1195 #if DRAW_IN_FRAME_DEBUG |
|
1196 CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); |
|
1197 CGContextFillRect(cgContext, inBoxRect); |
|
1198 #endif |
|
1199 |
|
1200 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1201 } |
|
1202 |
|
1203 static const CellRenderSettings dropdownSettings = { |
|
1204 { |
|
1205 NSMakeSize(0, 16), // mini |
|
1206 NSMakeSize(0, 19), // small |
|
1207 NSMakeSize(0, 22) // regular |
|
1208 }, |
|
1209 { |
|
1210 NSMakeSize(18, 0), // mini |
|
1211 NSMakeSize(38, 0), // small |
|
1212 NSMakeSize(44, 0) // regular |
|
1213 }, |
|
1214 { |
|
1215 { // Leopard |
|
1216 {1, 1, 2, 1}, // mini |
|
1217 {3, 0, 3, 1}, // small |
|
1218 {3, 0, 3, 0} // regular |
|
1219 } |
|
1220 } |
|
1221 }; |
|
1222 |
|
1223 static const CellRenderSettings editableMenulistSettings = { |
|
1224 { |
|
1225 NSMakeSize(0, 15), // mini |
|
1226 NSMakeSize(0, 18), // small |
|
1227 NSMakeSize(0, 21) // regular |
|
1228 }, |
|
1229 { |
|
1230 NSMakeSize(18, 0), // mini |
|
1231 NSMakeSize(38, 0), // small |
|
1232 NSMakeSize(44, 0) // regular |
|
1233 }, |
|
1234 { |
|
1235 { // Leopard |
|
1236 {0, 0, 2, 2}, // mini |
|
1237 {0, 0, 3, 2}, // small |
|
1238 {0, 1, 3, 3} // regular |
|
1239 } |
|
1240 } |
|
1241 }; |
|
1242 |
|
1243 void |
|
1244 nsNativeThemeCocoa::DrawDropdown(CGContextRef cgContext, const HIRect& inBoxRect, |
|
1245 EventStates inState, uint8_t aWidgetType, |
|
1246 nsIFrame* aFrame) |
|
1247 { |
|
1248 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1249 |
|
1250 [mDropdownCell setPullsDown:(aWidgetType == NS_THEME_BUTTON)]; |
|
1251 |
|
1252 BOOL isEditable = (aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD); |
|
1253 NSCell* cell = isEditable ? (NSCell*)mComboBoxCell : (NSCell*)mDropdownCell; |
|
1254 |
|
1255 [cell setEnabled:!IsDisabled(aFrame, inState)]; |
|
1256 [cell setShowsFirstResponder:(IsFocused(aFrame) || inState.HasState(NS_EVENT_STATE_FOCUS))]; |
|
1257 [cell setHighlighted:IsOpenButton(aFrame)]; |
|
1258 [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)]; |
|
1259 |
|
1260 const CellRenderSettings& settings = isEditable ? editableMenulistSettings : dropdownSettings; |
|
1261 DrawCellWithSnapping(cell, cgContext, inBoxRect, settings, |
|
1262 0.5f, mCellDrawView, IsFrameRTL(aFrame)); |
|
1263 |
|
1264 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1265 } |
|
1266 |
|
1267 static const CellRenderSettings spinnerSettings = { |
|
1268 { |
|
1269 NSMakeSize(11, 16), // mini (width trimmed by 2px to reduce blank border) |
|
1270 NSMakeSize(15, 22), // small |
|
1271 NSMakeSize(19, 27) // regular |
|
1272 }, |
|
1273 { |
|
1274 NSMakeSize(11, 16), // mini (width trimmed by 2px to reduce blank border) |
|
1275 NSMakeSize(15, 22), // small |
|
1276 NSMakeSize(19, 27) // regular |
|
1277 }, |
|
1278 { |
|
1279 { // Leopard |
|
1280 {0, 0, 0, 0}, // mini |
|
1281 {0, 0, 0, 0}, // small |
|
1282 {0, 0, 0, 0} // regular |
|
1283 } |
|
1284 } |
|
1285 }; |
|
1286 |
|
1287 void |
|
1288 nsNativeThemeCocoa::DrawSpinButtons(CGContextRef cgContext, ThemeButtonKind inKind, |
|
1289 const HIRect& inBoxRect, ThemeDrawState inDrawState, |
|
1290 ThemeButtonAdornment inAdornment, |
|
1291 EventStates inState, nsIFrame* aFrame) |
|
1292 { |
|
1293 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1294 |
|
1295 HIThemeButtonDrawInfo bdi; |
|
1296 bdi.version = 0; |
|
1297 bdi.kind = inKind; |
|
1298 bdi.value = kThemeButtonOff; |
|
1299 bdi.adornment = inAdornment; |
|
1300 |
|
1301 if (IsDisabled(aFrame, inState)) |
|
1302 bdi.state = kThemeStateUnavailable; |
|
1303 else |
|
1304 bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive; |
|
1305 |
|
1306 HIThemeDrawButton(&inBoxRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL); |
|
1307 |
|
1308 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1309 } |
|
1310 |
|
1311 void |
|
1312 nsNativeThemeCocoa::DrawSpinButton(CGContextRef cgContext, |
|
1313 ThemeButtonKind inKind, |
|
1314 const HIRect& inBoxRect, |
|
1315 ThemeDrawState inDrawState, |
|
1316 ThemeButtonAdornment inAdornment, |
|
1317 EventStates inState, |
|
1318 nsIFrame* aFrame, |
|
1319 uint8_t aWidgetType) |
|
1320 { |
|
1321 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1322 |
|
1323 MOZ_ASSERT(aWidgetType == NS_THEME_SPINNER_UP_BUTTON || |
|
1324 aWidgetType == NS_THEME_SPINNER_DOWN_BUTTON); |
|
1325 |
|
1326 HIThemeButtonDrawInfo bdi; |
|
1327 bdi.version = 0; |
|
1328 bdi.kind = inKind; |
|
1329 bdi.value = kThemeButtonOff; |
|
1330 bdi.adornment = inAdornment; |
|
1331 |
|
1332 if (IsDisabled(aFrame, inState)) |
|
1333 bdi.state = kThemeStateUnavailable; |
|
1334 else |
|
1335 bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive; |
|
1336 |
|
1337 // Cocoa only allows kThemeIncDecButton to paint the up and down spin buttons |
|
1338 // together as a single unit (presumably because when one button is active, |
|
1339 // the appearance of both changes (in different ways)). Here we have to paint |
|
1340 // both buttons, using clip to hide the one we don't want to paint. |
|
1341 HIRect drawRect = inBoxRect; |
|
1342 drawRect.size.height *= 2; |
|
1343 if (aWidgetType == NS_THEME_SPINNER_DOWN_BUTTON) { |
|
1344 drawRect.origin.y -= inBoxRect.size.height; |
|
1345 } |
|
1346 |
|
1347 // Shift the drawing a little to the left, since cocoa paints with more |
|
1348 // blank space around the visual buttons than we'd like: |
|
1349 drawRect.origin.x -= 1; |
|
1350 |
|
1351 CGContextSaveGState(cgContext); |
|
1352 CGContextClipToRect(cgContext, inBoxRect); |
|
1353 |
|
1354 HIThemeDrawButton(&drawRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL); |
|
1355 |
|
1356 CGContextRestoreGState(cgContext); |
|
1357 |
|
1358 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1359 } |
|
1360 |
|
1361 void |
|
1362 nsNativeThemeCocoa::DrawFrame(CGContextRef cgContext, HIThemeFrameKind inKind, |
|
1363 const HIRect& inBoxRect, bool inDisabled, |
|
1364 EventStates inState) |
|
1365 { |
|
1366 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1367 |
|
1368 HIThemeFrameDrawInfo fdi; |
|
1369 fdi.version = 0; |
|
1370 fdi.kind = inKind; |
|
1371 |
|
1372 // We don't ever set an inactive state for this because it doesn't |
|
1373 // look right (see other apps). |
|
1374 fdi.state = inDisabled ? kThemeStateUnavailable : kThemeStateActive; |
|
1375 |
|
1376 // for some reason focus rings on listboxes draw incorrectly |
|
1377 if (inKind == kHIThemeFrameListBox) |
|
1378 fdi.isFocused = 0; |
|
1379 else |
|
1380 fdi.isFocused = inState.HasState(NS_EVENT_STATE_FOCUS); |
|
1381 |
|
1382 // HIThemeDrawFrame takes the rect for the content area of the frame, not |
|
1383 // the bounding rect for the frame. Here we reduce the size of the rect we |
|
1384 // will pass to make it the size of the content. |
|
1385 HIRect drawRect = inBoxRect; |
|
1386 if (inKind == kHIThemeFrameTextFieldSquare) { |
|
1387 SInt32 frameOutset = 0; |
|
1388 ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset); |
|
1389 drawRect.origin.x += frameOutset; |
|
1390 drawRect.origin.y += frameOutset; |
|
1391 drawRect.size.width -= frameOutset * 2; |
|
1392 drawRect.size.height -= frameOutset * 2; |
|
1393 } |
|
1394 else if (inKind == kHIThemeFrameListBox) { |
|
1395 SInt32 frameOutset = 0; |
|
1396 ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset); |
|
1397 drawRect.origin.x += frameOutset; |
|
1398 drawRect.origin.y += frameOutset; |
|
1399 drawRect.size.width -= frameOutset * 2; |
|
1400 drawRect.size.height -= frameOutset * 2; |
|
1401 } |
|
1402 |
|
1403 #if DRAW_IN_FRAME_DEBUG |
|
1404 CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); |
|
1405 CGContextFillRect(cgContext, inBoxRect); |
|
1406 #endif |
|
1407 |
|
1408 HIThemeDrawFrame(&drawRect, &fdi, cgContext, HITHEME_ORIENTATION); |
|
1409 |
|
1410 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1411 } |
|
1412 |
|
1413 static const CellRenderSettings progressSettings[2][2] = { |
|
1414 // Vertical progress bar. |
|
1415 { |
|
1416 // Determined settings. |
|
1417 { |
|
1418 { |
|
1419 NSZeroSize, // mini |
|
1420 NSMakeSize(10, 0), // small |
|
1421 NSMakeSize(16, 0) // regular |
|
1422 }, |
|
1423 { |
|
1424 NSZeroSize, NSZeroSize, NSZeroSize |
|
1425 }, |
|
1426 { |
|
1427 { // Leopard |
|
1428 {0, 0, 0, 0}, // mini |
|
1429 {1, 1, 1, 1}, // small |
|
1430 {1, 1, 1, 1} // regular |
|
1431 } |
|
1432 } |
|
1433 }, |
|
1434 // There is no horizontal margin in regular undetermined size. |
|
1435 { |
|
1436 { |
|
1437 NSZeroSize, // mini |
|
1438 NSMakeSize(10, 0), // small |
|
1439 NSMakeSize(16, 0) // regular |
|
1440 }, |
|
1441 { |
|
1442 NSZeroSize, NSZeroSize, NSZeroSize |
|
1443 }, |
|
1444 { |
|
1445 { // Leopard |
|
1446 {0, 0, 0, 0}, // mini |
|
1447 {1, 1, 1, 1}, // small |
|
1448 {1, 0, 1, 0} // regular |
|
1449 } |
|
1450 } |
|
1451 } |
|
1452 }, |
|
1453 // Horizontal progress bar. |
|
1454 { |
|
1455 // Determined settings. |
|
1456 { |
|
1457 { |
|
1458 NSZeroSize, // mini |
|
1459 NSMakeSize(0, 10), // small |
|
1460 NSMakeSize(0, 16) // regular |
|
1461 }, |
|
1462 { |
|
1463 NSZeroSize, NSZeroSize, NSZeroSize |
|
1464 }, |
|
1465 { |
|
1466 { // Leopard |
|
1467 {0, 0, 0, 0}, // mini |
|
1468 {1, 1, 1, 1}, // small |
|
1469 {1, 1, 1, 1} // regular |
|
1470 } |
|
1471 } |
|
1472 }, |
|
1473 // There is no horizontal margin in regular undetermined size. |
|
1474 { |
|
1475 { |
|
1476 NSZeroSize, // mini |
|
1477 NSMakeSize(0, 10), // small |
|
1478 NSMakeSize(0, 16) // regular |
|
1479 }, |
|
1480 { |
|
1481 NSZeroSize, NSZeroSize, NSZeroSize |
|
1482 }, |
|
1483 { |
|
1484 { // Leopard |
|
1485 {0, 0, 0, 0}, // mini |
|
1486 {1, 1, 1, 1}, // small |
|
1487 {0, 1, 0, 1} // regular |
|
1488 } |
|
1489 } |
|
1490 } |
|
1491 } |
|
1492 }; |
|
1493 |
|
1494 void |
|
1495 nsNativeThemeCocoa::DrawProgress(CGContextRef cgContext, const HIRect& inBoxRect, |
|
1496 bool inIsIndeterminate, bool inIsHorizontal, |
|
1497 double inValue, double inMaxValue, |
|
1498 nsIFrame* aFrame) |
|
1499 { |
|
1500 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1501 |
|
1502 NSProgressBarCell* cell = mProgressBarCell; |
|
1503 |
|
1504 [cell setValue:inValue]; |
|
1505 [cell setMax:inMaxValue]; |
|
1506 [cell setIndeterminate:inIsIndeterminate]; |
|
1507 [cell setHorizontal:inIsHorizontal]; |
|
1508 [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] |
|
1509 : NSClearControlTint)]; |
|
1510 |
|
1511 DrawCellWithSnapping(cell, cgContext, inBoxRect, |
|
1512 progressSettings[inIsHorizontal][inIsIndeterminate], |
|
1513 VerticalAlignFactor(aFrame), mCellDrawView, |
|
1514 IsFrameRTL(aFrame)); |
|
1515 |
|
1516 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1517 } |
|
1518 |
|
1519 static const CellRenderSettings meterSetting = { |
|
1520 { |
|
1521 NSMakeSize(0, 16), // mini |
|
1522 NSMakeSize(0, 16), // small |
|
1523 NSMakeSize(0, 16) // regular |
|
1524 }, |
|
1525 { |
|
1526 NSZeroSize, NSZeroSize, NSZeroSize |
|
1527 }, |
|
1528 { |
|
1529 { // Leopard |
|
1530 {1, 1, 1, 1}, // mini |
|
1531 {1, 1, 1, 1}, // small |
|
1532 {1, 1, 1, 1} // regular |
|
1533 } |
|
1534 } |
|
1535 }; |
|
1536 |
|
1537 void |
|
1538 nsNativeThemeCocoa::DrawMeter(CGContextRef cgContext, const HIRect& inBoxRect, |
|
1539 nsIFrame* aFrame) |
|
1540 { |
|
1541 NS_OBJC_BEGIN_TRY_ABORT_BLOCK |
|
1542 |
|
1543 NS_PRECONDITION(aFrame, "aFrame should not be null here!"); |
|
1544 |
|
1545 // When using -moz-meterbar on an non meter element, we will not be able to |
|
1546 // get all the needed information so we just draw an empty meter. |
|
1547 nsIContent* content = aFrame->GetContent(); |
|
1548 if (!(content && content->IsHTML(nsGkAtoms::meter))) { |
|
1549 DrawCellWithSnapping(mMeterBarCell, cgContext, inBoxRect, |
|
1550 meterSetting, VerticalAlignFactor(aFrame), |
|
1551 mCellDrawView, IsFrameRTL(aFrame)); |
|
1552 return; |
|
1553 } |
|
1554 |
|
1555 HTMLMeterElement* meterElement = static_cast<HTMLMeterElement*>(content); |
|
1556 double value = meterElement->Value(); |
|
1557 double min = meterElement->Min(); |
|
1558 double max = meterElement->Max(); |
|
1559 |
|
1560 NSLevelIndicatorCell* cell = mMeterBarCell; |
|
1561 |
|
1562 [cell setMinValue:min]; |
|
1563 [cell setMaxValue:max]; |
|
1564 [cell setDoubleValue:value]; |
|
1565 |
|
1566 /** |
|
1567 * The way HTML and Cocoa defines the meter/indicator widget are different. |
|
1568 * So, we are going to use a trick to get the Cocoa widget showing what we |
|
1569 * are expecting: we set the warningValue or criticalValue to the current |
|
1570 * value when we want to have the widget to be in the warning or critical |
|
1571 * state. |
|
1572 */ |
|
1573 EventStates states = aFrame->GetContent()->AsElement()->State(); |
|
1574 |
|
1575 // Reset previously set warning and critical values. |
|
1576 [cell setWarningValue:max+1]; |
|
1577 [cell setCriticalValue:max+1]; |
|
1578 |
|
1579 if (states.HasState(NS_EVENT_STATE_SUB_OPTIMUM)) { |
|
1580 [cell setWarningValue:value]; |
|
1581 } else if (states.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM)) { |
|
1582 [cell setCriticalValue:value]; |
|
1583 } |
|
1584 |
|
1585 HIRect rect = CGRectStandardize(inBoxRect); |
|
1586 BOOL vertical = IsVerticalMeter(aFrame); |
|
1587 |
|
1588 CGContextSaveGState(cgContext); |
|
1589 |
|
1590 if (vertical) { |
|
1591 /** |
|
1592 * Cocoa doesn't provide a vertical meter bar so to show one, we have to |
|
1593 * show a rotated horizontal meter bar. |
|
1594 * Given that we want to show a vertical meter bar, we assume that the rect |
|
1595 * has vertical dimensions but we can't correctly draw a meter widget inside |
|
1596 * such a rectangle so we need to inverse width and height (and re-position) |
|
1597 * to get a rectangle with horizontal dimensions. |
|
1598 * Finally, we want to show a vertical meter so we want to rotate the result |
|
1599 * so it is vertical. We do that by changing the context. |
|
1600 */ |
|
1601 CGFloat tmp = rect.size.width; |
|
1602 rect.size.width = rect.size.height; |
|
1603 rect.size.height = tmp; |
|
1604 rect.origin.x += rect.size.height / 2.f - rect.size.width / 2.f; |
|
1605 rect.origin.y += rect.size.width / 2.f - rect.size.height / 2.f; |
|
1606 |
|
1607 CGContextTranslateCTM(cgContext, CGRectGetMidX(rect), CGRectGetMidY(rect)); |
|
1608 CGContextRotateCTM(cgContext, -M_PI / 2.f); |
|
1609 CGContextTranslateCTM(cgContext, -CGRectGetMidX(rect), -CGRectGetMidY(rect)); |
|
1610 } |
|
1611 |
|
1612 DrawCellWithSnapping(cell, cgContext, rect, |
|
1613 meterSetting, VerticalAlignFactor(aFrame), |
|
1614 mCellDrawView, !vertical && IsFrameRTL(aFrame)); |
|
1615 |
|
1616 CGContextRestoreGState(cgContext); |
|
1617 |
|
1618 NS_OBJC_END_TRY_ABORT_BLOCK |
|
1619 } |
|
1620 |
|
1621 void |
|
1622 nsNativeThemeCocoa::DrawTabPanel(CGContextRef cgContext, const HIRect& inBoxRect, |
|
1623 nsIFrame* aFrame) |
|
1624 { |
|
1625 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1626 |
|
1627 HIThemeTabPaneDrawInfo tpdi; |
|
1628 |
|
1629 tpdi.version = 1; |
|
1630 tpdi.state = FrameIsInActiveWindow(aFrame) ? kThemeStateActive : kThemeStateInactive; |
|
1631 tpdi.direction = kThemeTabNorth; |
|
1632 tpdi.size = kHIThemeTabSizeNormal; |
|
1633 tpdi.kind = kHIThemeTabKindNormal; |
|
1634 |
|
1635 HIThemeDrawTabPane(&inBoxRect, &tpdi, cgContext, HITHEME_ORIENTATION); |
|
1636 |
|
1637 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1638 } |
|
1639 |
|
1640 void |
|
1641 nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect, |
|
1642 EventStates inState, bool inIsVertical, |
|
1643 bool inIsReverse, int32_t inCurrentValue, |
|
1644 int32_t inMinValue, int32_t inMaxValue, |
|
1645 nsIFrame* aFrame) |
|
1646 { |
|
1647 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1648 |
|
1649 HIThemeTrackDrawInfo tdi; |
|
1650 |
|
1651 tdi.version = 0; |
|
1652 tdi.kind = kThemeMediumSlider; |
|
1653 tdi.bounds = inBoxRect; |
|
1654 tdi.min = inMinValue; |
|
1655 tdi.max = inMaxValue; |
|
1656 tdi.value = inCurrentValue; |
|
1657 tdi.attributes = kThemeTrackShowThumb; |
|
1658 if (!inIsVertical) |
|
1659 tdi.attributes |= kThemeTrackHorizontal; |
|
1660 if (inIsReverse) |
|
1661 tdi.attributes |= kThemeTrackRightToLeft; |
|
1662 if (inState.HasState(NS_EVENT_STATE_FOCUS)) |
|
1663 tdi.attributes |= kThemeTrackHasFocus; |
|
1664 if (IsDisabled(aFrame, inState)) |
|
1665 tdi.enableState = kThemeTrackDisabled; |
|
1666 else |
|
1667 tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive; |
|
1668 tdi.trackInfo.slider.thumbDir = kThemeThumbPlain; |
|
1669 tdi.trackInfo.slider.pressState = 0; |
|
1670 |
|
1671 HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION); |
|
1672 |
|
1673 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1674 } |
|
1675 |
|
1676 nsIFrame* |
|
1677 nsNativeThemeCocoa::SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter) |
|
1678 { |
|
1679 // Usually a separator is drawn by the segment to the right of the |
|
1680 // separator, but pressed and selected segments have higher priority. |
|
1681 if (!aBefore || !aAfter) |
|
1682 return nullptr; |
|
1683 if (IsSelectedButton(aAfter)) |
|
1684 return aAfter; |
|
1685 if (IsSelectedButton(aBefore) || IsPressedButton(aBefore)) |
|
1686 return aBefore; |
|
1687 return aAfter; |
|
1688 } |
|
1689 |
|
1690 CGRect |
|
1691 nsNativeThemeCocoa::SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft, |
|
1692 nsIFrame* aCurrent, nsIFrame* aRight) |
|
1693 { |
|
1694 // A separator between two segments should always be located in the leftmost |
|
1695 // pixel column of the segment to the right of the separator, regardless of |
|
1696 // who ends up drawing it. |
|
1697 // CoreUI draws the separators inside the drawing rect. |
|
1698 if (aLeft && SeparatorResponsibility(aLeft, aCurrent) == aLeft) { |
|
1699 // The left button draws the separator, so we need to make room for it. |
|
1700 aRect.origin.x += 1; |
|
1701 aRect.size.width -= 1; |
|
1702 } |
|
1703 if (SeparatorResponsibility(aCurrent, aRight) == aCurrent) { |
|
1704 // We draw the right separator, so we need to extend the draw rect into the |
|
1705 // segment to our right. |
|
1706 aRect.size.width += 1; |
|
1707 } |
|
1708 return aRect; |
|
1709 } |
|
1710 |
|
1711 static NSString* ToolbarButtonPosition(BOOL aIsFirst, BOOL aIsLast) |
|
1712 { |
|
1713 if (aIsFirst) { |
|
1714 if (aIsLast) |
|
1715 return @"kCUISegmentPositionOnly"; |
|
1716 return @"kCUISegmentPositionFirst"; |
|
1717 } |
|
1718 if (aIsLast) |
|
1719 return @"kCUISegmentPositionLast"; |
|
1720 return @"kCUISegmentPositionMiddle"; |
|
1721 } |
|
1722 |
|
1723 struct SegmentedControlRenderSettings { |
|
1724 const CGFloat* heights; |
|
1725 const NSString* widgetName; |
|
1726 const BOOL ignoresPressedWhenSelected; |
|
1727 const BOOL isToolbarControl; |
|
1728 }; |
|
1729 |
|
1730 static const CGFloat tabHeights[3] = { 17, 20, 23 }; |
|
1731 |
|
1732 static const SegmentedControlRenderSettings tabRenderSettings = { |
|
1733 tabHeights, @"tab", YES, NO |
|
1734 }; |
|
1735 |
|
1736 static const CGFloat toolbarButtonHeights[3] = { 15, 18, 22 }; |
|
1737 |
|
1738 static const SegmentedControlRenderSettings toolbarButtonRenderSettings = { |
|
1739 toolbarButtonHeights, @"kCUIWidgetButtonSegmentedSCurve", NO, YES |
|
1740 }; |
|
1741 |
|
1742 void |
|
1743 nsNativeThemeCocoa::DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect, |
|
1744 EventStates inState, nsIFrame* aFrame, |
|
1745 const SegmentedControlRenderSettings& aSettings) |
|
1746 { |
|
1747 BOOL isActive = IsActive(aFrame, aSettings.isToolbarControl); |
|
1748 BOOL isFocused = inState.HasState(NS_EVENT_STATE_FOCUS); |
|
1749 BOOL isSelected = IsSelectedButton(aFrame); |
|
1750 BOOL isPressed = IsPressedButton(aFrame); |
|
1751 if (isSelected && aSettings.ignoresPressedWhenSelected) { |
|
1752 isPressed = NO; |
|
1753 } |
|
1754 |
|
1755 BOOL isRTL = IsFrameRTL(aFrame); |
|
1756 nsIFrame* left = GetAdjacentSiblingFrameWithSameAppearance(aFrame, isRTL); |
|
1757 nsIFrame* right = GetAdjacentSiblingFrameWithSameAppearance(aFrame, !isRTL); |
|
1758 CGRect drawRect = SeparatorAdjustedRect(inBoxRect, left, aFrame, right); |
|
1759 if (drawRect.size.width * drawRect.size.height > CUIDRAW_MAX_AREA) { |
|
1760 return; |
|
1761 } |
|
1762 BOOL drawLeftSeparator = SeparatorResponsibility(left, aFrame) == aFrame; |
|
1763 BOOL drawRightSeparator = SeparatorResponsibility(aFrame, right) == aFrame; |
|
1764 NSControlSize controlSize = FindControlSize(drawRect.size.height, aSettings.heights, 4.0f); |
|
1765 |
|
1766 CUIDraw([NSWindow coreUIRenderer], drawRect, cgContext, |
|
1767 (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: |
|
1768 aSettings.widgetName, @"widget", |
|
1769 ToolbarButtonPosition(!left, !right), @"kCUIPositionKey", |
|
1770 [NSNumber numberWithBool:drawLeftSeparator], @"kCUISegmentLeadingSeparatorKey", |
|
1771 [NSNumber numberWithBool:drawRightSeparator], @"kCUISegmentTrailingSeparatorKey", |
|
1772 [NSNumber numberWithBool:isSelected], @"value", |
|
1773 (isPressed ? @"pressed" : (isActive ? @"normal" : @"inactive")), @"state", |
|
1774 [NSNumber numberWithBool:isFocused], @"focus", |
|
1775 CUIControlSizeForCocoaSize(controlSize), @"size", |
|
1776 [NSNumber numberWithBool:YES], @"is.flipped", |
|
1777 @"up", @"direction", |
|
1778 nil], |
|
1779 nil); |
|
1780 } |
|
1781 |
|
1782 static inline UInt8 |
|
1783 ConvertToPressState(EventStates aButtonState, UInt8 aPressState) |
|
1784 { |
|
1785 // If the button is pressed, return the press state passed in. Otherwise, return 0. |
|
1786 return aButtonState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER) ? aPressState : 0; |
|
1787 } |
|
1788 |
|
1789 void |
|
1790 nsNativeThemeCocoa::GetScrollbarPressStates(nsIFrame* aFrame, |
|
1791 EventStates aButtonStates[]) |
|
1792 { |
|
1793 static nsIContent::AttrValuesArray attributeValues[] = { |
|
1794 &nsGkAtoms::scrollbarUpTop, |
|
1795 &nsGkAtoms::scrollbarDownTop, |
|
1796 &nsGkAtoms::scrollbarUpBottom, |
|
1797 &nsGkAtoms::scrollbarDownBottom, |
|
1798 nullptr |
|
1799 }; |
|
1800 |
|
1801 // Get the state of any scrollbar buttons in our child frames |
|
1802 for (nsIFrame *childFrame = aFrame->GetFirstPrincipalChild(); |
|
1803 childFrame; |
|
1804 childFrame = childFrame->GetNextSibling()) { |
|
1805 |
|
1806 nsIContent *childContent = childFrame->GetContent(); |
|
1807 if (!childContent) continue; |
|
1808 int32_t attrIndex = childContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::sbattr, |
|
1809 attributeValues, eCaseMatters); |
|
1810 if (attrIndex < 0) continue; |
|
1811 |
|
1812 aButtonStates[attrIndex] = GetContentState(childFrame, NS_THEME_BUTTON); |
|
1813 } |
|
1814 } |
|
1815 |
|
1816 // Both of the following sets of numbers were derived by loading the testcase in |
|
1817 // bmo bug 380185 in Safari and observing its behavior for various heights of scrollbar. |
|
1818 // These magic numbers are the minimum sizes we can draw a scrollbar and still |
|
1819 // have room for everything to display, including the thumb |
|
1820 #define MIN_SCROLLBAR_SIZE_WITH_THUMB 61 |
|
1821 #define MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB 49 |
|
1822 // And these are the minimum sizes if we don't draw the thumb |
|
1823 #define MIN_SCROLLBAR_SIZE 56 |
|
1824 #define MIN_SMALL_SCROLLBAR_SIZE 46 |
|
1825 |
|
1826 void |
|
1827 nsNativeThemeCocoa::GetScrollbarDrawInfo(HIThemeTrackDrawInfo& aTdi, nsIFrame *aFrame, |
|
1828 const CGSize& aSize, bool aShouldGetButtonStates) |
|
1829 { |
|
1830 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1831 |
|
1832 int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); |
|
1833 int32_t minpos = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0); |
|
1834 int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); |
|
1835 int32_t thumbSize = CheckIntAttr(aFrame, nsGkAtoms::pageincrement, 10); |
|
1836 |
|
1837 bool isHorizontal = aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::orient, |
|
1838 nsGkAtoms::horizontal, eCaseMatters); |
|
1839 bool isSmall = aFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL; |
|
1840 |
|
1841 aTdi.version = 0; |
|
1842 aTdi.kind = isSmall ? kThemeSmallScrollBar : kThemeMediumScrollBar; |
|
1843 aTdi.bounds.origin = CGPointZero; |
|
1844 aTdi.bounds.size = aSize; |
|
1845 aTdi.min = minpos; |
|
1846 aTdi.max = maxpos; |
|
1847 aTdi.value = curpos; |
|
1848 aTdi.attributes = 0; |
|
1849 aTdi.enableState = kThemeTrackActive; |
|
1850 if (isHorizontal) |
|
1851 aTdi.attributes |= kThemeTrackHorizontal; |
|
1852 |
|
1853 aTdi.trackInfo.scrollbar.viewsize = (SInt32)thumbSize; |
|
1854 |
|
1855 // This should be done early on so things like "kThemeTrackNothingToScroll" can |
|
1856 // override the active enable state. |
|
1857 aTdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive; |
|
1858 |
|
1859 /* Only display features if we have enough room for them. |
|
1860 * Gecko still maintains the scrollbar info; this is just a visual issue (bug 380185). |
|
1861 */ |
|
1862 int32_t longSideLength = (int32_t)(isHorizontal ? (aSize.width) : (aSize.height)); |
|
1863 if (longSideLength >= (isSmall ? MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB : MIN_SCROLLBAR_SIZE_WITH_THUMB)) { |
|
1864 aTdi.attributes |= kThemeTrackShowThumb; |
|
1865 } |
|
1866 else if (longSideLength < (isSmall ? MIN_SMALL_SCROLLBAR_SIZE : MIN_SCROLLBAR_SIZE)) { |
|
1867 aTdi.enableState = kThemeTrackNothingToScroll; |
|
1868 return; |
|
1869 } |
|
1870 |
|
1871 aTdi.trackInfo.scrollbar.pressState = 0; |
|
1872 |
|
1873 // Only go get these scrollbar button states if we need it. For example, |
|
1874 // there's no reason to look up scrollbar button states when we're only |
|
1875 // creating a TrackDrawInfo to determine the size of the thumb. There's |
|
1876 // also no reason to do this on Lion or later, whose scrollbars have no |
|
1877 // arrow buttons. |
|
1878 if (aShouldGetButtonStates && !nsCocoaFeatures::OnLionOrLater()) { |
|
1879 EventStates buttonStates[4]; |
|
1880 GetScrollbarPressStates(aFrame, buttonStates); |
|
1881 NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"]; |
|
1882 // It seems that unless all four buttons are showing, kThemeTopOutsideArrowPressed is the correct constant for |
|
1883 // the up scrollbar button. |
|
1884 if ([buttonPlacement isEqualToString:@"DoubleBoth"]) { |
|
1885 aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) | |
|
1886 ConvertToPressState(buttonStates[1], kThemeTopInsideArrowPressed) | |
|
1887 ConvertToPressState(buttonStates[2], kThemeBottomInsideArrowPressed) | |
|
1888 ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed); |
|
1889 } else { |
|
1890 aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) | |
|
1891 ConvertToPressState(buttonStates[1], kThemeBottomOutsideArrowPressed) | |
|
1892 ConvertToPressState(buttonStates[2], kThemeTopOutsideArrowPressed) | |
|
1893 ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed); |
|
1894 } |
|
1895 } |
|
1896 |
|
1897 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1898 } |
|
1899 |
|
1900 static void |
|
1901 RenderScrollbar(CGContextRef cgContext, const HIRect& aRenderRect, void* aData) |
|
1902 { |
|
1903 HIThemeTrackDrawInfo* tdi = (HIThemeTrackDrawInfo*)aData; |
|
1904 HIThemeDrawTrack(tdi, NULL, cgContext, HITHEME_ORIENTATION); |
|
1905 } |
|
1906 |
|
1907 void |
|
1908 nsNativeThemeCocoa::DrawScrollbar(CGContextRef aCGContext, const HIRect& aBoxRect, nsIFrame *aFrame) |
|
1909 { |
|
1910 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1911 |
|
1912 HIThemeTrackDrawInfo tdi; |
|
1913 GetScrollbarDrawInfo(tdi, aFrame, aBoxRect.size, true); // True means we want the press states |
|
1914 RenderTransformedHIThemeControl(aCGContext, aBoxRect, RenderScrollbar, &tdi); |
|
1915 |
|
1916 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1917 } |
|
1918 |
|
1919 nsIFrame* |
|
1920 nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame) |
|
1921 { |
|
1922 // Walk our parents to find a scrollbar frame |
|
1923 nsIFrame *scrollbarFrame = aFrame; |
|
1924 do { |
|
1925 if (scrollbarFrame->GetType() == nsGkAtoms::scrollbarFrame) break; |
|
1926 } while ((scrollbarFrame = scrollbarFrame->GetParent())); |
|
1927 |
|
1928 // We return null if we can't find a parent scrollbar frame |
|
1929 return scrollbarFrame; |
|
1930 } |
|
1931 |
|
1932 static bool |
|
1933 ToolbarCanBeUnified(CGContextRef cgContext, const HIRect& inBoxRect, NSWindow* aWindow) |
|
1934 { |
|
1935 if (![aWindow isKindOfClass:[ToolbarWindow class]]) |
|
1936 return false; |
|
1937 |
|
1938 ToolbarWindow* win = (ToolbarWindow*)aWindow; |
|
1939 float unifiedToolbarHeight = [win unifiedToolbarHeight]; |
|
1940 return inBoxRect.origin.x == 0 && |
|
1941 inBoxRect.size.width >= [win frame].size.width && |
|
1942 CGRectGetMaxY(inBoxRect) <= unifiedToolbarHeight; |
|
1943 } |
|
1944 |
|
1945 // By default, kCUIWidgetWindowFrame drawing draws rounded corners in the |
|
1946 // upper corners. Depending on the context type, it fills the background in |
|
1947 // the corners with black or leaves it transparent. Unfortunately, this corner |
|
1948 // rounding interacts poorly with the window corner masking we apply during |
|
1949 // titlebar drawing and results in small remnants of the corner background |
|
1950 // appearing at the rounded edge. |
|
1951 // So we draw square corners. |
|
1952 static void |
|
1953 DrawNativeTitlebarToolbarWithSquareCorners(CGContextRef aContext, const CGRect& aRect, |
|
1954 CGFloat aUnifiedHeight, BOOL aIsMain) |
|
1955 { |
|
1956 // We extend the draw rect horizontally and clip away the rounded corners. |
|
1957 const CGFloat extendHorizontal = 10; |
|
1958 CGRect drawRect = CGRectInset(aRect, -extendHorizontal, 0); |
|
1959 if (drawRect.size.width * drawRect.size.height <= CUIDRAW_MAX_AREA) { |
|
1960 CGContextSaveGState(aContext); |
|
1961 CGContextClipToRect(aContext, aRect); |
|
1962 |
|
1963 CUIDraw([NSWindow coreUIRenderer], drawRect, aContext, |
|
1964 (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: |
|
1965 @"kCUIWidgetWindowFrame", @"widget", |
|
1966 @"regularwin", @"windowtype", |
|
1967 (aIsMain ? @"normal" : @"inactive"), @"state", |
|
1968 [NSNumber numberWithDouble:aUnifiedHeight], @"kCUIWindowFrameUnifiedTitleBarHeightKey", |
|
1969 [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawTitleSeparatorKey", |
|
1970 [NSNumber numberWithBool:YES], @"is.flipped", |
|
1971 nil], |
|
1972 nil); |
|
1973 |
|
1974 CGContextRestoreGState(aContext); |
|
1975 } |
|
1976 } |
|
1977 |
|
1978 void |
|
1979 nsNativeThemeCocoa::DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect, |
|
1980 NSWindow* aWindow) |
|
1981 { |
|
1982 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
1983 |
|
1984 CGContextSaveGState(cgContext); |
|
1985 CGContextClipToRect(cgContext, inBoxRect); |
|
1986 |
|
1987 CGFloat unifiedHeight = std::max([(ToolbarWindow*)aWindow unifiedToolbarHeight], |
|
1988 inBoxRect.size.height); |
|
1989 BOOL isMain = [aWindow isMainWindow]; |
|
1990 CGFloat titlebarHeight = unifiedHeight - inBoxRect.size.height; |
|
1991 CGRect drawRect = CGRectMake(inBoxRect.origin.x, inBoxRect.origin.y - titlebarHeight, |
|
1992 inBoxRect.size.width, inBoxRect.size.height + titlebarHeight); |
|
1993 DrawNativeTitlebarToolbarWithSquareCorners(cgContext, drawRect, unifiedHeight, isMain); |
|
1994 |
|
1995 CGContextRestoreGState(cgContext); |
|
1996 |
|
1997 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
1998 } |
|
1999 |
|
2000 void |
|
2001 nsNativeThemeCocoa::DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect, |
|
2002 nsIFrame *aFrame) |
|
2003 { |
|
2004 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
2005 |
|
2006 if (inBoxRect.size.height < 2.0f) |
|
2007 return; |
|
2008 |
|
2009 CGContextSaveGState(cgContext); |
|
2010 CGContextClipToRect(cgContext, inBoxRect); |
|
2011 |
|
2012 // kCUIWidgetWindowFrame draws a complete window frame with both title bar |
|
2013 // and bottom bar. We only want the bottom bar, so we extend the draw rect |
|
2014 // upwards to make space for the title bar, and then we clip it away. |
|
2015 CGRect drawRect = inBoxRect; |
|
2016 const int extendUpwards = 40; |
|
2017 drawRect.origin.y -= extendUpwards; |
|
2018 drawRect.size.height += extendUpwards; |
|
2019 if (drawRect.size.width * drawRect.size.height <= CUIDRAW_MAX_AREA) { |
|
2020 CUIDraw([NSWindow coreUIRenderer], drawRect, cgContext, |
|
2021 (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: |
|
2022 @"kCUIWidgetWindowFrame", @"widget", |
|
2023 @"regularwin", @"windowtype", |
|
2024 (IsActive(aFrame, YES) ? @"normal" : @"inactive"), @"state", |
|
2025 [NSNumber numberWithInt:inBoxRect.size.height], @"kCUIWindowFrameBottomBarHeightKey", |
|
2026 [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawBottomBarSeparatorKey", |
|
2027 [NSNumber numberWithBool:YES], @"is.flipped", |
|
2028 nil], |
|
2029 nil); |
|
2030 } |
|
2031 |
|
2032 CGContextRestoreGState(cgContext); |
|
2033 |
|
2034 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
2035 } |
|
2036 |
|
2037 void |
|
2038 nsNativeThemeCocoa::DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect, |
|
2039 CGFloat aUnifiedHeight, BOOL aIsMain) |
|
2040 { |
|
2041 CGFloat unifiedHeight = std::max(aUnifiedHeight, aTitlebarRect.size.height); |
|
2042 DrawNativeTitlebarToolbarWithSquareCorners(aContext, aTitlebarRect, unifiedHeight, aIsMain); |
|
2043 } |
|
2044 |
|
2045 static void |
|
2046 RenderResizer(CGContextRef cgContext, const HIRect& aRenderRect, void* aData) |
|
2047 { |
|
2048 HIThemeGrowBoxDrawInfo* drawInfo = (HIThemeGrowBoxDrawInfo*)aData; |
|
2049 HIThemeDrawGrowBox(&CGPointZero, drawInfo, cgContext, kHIThemeOrientationNormal); |
|
2050 } |
|
2051 |
|
2052 void |
|
2053 nsNativeThemeCocoa::DrawResizer(CGContextRef cgContext, const HIRect& aRect, |
|
2054 nsIFrame *aFrame) |
|
2055 { |
|
2056 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
2057 |
|
2058 HIThemeGrowBoxDrawInfo drawInfo; |
|
2059 drawInfo.version = 0; |
|
2060 drawInfo.state = kThemeStateActive; |
|
2061 drawInfo.kind = kHIThemeGrowBoxKindNormal; |
|
2062 drawInfo.direction = kThemeGrowRight | kThemeGrowDown; |
|
2063 drawInfo.size = kHIThemeGrowBoxSizeNormal; |
|
2064 |
|
2065 RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo, |
|
2066 IsFrameRTL(aFrame)); |
|
2067 |
|
2068 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
2069 } |
|
2070 |
|
2071 static bool |
|
2072 IsHiDPIContext(nsDeviceContext* aContext) |
|
2073 { |
|
2074 return nsPresContext::AppUnitsPerCSSPixel() >= |
|
2075 2 * aContext->UnscaledAppUnitsPerDevPixel(); |
|
2076 } |
|
2077 |
|
2078 NS_IMETHODIMP |
|
2079 nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext, |
|
2080 nsIFrame* aFrame, |
|
2081 uint8_t aWidgetType, |
|
2082 const nsRect& aRect, |
|
2083 const nsRect& aDirtyRect) |
|
2084 { |
|
2085 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
2086 |
|
2087 // setup to draw into the correct port |
|
2088 int32_t p2a = aContext->AppUnitsPerDevPixel(); |
|
2089 |
|
2090 gfxRect nativeDirtyRect(aDirtyRect.x, aDirtyRect.y, |
|
2091 aDirtyRect.width, aDirtyRect.height); |
|
2092 gfxRect nativeWidgetRect(aRect.x, aRect.y, aRect.width, aRect.height); |
|
2093 nativeWidgetRect.ScaleInverse(gfxFloat(p2a)); |
|
2094 nativeDirtyRect.ScaleInverse(gfxFloat(p2a)); |
|
2095 nativeWidgetRect.Round(); |
|
2096 if (nativeWidgetRect.IsEmpty()) |
|
2097 return NS_OK; // Don't attempt to draw invisible widgets. |
|
2098 |
|
2099 gfxContext* thebesCtx = aContext->ThebesContext(); |
|
2100 if (!thebesCtx) |
|
2101 return NS_ERROR_FAILURE; |
|
2102 |
|
2103 gfxContextMatrixAutoSaveRestore save(thebesCtx); |
|
2104 |
|
2105 bool hidpi = IsHiDPIContext(aContext->DeviceContext()); |
|
2106 if (hidpi) { |
|
2107 // Use high-resolution drawing. |
|
2108 nativeWidgetRect.ScaleInverse(2.0f); |
|
2109 nativeDirtyRect.ScaleInverse(2.0f); |
|
2110 thebesCtx->Scale(2.0f, 2.0f); |
|
2111 } |
|
2112 |
|
2113 gfxQuartzNativeDrawing nativeDrawing(thebesCtx, nativeDirtyRect, |
|
2114 hidpi ? 2.0f : 1.0f); |
|
2115 |
|
2116 CGContextRef cgContext = nativeDrawing.BeginNativeDrawing(); |
|
2117 if (cgContext == nullptr) { |
|
2118 // The Quartz surface handles 0x0 surfaces by internally |
|
2119 // making all operations no-ops; there's no cgcontext created for them. |
|
2120 // Unfortunately, this means that callers that want to render |
|
2121 // directly to the CGContext need to be aware of this quirk. |
|
2122 return NS_OK; |
|
2123 } |
|
2124 |
|
2125 #if 0 |
|
2126 if (1 /*aWidgetType == NS_THEME_TEXTFIELD*/) { |
|
2127 fprintf(stderr, "Native theme drawing widget %d [%p] dis:%d in rect [%d %d %d %d]\n", |
|
2128 aWidgetType, aFrame, IsDisabled(aFrame), aRect.x, aRect.y, aRect.width, aRect.height); |
|
2129 fprintf(stderr, "Cairo matrix: [%f %f %f %f %f %f]\n", |
|
2130 mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0); |
|
2131 fprintf(stderr, "Native theme xform[0]: [%f %f %f %f %f %f]\n", |
|
2132 mm0.a, mm0.b, mm0.c, mm0.d, mm0.tx, mm0.ty); |
|
2133 CGAffineTransform mm = CGContextGetCTM(cgContext); |
|
2134 fprintf(stderr, "Native theme xform[1]: [%f %f %f %f %f %f]\n", |
|
2135 mm.a, mm.b, mm.c, mm.d, mm.tx, mm.ty); |
|
2136 } |
|
2137 #endif |
|
2138 |
|
2139 CGRect macRect = CGRectMake(nativeWidgetRect.X(), nativeWidgetRect.Y(), |
|
2140 nativeWidgetRect.Width(), nativeWidgetRect.Height()); |
|
2141 |
|
2142 #if 0 |
|
2143 fprintf(stderr, " --> macRect %f %f %f %f\n", |
|
2144 macRect.origin.x, macRect.origin.y, macRect.size.width, macRect.size.height); |
|
2145 CGRect bounds = CGContextGetClipBoundingBox(cgContext); |
|
2146 fprintf(stderr, " --> clip bounds: %f %f %f %f\n", |
|
2147 bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); |
|
2148 |
|
2149 //CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 1.0, 0.1); |
|
2150 //CGContextFillRect(cgContext, bounds); |
|
2151 #endif |
|
2152 |
|
2153 EventStates eventState = GetContentState(aFrame, aWidgetType); |
|
2154 |
|
2155 switch (aWidgetType) { |
|
2156 case NS_THEME_DIALOG: { |
|
2157 HIThemeSetFill(kThemeBrushDialogBackgroundActive, NULL, cgContext, HITHEME_ORIENTATION); |
|
2158 CGContextFillRect(cgContext, macRect); |
|
2159 } |
|
2160 break; |
|
2161 |
|
2162 case NS_THEME_MENUPOPUP: { |
|
2163 HIThemeMenuDrawInfo mdi; |
|
2164 memset(&mdi, 0, sizeof(mdi)); |
|
2165 mdi.version = 0; |
|
2166 mdi.menuType = IsDisabled(aFrame, eventState) ? |
|
2167 static_cast<ThemeMenuType>(kThemeMenuTypeInactive) : |
|
2168 static_cast<ThemeMenuType>(kThemeMenuTypePopUp); |
|
2169 |
|
2170 bool isLeftOfParent = false; |
|
2171 if (IsSubmenu(aFrame, &isLeftOfParent) && !isLeftOfParent) { |
|
2172 mdi.menuType = kThemeMenuTypeHierarchical; |
|
2173 } |
|
2174 |
|
2175 // The rounded corners draw outside the frame. |
|
2176 CGRect deflatedRect = CGRectMake(macRect.origin.x, macRect.origin.y + 4, |
|
2177 macRect.size.width, macRect.size.height - 8); |
|
2178 HIThemeDrawMenuBackground(&deflatedRect, &mdi, cgContext, HITHEME_ORIENTATION); |
|
2179 } |
|
2180 break; |
|
2181 |
|
2182 case NS_THEME_MENUITEM: { |
|
2183 bool isTransparent; |
|
2184 if (thebesCtx->IsCairo()) { |
|
2185 isTransparent = thebesCtx->OriginalSurface()->GetContentType() == gfxContentType::COLOR_ALPHA; |
|
2186 } else { |
|
2187 SurfaceFormat format = thebesCtx->GetDrawTarget()->GetFormat(); |
|
2188 isTransparent = (format == SurfaceFormat::R8G8B8A8) || |
|
2189 (format == SurfaceFormat::B8G8R8A8); |
|
2190 } |
|
2191 if (isTransparent) { |
|
2192 // Clear the background to get correct transparency. |
|
2193 CGContextClearRect(cgContext, macRect); |
|
2194 } |
|
2195 |
|
2196 // maybe use kThemeMenuItemHierBackground or PopUpBackground instead of just Plain? |
|
2197 HIThemeMenuItemDrawInfo drawInfo; |
|
2198 memset(&drawInfo, 0, sizeof(drawInfo)); |
|
2199 drawInfo.version = 0; |
|
2200 drawInfo.itemType = kThemeMenuItemPlain; |
|
2201 drawInfo.state = (IsDisabled(aFrame, eventState) ? |
|
2202 static_cast<ThemeMenuState>(kThemeMenuDisabled) : |
|
2203 CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ? |
|
2204 static_cast<ThemeMenuState>(kThemeMenuSelected) : |
|
2205 static_cast<ThemeMenuState>(kThemeMenuActive)); |
|
2206 |
|
2207 // XXX pass in the menu rect instead of always using the item rect |
|
2208 HIRect ignored; |
|
2209 HIThemeDrawMenuItem(&macRect, &macRect, &drawInfo, cgContext, HITHEME_ORIENTATION, &ignored); |
|
2210 } |
|
2211 break; |
|
2212 |
|
2213 case NS_THEME_MENUSEPARATOR: { |
|
2214 ThemeMenuState menuState; |
|
2215 if (IsDisabled(aFrame, eventState)) { |
|
2216 menuState = kThemeMenuDisabled; |
|
2217 } |
|
2218 else { |
|
2219 menuState = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ? |
|
2220 kThemeMenuSelected : kThemeMenuActive; |
|
2221 } |
|
2222 |
|
2223 HIThemeMenuItemDrawInfo midi = { 0, kThemeMenuItemPlain, menuState }; |
|
2224 HIThemeDrawMenuSeparator(&macRect, &macRect, &midi, cgContext, HITHEME_ORIENTATION); |
|
2225 } |
|
2226 break; |
|
2227 |
|
2228 case NS_THEME_TOOLTIP: |
|
2229 CGContextSetRGBFillColor(cgContext, 0.996, 1.000, 0.792, 0.950); |
|
2230 CGContextFillRect(cgContext, macRect); |
|
2231 break; |
|
2232 |
|
2233 case NS_THEME_CHECKBOX: |
|
2234 case NS_THEME_RADIO: { |
|
2235 bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX); |
|
2236 DrawCheckboxOrRadio(cgContext, isCheckbox, macRect, GetCheckedOrSelected(aFrame, !isCheckbox), |
|
2237 eventState, aFrame); |
|
2238 } |
|
2239 break; |
|
2240 |
|
2241 case NS_THEME_BUTTON: |
|
2242 if (IsDefaultButton(aFrame)) { |
|
2243 if (!IsDisabled(aFrame, eventState) && FrameIsInActiveWindow(aFrame) && |
|
2244 !QueueAnimatedContentForRefresh(aFrame->GetContent(), 10)) { |
|
2245 NS_WARNING("Unable to animate button!"); |
|
2246 } |
|
2247 DrawButton(cgContext, kThemePushButton, macRect, true, |
|
2248 kThemeButtonOff, kThemeAdornmentNone, eventState, aFrame); |
|
2249 } else if (IsButtonTypeMenu(aFrame)) { |
|
2250 DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame); |
|
2251 } else { |
|
2252 DrawPushButton(cgContext, macRect, eventState, aWidgetType, aFrame); |
|
2253 } |
|
2254 break; |
|
2255 |
|
2256 case NS_THEME_MOZ_MAC_HELP_BUTTON: |
|
2257 DrawPushButton(cgContext, macRect, eventState, aWidgetType, aFrame); |
|
2258 break; |
|
2259 |
|
2260 case NS_THEME_BUTTON_BEVEL: |
|
2261 DrawButton(cgContext, kThemeMediumBevelButton, macRect, |
|
2262 IsDefaultButton(aFrame), kThemeButtonOff, kThemeAdornmentNone, |
|
2263 eventState, aFrame); |
|
2264 break; |
|
2265 |
|
2266 case NS_THEME_SPINNER: { |
|
2267 nsIContent* content = aFrame->GetContent(); |
|
2268 if (content->IsHTML()) { |
|
2269 // In HTML the theming for the spin buttons is drawn individually into |
|
2270 // their own backgrounds instead of being drawn into the background of |
|
2271 // their spinner parent as it is for XUL. |
|
2272 break; |
|
2273 } |
|
2274 ThemeDrawState state = kThemeStateActive; |
|
2275 if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::state, |
|
2276 NS_LITERAL_STRING("up"), eCaseMatters)) { |
|
2277 state = kThemeStatePressedUp; |
|
2278 } |
|
2279 else if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::state, |
|
2280 NS_LITERAL_STRING("down"), eCaseMatters)) { |
|
2281 state = kThemeStatePressedDown; |
|
2282 } |
|
2283 |
|
2284 DrawSpinButtons(cgContext, kThemeIncDecButton, macRect, state, |
|
2285 kThemeAdornmentNone, eventState, aFrame); |
|
2286 } |
|
2287 break; |
|
2288 |
|
2289 case NS_THEME_SPINNER_UP_BUTTON: |
|
2290 case NS_THEME_SPINNER_DOWN_BUTTON: { |
|
2291 nsNumberControlFrame* numberControlFrame = |
|
2292 nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame); |
|
2293 if (numberControlFrame) { |
|
2294 ThemeDrawState state = kThemeStateActive; |
|
2295 if (numberControlFrame->SpinnerUpButtonIsDepressed()) { |
|
2296 state = kThemeStatePressedUp; |
|
2297 } else if (numberControlFrame->SpinnerDownButtonIsDepressed()) { |
|
2298 state = kThemeStatePressedDown; |
|
2299 } |
|
2300 DrawSpinButton(cgContext, kThemeIncDecButtonMini, macRect, state, |
|
2301 kThemeAdornmentNone, eventState, aFrame, aWidgetType); |
|
2302 } |
|
2303 } |
|
2304 break; |
|
2305 |
|
2306 case NS_THEME_TOOLBAR_BUTTON: |
|
2307 DrawSegment(cgContext, macRect, eventState, aFrame, toolbarButtonRenderSettings); |
|
2308 break; |
|
2309 |
|
2310 case NS_THEME_TOOLBAR_SEPARATOR: { |
|
2311 HIThemeSeparatorDrawInfo sdi = { 0, kThemeStateActive }; |
|
2312 HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION); |
|
2313 } |
|
2314 break; |
|
2315 |
|
2316 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: |
|
2317 case NS_THEME_TOOLBAR: { |
|
2318 NSWindow* win = NativeWindowForFrame(aFrame); |
|
2319 if (ToolbarCanBeUnified(cgContext, macRect, win)) { |
|
2320 DrawUnifiedToolbar(cgContext, macRect, win); |
|
2321 break; |
|
2322 } |
|
2323 BOOL isMain = [win isMainWindow]; |
|
2324 CGRect drawRect = macRect; |
|
2325 |
|
2326 // top border |
|
2327 drawRect.size.height = 1.0f; |
|
2328 DrawNativeGreyColorInRect(cgContext, toolbarTopBorderGrey, drawRect, isMain); |
|
2329 |
|
2330 // background |
|
2331 drawRect.origin.y += drawRect.size.height; |
|
2332 drawRect.size.height = macRect.size.height - 2.0f; |
|
2333 DrawNativeGreyColorInRect(cgContext, toolbarFillGrey, drawRect, isMain); |
|
2334 |
|
2335 // bottom border |
|
2336 drawRect.origin.y += drawRect.size.height; |
|
2337 drawRect.size.height = 1.0f; |
|
2338 DrawNativeGreyColorInRect(cgContext, toolbarBottomBorderGrey, drawRect, isMain); |
|
2339 } |
|
2340 break; |
|
2341 |
|
2342 case NS_THEME_WINDOW_TITLEBAR: { |
|
2343 NSWindow* win = NativeWindowForFrame(aFrame); |
|
2344 BOOL isMain = [win isMainWindow]; |
|
2345 float unifiedToolbarHeight = [win isKindOfClass:[ToolbarWindow class]] ? |
|
2346 [(ToolbarWindow*)win unifiedToolbarHeight] : macRect.size.height; |
|
2347 DrawNativeTitlebar(cgContext, macRect, unifiedToolbarHeight, isMain); |
|
2348 } |
|
2349 break; |
|
2350 |
|
2351 case NS_THEME_TOOLBOX: { |
|
2352 HIThemeHeaderDrawInfo hdi = { 0, kThemeStateActive, kHIThemeHeaderKindWindow }; |
|
2353 HIThemeDrawHeader(&macRect, &hdi, cgContext, HITHEME_ORIENTATION); |
|
2354 } |
|
2355 break; |
|
2356 |
|
2357 case NS_THEME_STATUSBAR: |
|
2358 DrawStatusBar(cgContext, macRect, aFrame); |
|
2359 break; |
|
2360 |
|
2361 case NS_THEME_DROPDOWN: |
|
2362 case NS_THEME_DROPDOWN_TEXTFIELD: |
|
2363 DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame); |
|
2364 break; |
|
2365 |
|
2366 case NS_THEME_DROPDOWN_BUTTON: |
|
2367 DrawButton(cgContext, kThemeArrowButton, macRect, false, kThemeButtonOn, |
|
2368 kThemeAdornmentArrowDownArrow, eventState, aFrame); |
|
2369 break; |
|
2370 |
|
2371 case NS_THEME_GROUPBOX: { |
|
2372 HIThemeGroupBoxDrawInfo gdi = { 0, kThemeStateActive, kHIThemeGroupBoxKindPrimary }; |
|
2373 HIThemeDrawGroupBox(&macRect, &gdi, cgContext, HITHEME_ORIENTATION); |
|
2374 break; |
|
2375 } |
|
2376 |
|
2377 case NS_THEME_TEXTFIELD: |
|
2378 case NS_THEME_NUMBER_INPUT: |
|
2379 // HIThemeSetFill is not available on 10.3 |
|
2380 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); |
|
2381 CGContextFillRect(cgContext, macRect); |
|
2382 |
|
2383 // XUL textboxes set the native appearance on the containing box, while |
|
2384 // concrete focus is set on the html:input element within it. We can |
|
2385 // though, check the focused attribute of xul textboxes in this case. |
|
2386 // On Mac, focus rings are always shown for textboxes, so we do not need |
|
2387 // to check the window's focus ring state here |
|
2388 if (aFrame->GetContent()->IsXUL() && IsFocused(aFrame)) { |
|
2389 eventState |= NS_EVENT_STATE_FOCUS; |
|
2390 } |
|
2391 |
|
2392 DrawFrame(cgContext, kHIThemeFrameTextFieldSquare, macRect, |
|
2393 IsDisabled(aFrame, eventState) || IsReadOnly(aFrame), eventState); |
|
2394 break; |
|
2395 |
|
2396 case NS_THEME_SEARCHFIELD: |
|
2397 DrawSearchField(cgContext, macRect, aFrame, eventState); |
|
2398 break; |
|
2399 |
|
2400 case NS_THEME_PROGRESSBAR: |
|
2401 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) { |
|
2402 NS_WARNING("Unable to animate progressbar!"); |
|
2403 } |
|
2404 DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame, eventState), |
|
2405 aFrame->StyleDisplay()->mOrient != NS_STYLE_ORIENT_VERTICAL, |
|
2406 GetProgressValue(aFrame), GetProgressMaxValue(aFrame), aFrame); |
|
2407 break; |
|
2408 |
|
2409 case NS_THEME_PROGRESSBAR_VERTICAL: |
|
2410 DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame, eventState), |
|
2411 false, GetProgressValue(aFrame), |
|
2412 GetProgressMaxValue(aFrame), aFrame); |
|
2413 break; |
|
2414 |
|
2415 case NS_THEME_METERBAR: |
|
2416 DrawMeter(cgContext, macRect, aFrame); |
|
2417 break; |
|
2418 |
|
2419 case NS_THEME_PROGRESSBAR_CHUNK: |
|
2420 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: |
|
2421 case NS_THEME_METERBAR_CHUNK: |
|
2422 // Do nothing: progress and meter bars cases will draw chunks. |
|
2423 break; |
|
2424 |
|
2425 case NS_THEME_TREEVIEW_TWISTY: |
|
2426 DrawButton(cgContext, kThemeDisclosureButton, macRect, false, |
|
2427 kThemeDisclosureRight, kThemeAdornmentNone, eventState, aFrame); |
|
2428 break; |
|
2429 |
|
2430 case NS_THEME_TREEVIEW_TWISTY_OPEN: |
|
2431 DrawButton(cgContext, kThemeDisclosureButton, macRect, false, |
|
2432 kThemeDisclosureDown, kThemeAdornmentNone, eventState, aFrame); |
|
2433 break; |
|
2434 |
|
2435 case NS_THEME_TREEVIEW_HEADER_CELL: { |
|
2436 TreeSortDirection sortDirection = GetTreeSortDirection(aFrame); |
|
2437 DrawButton(cgContext, kThemeListHeaderButton, macRect, false, |
|
2438 sortDirection == eTreeSortDirection_Natural ? kThemeButtonOff : kThemeButtonOn, |
|
2439 sortDirection == eTreeSortDirection_Ascending ? |
|
2440 kThemeAdornmentHeaderButtonSortUp : kThemeAdornmentNone, eventState, aFrame); |
|
2441 } |
|
2442 break; |
|
2443 |
|
2444 case NS_THEME_TREEVIEW_TREEITEM: |
|
2445 case NS_THEME_TREEVIEW: |
|
2446 // HIThemeSetFill is not available on 10.3 |
|
2447 // HIThemeSetFill(kThemeBrushWhite, NULL, cgContext, HITHEME_ORIENTATION); |
|
2448 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); |
|
2449 CGContextFillRect(cgContext, macRect); |
|
2450 break; |
|
2451 |
|
2452 case NS_THEME_TREEVIEW_HEADER: |
|
2453 // do nothing, taken care of by individual header cells |
|
2454 case NS_THEME_TREEVIEW_HEADER_SORTARROW: |
|
2455 // do nothing, taken care of by treeview header |
|
2456 case NS_THEME_TREEVIEW_LINE: |
|
2457 // do nothing, these lines don't exist on macos |
|
2458 break; |
|
2459 |
|
2460 case NS_THEME_SCALE_HORIZONTAL: |
|
2461 case NS_THEME_SCALE_VERTICAL: { |
|
2462 int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); |
|
2463 int32_t minpos = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0); |
|
2464 int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); |
|
2465 if (!maxpos) |
|
2466 maxpos = 100; |
|
2467 |
|
2468 bool reverse = aFrame->GetContent()-> |
|
2469 AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, |
|
2470 NS_LITERAL_STRING("reverse"), eCaseMatters); |
|
2471 DrawScale(cgContext, macRect, eventState, |
|
2472 (aWidgetType == NS_THEME_SCALE_VERTICAL), reverse, |
|
2473 curpos, minpos, maxpos, aFrame); |
|
2474 } |
|
2475 break; |
|
2476 |
|
2477 case NS_THEME_SCALE_THUMB_HORIZONTAL: |
|
2478 case NS_THEME_SCALE_THUMB_VERTICAL: |
|
2479 // do nothing, drawn by scale |
|
2480 break; |
|
2481 |
|
2482 case NS_THEME_RANGE: { |
|
2483 nsRangeFrame *rangeFrame = do_QueryFrame(aFrame); |
|
2484 if (!rangeFrame) { |
|
2485 break; |
|
2486 } |
|
2487 // DrawScale requires integer min, max and value. This is purely for |
|
2488 // drawing, so we normalize to a range 0-1000 here. |
|
2489 int32_t value = int32_t(rangeFrame->GetValueAsFractionOfRange() * 1000); |
|
2490 int32_t min = 0; |
|
2491 int32_t max = 1000; |
|
2492 bool isVertical = !IsRangeHorizontal(aFrame); |
|
2493 bool reverseDir = |
|
2494 isVertical || |
|
2495 rangeFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
|
2496 DrawScale(cgContext, macRect, eventState, isVertical, reverseDir, |
|
2497 value, min, max, aFrame); |
|
2498 break; |
|
2499 } |
|
2500 |
|
2501 case NS_THEME_SCROLLBAR_SMALL: |
|
2502 case NS_THEME_SCROLLBAR: |
|
2503 if (!nsLookAndFeel::UseOverlayScrollbars()) { |
|
2504 DrawScrollbar(cgContext, macRect, aFrame); |
|
2505 } |
|
2506 break; |
|
2507 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: |
|
2508 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: |
|
2509 if (nsLookAndFeel::UseOverlayScrollbars()) { |
|
2510 BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL); |
|
2511 BOOL isRolledOver = CheckBooleanAttr(GetParentScrollbarFrame(aFrame), |
|
2512 nsGkAtoms::hover); |
|
2513 if (!nsCocoaFeatures::OnMountainLionOrLater() || !isRolledOver) { |
|
2514 if (isHorizontal) { |
|
2515 macRect.origin.y += 4; |
|
2516 macRect.size.height -= 4; |
|
2517 } else { |
|
2518 if (aFrame->StyleVisibility()->mDirection != |
|
2519 NS_STYLE_DIRECTION_RTL) { |
|
2520 macRect.origin.x += 4; |
|
2521 } |
|
2522 macRect.size.width -= 4; |
|
2523 } |
|
2524 } |
|
2525 const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame); |
|
2526 CUIDraw([NSWindow coreUIRenderer], macRect, cgContext, |
|
2527 (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: |
|
2528 @"kCUIWidgetOverlayScrollBar", @"widget", |
|
2529 @"regular", @"size", |
|
2530 (isRolledOver ? @"rollover" : @""), @"state", |
|
2531 (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey", |
|
2532 (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey", |
|
2533 [NSNumber numberWithBool:YES], @"indiconly", |
|
2534 [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey", |
|
2535 [NSNumber numberWithBool:YES], @"is.flipped", |
|
2536 nil], |
|
2537 nil); |
|
2538 } |
|
2539 break; |
|
2540 case NS_THEME_SCROLLBAR_BUTTON_UP: |
|
2541 case NS_THEME_SCROLLBAR_BUTTON_LEFT: |
|
2542 #if SCROLLBARS_VISUAL_DEBUG |
|
2543 CGContextSetRGBFillColor(cgContext, 1.0, 0, 0, 0.6); |
|
2544 CGContextFillRect(cgContext, macRect); |
|
2545 #endif |
|
2546 break; |
|
2547 case NS_THEME_SCROLLBAR_BUTTON_DOWN: |
|
2548 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: |
|
2549 #if SCROLLBARS_VISUAL_DEBUG |
|
2550 CGContextSetRGBFillColor(cgContext, 0, 1.0, 0, 0.6); |
|
2551 CGContextFillRect(cgContext, macRect); |
|
2552 #endif |
|
2553 break; |
|
2554 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
2555 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
2556 if (nsLookAndFeel::UseOverlayScrollbars() && |
|
2557 CheckBooleanAttr(GetParentScrollbarFrame(aFrame), nsGkAtoms::hover)) { |
|
2558 BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL); |
|
2559 if (!nsCocoaFeatures::OnMountainLionOrLater()) { |
|
2560 // On OSX 10.7, scrollbars don't grow when hovered. The adjustments |
|
2561 // below were obtained by trial and error. |
|
2562 if (isHorizontal) { |
|
2563 macRect.origin.y += 2.0; |
|
2564 } else { |
|
2565 if (aFrame->StyleVisibility()->mDirection != |
|
2566 NS_STYLE_DIRECTION_RTL) { |
|
2567 macRect.origin.x += 3.0; |
|
2568 } else { |
|
2569 macRect.origin.x -= 1.0; |
|
2570 } |
|
2571 } |
|
2572 } |
|
2573 const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame); |
|
2574 CUIDraw([NSWindow coreUIRenderer], macRect, cgContext, |
|
2575 (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: |
|
2576 @"kCUIWidgetOverlayScrollBar", @"widget", |
|
2577 @"regular", @"size", |
|
2578 (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey", |
|
2579 (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey", |
|
2580 [NSNumber numberWithBool:YES], @"noindicator", |
|
2581 [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey", |
|
2582 [NSNumber numberWithBool:YES], @"is.flipped", |
|
2583 nil], |
|
2584 nil); |
|
2585 } |
|
2586 break; |
|
2587 |
|
2588 case NS_THEME_TEXTFIELD_MULTILINE: { |
|
2589 // we have to draw this by hand because there is no HITheme value for it |
|
2590 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); |
|
2591 |
|
2592 CGContextFillRect(cgContext, macRect); |
|
2593 |
|
2594 CGContextSetLineWidth(cgContext, 1.0); |
|
2595 CGContextSetShouldAntialias(cgContext, false); |
|
2596 |
|
2597 // stroke everything but the top line of the text area |
|
2598 CGContextSetRGBStrokeColor(cgContext, 0.6, 0.6, 0.6, 1.0); |
|
2599 CGContextBeginPath(cgContext); |
|
2600 CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1); |
|
2601 CGContextAddLineToPoint(cgContext, macRect.origin.x, macRect.origin.y + macRect.size.height); |
|
2602 CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + macRect.size.height); |
|
2603 CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1); |
|
2604 CGContextStrokePath(cgContext); |
|
2605 |
|
2606 // stroke the line across the top of the text area |
|
2607 CGContextSetRGBStrokeColor(cgContext, 0.4510, 0.4510, 0.4510, 1.0); |
|
2608 CGContextBeginPath(cgContext); |
|
2609 CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1); |
|
2610 CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1); |
|
2611 CGContextStrokePath(cgContext); |
|
2612 |
|
2613 // draw a focus ring |
|
2614 if (eventState.HasState(NS_EVENT_STATE_FOCUS)) { |
|
2615 // We need to bring the rectangle in by 1 pixel on each side. |
|
2616 CGRect cgr = CGRectMake(macRect.origin.x + 1, |
|
2617 macRect.origin.y + 1, |
|
2618 macRect.size.width - 2, |
|
2619 macRect.size.height - 2); |
|
2620 HIThemeDrawFocusRect(&cgr, true, cgContext, kHIThemeOrientationNormal); |
|
2621 } |
|
2622 } |
|
2623 break; |
|
2624 |
|
2625 case NS_THEME_LISTBOX: { |
|
2626 // We have to draw this by hand because kHIThemeFrameListBox drawing |
|
2627 // is buggy on 10.5, see bug 579259. |
|
2628 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); |
|
2629 CGContextFillRect(cgContext, macRect); |
|
2630 |
|
2631 // #8E8E8E for the top border, #BEBEBE for the rest. |
|
2632 float x = macRect.origin.x, y = macRect.origin.y; |
|
2633 float w = macRect.size.width, h = macRect.size.height; |
|
2634 CGContextSetRGBFillColor(cgContext, 0.557, 0.557, 0.557, 1.0); |
|
2635 CGContextFillRect(cgContext, CGRectMake(x, y, w, 1)); |
|
2636 CGContextSetRGBFillColor(cgContext, 0.745, 0.745, 0.745, 1.0); |
|
2637 CGContextFillRect(cgContext, CGRectMake(x, y + 1, 1, h - 1)); |
|
2638 CGContextFillRect(cgContext, CGRectMake(x + w - 1, y + 1, 1, h - 1)); |
|
2639 CGContextFillRect(cgContext, CGRectMake(x + 1, y + h - 1, w - 2, 1)); |
|
2640 } |
|
2641 break; |
|
2642 |
|
2643 case NS_THEME_TAB: |
|
2644 DrawSegment(cgContext, macRect, eventState, aFrame, tabRenderSettings); |
|
2645 break; |
|
2646 |
|
2647 case NS_THEME_TAB_PANELS: |
|
2648 DrawTabPanel(cgContext, macRect, aFrame); |
|
2649 break; |
|
2650 |
|
2651 case NS_THEME_RESIZER: |
|
2652 DrawResizer(cgContext, macRect, aFrame); |
|
2653 break; |
|
2654 } |
|
2655 |
|
2656 nativeDrawing.EndNativeDrawing(); |
|
2657 |
|
2658 return NS_OK; |
|
2659 |
|
2660 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
2661 } |
|
2662 |
|
2663 nsIntMargin |
|
2664 nsNativeThemeCocoa::RTLAwareMargin(const nsIntMargin& aMargin, nsIFrame* aFrame) |
|
2665 { |
|
2666 if (IsFrameRTL(aFrame)) { |
|
2667 // Return a copy of aMargin w/ right & left reversed: |
|
2668 return nsIntMargin(aMargin.top, aMargin.left, |
|
2669 aMargin.bottom, aMargin.right); |
|
2670 } |
|
2671 |
|
2672 return aMargin; |
|
2673 } |
|
2674 |
|
2675 static const nsIntMargin kAquaDropdownBorder(1, 22, 2, 5); |
|
2676 static const nsIntMargin kAquaComboboxBorder(3, 20, 3, 4); |
|
2677 static const nsIntMargin kAquaSearchfieldBorder(3, 5, 2, 19); |
|
2678 |
|
2679 NS_IMETHODIMP |
|
2680 nsNativeThemeCocoa::GetWidgetBorder(nsDeviceContext* aContext, |
|
2681 nsIFrame* aFrame, |
|
2682 uint8_t aWidgetType, |
|
2683 nsIntMargin* aResult) |
|
2684 { |
|
2685 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
2686 |
|
2687 aResult->SizeTo(0, 0, 0, 0); |
|
2688 |
|
2689 switch (aWidgetType) { |
|
2690 case NS_THEME_BUTTON: |
|
2691 { |
|
2692 if (IsButtonTypeMenu(aFrame)) { |
|
2693 *aResult = RTLAwareMargin(kAquaDropdownBorder, aFrame); |
|
2694 } else { |
|
2695 aResult->SizeTo(1, 7, 3, 7); |
|
2696 } |
|
2697 break; |
|
2698 } |
|
2699 |
|
2700 case NS_THEME_TOOLBAR_BUTTON: |
|
2701 { |
|
2702 aResult->SizeTo(1, 4, 1, 4); |
|
2703 break; |
|
2704 } |
|
2705 |
|
2706 case NS_THEME_CHECKBOX: |
|
2707 case NS_THEME_RADIO: |
|
2708 { |
|
2709 // nsFormControlFrame::GetIntrinsicWidth and nsFormControlFrame::GetIntrinsicHeight |
|
2710 // assume a border width of 2px. |
|
2711 aResult->SizeTo(2, 2, 2, 2); |
|
2712 break; |
|
2713 } |
|
2714 |
|
2715 case NS_THEME_DROPDOWN: |
|
2716 case NS_THEME_DROPDOWN_BUTTON: |
|
2717 *aResult = RTLAwareMargin(kAquaDropdownBorder, aFrame); |
|
2718 break; |
|
2719 |
|
2720 case NS_THEME_DROPDOWN_TEXTFIELD: |
|
2721 *aResult = RTLAwareMargin(kAquaComboboxBorder, aFrame); |
|
2722 break; |
|
2723 |
|
2724 case NS_THEME_NUMBER_INPUT: |
|
2725 case NS_THEME_TEXTFIELD: |
|
2726 { |
|
2727 SInt32 frameOutset = 0; |
|
2728 ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset); |
|
2729 |
|
2730 SInt32 textPadding = 0; |
|
2731 ::GetThemeMetric(kThemeMetricEditTextWhitespace, &textPadding); |
|
2732 |
|
2733 frameOutset += textPadding; |
|
2734 |
|
2735 aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset); |
|
2736 break; |
|
2737 } |
|
2738 |
|
2739 case NS_THEME_TEXTFIELD_MULTILINE: |
|
2740 aResult->SizeTo(1, 1, 1, 1); |
|
2741 break; |
|
2742 |
|
2743 case NS_THEME_SEARCHFIELD: |
|
2744 *aResult = RTLAwareMargin(kAquaSearchfieldBorder, aFrame); |
|
2745 break; |
|
2746 |
|
2747 case NS_THEME_LISTBOX: |
|
2748 { |
|
2749 SInt32 frameOutset = 0; |
|
2750 ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset); |
|
2751 aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset); |
|
2752 break; |
|
2753 } |
|
2754 |
|
2755 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
2756 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
2757 { |
|
2758 bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL); |
|
2759 |
|
2760 // On Lion and later, scrollbars have no arrows. |
|
2761 if (!nsCocoaFeatures::OnLionOrLater()) { |
|
2762 // There's only an endcap to worry about when both arrows are on the bottom |
|
2763 NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"]; |
|
2764 if (!buttonPlacement || [buttonPlacement isEqualToString:@"DoubleMax"]) { |
|
2765 nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); |
|
2766 if (!scrollbarFrame) return NS_ERROR_FAILURE; |
|
2767 bool isSmall = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL); |
|
2768 |
|
2769 // There isn't a metric for this, so just hardcode a best guess at the value. |
|
2770 // This value is even less exact due to the fact that the endcap is partially concave. |
|
2771 int32_t endcapSize = isSmall ? 5 : 6; |
|
2772 |
|
2773 if (isHorizontal) |
|
2774 aResult->SizeTo(0, 0, 0, endcapSize); |
|
2775 else |
|
2776 aResult->SizeTo(endcapSize, 0, 0, 0); |
|
2777 } |
|
2778 } |
|
2779 |
|
2780 if (nsLookAndFeel::UseOverlayScrollbars()) { |
|
2781 if (isHorizontal) { |
|
2782 aResult->SizeTo(2, 1, 1, 1); |
|
2783 } else { |
|
2784 aResult->SizeTo(1, 1, 1, 2); |
|
2785 } |
|
2786 } |
|
2787 |
|
2788 break; |
|
2789 } |
|
2790 |
|
2791 case NS_THEME_STATUSBAR: |
|
2792 aResult->SizeTo(1, 0, 0, 0); |
|
2793 break; |
|
2794 } |
|
2795 |
|
2796 if (IsHiDPIContext(aContext)) { |
|
2797 *aResult = *aResult + *aResult; // doubled |
|
2798 } |
|
2799 |
|
2800 return NS_OK; |
|
2801 |
|
2802 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
2803 } |
|
2804 |
|
2805 // Return false here to indicate that CSS padding values should be used. There is |
|
2806 // no reason to make a distinction between padding and border values, just specify |
|
2807 // whatever values you want in GetWidgetBorder and only use this to return true |
|
2808 // if you want to override CSS padding values. |
|
2809 bool |
|
2810 nsNativeThemeCocoa::GetWidgetPadding(nsDeviceContext* aContext, |
|
2811 nsIFrame* aFrame, |
|
2812 uint8_t aWidgetType, |
|
2813 nsIntMargin* aResult) |
|
2814 { |
|
2815 // We don't want CSS padding being used for certain widgets. |
|
2816 // See bug 381639 for an example of why. |
|
2817 switch (aWidgetType) { |
|
2818 case NS_THEME_BUTTON: |
|
2819 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize |
|
2820 // and have a meaningful baseline, so they can't have |
|
2821 // author-specified padding. |
|
2822 case NS_THEME_CHECKBOX: |
|
2823 case NS_THEME_RADIO: |
|
2824 aResult->SizeTo(0, 0, 0, 0); |
|
2825 return true; |
|
2826 } |
|
2827 return false; |
|
2828 } |
|
2829 |
|
2830 bool |
|
2831 nsNativeThemeCocoa::GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFrame, |
|
2832 uint8_t aWidgetType, nsRect* aOverflowRect) |
|
2833 { |
|
2834 int32_t p2a = aContext->AppUnitsPerDevPixel(); |
|
2835 switch (aWidgetType) { |
|
2836 case NS_THEME_BUTTON: |
|
2837 case NS_THEME_MOZ_MAC_HELP_BUTTON: |
|
2838 case NS_THEME_TOOLBAR_BUTTON: |
|
2839 case NS_THEME_NUMBER_INPUT: |
|
2840 case NS_THEME_TEXTFIELD: |
|
2841 case NS_THEME_TEXTFIELD_MULTILINE: |
|
2842 case NS_THEME_SEARCHFIELD: |
|
2843 case NS_THEME_LISTBOX: |
|
2844 case NS_THEME_DROPDOWN: |
|
2845 case NS_THEME_DROPDOWN_BUTTON: |
|
2846 case NS_THEME_DROPDOWN_TEXTFIELD: |
|
2847 case NS_THEME_CHECKBOX: |
|
2848 case NS_THEME_RADIO: |
|
2849 case NS_THEME_TAB: |
|
2850 { |
|
2851 // We assume that the above widgets can draw a focus ring that will be less than |
|
2852 // or equal to 4 pixels thick. |
|
2853 nsIntMargin extraSize = nsIntMargin(MAX_FOCUS_RING_WIDTH, |
|
2854 MAX_FOCUS_RING_WIDTH, |
|
2855 MAX_FOCUS_RING_WIDTH, |
|
2856 MAX_FOCUS_RING_WIDTH); |
|
2857 nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a), |
|
2858 NSIntPixelsToAppUnits(extraSize.right, p2a), |
|
2859 NSIntPixelsToAppUnits(extraSize.bottom, p2a), |
|
2860 NSIntPixelsToAppUnits(extraSize.left, p2a)); |
|
2861 aOverflowRect->Inflate(m); |
|
2862 return true; |
|
2863 } |
|
2864 case NS_THEME_PROGRESSBAR: |
|
2865 { |
|
2866 // Progress bars draw a 2 pixel white shadow under their progress indicators |
|
2867 nsMargin m(0, 0, NSIntPixelsToAppUnits(2, p2a), 0); |
|
2868 aOverflowRect->Inflate(m); |
|
2869 return true; |
|
2870 } |
|
2871 } |
|
2872 |
|
2873 return false; |
|
2874 } |
|
2875 |
|
2876 static const int32_t kRegularScrollbarThumbMinSize = 26; |
|
2877 static const int32_t kSmallScrollbarThumbMinSize = 26; |
|
2878 |
|
2879 NS_IMETHODIMP |
|
2880 nsNativeThemeCocoa::GetMinimumWidgetSize(nsRenderingContext* aContext, |
|
2881 nsIFrame* aFrame, |
|
2882 uint8_t aWidgetType, |
|
2883 nsIntSize* aResult, |
|
2884 bool* aIsOverridable) |
|
2885 { |
|
2886 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
2887 |
|
2888 aResult->SizeTo(0,0); |
|
2889 *aIsOverridable = true; |
|
2890 |
|
2891 switch (aWidgetType) { |
|
2892 case NS_THEME_BUTTON: |
|
2893 { |
|
2894 aResult->SizeTo(pushButtonSettings.minimumSizes[miniControlSize].width, |
|
2895 pushButtonSettings.naturalSizes[miniControlSize].height); |
|
2896 break; |
|
2897 } |
|
2898 |
|
2899 case NS_THEME_MOZ_MAC_HELP_BUTTON: |
|
2900 { |
|
2901 aResult->SizeTo(kHelpButtonSize.width, kHelpButtonSize.height); |
|
2902 *aIsOverridable = false; |
|
2903 break; |
|
2904 } |
|
2905 |
|
2906 case NS_THEME_TOOLBAR_BUTTON: |
|
2907 { |
|
2908 aResult->SizeTo(0, toolbarButtonHeights[miniControlSize]); |
|
2909 break; |
|
2910 } |
|
2911 |
|
2912 case NS_THEME_SPINNER: |
|
2913 case NS_THEME_SPINNER_UP_BUTTON: |
|
2914 case NS_THEME_SPINNER_DOWN_BUTTON: |
|
2915 { |
|
2916 SInt32 buttonHeight = 0, buttonWidth = 0; |
|
2917 if (aFrame->GetContent()->IsXUL()) { |
|
2918 ::GetThemeMetric(kThemeMetricLittleArrowsWidth, &buttonWidth); |
|
2919 ::GetThemeMetric(kThemeMetricLittleArrowsHeight, &buttonHeight); |
|
2920 } else { |
|
2921 NSSize size = |
|
2922 spinnerSettings.minimumSizes[EnumSizeForCocoaSize(NSMiniControlSize)]; |
|
2923 buttonWidth = size.width; |
|
2924 buttonHeight = size.height; |
|
2925 if (aWidgetType != NS_THEME_SPINNER) { |
|
2926 // the buttons are half the height of the spinner |
|
2927 buttonHeight /= 2; |
|
2928 } |
|
2929 } |
|
2930 aResult->SizeTo(buttonWidth, buttonHeight); |
|
2931 *aIsOverridable = true; |
|
2932 break; |
|
2933 } |
|
2934 |
|
2935 case NS_THEME_DROPDOWN: |
|
2936 case NS_THEME_DROPDOWN_BUTTON: |
|
2937 { |
|
2938 SInt32 popupHeight = 0; |
|
2939 ::GetThemeMetric(kThemeMetricPopupButtonHeight, &popupHeight); |
|
2940 aResult->SizeTo(0, popupHeight); |
|
2941 break; |
|
2942 } |
|
2943 |
|
2944 case NS_THEME_NUMBER_INPUT: |
|
2945 case NS_THEME_TEXTFIELD: |
|
2946 case NS_THEME_TEXTFIELD_MULTILINE: |
|
2947 case NS_THEME_SEARCHFIELD: |
|
2948 { |
|
2949 // at minimum, we should be tall enough for 9pt text. |
|
2950 // I'm using hardcoded values here because the appearance manager |
|
2951 // values for the frame size are incorrect. |
|
2952 aResult->SizeTo(0, (2 + 2) /* top */ + 9 + (1 + 1) /* bottom */); |
|
2953 break; |
|
2954 } |
|
2955 |
|
2956 case NS_THEME_WINDOW_BUTTON_BOX: { |
|
2957 NSSize size = WindowButtonsSize(aFrame); |
|
2958 aResult->SizeTo(size.width, size.height); |
|
2959 *aIsOverridable = false; |
|
2960 break; |
|
2961 } |
|
2962 |
|
2963 case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON: { |
|
2964 if ([NativeWindowForFrame(aFrame) respondsToSelector:@selector(toggleFullScreen:)]) { |
|
2965 // This value is hardcoded because it's needed before we can measure the |
|
2966 // position and size of the fullscreen button. |
|
2967 aResult->SizeTo(16, 17); |
|
2968 } |
|
2969 *aIsOverridable = false; |
|
2970 break; |
|
2971 } |
|
2972 |
|
2973 case NS_THEME_PROGRESSBAR: |
|
2974 { |
|
2975 SInt32 barHeight = 0; |
|
2976 ::GetThemeMetric(kThemeMetricNormalProgressBarThickness, &barHeight); |
|
2977 aResult->SizeTo(0, barHeight); |
|
2978 break; |
|
2979 } |
|
2980 |
|
2981 case NS_THEME_TREEVIEW_TWISTY: |
|
2982 case NS_THEME_TREEVIEW_TWISTY_OPEN: |
|
2983 { |
|
2984 SInt32 twistyHeight = 0, twistyWidth = 0; |
|
2985 ::GetThemeMetric(kThemeMetricDisclosureButtonWidth, &twistyWidth); |
|
2986 ::GetThemeMetric(kThemeMetricDisclosureButtonHeight, &twistyHeight); |
|
2987 aResult->SizeTo(twistyWidth, twistyHeight); |
|
2988 *aIsOverridable = false; |
|
2989 break; |
|
2990 } |
|
2991 |
|
2992 case NS_THEME_TREEVIEW_HEADER: |
|
2993 case NS_THEME_TREEVIEW_HEADER_CELL: |
|
2994 { |
|
2995 SInt32 headerHeight = 0; |
|
2996 ::GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight); |
|
2997 aResult->SizeTo(0, headerHeight - 1); // We don't need the top border. |
|
2998 break; |
|
2999 } |
|
3000 |
|
3001 case NS_THEME_TAB: |
|
3002 { |
|
3003 aResult->SizeTo(0, tabHeights[miniControlSize]); |
|
3004 break; |
|
3005 } |
|
3006 |
|
3007 case NS_THEME_RANGE: |
|
3008 { |
|
3009 // The Mac Appearance Manager API (the old API we're currently using) |
|
3010 // doesn't define constants to obtain a minimum size for sliders. We use |
|
3011 // the "thickness" of a slider that has default dimensions for both the |
|
3012 // minimum width and height to get something sane and so that paint |
|
3013 // invalidation works. |
|
3014 SInt32 size = 0; |
|
3015 if (IsRangeHorizontal(aFrame)) { |
|
3016 ::GetThemeMetric(kThemeMetricHSliderHeight, &size); |
|
3017 } else { |
|
3018 ::GetThemeMetric(kThemeMetricVSliderWidth, &size); |
|
3019 } |
|
3020 aResult->SizeTo(size, size); |
|
3021 *aIsOverridable = true; |
|
3022 break; |
|
3023 } |
|
3024 |
|
3025 case NS_THEME_RANGE_THUMB: |
|
3026 { |
|
3027 SInt32 width = 0; |
|
3028 SInt32 height = 0; |
|
3029 ::GetThemeMetric(kThemeMetricSliderMinThumbWidth, &width); |
|
3030 ::GetThemeMetric(kThemeMetricSliderMinThumbHeight, &height); |
|
3031 aResult->SizeTo(width, height); |
|
3032 *aIsOverridable = false; |
|
3033 break; |
|
3034 } |
|
3035 |
|
3036 case NS_THEME_SCALE_HORIZONTAL: |
|
3037 { |
|
3038 SInt32 scaleHeight = 0; |
|
3039 ::GetThemeMetric(kThemeMetricHSliderHeight, &scaleHeight); |
|
3040 aResult->SizeTo(scaleHeight, scaleHeight); |
|
3041 *aIsOverridable = false; |
|
3042 break; |
|
3043 } |
|
3044 |
|
3045 case NS_THEME_SCALE_VERTICAL: |
|
3046 { |
|
3047 SInt32 scaleWidth = 0; |
|
3048 ::GetThemeMetric(kThemeMetricVSliderWidth, &scaleWidth); |
|
3049 aResult->SizeTo(scaleWidth, scaleWidth); |
|
3050 *aIsOverridable = false; |
|
3051 break; |
|
3052 } |
|
3053 |
|
3054 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: |
|
3055 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: |
|
3056 { |
|
3057 // Find our parent scrollbar frame in order to find out whether we're in |
|
3058 // a small or a large scrollbar. |
|
3059 nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); |
|
3060 if (!scrollbarFrame) |
|
3061 return NS_ERROR_FAILURE; |
|
3062 |
|
3063 bool isSmall = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL); |
|
3064 bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL); |
|
3065 int32_t& minSize = isHorizontal ? aResult->width : aResult->height; |
|
3066 minSize = isSmall ? kSmallScrollbarThumbMinSize : kRegularScrollbarThumbMinSize; |
|
3067 break; |
|
3068 } |
|
3069 |
|
3070 case NS_THEME_SCROLLBAR: |
|
3071 case NS_THEME_SCROLLBAR_SMALL: |
|
3072 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
3073 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
3074 { |
|
3075 *aIsOverridable = false; |
|
3076 |
|
3077 if (nsLookAndFeel::UseOverlayScrollbars()) { |
|
3078 nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); |
|
3079 if (scrollbarFrame && |
|
3080 scrollbarFrame->StyleDisplay()->mAppearance == |
|
3081 NS_THEME_SCROLLBAR_SMALL) { |
|
3082 aResult->SizeTo(14, 14); |
|
3083 } |
|
3084 else { |
|
3085 aResult->SizeTo(16, 16); |
|
3086 } |
|
3087 break; |
|
3088 } |
|
3089 |
|
3090 // yeah, i know i'm cheating a little here, but i figure that it |
|
3091 // really doesn't matter if the scrollbar is vertical or horizontal |
|
3092 // and the width metric is a really good metric for every piece |
|
3093 // of the scrollbar. |
|
3094 |
|
3095 nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); |
|
3096 if (!scrollbarFrame) return NS_ERROR_FAILURE; |
|
3097 |
|
3098 int32_t themeMetric = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ? |
|
3099 kThemeMetricSmallScrollBarWidth : |
|
3100 kThemeMetricScrollBarWidth; |
|
3101 SInt32 scrollbarWidth = 0; |
|
3102 ::GetThemeMetric(themeMetric, &scrollbarWidth); |
|
3103 aResult->SizeTo(scrollbarWidth, scrollbarWidth); |
|
3104 break; |
|
3105 } |
|
3106 |
|
3107 case NS_THEME_SCROLLBAR_NON_DISAPPEARING: |
|
3108 { |
|
3109 int32_t themeMetric = kThemeMetricScrollBarWidth; |
|
3110 |
|
3111 if (aFrame) { |
|
3112 nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); |
|
3113 if (scrollbarFrame && |
|
3114 scrollbarFrame->StyleDisplay()->mAppearance == |
|
3115 NS_THEME_SCROLLBAR_SMALL) { |
|
3116 // XXX We're interested in the width of non-disappearing scrollbars |
|
3117 // to leave enough space for a dropmarker in non-native styled |
|
3118 // comboboxes (bug 869314). It isn't clear to me if comboboxes can |
|
3119 // ever have small scrollbars. |
|
3120 themeMetric = kThemeMetricSmallScrollBarWidth; |
|
3121 } |
|
3122 } |
|
3123 |
|
3124 SInt32 scrollbarWidth = 0; |
|
3125 ::GetThemeMetric(themeMetric, &scrollbarWidth); |
|
3126 aResult->SizeTo(scrollbarWidth, scrollbarWidth); |
|
3127 break; |
|
3128 } |
|
3129 |
|
3130 case NS_THEME_SCROLLBAR_BUTTON_UP: |
|
3131 case NS_THEME_SCROLLBAR_BUTTON_DOWN: |
|
3132 case NS_THEME_SCROLLBAR_BUTTON_LEFT: |
|
3133 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: |
|
3134 { |
|
3135 nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); |
|
3136 if (!scrollbarFrame) return NS_ERROR_FAILURE; |
|
3137 |
|
3138 // Since there is no NS_THEME_SCROLLBAR_BUTTON_UP_SMALL we need to ask the parent what appearance style it has. |
|
3139 int32_t themeMetric = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ? |
|
3140 kThemeMetricSmallScrollBarWidth : |
|
3141 kThemeMetricScrollBarWidth; |
|
3142 SInt32 scrollbarWidth = 0; |
|
3143 ::GetThemeMetric(themeMetric, &scrollbarWidth); |
|
3144 |
|
3145 // It seems that for both sizes of scrollbar, the buttons are one pixel "longer". |
|
3146 if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT || aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) |
|
3147 aResult->SizeTo(scrollbarWidth+1, scrollbarWidth); |
|
3148 else |
|
3149 aResult->SizeTo(scrollbarWidth, scrollbarWidth+1); |
|
3150 |
|
3151 *aIsOverridable = false; |
|
3152 break; |
|
3153 } |
|
3154 case NS_THEME_RESIZER: |
|
3155 { |
|
3156 HIThemeGrowBoxDrawInfo drawInfo; |
|
3157 drawInfo.version = 0; |
|
3158 drawInfo.state = kThemeStateActive; |
|
3159 drawInfo.kind = kHIThemeGrowBoxKindNormal; |
|
3160 drawInfo.direction = kThemeGrowRight | kThemeGrowDown; |
|
3161 drawInfo.size = kHIThemeGrowBoxSizeNormal; |
|
3162 HIPoint pnt = { 0, 0 }; |
|
3163 HIRect bounds; |
|
3164 HIThemeGetGrowBoxBounds(&pnt, &drawInfo, &bounds); |
|
3165 aResult->SizeTo(bounds.size.width, bounds.size.height); |
|
3166 *aIsOverridable = false; |
|
3167 } |
|
3168 } |
|
3169 |
|
3170 if (IsHiDPIContext(aContext->DeviceContext())) { |
|
3171 *aResult = *aResult * 2; |
|
3172 } |
|
3173 |
|
3174 return NS_OK; |
|
3175 |
|
3176 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
3177 } |
|
3178 |
|
3179 NS_IMETHODIMP |
|
3180 nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, |
|
3181 nsIAtom* aAttribute, bool* aShouldRepaint) |
|
3182 { |
|
3183 // Some widget types just never change state. |
|
3184 switch (aWidgetType) { |
|
3185 case NS_THEME_WINDOW_TITLEBAR: |
|
3186 case NS_THEME_TOOLBOX: |
|
3187 case NS_THEME_TOOLBAR: |
|
3188 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: |
|
3189 case NS_THEME_STATUSBAR: |
|
3190 case NS_THEME_STATUSBAR_PANEL: |
|
3191 case NS_THEME_STATUSBAR_RESIZER_PANEL: |
|
3192 case NS_THEME_TOOLTIP: |
|
3193 case NS_THEME_TAB_PANELS: |
|
3194 case NS_THEME_TAB_PANEL: |
|
3195 case NS_THEME_DIALOG: |
|
3196 case NS_THEME_MENUPOPUP: |
|
3197 case NS_THEME_GROUPBOX: |
|
3198 case NS_THEME_PROGRESSBAR_CHUNK: |
|
3199 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: |
|
3200 case NS_THEME_PROGRESSBAR: |
|
3201 case NS_THEME_PROGRESSBAR_VERTICAL: |
|
3202 case NS_THEME_METERBAR: |
|
3203 case NS_THEME_METERBAR_CHUNK: |
|
3204 *aShouldRepaint = false; |
|
3205 return NS_OK; |
|
3206 } |
|
3207 |
|
3208 // XXXdwh Not sure what can really be done here. Can at least guess for |
|
3209 // specific widgets that they're highly unlikely to have certain states. |
|
3210 // For example, a toolbar doesn't care about any states. |
|
3211 if (!aAttribute) { |
|
3212 // Hover/focus/active changed. Always repaint. |
|
3213 *aShouldRepaint = true; |
|
3214 } else { |
|
3215 // Check the attribute to see if it's relevant. |
|
3216 // disabled, checked, dlgtype, default, etc. |
|
3217 *aShouldRepaint = false; |
|
3218 if (aAttribute == nsGkAtoms::disabled || |
|
3219 aAttribute == nsGkAtoms::checked || |
|
3220 aAttribute == nsGkAtoms::selected || |
|
3221 aAttribute == nsGkAtoms::menuactive || |
|
3222 aAttribute == nsGkAtoms::sortDirection || |
|
3223 aAttribute == nsGkAtoms::focused || |
|
3224 aAttribute == nsGkAtoms::_default || |
|
3225 aAttribute == nsGkAtoms::open || |
|
3226 aAttribute == nsGkAtoms::hover) |
|
3227 *aShouldRepaint = true; |
|
3228 } |
|
3229 |
|
3230 return NS_OK; |
|
3231 } |
|
3232 |
|
3233 NS_IMETHODIMP |
|
3234 nsNativeThemeCocoa::ThemeChanged() |
|
3235 { |
|
3236 // This is unimplemented because we don't care if gecko changes its theme |
|
3237 // and Mac OS X doesn't have themes. |
|
3238 return NS_OK; |
|
3239 } |
|
3240 |
|
3241 bool |
|
3242 nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame, |
|
3243 uint8_t aWidgetType) |
|
3244 { |
|
3245 // We don't have CSS set up to render non-native scrollbars on Mac OS X so we |
|
3246 // render natively even if native theme support is disabled. |
|
3247 if (aWidgetType != NS_THEME_SCROLLBAR && |
|
3248 aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled()) |
|
3249 return false; |
|
3250 |
|
3251 // if this is a dropdown button in a combobox the answer is always no |
|
3252 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON) { |
|
3253 nsIFrame* parentFrame = aFrame->GetParent(); |
|
3254 if (parentFrame && (parentFrame->GetType() == nsGkAtoms::comboboxControlFrame)) |
|
3255 return false; |
|
3256 } |
|
3257 |
|
3258 switch (aWidgetType) { |
|
3259 case NS_THEME_LISTBOX: |
|
3260 |
|
3261 case NS_THEME_DIALOG: |
|
3262 case NS_THEME_WINDOW: |
|
3263 case NS_THEME_WINDOW_BUTTON_BOX: |
|
3264 case NS_THEME_WINDOW_TITLEBAR: |
|
3265 case NS_THEME_MENUPOPUP: |
|
3266 case NS_THEME_MENUITEM: |
|
3267 case NS_THEME_MENUSEPARATOR: |
|
3268 case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON: |
|
3269 case NS_THEME_TOOLTIP: |
|
3270 |
|
3271 case NS_THEME_CHECKBOX: |
|
3272 case NS_THEME_CHECKBOX_CONTAINER: |
|
3273 case NS_THEME_RADIO: |
|
3274 case NS_THEME_RADIO_CONTAINER: |
|
3275 case NS_THEME_GROUPBOX: |
|
3276 case NS_THEME_MOZ_MAC_HELP_BUTTON: |
|
3277 case NS_THEME_BUTTON: |
|
3278 case NS_THEME_BUTTON_BEVEL: |
|
3279 case NS_THEME_TOOLBAR_BUTTON: |
|
3280 case NS_THEME_SPINNER: |
|
3281 case NS_THEME_SPINNER_UP_BUTTON: |
|
3282 case NS_THEME_SPINNER_DOWN_BUTTON: |
|
3283 case NS_THEME_TOOLBAR: |
|
3284 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: |
|
3285 case NS_THEME_STATUSBAR: |
|
3286 case NS_THEME_NUMBER_INPUT: |
|
3287 case NS_THEME_TEXTFIELD: |
|
3288 case NS_THEME_TEXTFIELD_MULTILINE: |
|
3289 case NS_THEME_SEARCHFIELD: |
|
3290 //case NS_THEME_TOOLBOX: |
|
3291 //case NS_THEME_TOOLBAR_BUTTON: |
|
3292 case NS_THEME_PROGRESSBAR: |
|
3293 case NS_THEME_PROGRESSBAR_VERTICAL: |
|
3294 case NS_THEME_PROGRESSBAR_CHUNK: |
|
3295 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: |
|
3296 case NS_THEME_METERBAR: |
|
3297 case NS_THEME_METERBAR_CHUNK: |
|
3298 case NS_THEME_TOOLBAR_SEPARATOR: |
|
3299 |
|
3300 case NS_THEME_TAB_PANELS: |
|
3301 case NS_THEME_TAB: |
|
3302 |
|
3303 case NS_THEME_TREEVIEW_TWISTY: |
|
3304 case NS_THEME_TREEVIEW_TWISTY_OPEN: |
|
3305 case NS_THEME_TREEVIEW: |
|
3306 case NS_THEME_TREEVIEW_HEADER: |
|
3307 case NS_THEME_TREEVIEW_HEADER_CELL: |
|
3308 case NS_THEME_TREEVIEW_HEADER_SORTARROW: |
|
3309 case NS_THEME_TREEVIEW_TREEITEM: |
|
3310 case NS_THEME_TREEVIEW_LINE: |
|
3311 |
|
3312 case NS_THEME_RANGE: |
|
3313 |
|
3314 case NS_THEME_SCALE_HORIZONTAL: |
|
3315 case NS_THEME_SCALE_THUMB_HORIZONTAL: |
|
3316 case NS_THEME_SCALE_VERTICAL: |
|
3317 case NS_THEME_SCALE_THUMB_VERTICAL: |
|
3318 |
|
3319 case NS_THEME_SCROLLBAR: |
|
3320 case NS_THEME_SCROLLBAR_SMALL: |
|
3321 case NS_THEME_SCROLLBAR_BUTTON_UP: |
|
3322 case NS_THEME_SCROLLBAR_BUTTON_DOWN: |
|
3323 case NS_THEME_SCROLLBAR_BUTTON_LEFT: |
|
3324 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: |
|
3325 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: |
|
3326 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: |
|
3327 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
3328 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
3329 case NS_THEME_SCROLLBAR_NON_DISAPPEARING: |
|
3330 |
|
3331 case NS_THEME_DROPDOWN: |
|
3332 case NS_THEME_DROPDOWN_BUTTON: |
|
3333 case NS_THEME_DROPDOWN_TEXT: |
|
3334 case NS_THEME_DROPDOWN_TEXTFIELD: |
|
3335 return !IsWidgetStyled(aPresContext, aFrame, aWidgetType); |
|
3336 break; |
|
3337 |
|
3338 case NS_THEME_RESIZER: |
|
3339 { |
|
3340 nsIFrame* parentFrame = aFrame->GetParent(); |
|
3341 if (!parentFrame || parentFrame->GetType() != nsGkAtoms::scrollFrame) |
|
3342 return true; |
|
3343 |
|
3344 // Note that IsWidgetStyled is not called for resizers on Mac. This is |
|
3345 // because for scrollable containers, the native resizer looks better |
|
3346 // when (non-overlay) scrollbars are present even when the style is |
|
3347 // overriden, and the custom transparent resizer looks better when |
|
3348 // scrollbars are not present. |
|
3349 nsIScrollableFrame* scrollFrame = do_QueryFrame(parentFrame); |
|
3350 return (!nsLookAndFeel::UseOverlayScrollbars() && |
|
3351 scrollFrame && scrollFrame->GetScrollbarVisibility()); |
|
3352 break; |
|
3353 } |
|
3354 } |
|
3355 |
|
3356 return false; |
|
3357 } |
|
3358 |
|
3359 bool |
|
3360 nsNativeThemeCocoa::WidgetIsContainer(uint8_t aWidgetType) |
|
3361 { |
|
3362 // flesh this out at some point |
|
3363 switch (aWidgetType) { |
|
3364 case NS_THEME_DROPDOWN_BUTTON: |
|
3365 case NS_THEME_RADIO: |
|
3366 case NS_THEME_CHECKBOX: |
|
3367 case NS_THEME_PROGRESSBAR: |
|
3368 case NS_THEME_METERBAR: |
|
3369 case NS_THEME_RANGE: |
|
3370 case NS_THEME_MOZ_MAC_HELP_BUTTON: |
|
3371 return false; |
|
3372 break; |
|
3373 } |
|
3374 return true; |
|
3375 } |
|
3376 |
|
3377 bool |
|
3378 nsNativeThemeCocoa::ThemeDrawsFocusForWidget(uint8_t aWidgetType) |
|
3379 { |
|
3380 if (aWidgetType == NS_THEME_DROPDOWN || |
|
3381 aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD || |
|
3382 aWidgetType == NS_THEME_BUTTON || |
|
3383 aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON || |
|
3384 aWidgetType == NS_THEME_RADIO || |
|
3385 aWidgetType == NS_THEME_RANGE || |
|
3386 aWidgetType == NS_THEME_CHECKBOX) |
|
3387 return true; |
|
3388 |
|
3389 return false; |
|
3390 } |
|
3391 |
|
3392 bool |
|
3393 nsNativeThemeCocoa::ThemeNeedsComboboxDropmarker() |
|
3394 { |
|
3395 return false; |
|
3396 } |
|
3397 |
|
3398 bool |
|
3399 nsNativeThemeCocoa::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType) |
|
3400 { |
|
3401 switch (aWidgetType) { |
|
3402 case NS_THEME_DIALOG: |
|
3403 case NS_THEME_GROUPBOX: |
|
3404 case NS_THEME_TAB_PANELS: |
|
3405 case NS_THEME_MENUPOPUP: |
|
3406 case NS_THEME_MENUITEM: |
|
3407 case NS_THEME_MENUSEPARATOR: |
|
3408 case NS_THEME_TOOLTIP: |
|
3409 case NS_THEME_SPINNER: |
|
3410 case NS_THEME_SPINNER_UP_BUTTON: |
|
3411 case NS_THEME_SPINNER_DOWN_BUTTON: |
|
3412 case NS_THEME_TOOLBAR_SEPARATOR: |
|
3413 case NS_THEME_TOOLBOX: |
|
3414 case NS_THEME_NUMBER_INPUT: |
|
3415 case NS_THEME_TEXTFIELD: |
|
3416 case NS_THEME_TREEVIEW: |
|
3417 case NS_THEME_TREEVIEW_LINE: |
|
3418 case NS_THEME_TEXTFIELD_MULTILINE: |
|
3419 case NS_THEME_LISTBOX: |
|
3420 case NS_THEME_RESIZER: |
|
3421 return false; |
|
3422 default: |
|
3423 return true; |
|
3424 } |
|
3425 } |
|
3426 |
|
3427 nsITheme::Transparency |
|
3428 nsNativeThemeCocoa::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType) |
|
3429 { |
|
3430 switch (aWidgetType) { |
|
3431 case NS_THEME_MENUPOPUP: |
|
3432 case NS_THEME_TOOLTIP: |
|
3433 return eTransparent; |
|
3434 |
|
3435 case NS_THEME_SCROLLBAR_SMALL: |
|
3436 case NS_THEME_SCROLLBAR: |
|
3437 return nsLookAndFeel::UseOverlayScrollbars() ? eTransparent : eOpaque; |
|
3438 |
|
3439 case NS_THEME_STATUSBAR: |
|
3440 // Knowing that scrollbars and statusbars are opaque improves |
|
3441 // performance, because we create layers for them. |
|
3442 return eOpaque; |
|
3443 |
|
3444 case NS_THEME_TOOLBAR: |
|
3445 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: |
|
3446 return eOpaque; |
|
3447 |
|
3448 default: |
|
3449 return eUnknownTransparency; |
|
3450 } |
|
3451 } |