1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Mike Pinkerton (pinkerton@netscape.com).
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
23 * Vladimir Vukicevic <vladimir@pobox.com> (HITheme rewrite)
24 * Josh Aas <josh@mozilla.com>
25 * Colin Barrett <cbarrett@mozilla.com>
26 * Matthew Gregan <kinetik@flim.org>
27 * Markus Stange <mstange@themasta.com>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
43 #include "nsNativeThemeCocoa.h"
44 #include "nsObjCExceptions.h"
45 #include "nsIRenderingContext.h"
48 #include "nsThemeConstants.h"
49 #include "nsIPresShell.h"
50 #include "nsPresContext.h"
51 #include "nsIContent.h"
52 #include "nsIDocument.h"
55 #include "nsIEventStateManager.h"
56 #include "nsINameSpaceManager.h"
57 #include "nsPresContext.h"
58 #include "nsILookAndFeel.h"
59 #include "nsWidgetAtoms.h"
60 #include "nsToolkit.h"
61 #include "nsCocoaWindow.h"
62 #include "nsNativeThemeColors.h"
63 #include "nsIScrollableFrame.h"
65 #include "gfxContext.h"
66 #include "gfxQuartzSurface.h"
67 #include "gfxQuartzNativeDrawing.h"
69 #define DRAW_IN_FRAME_DEBUG 0
70 #define SCROLLBARS_VISUAL_DEBUG 0
72 // private Quartz routines needed here
74 CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
77 // Workaround for NSCell control tint drawing
78 // Without this workaround, NSCells are always drawn with the clear control tint
79 // as long as they're not attached to an NSControl which is a subview of an active window.
80 // XXXmstange Why doesn't Webkit need this?
81 @implementation NSCell (ControlTintWorkaround)
82 - (int)_realControlTint { return [self controlTint]; }
85 // The purpose of this class is to provide objects that can be used when drawing
86 // NSCells using drawWithFrame:inView: without causing any harm. The only
87 // messages that will be sent to such an object are "isFlipped" and
88 // "currentEditor": isFlipped needs to return YES in order to avoid drawing bugs
89 // on 10.4 (see bug 465069); currentEditor (which isn't even a method of
90 // NSView) will be called when drawing search fields, and we only provide it in
91 // order to prevent "unrecognized selector" exceptions.
92 // There's no need to pass the actual NSView that we're drawing into to
93 // drawWithFrame:inView:. What's more, doing so even causes unnecessary
94 // invalidations as soon as we draw a focusring!
95 @interface CellDrawView : NSView
99 @implementation CellDrawView
106 - (NSText*)currentEditor
113 // Workaround for Bug 542048
114 // On 64-bit, NSSearchFieldCells don't draw focus rings.
115 #if defined(__x86_64__)
117 static void DrawFocusRing(NSRect rect, float radius)
119 NSSetFocusRingStyle(NSFocusRingOnly);
120 NSBezierPath* path = [NSBezierPath bezierPath];
121 rect = NSInsetRect(rect, radius, radius);
122 [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0];
123 [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0];
124 [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle: 0.0 endAngle: 90.0];
125 [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle: 90.0 endAngle:180.0];
130 @interface SearchFieldCellWithFocusRing : NSSearchFieldCell {} @end
132 @implementation SearchFieldCellWithFocusRing
134 - (void)drawWithFrame:(NSRect)rect inView:(NSView*)controlView
136 [super drawWithFrame:rect inView:controlView];
137 if ([self showsFirstResponder]) {
138 DrawFocusRing(rect, NSHeight(rect) / 2);
146 // Copied from nsLookAndFeel.h
147 // Apple hasn't defined a constant for scollbars with two arrows on each end, so we'll use this one.
148 static const int kThemeScrollBarArrowsBoth = 2;
150 #define HITHEME_ORIENTATION kHIThemeOrientationNormal
151 #define MAX_FOCUS_RING_WIDTH 4
153 // These enums are for indexing into the margin array.
171 static int EnumSizeForCocoaSize(NSControlSize cocoaControlSize) {
172 if (cocoaControlSize == NSMiniControlSize)
173 return miniControlSize;
174 else if (cocoaControlSize == NSSmallControlSize)
175 return smallControlSize;
177 return regularControlSize;
180 static void InflateControlRect(NSRect* rect, NSControlSize cocoaControlSize, const float marginSet[][3][4])
185 static int osIndex = leopardOS;
186 int controlSize = EnumSizeForCocoaSize(cocoaControlSize);
187 const float* buttonMargins = marginSet[osIndex][controlSize];
188 rect->origin.x -= buttonMargins[leftMargin];
189 rect->origin.y -= buttonMargins[bottomMargin];
190 rect->size.width += buttonMargins[leftMargin] + buttonMargins[rightMargin];
191 rect->size.height += buttonMargins[bottomMargin] + buttonMargins[topMargin];
194 static NSWindow* NativeWindowForFrame(nsIFrame* aFrame,
195 nsIWidget** aTopLevelWidget = NULL)
200 nsIWidget* widget = aFrame->GetNearestWidget();
204 nsIWidget* topLevelWidget = widget->GetTopLevelWidget();
206 *aTopLevelWidget = topLevelWidget;
208 return (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW);
211 static BOOL FrameIsInActiveWindow(nsIFrame* aFrame)
213 nsIWidget* topLevelWidget = NULL;
214 NSWindow* win = NativeWindowForFrame(aFrame, &topLevelWidget);
215 if (!topLevelWidget || !win)
218 // XUL popups, e.g. the toolbar customization popup, can't become key windows,
219 // but controls in these windows should still get the active look.
220 nsWindowType windowType;
221 topLevelWidget->GetWindowType(windowType);
222 if (windowType == eWindowType_popup)
225 return [win isKeyWindow];
226 return [win isMainWindow] && ![win attachedSheet];
229 NS_IMPL_ISUPPORTS_INHERITED1(nsNativeThemeCocoa, nsNativeTheme, nsITheme)
232 nsNativeThemeCocoa::nsNativeThemeCocoa()
234 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
236 mPushButtonCell = [[NSButtonCell alloc] initTextCell:nil];
237 [mPushButtonCell setButtonType:NSMomentaryPushInButton];
238 [mPushButtonCell setHighlightsBy:NSPushInCellMask];
240 mRadioButtonCell = [[NSButtonCell alloc] initTextCell:nil];
241 [mRadioButtonCell setButtonType:NSRadioButton];
243 mCheckboxCell = [[NSButtonCell alloc] initTextCell:nil];
244 [mCheckboxCell setButtonType:NSSwitchButton];
245 [mCheckboxCell setAllowsMixedState:YES];
247 #if defined(__x86_64__)
248 mSearchFieldCell = [[SearchFieldCellWithFocusRing alloc] initTextCell:@""];
250 mSearchFieldCell = [[NSSearchFieldCell alloc] initTextCell:@""];
252 [mSearchFieldCell setBezelStyle:NSTextFieldRoundedBezel];
253 [mSearchFieldCell setBezeled:YES];
254 [mSearchFieldCell setEditable:YES];
255 [mSearchFieldCell setFocusRingType:NSFocusRingTypeExterior];
257 mDropdownCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO];
259 mComboBoxCell = [[NSComboBoxCell alloc] initTextCell:@""];
260 [mComboBoxCell setBezeled:YES];
261 [mComboBoxCell setEditable:YES];
262 [mComboBoxCell setFocusRingType:NSFocusRingTypeExterior];
264 mCellDrawView = [[CellDrawView alloc] init];
266 NS_OBJC_END_TRY_ABORT_BLOCK;
269 nsNativeThemeCocoa::~nsNativeThemeCocoa()
271 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
273 [mPushButtonCell release];
274 [mRadioButtonCell release];
275 [mCheckboxCell release];
276 [mSearchFieldCell release];
277 [mDropdownCell release];
278 [mComboBoxCell release];
279 [mCellDrawView release];
281 NS_OBJC_END_TRY_ABORT_BLOCK;
284 // Limit on the area of the target rect (in pixels^2) in
285 // DrawCellWithScaling(), DrawButton() and DrawScrollbar(), above which we
286 // don't draw the object into a bitmap buffer. This is to avoid crashes in
287 // [NSGraphicsContext graphicsContextWithGraphicsPort:flipped:] and
288 // CGContextDrawImage(), and also to avoid very poor drawing performance in
289 // CGContextDrawImage() when it scales the bitmap (particularly if xscale or
290 // yscale is less than but near 1 -- e.g. 0.9). This value was determined
291 // by trial and error, on OS X 10.4.11 and 10.5.4, and on systems with
292 // different amounts of RAM.
293 #define BITMAP_MAX_AREA 500000
296 * Draw the given NSCell into the given cgContext.
298 * destRect - the size and position of the resulting control rectangle
299 * controlSize - the NSControlSize which will be given to the NSCell before
300 * asking it to render
301 * naturalSize - The natural dimensions of this control.
302 * If the control rect size is not equal to either of these, a scale
303 * will be applied to the context so that rendering the control at the
304 * natural size will result in it filling the destRect space.
305 * If a control has no natural dimensions in either/both axes, pass 0.0f.
306 * minimumSize - The minimum dimensions of this control.
307 * If the control rect size is less than the minimum for a given axis,
308 * a scale will be applied to the context so that the minimum is used
309 * for drawing. If a control has no minimum dimensions in either/both
311 * marginSet - an array of margins; a multidimensional array of [2][3][4],
312 * with the first dimension being the OS version (Tiger or Leopard),
313 * the second being the control size (mini, small, regular), and the third
314 * being the 4 margin values (left, top, right, bottom).
315 * view - The NSView that we're drawing into. As far as I can tell, it doesn't
316 * matter if this is really the right view; it just has to return YES when
317 * asked for isFlipped. Otherwise we'll get drawing bugs on 10.4.
318 * mirrorHorizontal - whether to mirror the cell horizontally
320 static void DrawCellWithScaling(NSCell *cell,
321 CGContextRef cgContext,
322 const HIRect& destRect,
323 NSControlSize controlSize,
326 const float marginSet[][3][4],
328 BOOL mirrorHorizontal)
330 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
332 NSRect drawRect = NSMakeRect(destRect.origin.x, destRect.origin.y, destRect.size.width, destRect.size.height);
334 if (naturalSize.width != 0.0f)
335 drawRect.size.width = naturalSize.width;
336 if (naturalSize.height != 0.0f)
337 drawRect.size.height = naturalSize.height;
339 // Keep aspect ratio when scaling if one dimension is free.
340 if (naturalSize.width == 0.0f && naturalSize.height != 0.0f)
341 drawRect.size.width = destRect.size.width * naturalSize.height / destRect.size.height;
342 if (naturalSize.height == 0.0f && naturalSize.width != 0.0f)
343 drawRect.size.height = destRect.size.height * naturalSize.width / destRect.size.width;
345 // Honor minimum sizes.
346 if (drawRect.size.width < minimumSize.width)
347 drawRect.size.width = minimumSize.width;
348 if (drawRect.size.height < minimumSize.height)
349 drawRect.size.height = minimumSize.height;
351 [NSGraphicsContext saveGraphicsState];
353 // Only skip the buffer if the area of our cell (in pixels^2) is too large.
354 if (drawRect.size.width * drawRect.size.height > BITMAP_MAX_AREA) {
355 // Inflate the rect Gecko gave us by the margin for the control.
356 InflateControlRect(&drawRect, controlSize, marginSet);
358 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
359 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]];
361 [cell drawWithFrame:drawRect inView:view];
363 [NSGraphicsContext setCurrentContext:savedContext];
366 float w = ceil(drawRect.size.width);
367 float h = ceil(drawRect.size.height);
368 NSRect tmpRect = NSMakeRect(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, w, h);
370 // inflate to figure out the frame we need to tell NSCell to draw in, to get something that's 0,0,w,h
371 InflateControlRect(&tmpRect, controlSize, marginSet);
373 // and then, expand by MAX_FOCUS_RING_WIDTH size to make sure we can capture any focus ring
374 w += MAX_FOCUS_RING_WIDTH * 2.0;
375 h += MAX_FOCUS_RING_WIDTH * 2.0;
377 CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
378 CGContextRef ctx = CGBitmapContextCreate(NULL,
381 rgb, kCGImageAlphaPremultipliedFirst);
382 CGColorSpaceRelease(rgb);
384 // We need to flip the image twice in order to avoid drawing bugs on 10.4, see bug 465069.
385 // This is the first flip transform, applied to cgContext.
386 CGContextScaleCTM(cgContext, 1.0f, -1.0f);
387 CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height));
388 if (mirrorHorizontal) {
389 CGContextScaleCTM(cgContext, -1.0f, 1.0f);
390 CGContextTranslateCTM(cgContext, -(2.0 * destRect.origin.x + destRect.size.width), 0.0f);
393 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
394 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]];
396 // This is the second flip transform, applied to ctx.
397 CGContextScaleCTM(ctx, 1.0f, -1.0f);
398 CGContextTranslateCTM(ctx, 0.0f, -(2.0 * tmpRect.origin.y + tmpRect.size.height));
400 [cell drawWithFrame:tmpRect inView:view];
402 [NSGraphicsContext setCurrentContext:savedContext];
404 CGImageRef img = CGBitmapContextCreateImage(ctx);
406 // Drop the image into the original destination rectangle, scaling to fit
407 // Only scale MAX_FOCUS_RING_WIDTH by xscale/yscale when the resulting rect
408 // doesn't extend beyond the overflow rect
409 float xscale = destRect.size.width / drawRect.size.width;
410 float yscale = destRect.size.height / drawRect.size.height;
411 float scaledFocusRingX = xscale < 1.0f ? MAX_FOCUS_RING_WIDTH * xscale : MAX_FOCUS_RING_WIDTH;
412 float scaledFocusRingY = yscale < 1.0f ? MAX_FOCUS_RING_WIDTH * yscale : MAX_FOCUS_RING_WIDTH;
413 CGContextDrawImage(cgContext, CGRectMake(destRect.origin.x - scaledFocusRingX,
414 destRect.origin.y - scaledFocusRingY,
415 destRect.size.width + scaledFocusRingX * 2,
416 destRect.size.height + scaledFocusRingY * 2),
420 CGContextRelease(ctx);
423 [NSGraphicsContext restoreGraphicsState];
425 #if DRAW_IN_FRAME_DEBUG
426 CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
427 CGContextFillRect(cgContext, destRect);
430 NS_OBJC_END_TRY_ABORT_BLOCK;
433 struct CellRenderSettings {
434 // The natural dimensions of the control.
435 // If a control has no natural dimensions in either/both axes, set to 0.0f.
436 NSSize naturalSizes[3];
438 // The minimum dimensions of the control.
439 // If a control has no minimum dimensions in either/both axes, set to 0.0f.
440 NSSize minimumSizes[3];
442 // A multidimensional array of [2][3][4],
443 // with the first dimension being the OS version (Tiger or Leopard),
444 // the second being the control size (mini, small, regular), and the third
445 // being the 4 margin values (left, top, right, bottom).
446 float margins[2][3][4];
450 * Draw the given NSCell into the given cgContext with a nice control size.
452 * This function is similar to DrawCellWithScaling, but it decides what
453 * control size to use based on the destRect's size.
454 * Scaling is only applied when the difference between the destRect's size
455 * and the next smaller natural size is greater than snapTolerance. Otherwise
456 * it snaps to the next smaller control size without scaling because unscaled
457 * controls look nicer.
459 static void DrawCellWithSnapping(NSCell *cell,
460 CGContextRef cgContext,
461 const HIRect& destRect,
462 const CellRenderSettings settings,
463 float verticalAlignFactor,
465 BOOL mirrorHorizontal,
466 float snapTolerance = 2.0f)
468 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
470 const float rectWidth = destRect.size.width, rectHeight = destRect.size.height;
471 const NSSize *sizes = settings.naturalSizes;
472 const NSSize miniSize = sizes[EnumSizeForCocoaSize(NSMiniControlSize)];
473 const NSSize smallSize = sizes[EnumSizeForCocoaSize(NSSmallControlSize)];
474 const NSSize regularSize = sizes[EnumSizeForCocoaSize(NSRegularControlSize)];
476 NSControlSize controlSizeX = NSRegularControlSize, controlSizeY = NSRegularControlSize;
477 HIRect drawRect = destRect;
479 if (rectWidth <= miniSize.width + snapTolerance && rectWidth < smallSize.width)
480 controlSizeX = NSMiniControlSize;
481 else if(rectWidth <= smallSize.width + snapTolerance && rectWidth < regularSize.width)
482 controlSizeX = NSSmallControlSize;
484 if (rectHeight <= miniSize.height + snapTolerance && rectHeight < smallSize.height)
485 controlSizeY = NSMiniControlSize;
486 else if(rectHeight <= smallSize.height + snapTolerance && rectHeight < regularSize.height)
487 controlSizeY = NSSmallControlSize;
489 NSControlSize controlSize = NSRegularControlSize;
492 // At some sizes, don't scale but snap.
493 const NSControlSize smallerControlSize =
494 EnumSizeForCocoaSize(controlSizeX) < EnumSizeForCocoaSize(controlSizeY) ?
495 controlSizeX : controlSizeY;
496 const int smallerControlSizeIndex = EnumSizeForCocoaSize(smallerControlSize);
497 const NSSize size = sizes[smallerControlSizeIndex];
498 float diffWidth = size.width ? rectWidth - size.width : 0.0f;
499 float diffHeight = size.height ? rectHeight - size.height : 0.0f;
500 if (diffWidth >= 0.0f && diffHeight >= 0.0f &&
501 diffWidth <= snapTolerance && diffHeight <= snapTolerance) {
502 // Snap to the smaller control size.
503 controlSize = smallerControlSize;
504 sizeIndex = smallerControlSizeIndex;
505 // Resize and center the drawRect.
506 if (sizes[sizeIndex].width) {
507 drawRect.origin.x += ceil((destRect.size.width - sizes[sizeIndex].width) / 2);
508 drawRect.size.width = sizes[sizeIndex].width;
510 if (sizes[sizeIndex].height) {
511 drawRect.origin.y += floor((destRect.size.height - sizes[sizeIndex].height) * verticalAlignFactor);
512 drawRect.size.height = sizes[sizeIndex].height;
515 // Use the larger control size.
516 controlSize = EnumSizeForCocoaSize(controlSizeX) > EnumSizeForCocoaSize(controlSizeY) ?
517 controlSizeX : controlSizeY;
518 sizeIndex = EnumSizeForCocoaSize(controlSize);
521 [cell setControlSize:controlSize];
523 NSSize minimumSize = settings.minimumSizes ? settings.minimumSizes[sizeIndex] : NSZeroSize;
524 DrawCellWithScaling(cell, cgContext, drawRect, controlSize, sizes[sizeIndex],
525 minimumSize, settings.margins, view, mirrorHorizontal);
527 NS_OBJC_END_TRY_ABORT_BLOCK;
530 static float VerticalAlignFactor(nsIFrame *aFrame)
533 return 0.5f; // default: center
535 const nsStyleCoord& va = aFrame->GetStyleTextReset()->mVerticalAlign;
536 PRUint8 intval = (va.GetUnit() == eStyleUnit_Enumerated)
538 : NS_STYLE_VERTICAL_ALIGN_MIDDLE;
540 case NS_STYLE_VERTICAL_ALIGN_TOP:
541 case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
544 case NS_STYLE_VERTICAL_ALIGN_SUB:
545 case NS_STYLE_VERTICAL_ALIGN_SUPER:
546 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
547 case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
550 case NS_STYLE_VERTICAL_ALIGN_BASELINE:
551 case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
552 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
556 NS_NOTREACHED("invalid vertical-align");
561 // These are the sizes that Gecko needs to request to draw if it wants
562 // to get a standard-sized Aqua radio button drawn. Note that the rects
563 // that draw these are actually a little bigger.
564 static const CellRenderSettings radioSettings = {
566 NSMakeSize(11, 11), // mini
567 NSMakeSize(13, 13), // small
568 NSMakeSize(16, 16) // regular
571 NSZeroSize, NSZeroSize, NSZeroSize
575 {0, 0, 0, 0}, // mini
576 {0, 1, 1, 2}, // small
577 {0, -1, 0, 1} // regular
580 {0, 0, 0, 0}, // mini
581 {0, 1, 1, 1}, // small
582 {0, 0, 0, 0} // regular
587 static const CellRenderSettings checkboxSettings = {
589 NSMakeSize(11, 11), // mini
590 NSMakeSize(13, 13), // small
591 NSMakeSize(16, 16) // regular
594 NSZeroSize, NSZeroSize, NSZeroSize
598 {0, 1, 0, 0}, // mini
599 {0, 2, 0, 1}, // small
600 {0, 1, 0, 1} // regular
603 {0, 1, 0, 0}, // mini
604 {0, 1, 0, 1}, // small
605 {0, 1, 0, 1} // regular
611 nsNativeThemeCocoa::DrawCheckboxOrRadio(CGContextRef cgContext, PRBool inCheckbox,
612 const HIRect& inBoxRect, PRBool inSelected,
613 nsEventStates inState, nsIFrame* aFrame)
615 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
617 NSButtonCell *cell = inCheckbox ? mCheckboxCell : mRadioButtonCell;
618 NSCellStateValue state = inSelected ? NSOnState : NSOffState;
620 // Check if we have an indeterminate checkbox
621 if (inCheckbox && GetIndeterminate(aFrame))
622 state = NSMixedState;
624 [cell setEnabled:!IsDisabled(aFrame, inState)];
625 [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS)];
626 [cell setState:state];
627 [cell setHighlighted:inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)];
628 [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)];
630 // Ensure that the control is square.
631 float length = PR_MIN(inBoxRect.size.width, inBoxRect.size.height);
632 HIRect drawRect = CGRectMake(inBoxRect.origin.x + (int)((inBoxRect.size.width - length) / 2.0f),
633 inBoxRect.origin.y + (int)((inBoxRect.size.height - length) / 2.0f),
636 DrawCellWithSnapping(cell, cgContext, drawRect,
637 inCheckbox ? checkboxSettings : radioSettings,
638 VerticalAlignFactor(aFrame), mCellDrawView, NO);
640 NS_OBJC_END_TRY_ABORT_BLOCK;
643 static const CellRenderSettings searchFieldSettings = {
645 NSMakeSize(0, 16), // mini
646 NSMakeSize(0, 19), // small
647 NSMakeSize(0, 22) // regular
650 NSMakeSize(32, 0), // mini
651 NSMakeSize(38, 0), // small
652 NSMakeSize(44, 0) // regular
656 {0, 0, 0, 0}, // mini
657 {0, 0, 0, 0}, // small
658 {0, 0, 0, 0} // regular
661 {0, 0, 0, 0}, // mini
662 {0, 0, 0, 0}, // small
663 {0, 0, 0, 0} // regular
669 nsNativeThemeCocoa::DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect,
670 nsIFrame* aFrame, nsEventStates inState)
672 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
674 NSSearchFieldCell* cell = mSearchFieldCell;
675 [cell setEnabled:!IsDisabled(aFrame, inState)];
676 // NOTE: this could probably use inState
677 [cell setShowsFirstResponder:IsFocused(aFrame)];
679 DrawCellWithSnapping(cell, cgContext, inBoxRect, searchFieldSettings,
680 VerticalAlignFactor(aFrame), mCellDrawView,
683 NS_OBJC_END_TRY_ABORT_BLOCK;
686 static const CellRenderSettings pushButtonSettings = {
688 NSMakeSize(0, 16), // mini
689 NSMakeSize(0, 19), // small
690 NSMakeSize(0, 22) // regular
693 NSMakeSize(18, 0), // mini
694 NSMakeSize(26, 0), // small
695 NSMakeSize(30, 0) // regular
699 {1, 1, 1, 1}, // mini
700 {5, 0, 5, 2}, // small
701 {6, 0, 6, 2} // regular
704 {0, 0, 0, 0}, // mini
705 {4, 0, 4, 1}, // small
706 {5, 0, 5, 2} // regular
711 // The height at which we start doing square buttons instead of rounded buttons
712 // Rounded buttons look bad if drawn at a height greater than 26, so at that point
713 // we switch over to doing square buttons which looks fine at any size.
714 #define DO_SQUARE_BUTTON_HEIGHT 26
717 nsNativeThemeCocoa::DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRect,
718 nsEventStates inState, nsIFrame* aFrame)
720 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
722 BOOL isActive = FrameIsInActiveWindow(aFrame);
723 BOOL isDisabled = IsDisabled(aFrame, inState);
725 [mPushButtonCell setEnabled:!isDisabled];
726 [mPushButtonCell setHighlighted:isActive &&
727 inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)];
728 [mPushButtonCell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS) && !isDisabled && isActive];
730 // If the button is tall enough, draw the square button style so that buttons with
731 // non-standard content look good. Otherwise draw normal rounded aqua buttons.
732 if (inBoxRect.size.height > DO_SQUARE_BUTTON_HEIGHT) {
733 [mPushButtonCell setBezelStyle:NSShadowlessSquareBezelStyle];
734 DrawCellWithScaling(mPushButtonCell, cgContext, inBoxRect, NSRegularControlSize,
735 NSZeroSize, NSMakeSize(14, 0), NULL,
736 mCellDrawView, IsFrameRTL(aFrame));
738 [mPushButtonCell setBezelStyle:NSRoundedBezelStyle];
740 DrawCellWithSnapping(mPushButtonCell, cgContext, inBoxRect, pushButtonSettings,
741 0.5f, mCellDrawView, IsFrameRTL(aFrame), 1.0f);
744 #if DRAW_IN_FRAME_DEBUG
745 CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
746 CGContextFillRect(cgContext, inBoxRect);
749 NS_OBJC_END_TRY_ABORT_BLOCK;
752 typedef void (*RenderHIThemeControlFunction)(CGContextRef cgContext, const HIRect& aRenderRect, void* aData);
755 RenderTransformedHIThemeControl(CGContextRef aCGContext, const HIRect& aRect,
756 RenderHIThemeControlFunction aFunc, void* aData,
757 BOOL mirrorHorizontally = NO)
759 CGAffineTransform savedCTM = CGContextGetCTM(aCGContext);
760 CGContextTranslateCTM(aCGContext, aRect.origin.x, aRect.origin.y);
763 HIRect drawRect = aRect;
764 drawRect.origin = CGPointZero;
766 if (!mirrorHorizontally && savedCTM.a == 1.0f && savedCTM.b == 0.0f &&
767 savedCTM.c == 0.0f && (savedCTM.d == 1.0f || savedCTM.d == -1.0f)) {
773 // Fall back to no bitmap buffer if the area of our control (in pixels^2)
775 if (drawDirect || (aRect.size.width * aRect.size.height > BITMAP_MAX_AREA)) {
776 aFunc(aCGContext, drawRect, aData);
778 // Inflate the buffer to capture focus rings.
779 int w = ceil(drawRect.size.width) + 2 * MAX_FOCUS_RING_WIDTH;
780 int h = ceil(drawRect.size.height) + 2 * MAX_FOCUS_RING_WIDTH;
782 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
783 CGContextRef bitmapctx = CGBitmapContextCreate(NULL, w, h, 8, w * 4,
785 kCGImageAlphaPremultipliedFirst);
786 CGColorSpaceRelease(colorSpace);
788 CGContextTranslateCTM(bitmapctx, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH);
790 // HITheme always wants to draw into a flipped context, or things
792 CGContextTranslateCTM(bitmapctx, 0.0f, aRect.size.height);
793 CGContextScaleCTM(bitmapctx, 1.0f, -1.0f);
795 aFunc(bitmapctx, drawRect, aData);
797 CGImageRef bitmap = CGBitmapContextCreateImage(bitmapctx);
799 CGAffineTransform ctm = CGContextGetCTM(aCGContext);
801 // We need to unflip, so that we can do a DrawImage without getting a flipped image.
802 CGContextTranslateCTM(aCGContext, 0.0f, aRect.size.height);
803 CGContextScaleCTM(aCGContext, 1.0f, -1.0f);
805 if (mirrorHorizontally) {
806 CGContextTranslateCTM(aCGContext, aRect.size.width, 0);
807 CGContextScaleCTM(aCGContext, -1.0f, 1.0f);
810 HIRect inflatedDrawRect = CGRectMake(-MAX_FOCUS_RING_WIDTH, -MAX_FOCUS_RING_WIDTH, w, h);
811 CGContextDrawImage(aCGContext, inflatedDrawRect, bitmap);
813 CGContextSetCTM(aCGContext, ctm);
815 CGImageRelease(bitmap);
816 CGContextRelease(bitmapctx);
819 CGContextSetCTM(aCGContext, savedCTM);
823 RenderButton(CGContextRef cgContext, const HIRect& aRenderRect, void* aData)
825 HIThemeButtonDrawInfo* bdi = (HIThemeButtonDrawInfo*)aData;
826 HIThemeDrawButton(&aRenderRect, bdi, cgContext, kHIThemeOrientationNormal, NULL);
830 nsNativeThemeCocoa::DrawButton(CGContextRef cgContext, ThemeButtonKind inKind,
831 const HIRect& inBoxRect, PRBool inIsDefault,
832 ThemeButtonValue inValue, ThemeButtonAdornment inAdornment,
833 nsEventStates inState, nsIFrame* aFrame)
835 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
837 BOOL isActive = FrameIsInActiveWindow(aFrame);
838 BOOL isDisabled = IsDisabled(aFrame, inState);
840 HIThemeButtonDrawInfo bdi;
844 bdi.adornment = inAdornment;
847 bdi.state = kThemeStateUnavailable;
849 else if (inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) {
850 bdi.state = kThemeStatePressed;
853 if (inKind == kThemeArrowButton)
854 bdi.state = kThemeStateUnavailable; // these are always drawn as unavailable
855 else if (!isActive && inKind == kThemeListHeaderButton)
856 bdi.state = kThemeStateInactive;
858 bdi.state = kThemeStateActive;
861 if (inState.HasState(NS_EVENT_STATE_FOCUS) && isActive)
862 bdi.adornment |= kThemeAdornmentFocus;
864 if (inIsDefault && !isDisabled && isActive &&
865 !inState.HasState(NS_EVENT_STATE_ACTIVE)) {
866 bdi.adornment |= kThemeAdornmentDefault;
867 bdi.animation.time.start = 0;
868 bdi.animation.time.current = CFAbsoluteTimeGetCurrent();
871 HIRect drawFrame = inBoxRect;
873 if (inKind == kThemePushButton) {
874 drawFrame.size.height -= 2;
875 if (inBoxRect.size.height < pushButtonSettings.naturalSizes[smallControlSize].height) {
876 bdi.kind = kThemePushButtonMini;
878 else if (inBoxRect.size.height < pushButtonSettings.naturalSizes[regularControlSize].height) {
879 bdi.kind = kThemePushButtonSmall;
880 drawFrame.origin.y -= 1;
881 drawFrame.origin.x += 1;
882 drawFrame.size.width -= 2;
885 else if (inKind == kThemeListHeaderButton) {
886 CGContextClipToRect(cgContext, inBoxRect);
887 // Always remove the top border.
888 drawFrame.origin.y -= 1;
889 drawFrame.size.height += 1;
890 // Remove the left border in LTR mode and the right border in RTL mode.
891 drawFrame.size.width += 1;
892 PRBool isLast = IsLastTreeHeaderCell(aFrame);
894 drawFrame.size.width += 1; // Also remove the other border.
895 if (!IsFrameRTL(aFrame) || isLast)
896 drawFrame.origin.x -= 1;
899 RenderTransformedHIThemeControl(cgContext, drawFrame, RenderButton, &bdi,
902 #if DRAW_IN_FRAME_DEBUG
903 CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
904 CGContextFillRect(cgContext, inBoxRect);
907 NS_OBJC_END_TRY_ABORT_BLOCK;
910 static const CellRenderSettings dropdownSettings = {
912 NSMakeSize(0, 16), // mini
913 NSMakeSize(0, 19), // small
914 NSMakeSize(0, 22) // regular
917 NSMakeSize(18, 0), // mini
918 NSMakeSize(38, 0), // small
919 NSMakeSize(44, 0) // regular
923 {1, 1, 2, 1}, // mini
924 {3, 0, 3, 1}, // small
925 {3, 0, 3, 0} // regular
928 {1, 1, 2, 1}, // mini
929 {3, 0, 3, 1}, // small
930 {3, 0, 3, 0} // regular
935 static const CellRenderSettings editableMenulistSettings = {
937 NSMakeSize(0, 15), // mini
938 NSMakeSize(0, 18), // small
939 NSMakeSize(0, 21) // regular
942 NSMakeSize(18, 0), // mini
943 NSMakeSize(38, 0), // small
944 NSMakeSize(44, 0) // regular
948 {0, 0, 2, 2}, // mini
949 {0, 0, 3, 2}, // small
950 {0, 1, 3, 3} // regular
953 {0, 0, 2, 2}, // mini
954 {0, 0, 3, 2}, // small
955 {0, 1, 3, 3} // regular
961 nsNativeThemeCocoa::DrawDropdown(CGContextRef cgContext, const HIRect& inBoxRect,
962 nsEventStates inState, PRUint8 aWidgetType,
965 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
967 [mDropdownCell setPullsDown:(aWidgetType == NS_THEME_BUTTON)];
969 BOOL isEditable = (aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD);
970 NSCell* cell = isEditable ? (NSCell*)mComboBoxCell : (NSCell*)mDropdownCell;
972 [cell setEnabled:!IsDisabled(aFrame, inState)];
973 [cell setShowsFirstResponder:(IsFocused(aFrame) || inState.HasState(NS_EVENT_STATE_FOCUS))];
974 [cell setHighlighted:IsOpenButton(aFrame)];
975 [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)];
977 const CellRenderSettings& settings = isEditable ? editableMenulistSettings : dropdownSettings;
978 DrawCellWithSnapping(cell, cgContext, inBoxRect, settings,
979 0.5f, mCellDrawView, IsFrameRTL(aFrame));
981 NS_OBJC_END_TRY_ABORT_BLOCK;
985 nsNativeThemeCocoa::DrawSpinButtons(CGContextRef cgContext, ThemeButtonKind inKind,
986 const HIRect& inBoxRect, ThemeDrawState inDrawState,
987 ThemeButtonAdornment inAdornment,
988 nsEventStates inState, nsIFrame* aFrame)
990 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
992 HIThemeButtonDrawInfo bdi;
995 bdi.value = kThemeButtonOff;
996 bdi.adornment = inAdornment;
998 if (IsDisabled(aFrame, inState))
999 bdi.state = kThemeStateUnavailable;
1001 bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive;
1003 HIThemeDrawButton(&inBoxRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL);
1005 NS_OBJC_END_TRY_ABORT_BLOCK;
1009 nsNativeThemeCocoa::DrawFrame(CGContextRef cgContext, HIThemeFrameKind inKind,
1010 const HIRect& inBoxRect, PRBool inDisabled,
1011 nsEventStates inState)
1013 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1015 HIThemeFrameDrawInfo fdi;
1019 // We don't ever set an inactive state for this because it doesn't
1020 // look right (see other apps).
1021 fdi.state = inDisabled ? kThemeStateUnavailable : kThemeStateActive;
1023 // for some reason focus rings on listboxes draw incorrectly
1024 if (inKind == kHIThemeFrameListBox)
1027 fdi.isFocused = inState.HasState(NS_EVENT_STATE_FOCUS);
1029 // HIThemeDrawFrame takes the rect for the content area of the frame, not
1030 // the bounding rect for the frame. Here we reduce the size of the rect we
1031 // will pass to make it the size of the content.
1032 HIRect drawRect = inBoxRect;
1033 if (inKind == kHIThemeFrameTextFieldSquare) {
1034 SInt32 frameOutset = 0;
1035 ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset);
1036 drawRect.origin.x += frameOutset;
1037 drawRect.origin.y += frameOutset;
1038 drawRect.size.width -= frameOutset * 2;
1039 drawRect.size.height -= frameOutset * 2;
1041 else if (inKind == kHIThemeFrameListBox) {
1042 SInt32 frameOutset = 0;
1043 ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset);
1044 drawRect.origin.x += frameOutset;
1045 drawRect.origin.y += frameOutset;
1046 drawRect.size.width -= frameOutset * 2;
1047 drawRect.size.height -= frameOutset * 2;
1050 #if DRAW_IN_FRAME_DEBUG
1051 CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
1052 CGContextFillRect(cgContext, inBoxRect);
1055 HIThemeDrawFrame(&drawRect, &fdi, cgContext, HITHEME_ORIENTATION);
1057 NS_OBJC_END_TRY_ABORT_BLOCK;
1061 RenderProgress(CGContextRef cgContext, const HIRect& aRenderRect, void* aData)
1063 HIThemeTrackDrawInfo* tdi = (HIThemeTrackDrawInfo*)aData;
1064 tdi->bounds = aRenderRect;
1065 HIThemeDrawTrack(tdi, NULL, cgContext, kHIThemeOrientationNormal);
1069 nsNativeThemeCocoa::DrawProgress(CGContextRef cgContext, const HIRect& inBoxRect,
1070 PRBool inIsIndeterminate, PRBool inIsHorizontal,
1071 PRInt32 inValue, PRInt32 inMaxValue,
1074 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1076 HIThemeTrackDrawInfo tdi;
1078 PRInt32 stepsPerSecond = inIsIndeterminate ? 60 : 30;
1079 PRInt32 milliSecondsPerStep = 1000 / stepsPerSecond;
1082 tdi.kind = inIsIndeterminate ? kThemeMediumIndeterminateBar: kThemeMediumProgressBar;
1083 tdi.bounds = inBoxRect;
1085 tdi.max = inMaxValue;
1086 tdi.value = inValue;
1087 tdi.attributes = inIsHorizontal ? kThemeTrackHorizontal : 0;
1088 tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
1089 tdi.trackInfo.progress.phase = PR_IntervalToMilliseconds(PR_IntervalNow()) /
1090 milliSecondsPerStep % 16;
1092 RenderTransformedHIThemeControl(cgContext, inBoxRect, RenderProgress, &tdi,
1093 IsFrameRTL(aFrame));
1095 NS_OBJC_END_TRY_ABORT_BLOCK;
1099 nsNativeThemeCocoa::DrawTabPanel(CGContextRef cgContext, const HIRect& inBoxRect,
1102 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1104 HIThemeTabPaneDrawInfo tpdi;
1107 tpdi.state = FrameIsInActiveWindow(aFrame) ? kThemeStateActive : kThemeStateInactive;
1108 tpdi.direction = kThemeTabNorth;
1109 tpdi.size = kHIThemeTabSizeNormal;
1110 tpdi.kind = kHIThemeTabKindNormal;
1112 HIThemeDrawTabPane(&inBoxRect, &tpdi, cgContext, HITHEME_ORIENTATION);
1114 NS_OBJC_END_TRY_ABORT_BLOCK;
1118 nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect,
1119 nsEventStates inState, PRBool inIsVertical,
1120 PRBool inIsReverse, PRInt32 inCurrentValue,
1121 PRInt32 inMinValue, PRInt32 inMaxValue,
1124 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1126 HIThemeTrackDrawInfo tdi;
1129 tdi.kind = kThemeMediumSlider;
1130 tdi.bounds = inBoxRect;
1131 tdi.min = inMinValue;
1132 tdi.max = inMaxValue;
1133 tdi.value = inCurrentValue;
1134 tdi.attributes = kThemeTrackShowThumb;
1136 tdi.attributes |= kThemeTrackHorizontal;
1138 tdi.attributes |= kThemeTrackRightToLeft;
1139 if (inState.HasState(NS_EVENT_STATE_FOCUS))
1140 tdi.attributes |= kThemeTrackHasFocus;
1141 if (IsDisabled(aFrame, inState))
1142 tdi.enableState = kThemeTrackDisabled;
1144 tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
1145 tdi.trackInfo.slider.thumbDir = kThemeThumbPlain;
1146 tdi.trackInfo.slider.pressState = 0;
1148 HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION);
1150 NS_OBJC_END_TRY_ABORT_BLOCK;
1153 #define NATURAL_MINI_TAB_BUTTON_HEIGHT 17
1154 #define NATURAL_SMALL_TAB_BUTTON_HEIGHT 20
1155 #define NATURAL_REGULAR_TAB_BUTTON_HEIGHT 23
1158 nsNativeThemeCocoa::DrawTab(CGContextRef cgContext, HIRect inBoxRect,
1159 nsEventStates inState, nsIFrame* aFrame)
1161 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1163 HIThemeTabDrawInfo tdi;
1165 tdi.kind = kHIThemeTabKindNormal;
1167 PRBool isSelected = IsSelectedTab(aFrame);
1168 PRBool isDisabled = IsDisabled(aFrame, inState);
1172 tdi.style = kThemeTabFrontUnavailable;
1174 tdi.style = FrameIsInActiveWindow(aFrame) ? kThemeTabFront : kThemeTabFrontInactive;
1177 tdi.style = kThemeTabNonFrontUnavailable;
1178 else if (inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER))
1179 tdi.style = kThemeTabNonFrontPressed;
1181 tdi.style = FrameIsInActiveWindow(aFrame) ? kThemeTabNonFront : kThemeTabNonFrontInactive;
1184 tdi.direction = kThemeTabNorth;
1185 tdi.size = kHIThemeTabSizeNormal;
1186 if (inBoxRect.size.height < NATURAL_REGULAR_TAB_BUTTON_HEIGHT)
1187 tdi.size = kHIThemeTabSizeSmall;
1188 if (inBoxRect.size.height < NATURAL_SMALL_TAB_BUTTON_HEIGHT)
1189 tdi.size = kHIThemeTabSizeMini;
1191 PRBool isRTL = IsFrameRTL(aFrame);
1192 PRBool isFirst = isRTL ? IsLastTab(aFrame) : IsFirstTab(aFrame);
1193 PRBool isLast = isRTL ? IsFirstTab(aFrame) : IsLastTab(aFrame);
1195 if (isFirst && isLast)
1196 tdi.position = kHIThemeTabPositionOnly;
1198 tdi.position = kHIThemeTabPositionFirst;
1200 tdi.position = kHIThemeTabPositionLast;
1202 tdi.position = kHIThemeTabPositionMiddle;
1204 // Tab separator management:
1205 // Normal tabs only draw their left separator, in the leftmost pixel row of
1206 // their frame. Selected tabs additionally draw their right separator, outside
1207 // of their frame. To prevent overlapping, the tab to the right of the
1208 // selected tab shouldn't draw its left separator.
1209 tdi.adornment = kHIThemeTabAdornmentNone;
1210 if (isRTL ? IsBeforeSelectedTab(aFrame) : IsAfterSelectedTab(aFrame)) {
1211 // On Leopard, the tab's left edge must be shifted 1px to the right.
1212 // On Tiger, this happens automatically when no leading separator is drawn.
1213 inBoxRect.origin.x += 1;
1214 inBoxRect.size.width -= 1;
1217 tdi.adornment = kHIThemeTabAdornmentLeadingSeparator;
1220 if (isSelected && !isLast) {
1221 tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
1222 // On Tiger, the right separator is drawn outside of the frame.
1223 // On Leopard, the right edge must be shifted 1px to the right.
1224 inBoxRect.size.width += 1;
1227 if (inState.HasState(NS_EVENT_STATE_FOCUS))
1228 tdi.adornment |= kThemeAdornmentFocus;
1230 HIThemeDrawTab(&inBoxRect, &tdi, cgContext, HITHEME_ORIENTATION, NULL);
1232 NS_OBJC_END_TRY_ABORT_BLOCK;
1236 ConvertToPressState(nsEventStates aButtonState, UInt8 aPressState)
1238 // If the button is pressed, return the press state passed in. Otherwise, return 0.
1239 return aButtonState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER) ? aPressState : 0;
1243 nsNativeThemeCocoa::GetScrollbarPressStates(nsIFrame *aFrame, nsEventStates aButtonStates[])
1245 static nsIContent::AttrValuesArray attributeValues[] = {
1246 &nsWidgetAtoms::scrollbarUpTop,
1247 &nsWidgetAtoms::scrollbarDownTop,
1248 &nsWidgetAtoms::scrollbarUpBottom,
1249 &nsWidgetAtoms::scrollbarDownBottom,
1253 // Get the state of any scrollbar buttons in our child frames
1254 for (nsIFrame *childFrame = aFrame->GetFirstChild(nsnull);
1256 childFrame = childFrame->GetNextSibling()) {
1258 nsIContent *childContent = childFrame->GetContent();
1259 if (!childContent) continue;
1260 PRInt32 attrIndex = childContent->FindAttrValueIn(kNameSpaceID_None, nsWidgetAtoms::sbattr,
1261 attributeValues, eCaseMatters);
1262 if (attrIndex < 0) continue;
1264 aButtonStates[attrIndex] = GetContentState(childFrame, NS_THEME_BUTTON);
1268 // Both of the following sets of numbers were derived by loading the testcase in
1269 // bmo bug 380185 in Safari and observing its behavior for various heights of scrollbar.
1270 // These magic numbers are the minimum sizes we can draw a scrollbar and still
1271 // have room for everything to display, including the thumb
1272 #define MIN_SCROLLBAR_SIZE_WITH_THUMB 61
1273 #define MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB 49
1274 // And these are the minimum sizes if we don't draw the thumb
1275 #define MIN_SCROLLBAR_SIZE 56
1276 #define MIN_SMALL_SCROLLBAR_SIZE 46
1279 nsNativeThemeCocoa::GetScrollbarDrawInfo(HIThemeTrackDrawInfo& aTdi, nsIFrame *aFrame,
1280 const CGSize& aSize, PRBool aShouldGetButtonStates)
1282 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1284 PRInt32 curpos = CheckIntAttr(aFrame, nsWidgetAtoms::curpos, 0);
1285 PRInt32 minpos = CheckIntAttr(aFrame, nsWidgetAtoms::minpos, 0);
1286 PRInt32 maxpos = CheckIntAttr(aFrame, nsWidgetAtoms::maxpos, 100);
1287 PRInt32 thumbSize = CheckIntAttr(aFrame, nsWidgetAtoms::pageincrement, 10);
1289 PRBool isHorizontal = aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::orient,
1290 nsWidgetAtoms::horizontal, eCaseMatters);
1291 PRBool isSmall = aFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL;
1294 aTdi.kind = isSmall ? kThemeSmallScrollBar : kThemeMediumScrollBar;
1295 aTdi.bounds.origin = CGPointZero;
1296 aTdi.bounds.size = aSize;
1299 aTdi.value = curpos;
1300 aTdi.attributes = 0;
1301 aTdi.enableState = kThemeTrackActive;
1303 aTdi.attributes |= kThemeTrackHorizontal;
1305 aTdi.trackInfo.scrollbar.viewsize = (SInt32)thumbSize;
1307 // This should be done early on so things like "kThemeTrackNothingToScroll" can
1308 // override the active enable state.
1309 aTdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
1311 /* Only display features if we have enough room for them.
1312 * Gecko still maintains the scrollbar info; this is just a visual issue (bug 380185).
1314 PRInt32 longSideLength = (PRInt32)(isHorizontal ? (aSize.width) : (aSize.height));
1315 if (longSideLength >= (isSmall ? MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB : MIN_SCROLLBAR_SIZE_WITH_THUMB)) {
1316 aTdi.attributes |= kThemeTrackShowThumb;
1318 else if (longSideLength < (isSmall ? MIN_SMALL_SCROLLBAR_SIZE : MIN_SCROLLBAR_SIZE)) {
1319 aTdi.enableState = kThemeTrackNothingToScroll;
1323 aTdi.trackInfo.scrollbar.pressState = 0;
1325 // Only go get these scrollbar button states if we need it. For example, there's no reaon to look up scrollbar button
1326 // states when we're only creating a TrackDrawInfo to determine the size of the thumb.
1327 if (aShouldGetButtonStates) {
1328 nsEventStates buttonStates[4];
1329 GetScrollbarPressStates(aFrame, buttonStates);
1330 NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
1331 // It seems that unless all four buttons are showing, kThemeTopOutsideArrowPressed is the correct constant for
1332 // the up scrollbar button.
1333 if ([buttonPlacement isEqualToString:@"DoubleBoth"]) {
1334 aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) |
1335 ConvertToPressState(buttonStates[1], kThemeTopInsideArrowPressed) |
1336 ConvertToPressState(buttonStates[2], kThemeBottomInsideArrowPressed) |
1337 ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed);
1339 aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) |
1340 ConvertToPressState(buttonStates[1], kThemeBottomOutsideArrowPressed) |
1341 ConvertToPressState(buttonStates[2], kThemeTopOutsideArrowPressed) |
1342 ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed);
1346 NS_OBJC_END_TRY_ABORT_BLOCK;
1350 RenderScrollbar(CGContextRef cgContext, const HIRect& aRenderRect, void* aData)
1352 HIThemeTrackDrawInfo* tdi = (HIThemeTrackDrawInfo*)aData;
1353 HIThemeDrawTrack(tdi, NULL, cgContext, HITHEME_ORIENTATION);
1357 nsNativeThemeCocoa::DrawScrollbar(CGContextRef aCGContext, const HIRect& aBoxRect, nsIFrame *aFrame)
1359 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1361 HIThemeTrackDrawInfo tdi;
1362 GetScrollbarDrawInfo(tdi, aFrame, aBoxRect.size, PR_TRUE); // True means we want the press states
1363 RenderTransformedHIThemeControl(aCGContext, aBoxRect, RenderScrollbar, &tdi);
1365 NS_OBJC_END_TRY_ABORT_BLOCK;
1369 nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame)
1371 // Walk our parents to find a scrollbar frame
1372 nsIFrame *scrollbarFrame = aFrame;
1374 if (scrollbarFrame->GetType() == nsWidgetAtoms::scrollbarFrame) break;
1375 } while ((scrollbarFrame = scrollbarFrame->GetParent()));
1377 // We return null if we can't find a parent scrollbar frame
1378 return scrollbarFrame;
1382 ToolbarCanBeUnified(CGContextRef cgContext, const HIRect& inBoxRect, NSWindow* aWindow)
1384 if (![aWindow isKindOfClass:[ToolbarWindow class]] ||
1385 [(ToolbarWindow*)aWindow drawsContentsIntoWindowFrame])
1388 float unifiedToolbarHeight = [(ToolbarWindow*)aWindow unifiedToolbarHeight];
1389 return inBoxRect.origin.x == 0 &&
1390 inBoxRect.size.width == [aWindow frame].size.width &&
1391 inBoxRect.origin.y <= 0.0 &&
1392 inBoxRect.origin.y + inBoxRect.size.height <= unifiedToolbarHeight;
1396 nsNativeThemeCocoa::DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect,
1399 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1401 float titlebarHeight = [(ToolbarWindow*)aWindow titlebarHeight];
1403 BOOL isMain = [aWindow isMainWindow] || ![NSView focusView];
1405 // Draw the gradient
1406 UnifiedGradientInfo info = { titlebarHeight, inBoxRect.size.height, isMain, NO };
1407 struct CGFunctionCallbacks callbacks = { 0, nsCocoaWindow::UnifiedShading, NULL };
1408 CGFunctionRef function = CGFunctionCreate(&info, 1, NULL, 4, NULL, &callbacks);
1409 float srcY = inBoxRect.origin.y;
1410 float dstY = srcY + inBoxRect.size.height - 1;
1411 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
1412 CGShadingRef shading = CGShadingCreateAxial(colorSpace,
1413 CGPointMake(0, srcY),
1414 CGPointMake(0, dstY), function,
1416 CGColorSpaceRelease(colorSpace);
1417 CGFunctionRelease(function);
1418 CGContextClipToRect(cgContext, inBoxRect);
1419 CGContextDrawShading(cgContext, shading);
1420 CGShadingRelease(shading);
1422 // Draw the border at the bottom of the toolbar.
1423 CGRect borderRect = CGRectMake(inBoxRect.origin.x, inBoxRect.origin.y +
1424 inBoxRect.size.height - 1.0f,
1425 inBoxRect.size.width, 1.0f);
1426 DrawNativeGreyColorInRect(cgContext, headerBorderGrey, borderRect, isMain);
1428 NS_OBJC_END_TRY_ABORT_BLOCK;
1431 struct GreyGradientInfo {
1436 static void GreyGradientCallback(void* aInfo, const CGFloat* aIn, CGFloat* aOut)
1438 GreyGradientInfo* info = static_cast<GreyGradientInfo*>(aInfo);
1439 CGFloat result = (1.0f - *aIn) * info->startGrey + *aIn * info->endGrey;
1446 static void DrawGreyGradient(CGContextRef cgContext, const HIRect& rect,
1447 float startGrey, float endGrey)
1449 if (rect.size.height <= 0.0f)
1452 GreyGradientInfo info = { startGrey, endGrey };
1453 struct CGFunctionCallbacks callbacks = { 0, GreyGradientCallback, NULL };
1454 CGFunctionRef function = CGFunctionCreate(&info, 1, NULL, 4, NULL, &callbacks);
1455 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
1456 CGShadingRef shading = CGShadingCreateAxial(colorSpace,
1457 CGPointMake(0, CGRectGetMinY(rect)),
1458 CGPointMake(0, CGRectGetMaxY(rect)),
1459 function, false, false);
1460 CGColorSpaceRelease(colorSpace);
1461 CGFunctionRelease(function);
1462 CGContextSaveGState(cgContext);
1463 CGContextClipToRect(cgContext, rect);
1464 CGContextDrawShading(cgContext, shading);
1465 CGContextRestoreGState(cgContext);
1466 CGShadingRelease(shading);
1470 nsNativeThemeCocoa::DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect,
1473 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1475 if (inBoxRect.size.height < 2.0f)
1478 BOOL isMain = [NativeWindowForFrame(aFrame) isMainWindow] || ![NSView focusView];
1480 // Draw the borders at the top of the statusbar.
1481 CGRect rect = CGRectMake(inBoxRect.origin.x, inBoxRect.origin.y,
1482 inBoxRect.size.width, 1.0f);
1483 DrawNativeGreyColorInRect(cgContext, statusbarFirstTopBorderGrey, rect, isMain);
1484 rect.origin.y += 1.0f;
1485 DrawNativeGreyColorInRect(cgContext, statusbarSecondTopBorderGrey, rect, isMain);
1487 // Draw the gradient.
1488 DrawGreyGradient(cgContext, CGRectMake(inBoxRect.origin.x, inBoxRect.origin.y + 2.0f,
1489 inBoxRect.size.width, inBoxRect.size.height - 2.0f),
1490 NativeGreyColorAsFloat(statusbarGradientStartGrey, isMain),
1491 NativeGreyColorAsFloat(statusbarGradientEndGrey, isMain));
1493 NS_OBJC_END_TRY_ABORT_BLOCK;
1497 RenderResizer(CGContextRef cgContext, const HIRect& aRenderRect, void* aData)
1499 HIThemeGrowBoxDrawInfo* drawInfo = (HIThemeGrowBoxDrawInfo*)aData;
1500 HIThemeDrawGrowBox(&CGPointZero, drawInfo, cgContext, kHIThemeOrientationNormal);
1504 nsNativeThemeCocoa::DrawResizer(CGContextRef cgContext, const HIRect& aRect,
1507 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1509 HIThemeGrowBoxDrawInfo drawInfo;
1510 drawInfo.version = 0;
1511 drawInfo.state = kThemeStateActive;
1512 drawInfo.kind = kHIThemeGrowBoxKindNormal;
1513 drawInfo.direction = kThemeGrowRight | kThemeGrowDown;
1514 drawInfo.size = kHIThemeGrowBoxSizeNormal;
1516 RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo,
1517 IsFrameRTL(aFrame));
1519 NS_OBJC_END_TRY_ABORT_BLOCK;
1523 nsNativeThemeCocoa::DrawWidgetBackground(nsIRenderingContext* aContext, nsIFrame* aFrame,
1524 PRUint8 aWidgetType, const nsRect& aRect,
1525 const nsRect& aDirtyRect)
1527 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1529 // setup to draw into the correct port
1530 nsCOMPtr<nsIDeviceContext> dctx;
1531 aContext->GetDeviceContext(*getter_AddRefs(dctx));
1532 PRInt32 p2a = dctx->AppUnitsPerDevPixel();
1534 gfxRect nativeDirtyRect(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
1535 gfxRect nativeWidgetRect(aRect.x, aRect.y, aRect.width, aRect.height);
1536 nativeWidgetRect.ScaleInverse(gfxFloat(p2a));
1537 nativeDirtyRect.ScaleInverse(gfxFloat(p2a));
1538 nativeWidgetRect.Round();
1539 if (nativeWidgetRect.IsEmpty())
1540 return NS_OK; // Don't attempt to draw invisible widgets.
1542 nsRefPtr<gfxContext> thebesCtx = aContext->ThebesContext();
1544 return NS_ERROR_FAILURE;
1546 gfxQuartzNativeDrawing nativeDrawing(thebesCtx, nativeDirtyRect);
1548 CGContextRef cgContext = nativeDrawing.BeginNativeDrawing();
1549 if (cgContext == nsnull) {
1550 // The Quartz surface handles 0x0 surfaces by internally
1551 // making all operations no-ops; there's no cgcontext created for them.
1552 // Unfortunately, this means that callers that want to render
1553 // directly to the CGContext need to be aware of this quirk.
1558 if (1 /*aWidgetType == NS_THEME_TEXTFIELD*/) {
1559 fprintf(stderr, "Native theme drawing widget %d [%p] dis:%d in rect [%d %d %d %d]\n",
1560 aWidgetType, aFrame, IsDisabled(aFrame), aRect.x, aRect.y, aRect.width, aRect.height);
1561 fprintf(stderr, "Cairo matrix: [%f %f %f %f %f %f]\n",
1562 mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0);
1563 fprintf(stderr, "Native theme xform[0]: [%f %f %f %f %f %f]\n",
1564 mm0.a, mm0.b, mm0.c, mm0.d, mm0.tx, mm0.ty);
1565 CGAffineTransform mm = CGContextGetCTM(cgContext);
1566 fprintf(stderr, "Native theme xform[1]: [%f %f %f %f %f %f]\n",
1567 mm.a, mm.b, mm.c, mm.d, mm.tx, mm.ty);
1571 CGRect macRect = CGRectMake(nativeWidgetRect.X(), nativeWidgetRect.Y(),
1572 nativeWidgetRect.Width(), nativeWidgetRect.Height());
1575 fprintf(stderr, " --> macRect %f %f %f %f\n",
1576 macRect.origin.x, macRect.origin.y, macRect.size.width, macRect.size.height);
1577 CGRect bounds = CGContextGetClipBoundingBox(cgContext);
1578 fprintf(stderr, " --> clip bounds: %f %f %f %f\n",
1579 bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
1581 //CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 1.0, 0.1);
1582 //CGContextFillRect(cgContext, bounds);
1585 nsEventStates eventState = GetContentState(aFrame, aWidgetType);
1587 switch (aWidgetType) {
1588 case NS_THEME_DIALOG: {
1589 HIThemeSetFill(kThemeBrushDialogBackgroundActive, NULL, cgContext, HITHEME_ORIENTATION);
1590 CGContextFillRect(cgContext, macRect);
1594 case NS_THEME_MENUPOPUP: {
1595 HIThemeMenuDrawInfo mdi = {
1597 menuType: IsDisabled(aFrame, eventState) ? kThemeMenuTypeInactive
1598 : kThemeMenuTypePopUp
1601 PRBool isLeftOfParent = PR_FALSE;
1602 if (IsSubmenu(aFrame, &isLeftOfParent) && !isLeftOfParent) {
1603 mdi.menuType = kThemeMenuTypeHierarchical;
1606 // The rounded corners draw outside the frame.
1607 CGRect deflatedRect = CGRectMake(macRect.origin.x, macRect.origin.y + 4,
1608 macRect.size.width, macRect.size.height - 8);
1609 HIThemeDrawMenuBackground(&deflatedRect, &mdi, cgContext, HITHEME_ORIENTATION);
1613 case NS_THEME_MENUITEM: {
1614 // Clear the background to get correct transparency.
1615 CGContextClearRect(cgContext, macRect);
1617 // maybe use kThemeMenuItemHierBackground or PopUpBackground instead of just Plain?
1618 HIThemeMenuItemDrawInfo drawInfo = {
1620 itemType: kThemeMenuItemPlain,
1621 state: (IsDisabled(aFrame, eventState) ? kThemeMenuDisabled :
1622 CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive) ? kThemeMenuSelected :
1626 // XXX pass in the menu rect instead of always using the item rect
1628 HIThemeDrawMenuItem(&macRect, &macRect, &drawInfo, cgContext, HITHEME_ORIENTATION, &ignored);
1632 case NS_THEME_MENUSEPARATOR: {
1633 ThemeMenuState menuState;
1634 if (IsDisabled(aFrame, eventState)) {
1635 menuState = kThemeMenuDisabled;
1638 menuState = CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive) ?
1639 kThemeMenuSelected : kThemeMenuActive;
1642 HIThemeMenuItemDrawInfo midi = { 0, kThemeMenuItemPlain, menuState };
1643 HIThemeDrawMenuSeparator(&macRect, &macRect, &midi, cgContext, HITHEME_ORIENTATION);
1647 case NS_THEME_TOOLTIP:
1648 CGContextSetRGBFillColor(cgContext, 0.996, 1.000, 0.792, 0.950);
1649 CGContextFillRect(cgContext, macRect);
1652 case NS_THEME_CHECKBOX:
1653 case NS_THEME_RADIO: {
1654 PRBool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
1655 DrawCheckboxOrRadio(cgContext, isCheckbox, macRect, GetCheckedOrSelected(aFrame, !isCheckbox),
1656 eventState, aFrame);
1660 case NS_THEME_BUTTON:
1661 if (IsDefaultButton(aFrame)) {
1662 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 10)) {
1663 NS_WARNING("Unable to animate button!");
1665 DrawButton(cgContext, kThemePushButton, macRect, true,
1666 kThemeButtonOff, kThemeAdornmentNone, eventState, aFrame);
1667 } else if (IsButtonTypeMenu(aFrame)) {
1668 DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame);
1670 DrawPushButton(cgContext, macRect, eventState, aFrame);
1674 case NS_THEME_BUTTON_BEVEL:
1675 DrawButton(cgContext, kThemeMediumBevelButton, macRect,
1676 IsDefaultButton(aFrame), kThemeButtonOff, kThemeAdornmentNone,
1677 eventState, aFrame);
1680 case NS_THEME_SPINNER: {
1681 ThemeDrawState state = kThemeStateActive;
1682 nsIContent* content = aFrame->GetContent();
1683 if (content->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::state,
1684 NS_LITERAL_STRING("up"), eCaseMatters)) {
1685 state = kThemeStatePressedUp;
1687 else if (content->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::state,
1688 NS_LITERAL_STRING("down"), eCaseMatters)) {
1689 state = kThemeStatePressedDown;
1692 DrawSpinButtons(cgContext, kThemeIncDecButton, macRect, state,
1693 kThemeAdornmentNone, eventState, aFrame);
1697 case NS_THEME_TOOLBAR_BUTTON:
1698 DrawButton(cgContext, kThemePushButton, macRect,
1699 IsDefaultButton(aFrame), kThemeButtonOn, kThemeAdornmentNone,
1700 eventState, aFrame);
1703 case NS_THEME_TOOLBAR_SEPARATOR: {
1704 HIThemeSeparatorDrawInfo sdi = { 0, kThemeStateActive };
1705 HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION);
1709 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
1710 case NS_THEME_TOOLBAR: {
1711 NSWindow* win = NativeWindowForFrame(aFrame);
1712 if (ToolbarCanBeUnified(cgContext, macRect, win)) {
1713 DrawUnifiedToolbar(cgContext, macRect, win);
1716 BOOL isMain = [win isMainWindow] || ![NSView focusView];
1717 CGRect drawRect = macRect;
1720 drawRect.size.height = 1.0f;
1721 DrawNativeGreyColorInRect(cgContext, toolbarTopBorderGrey, drawRect, isMain);
1724 drawRect.origin.y += drawRect.size.height;
1725 drawRect.size.height = macRect.size.height - 2.0f;
1726 DrawNativeGreyColorInRect(cgContext, headerEndGrey, drawRect, isMain);
1729 drawRect.origin.y += drawRect.size.height;
1730 drawRect.size.height = 1.0f;
1731 DrawNativeGreyColorInRect(cgContext, headerBorderGrey, drawRect, isMain);
1735 case NS_THEME_TOOLBOX: {
1736 HIThemeHeaderDrawInfo hdi = { 0, kThemeStateActive, kHIThemeHeaderKindWindow };
1737 HIThemeDrawHeader(&macRect, &hdi, cgContext, HITHEME_ORIENTATION);
1741 case NS_THEME_STATUSBAR:
1742 DrawStatusBar(cgContext, macRect, aFrame);
1745 case NS_THEME_DROPDOWN:
1746 case NS_THEME_DROPDOWN_TEXTFIELD:
1747 DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame);
1750 case NS_THEME_DROPDOWN_BUTTON:
1751 DrawButton(cgContext, kThemeArrowButton, macRect, PR_FALSE, kThemeButtonOn,
1752 kThemeAdornmentArrowDownArrow, eventState, aFrame);
1755 case NS_THEME_GROUPBOX: {
1756 HIThemeGroupBoxDrawInfo gdi = { 0, kThemeStateActive, kHIThemeGroupBoxKindPrimary };
1757 HIThemeDrawGroupBox(&macRect, &gdi, cgContext, HITHEME_ORIENTATION);
1761 case NS_THEME_TEXTFIELD:
1762 // HIThemeSetFill is not available on 10.3
1763 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
1764 CGContextFillRect(cgContext, macRect);
1766 // XUL textboxes set the native appearance on the containing box, while
1767 // concrete focus is set on the html:input element within it. We can
1768 // though, check the focused attribute of xul textboxes in this case.
1769 // On Mac, focus rings are always shown for textboxes, so we do not need
1770 // to check the window's focus ring state here
1771 if (aFrame->GetContent()->IsXUL() && IsFocused(aFrame)) {
1772 eventState |= NS_EVENT_STATE_FOCUS;
1775 DrawFrame(cgContext, kHIThemeFrameTextFieldSquare, macRect,
1776 IsDisabled(aFrame, eventState) || IsReadOnly(aFrame), eventState);
1779 case NS_THEME_SEARCHFIELD:
1780 DrawSearchField(cgContext, macRect, aFrame, eventState);
1783 case NS_THEME_PROGRESSBAR:
1784 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
1785 NS_WARNING("Unable to animate progressbar!");
1787 DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame),
1788 PR_TRUE, GetProgressValue(aFrame),
1789 GetProgressMaxValue(aFrame), aFrame);
1792 case NS_THEME_PROGRESSBAR_VERTICAL:
1793 DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame),
1794 PR_FALSE, GetProgressValue(aFrame),
1795 GetProgressMaxValue(aFrame), aFrame);
1798 case NS_THEME_PROGRESSBAR_CHUNK:
1799 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
1800 // do nothing, covered by the progress bar cases above
1803 case NS_THEME_TREEVIEW_TWISTY:
1804 DrawButton(cgContext, kThemeDisclosureButton, macRect, PR_FALSE,
1805 kThemeDisclosureRight, kThemeAdornmentNone, eventState, aFrame);
1808 case NS_THEME_TREEVIEW_TWISTY_OPEN:
1809 DrawButton(cgContext, kThemeDisclosureButton, macRect, PR_FALSE,
1810 kThemeDisclosureDown, kThemeAdornmentNone, eventState, aFrame);
1813 case NS_THEME_TREEVIEW_HEADER_CELL: {
1814 TreeSortDirection sortDirection = GetTreeSortDirection(aFrame);
1815 DrawButton(cgContext, kThemeListHeaderButton, macRect, PR_FALSE,
1816 sortDirection == eTreeSortDirection_Natural ? kThemeButtonOff : kThemeButtonOn,
1817 sortDirection == eTreeSortDirection_Ascending ?
1818 kThemeAdornmentHeaderButtonSortUp : kThemeAdornmentNone, eventState, aFrame);
1822 case NS_THEME_TREEVIEW_TREEITEM:
1823 case NS_THEME_TREEVIEW:
1824 // HIThemeSetFill is not available on 10.3
1825 // HIThemeSetFill(kThemeBrushWhite, NULL, cgContext, HITHEME_ORIENTATION);
1826 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
1827 CGContextFillRect(cgContext, macRect);
1830 case NS_THEME_TREEVIEW_HEADER:
1831 // do nothing, taken care of by individual header cells
1832 case NS_THEME_TREEVIEW_HEADER_SORTARROW:
1833 // do nothing, taken care of by treeview header
1834 case NS_THEME_TREEVIEW_LINE:
1835 // do nothing, these lines don't exist on macos
1838 case NS_THEME_SCALE_HORIZONTAL:
1839 case NS_THEME_SCALE_VERTICAL: {
1840 PRInt32 curpos = CheckIntAttr(aFrame, nsWidgetAtoms::curpos, 0);
1841 PRInt32 minpos = CheckIntAttr(aFrame, nsWidgetAtoms::minpos, 0);
1842 PRInt32 maxpos = CheckIntAttr(aFrame, nsWidgetAtoms::maxpos, 100);
1846 PRBool reverse = aFrame->GetContent()->
1847 AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::dir,
1848 NS_LITERAL_STRING("reverse"), eCaseMatters);
1849 DrawScale(cgContext, macRect, eventState,
1850 (aWidgetType == NS_THEME_SCALE_VERTICAL), reverse,
1851 curpos, minpos, maxpos, aFrame);
1855 case NS_THEME_SCALE_THUMB_HORIZONTAL:
1856 case NS_THEME_SCALE_THUMB_VERTICAL:
1857 // do nothing, drawn by scale
1860 case NS_THEME_SCROLLBAR_SMALL:
1861 case NS_THEME_SCROLLBAR: {
1862 DrawScrollbar(cgContext, macRect, aFrame);
1865 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
1866 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
1867 #if SCROLLBARS_VISUAL_DEBUG
1868 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 0, 0.6);
1869 CGContextFillRect(cgContext, macRect);
1872 case NS_THEME_SCROLLBAR_BUTTON_UP:
1873 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
1874 #if SCROLLBARS_VISUAL_DEBUG
1875 CGContextSetRGBFillColor(cgContext, 1.0, 0, 0, 0.6);
1876 CGContextFillRect(cgContext, macRect);
1879 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
1880 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
1881 #if SCROLLBARS_VISUAL_DEBUG
1882 CGContextSetRGBFillColor(cgContext, 0, 1.0, 0, 0.6);
1883 CGContextFillRect(cgContext, macRect);
1886 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
1887 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
1888 // do nothing, drawn by scrollbar
1891 case NS_THEME_TEXTFIELD_MULTILINE: {
1892 // we have to draw this by hand because there is no HITheme value for it
1893 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
1895 CGContextFillRect(cgContext, macRect);
1897 CGContextSetLineWidth(cgContext, 1.0);
1898 CGContextSetShouldAntialias(cgContext, false);
1900 // stroke everything but the top line of the text area
1901 CGContextSetRGBStrokeColor(cgContext, 0.6, 0.6, 0.6, 1.0);
1902 CGContextBeginPath(cgContext);
1903 CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1);
1904 CGContextAddLineToPoint(cgContext, macRect.origin.x, macRect.origin.y + macRect.size.height);
1905 CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + macRect.size.height);
1906 CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1);
1907 CGContextStrokePath(cgContext);
1909 // stroke the line across the top of the text area
1910 CGContextSetRGBStrokeColor(cgContext, 0.4510, 0.4510, 0.4510, 1.0);
1911 CGContextBeginPath(cgContext);
1912 CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1);
1913 CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1);
1914 CGContextStrokePath(cgContext);
1916 // draw a focus ring
1917 if (eventState.HasState(NS_EVENT_STATE_FOCUS)) {
1918 // We need to bring the rectangle in by 1 pixel on each side.
1919 CGRect cgr = CGRectMake(macRect.origin.x + 1,
1920 macRect.origin.y + 1,
1921 macRect.size.width - 2,
1922 macRect.size.height - 2);
1923 HIThemeDrawFocusRect(&cgr, true, cgContext, kHIThemeOrientationNormal);
1928 case NS_THEME_LISTBOX: {
1929 // We have to draw this by hand because kHIThemeFrameListBox drawing
1930 // is buggy on 10.5, see bug 579259.
1931 CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
1932 CGContextFillRect(cgContext, macRect);
1934 // #8E8E8E for the top border, #BEBEBE for the rest.
1935 int x = macRect.origin.x, y = macRect.origin.y;
1936 int w = macRect.size.width, h = macRect.size.height;
1937 CGContextSetRGBFillColor(cgContext, 0.557, 0.557, 0.557, 1.0);
1938 CGContextFillRect(cgContext, CGRectMake(x, y, w, 1));
1939 CGContextSetRGBFillColor(cgContext, 0.745, 0.745, 0.745, 1.0);
1940 CGContextFillRect(cgContext, CGRectMake(x, y + 1, 1, h - 1));
1941 CGContextFillRect(cgContext, CGRectMake(x + w - 1, y + 1, 1, h - 1));
1942 CGContextFillRect(cgContext, CGRectMake(x + 1, y + h - 1, w - 2, 1));
1947 DrawTab(cgContext, macRect, eventState, aFrame);
1950 case NS_THEME_TAB_PANELS:
1951 DrawTabPanel(cgContext, macRect, aFrame);
1954 case NS_THEME_RESIZER:
1955 DrawResizer(cgContext, macRect, aFrame);
1959 nativeDrawing.EndNativeDrawing();
1963 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1967 nsNativeThemeCocoa::RTLAwareMargin(const nsIntMargin& aMargin, nsIFrame* aFrame)
1969 if (IsFrameRTL(aFrame))
1970 return nsIntMargin(aMargin.right, aMargin.top, aMargin.left, aMargin.bottom);
1975 static const nsIntMargin kAquaDropdownBorder(5, 1, 22, 2);
1976 static const nsIntMargin kAquaComboboxBorder(4, 3, 20, 3);
1977 static const nsIntMargin kAquaSearchfieldBorder(19, 3, 5, 2);
1980 nsNativeThemeCocoa::GetWidgetBorder(nsIDeviceContext* aContext,
1982 PRUint8 aWidgetType,
1983 nsIntMargin* aResult)
1985 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1987 aResult->SizeTo(0, 0, 0, 0);
1989 switch (aWidgetType) {
1990 case NS_THEME_BUTTON:
1992 if (IsButtonTypeMenu(aFrame)) {
1993 *aResult = RTLAwareMargin(kAquaDropdownBorder, aFrame);
1995 aResult->SizeTo(7, 1, 7, 3);
2000 case NS_THEME_CHECKBOX:
2001 case NS_THEME_RADIO:
2003 // nsFormControlFrame::GetIntrinsicWidth and nsFormControlFrame::GetIntrinsicHeight
2004 // assume a border width of 2px.
2005 aResult->SizeTo(2, 2, 2, 2);
2009 case NS_THEME_DROPDOWN:
2010 case NS_THEME_DROPDOWN_BUTTON:
2011 *aResult = RTLAwareMargin(kAquaDropdownBorder, aFrame);
2014 case NS_THEME_DROPDOWN_TEXTFIELD:
2015 *aResult = RTLAwareMargin(kAquaComboboxBorder, aFrame);
2018 case NS_THEME_TEXTFIELD:
2020 SInt32 frameOutset = 0;
2021 ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset);
2023 SInt32 textPadding = 0;
2024 ::GetThemeMetric(kThemeMetricEditTextWhitespace, &textPadding);
2026 frameOutset += textPadding;
2028 aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
2032 case NS_THEME_TEXTFIELD_MULTILINE:
2033 aResult->SizeTo(1, 1, 1, 1);
2036 case NS_THEME_SEARCHFIELD:
2037 *aResult = RTLAwareMargin(kAquaSearchfieldBorder, aFrame);
2040 case NS_THEME_LISTBOX:
2042 SInt32 frameOutset = 0;
2043 ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset);
2044 aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
2048 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2049 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2051 // There's only an endcap to worry about when both arrows are on the bottom
2052 NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
2053 if (!buttonPlacement || [buttonPlacement isEqualToString:@"DoubleMax"]) {
2054 PRBool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL);
2056 nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
2057 if (!scrollbarFrame) return NS_ERROR_FAILURE;
2058 PRBool isSmall = (scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
2060 // There isn't a metric for this, so just hardcode a best guess at the value.
2061 // This value is even less exact due to the fact that the endcap is partially concave.
2062 PRInt32 endcapSize = isSmall ? 5 : 6;
2065 aResult->SizeTo(endcapSize, 0, 0, 0);
2067 aResult->SizeTo(0, endcapSize, 0, 0);
2072 case NS_THEME_STATUSBAR:
2073 aResult->SizeTo(0, 1, 0, 0);
2079 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2082 // Return PR_FALSE here to indicate that CSS padding values should be used. There is
2083 // no reason to make a distinction between padding and border values, just specify
2084 // whatever values you want in GetWidgetBorder and only use this to return PR_TRUE
2085 // if you want to override CSS padding values.
2087 nsNativeThemeCocoa::GetWidgetPadding(nsIDeviceContext* aContext,
2089 PRUint8 aWidgetType,
2090 nsIntMargin* aResult)
2092 // We don't want CSS padding being used for certain widgets.
2093 // See bug 381639 for an example of why.
2094 switch (aWidgetType) {
2095 case NS_THEME_BUTTON:
2096 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
2097 // and have a meaningful baseline, so they can't have
2098 // author-specified padding.
2099 case NS_THEME_CHECKBOX:
2100 case NS_THEME_RADIO:
2101 aResult->SizeTo(0, 0, 0, 0);
2108 nsNativeThemeCocoa::GetWidgetOverflow(nsIDeviceContext* aContext, nsIFrame* aFrame,
2109 PRUint8 aWidgetType, nsRect* aOverflowRect)
2111 switch (aWidgetType) {
2112 case NS_THEME_BUTTON:
2113 case NS_THEME_TEXTFIELD:
2114 case NS_THEME_TEXTFIELD_MULTILINE:
2115 case NS_THEME_SEARCHFIELD:
2116 case NS_THEME_LISTBOX:
2117 case NS_THEME_DROPDOWN:
2118 case NS_THEME_DROPDOWN_BUTTON:
2119 case NS_THEME_DROPDOWN_TEXTFIELD:
2120 case NS_THEME_CHECKBOX:
2121 case NS_THEME_RADIO:
2124 // We assume that the above widgets can draw a focus ring that will be less than
2125 // or equal to 4 pixels thick.
2126 nsIntMargin extraSize = nsIntMargin(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH);
2127 PRInt32 p2a = aContext->AppUnitsPerDevPixel();
2128 nsMargin m(NSIntPixelsToAppUnits(extraSize.left, p2a),
2129 NSIntPixelsToAppUnits(extraSize.top, p2a),
2130 NSIntPixelsToAppUnits(extraSize.right, p2a),
2131 NSIntPixelsToAppUnits(extraSize.bottom, p2a));
2132 aOverflowRect->Inflate(m);
2140 static const PRInt32 kRegularScrollbarThumbMinSize = 22;
2141 static const PRInt32 kSmallScrollbarThumbMinSize = 19;
2144 nsNativeThemeCocoa::GetMinimumWidgetSize(nsIRenderingContext* aContext,
2146 PRUint8 aWidgetType,
2148 PRBool* aIsOverridable)
2150 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2152 aResult->SizeTo(0,0);
2153 *aIsOverridable = PR_TRUE;
2155 switch (aWidgetType) {
2156 case NS_THEME_BUTTON:
2158 aResult->SizeTo(pushButtonSettings.minimumSizes[miniControlSize].width,
2159 pushButtonSettings.naturalSizes[miniControlSize].height);
2163 case NS_THEME_SPINNER:
2165 SInt32 buttonHeight = 0, buttonWidth = 0;
2166 ::GetThemeMetric(kThemeMetricLittleArrowsWidth, &buttonWidth);
2167 ::GetThemeMetric(kThemeMetricLittleArrowsHeight, &buttonHeight);
2168 aResult->SizeTo(buttonWidth, buttonHeight);
2169 *aIsOverridable = PR_FALSE;
2173 case NS_THEME_DROPDOWN:
2174 case NS_THEME_DROPDOWN_BUTTON:
2176 SInt32 popupHeight = 0;
2177 ::GetThemeMetric(kThemeMetricPopupButtonHeight, &popupHeight);
2178 aResult->SizeTo(0, popupHeight);
2182 case NS_THEME_TEXTFIELD:
2183 case NS_THEME_TEXTFIELD_MULTILINE:
2184 case NS_THEME_SEARCHFIELD:
2186 // at minimum, we should be tall enough for 9pt text.
2187 // I'm using hardcoded values here because the appearance manager
2188 // values for the frame size are incorrect.
2189 aResult->SizeTo(0, (2 + 2) /* top */ + 9 + (1 + 1) /* bottom */);
2193 case NS_THEME_PROGRESSBAR:
2195 SInt32 barHeight = 0;
2196 ::GetThemeMetric(kThemeMetricNormalProgressBarThickness, &barHeight);
2197 aResult->SizeTo(0, barHeight);
2201 case NS_THEME_TREEVIEW_TWISTY:
2202 case NS_THEME_TREEVIEW_TWISTY_OPEN:
2204 SInt32 twistyHeight = 0, twistyWidth = 0;
2205 ::GetThemeMetric(kThemeMetricDisclosureButtonWidth, &twistyWidth);
2206 ::GetThemeMetric(kThemeMetricDisclosureButtonHeight, &twistyHeight);
2207 aResult->SizeTo(twistyWidth, twistyHeight);
2208 *aIsOverridable = PR_FALSE;
2212 case NS_THEME_TREEVIEW_HEADER:
2213 case NS_THEME_TREEVIEW_HEADER_CELL:
2215 SInt32 headerHeight = 0;
2216 ::GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
2217 aResult->SizeTo(0, headerHeight - 1); // We don't need the top border.
2223 aResult->SizeTo(0, NATURAL_MINI_TAB_BUTTON_HEIGHT);
2227 case NS_THEME_SCALE_HORIZONTAL:
2229 SInt32 scaleHeight = 0;
2230 ::GetThemeMetric(kThemeMetricHSliderHeight, &scaleHeight);
2231 aResult->SizeTo(scaleHeight, scaleHeight);
2232 *aIsOverridable = PR_FALSE;
2236 case NS_THEME_SCALE_VERTICAL:
2238 SInt32 scaleWidth = 0;
2239 ::GetThemeMetric(kThemeMetricVSliderWidth, &scaleWidth);
2240 aResult->SizeTo(scaleWidth, scaleWidth);
2241 *aIsOverridable = PR_FALSE;
2245 case NS_THEME_SCROLLBAR_SMALL:
2247 SInt32 scrollbarWidth = 0;
2248 ::GetThemeMetric(kThemeMetricSmallScrollBarWidth, &scrollbarWidth);
2249 aResult->SizeTo(scrollbarWidth, scrollbarWidth);
2250 *aIsOverridable = PR_FALSE;
2254 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2255 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2257 // Find our parent scrollbar frame in order to find out whether we're in
2258 // a small or a large scrollbar.
2259 nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
2260 if (!scrollbarFrame)
2261 return NS_ERROR_FAILURE;
2263 PRBool isSmall = (scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
2264 PRBool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL);
2265 PRInt32& minSize = isHorizontal ? aResult->width : aResult->height;
2266 minSize = isSmall ? kSmallScrollbarThumbMinSize : kRegularScrollbarThumbMinSize;
2270 case NS_THEME_SCROLLBAR:
2271 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2272 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2274 // yeah, i know i'm cheating a little here, but i figure that it
2275 // really doesn't matter if the scrollbar is vertical or horizontal
2276 // and the width metric is a really good metric for every piece
2277 // of the scrollbar.
2279 nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
2280 if (!scrollbarFrame) return NS_ERROR_FAILURE;
2282 PRInt32 themeMetric = (scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ?
2283 kThemeMetricSmallScrollBarWidth :
2284 kThemeMetricScrollBarWidth;
2285 SInt32 scrollbarWidth = 0;
2286 ::GetThemeMetric(themeMetric, &scrollbarWidth);
2287 aResult->SizeTo(scrollbarWidth, scrollbarWidth);
2288 *aIsOverridable = PR_FALSE;
2292 case NS_THEME_SCROLLBAR_BUTTON_UP:
2293 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2294 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2295 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2297 nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
2298 if (!scrollbarFrame) return NS_ERROR_FAILURE;
2300 // Since there is no NS_THEME_SCROLLBAR_BUTTON_UP_SMALL we need to ask the parent what appearance style it has.
2301 PRInt32 themeMetric = (scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ?
2302 kThemeMetricSmallScrollBarWidth :
2303 kThemeMetricScrollBarWidth;
2304 SInt32 scrollbarWidth = 0;
2305 ::GetThemeMetric(themeMetric, &scrollbarWidth);
2307 // It seems that for both sizes of scrollbar, the buttons are one pixel "longer".
2308 if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT || aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT)
2309 aResult->SizeTo(scrollbarWidth+1, scrollbarWidth);
2311 aResult->SizeTo(scrollbarWidth, scrollbarWidth+1);
2313 *aIsOverridable = PR_FALSE;
2316 case NS_THEME_RESIZER:
2318 HIThemeGrowBoxDrawInfo drawInfo;
2319 drawInfo.version = 0;
2320 drawInfo.state = kThemeStateActive;
2321 drawInfo.kind = kHIThemeGrowBoxKindNormal;
2322 drawInfo.direction = kThemeGrowRight | kThemeGrowDown;
2323 drawInfo.size = kHIThemeGrowBoxSizeNormal;
2324 HIPoint pnt = { 0, 0 };
2326 HIThemeGetGrowBoxBounds(&pnt, &drawInfo, &bounds);
2327 aResult->SizeTo(bounds.size.width, bounds.size.height);
2328 *aIsOverridable = PR_FALSE;
2334 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2338 nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType,
2339 nsIAtom* aAttribute, PRBool* aShouldRepaint)
2341 // Some widget types just never change state.
2342 switch (aWidgetType) {
2343 case NS_THEME_TOOLBOX:
2344 case NS_THEME_TOOLBAR:
2345 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
2346 case NS_THEME_TOOLBAR_BUTTON:
2347 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2348 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2349 case NS_THEME_STATUSBAR:
2350 case NS_THEME_STATUSBAR_PANEL:
2351 case NS_THEME_STATUSBAR_RESIZER_PANEL:
2352 case NS_THEME_TOOLTIP:
2353 case NS_THEME_TAB_PANELS:
2354 case NS_THEME_TAB_PANEL:
2355 case NS_THEME_DIALOG:
2356 case NS_THEME_MENUPOPUP:
2357 case NS_THEME_GROUPBOX:
2358 case NS_THEME_PROGRESSBAR_CHUNK:
2359 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2360 case NS_THEME_PROGRESSBAR:
2361 case NS_THEME_PROGRESSBAR_VERTICAL:
2362 *aShouldRepaint = PR_FALSE;
2366 // XXXdwh Not sure what can really be done here. Can at least guess for
2367 // specific widgets that they're highly unlikely to have certain states.
2368 // For example, a toolbar doesn't care about any states.
2370 // Hover/focus/active changed. Always repaint.
2371 *aShouldRepaint = PR_TRUE;
2373 // Check the attribute to see if it's relevant.
2374 // disabled, checked, dlgtype, default, etc.
2375 *aShouldRepaint = PR_FALSE;
2376 if (aAttribute == nsWidgetAtoms::disabled ||
2377 aAttribute == nsWidgetAtoms::checked ||
2378 aAttribute == nsWidgetAtoms::selected ||
2379 aAttribute == nsWidgetAtoms::mozmenuactive ||
2380 aAttribute == nsWidgetAtoms::sortdirection ||
2381 aAttribute == nsWidgetAtoms::focused ||
2382 aAttribute == nsWidgetAtoms::_default ||
2383 aAttribute == nsWidgetAtoms::open)
2384 *aShouldRepaint = PR_TRUE;
2391 nsNativeThemeCocoa::ThemeChanged()
2393 // This is unimplemented because we don't care if gecko changes its theme
2394 // and Mac OS X doesn't have themes.
2399 nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame,
2400 PRUint8 aWidgetType)
2402 // We don't have CSS set up to render non-native scrollbars on Mac OS X so we
2403 // render natively even if native theme support is disabled.
2404 if (aWidgetType != NS_THEME_SCROLLBAR &&
2405 aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled())
2408 // if this is a dropdown button in a combobox the answer is always no
2409 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON) {
2410 nsIFrame* parentFrame = aFrame->GetParent();
2411 if (parentFrame && (parentFrame->GetType() == nsWidgetAtoms::comboboxControlFrame))
2415 switch (aWidgetType) {
2416 case NS_THEME_LISTBOX:
2418 case NS_THEME_DIALOG:
2419 case NS_THEME_WINDOW:
2420 case NS_THEME_MENUPOPUP:
2421 case NS_THEME_MENUITEM:
2422 case NS_THEME_MENUSEPARATOR:
2423 case NS_THEME_TOOLTIP:
2425 case NS_THEME_CHECKBOX:
2426 case NS_THEME_CHECKBOX_CONTAINER:
2427 case NS_THEME_RADIO:
2428 case NS_THEME_RADIO_CONTAINER:
2429 case NS_THEME_GROUPBOX:
2430 case NS_THEME_BUTTON:
2431 case NS_THEME_BUTTON_BEVEL:
2432 case NS_THEME_SPINNER:
2433 case NS_THEME_TOOLBAR:
2434 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
2435 case NS_THEME_STATUSBAR:
2436 case NS_THEME_TEXTFIELD:
2437 case NS_THEME_TEXTFIELD_MULTILINE:
2438 case NS_THEME_SEARCHFIELD:
2439 //case NS_THEME_TOOLBOX:
2440 //case NS_THEME_TOOLBAR_BUTTON:
2441 case NS_THEME_PROGRESSBAR:
2442 case NS_THEME_PROGRESSBAR_VERTICAL:
2443 case NS_THEME_PROGRESSBAR_CHUNK:
2444 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2445 case NS_THEME_TOOLBAR_SEPARATOR:
2447 case NS_THEME_TAB_PANELS:
2450 case NS_THEME_TREEVIEW_TWISTY:
2451 case NS_THEME_TREEVIEW_TWISTY_OPEN:
2452 case NS_THEME_TREEVIEW:
2453 case NS_THEME_TREEVIEW_HEADER:
2454 case NS_THEME_TREEVIEW_HEADER_CELL:
2455 case NS_THEME_TREEVIEW_HEADER_SORTARROW:
2456 case NS_THEME_TREEVIEW_TREEITEM:
2457 case NS_THEME_TREEVIEW_LINE:
2459 case NS_THEME_SCALE_HORIZONTAL:
2460 case NS_THEME_SCALE_THUMB_HORIZONTAL:
2461 case NS_THEME_SCALE_VERTICAL:
2462 case NS_THEME_SCALE_THUMB_VERTICAL:
2464 case NS_THEME_SCROLLBAR:
2465 case NS_THEME_SCROLLBAR_SMALL:
2466 case NS_THEME_SCROLLBAR_BUTTON_UP:
2467 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2468 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2469 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2470 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2471 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2472 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2473 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2475 case NS_THEME_DROPDOWN:
2476 case NS_THEME_DROPDOWN_BUTTON:
2477 case NS_THEME_DROPDOWN_TEXT:
2478 case NS_THEME_DROPDOWN_TEXTFIELD:
2479 return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
2482 case NS_THEME_RESIZER:
2484 nsIFrame* parentFrame = aFrame->GetParent();
2485 if (!parentFrame || parentFrame->GetType() != nsWidgetAtoms::scrollFrame)
2488 // Note that IsWidgetStyled is not called for resizers on Mac. This is
2489 // because for scrollable containers, the native resizer looks better
2490 // when scrollbars are present even when the style is overriden, and the
2491 // custom transparent resizer looks better when scrollbars are not
2493 nsIScrollableFrame* scrollFrame = do_QueryFrame(parentFrame);
2494 return (scrollFrame && scrollFrame->GetScrollbarVisibility());
2503 nsNativeThemeCocoa::WidgetIsContainer(PRUint8 aWidgetType)
2505 // flesh this out at some point
2506 switch (aWidgetType) {
2507 case NS_THEME_DROPDOWN_BUTTON:
2508 case NS_THEME_RADIO:
2509 case NS_THEME_CHECKBOX:
2510 case NS_THEME_PROGRESSBAR:
2518 nsNativeThemeCocoa::ThemeDrawsFocusForWidget(nsPresContext* aPresContext, nsIFrame* aFrame, PRUint8 aWidgetType)
2520 if (aWidgetType == NS_THEME_DROPDOWN ||
2521 aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD ||
2522 aWidgetType == NS_THEME_BUTTON ||
2523 aWidgetType == NS_THEME_RADIO ||
2524 aWidgetType == NS_THEME_CHECKBOX)
2531 nsNativeThemeCocoa::ThemeNeedsComboboxDropmarker()
2536 nsITheme::Transparency
2537 nsNativeThemeCocoa::GetWidgetTransparency(nsIFrame* aFrame, PRUint8 aWidgetType)
2539 switch (aWidgetType) {
2540 case NS_THEME_MENUPOPUP:
2541 case NS_THEME_TOOLTIP:
2542 return eTransparent;
2544 case NS_THEME_SCROLLBAR_SMALL:
2545 case NS_THEME_SCROLLBAR:
2546 case NS_THEME_STATUSBAR:
2547 // Knowing that scrollbars and statusbars are opaque improves
2548 // performance, because we create layers for them.
2552 return eUnknownTransparency;