1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/cocoa/nsNativeThemeCocoa.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3451 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsNativeThemeCocoa.h" 1.10 +#include "nsObjCExceptions.h" 1.11 +#include "nsNumberControlFrame.h" 1.12 +#include "nsRangeFrame.h" 1.13 +#include "nsRenderingContext.h" 1.14 +#include "nsRect.h" 1.15 +#include "nsSize.h" 1.16 +#include "nsThemeConstants.h" 1.17 +#include "nsIPresShell.h" 1.18 +#include "nsPresContext.h" 1.19 +#include "nsIContent.h" 1.20 +#include "nsIDocument.h" 1.21 +#include "nsIFrame.h" 1.22 +#include "nsIAtom.h" 1.23 +#include "nsNameSpaceManager.h" 1.24 +#include "nsPresContext.h" 1.25 +#include "nsGkAtoms.h" 1.26 +#include "nsCocoaFeatures.h" 1.27 +#include "nsCocoaWindow.h" 1.28 +#include "nsNativeThemeColors.h" 1.29 +#include "nsIScrollableFrame.h" 1.30 +#include "mozilla/EventStates.h" 1.31 +#include "mozilla/dom/Element.h" 1.32 +#include "mozilla/dom/HTMLMeterElement.h" 1.33 +#include "nsLookAndFeel.h" 1.34 + 1.35 +#include "gfxContext.h" 1.36 +#include "gfxQuartzSurface.h" 1.37 +#include "gfxQuartzNativeDrawing.h" 1.38 +#include <algorithm> 1.39 + 1.40 +using namespace mozilla; 1.41 +using namespace mozilla::gfx; 1.42 +using mozilla::dom::HTMLMeterElement; 1.43 + 1.44 +#define DRAW_IN_FRAME_DEBUG 0 1.45 +#define SCROLLBARS_VISUAL_DEBUG 0 1.46 + 1.47 +// private Quartz routines needed here 1.48 +extern "C" { 1.49 + CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform); 1.50 +} 1.51 + 1.52 +// Workaround for NSCell control tint drawing 1.53 +// Without this workaround, NSCells are always drawn with the clear control tint 1.54 +// as long as they're not attached to an NSControl which is a subview of an active window. 1.55 +// XXXmstange Why doesn't Webkit need this? 1.56 +@implementation NSCell (ControlTintWorkaround) 1.57 +- (int)_realControlTint { return [self controlTint]; } 1.58 +@end 1.59 + 1.60 +// The purpose of this class is to provide objects that can be used when drawing 1.61 +// NSCells using drawWithFrame:inView: without causing any harm. The only 1.62 +// messages that will be sent to such an object are "isFlipped" and 1.63 +// "currentEditor": isFlipped needs to return YES in order to avoid drawing bugs 1.64 +// on 10.4 (see bug 465069); currentEditor (which isn't even a method of 1.65 +// NSView) will be called when drawing search fields, and we only provide it in 1.66 +// order to prevent "unrecognized selector" exceptions. 1.67 +// There's no need to pass the actual NSView that we're drawing into to 1.68 +// drawWithFrame:inView:. What's more, doing so even causes unnecessary 1.69 +// invalidations as soon as we draw a focusring! 1.70 +@interface CellDrawView : NSView 1.71 + 1.72 +@end; 1.73 + 1.74 +@implementation CellDrawView 1.75 + 1.76 +- (BOOL)isFlipped 1.77 +{ 1.78 + return YES; 1.79 +} 1.80 + 1.81 +- (NSText*)currentEditor 1.82 +{ 1.83 + return nil; 1.84 +} 1.85 + 1.86 +@end 1.87 + 1.88 +static void 1.89 +DrawCellIncludingFocusRing(NSCell* aCell, NSRect aWithFrame, NSView* aInView) 1.90 +{ 1.91 + [aCell drawWithFrame:aWithFrame inView:aInView]; 1.92 + 1.93 +#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 1.94 + // When building with the 10.8 SDK or higher, focus rings don't draw as part 1.95 + // of -[NSCell drawWithFrame:inView:] and must be drawn by a separate call 1.96 + // to -[NSCell drawFocusRingMaskWithFrame:inView:]; . 1.97 + // See the NSButtonCell section under 1.98 + // https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_8Notes 1.99 + if ([aCell showsFirstResponder]) { 1.100 + CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 1.101 + CGContextSaveGState(cgContext); 1.102 + 1.103 + // It's important to set the focus ring style before we enter the 1.104 + // transparency layer so that the transparency layer only contains 1.105 + // the normal button mask without the focus ring, and the conversion 1.106 + // to the focus ring shape happens only when the transparency layer is 1.107 + // ended. 1.108 + NSSetFocusRingStyle(NSFocusRingOnly); 1.109 + 1.110 + // We need to draw the whole button into a transparency layer because 1.111 + // many button types are composed of multiple parts, and if these parts 1.112 + // were drawn while the focus ring style was active, each individual part 1.113 + // would produce a focus ring for itself. But we only want one focus ring 1.114 + // for the whole button. The transparency layer is a way to merge the 1.115 + // individual button parts together before the focus ring shape is 1.116 + // calculated. 1.117 + CGContextBeginTransparencyLayerWithRect(cgContext, NSRectToCGRect(aWithFrame), 0); 1.118 + [aCell drawFocusRingMaskWithFrame:aWithFrame inView:aInView]; 1.119 + CGContextEndTransparencyLayer(cgContext); 1.120 + 1.121 + CGContextRestoreGState(cgContext); 1.122 + } 1.123 +#endif 1.124 +} 1.125 + 1.126 +/** 1.127 + * NSProgressBarCell is used to draw progress bars of any size. 1.128 + */ 1.129 +@interface NSProgressBarCell : NSCell 1.130 +{ 1.131 + /*All instance variables are private*/ 1.132 + double mValue; 1.133 + double mMax; 1.134 + bool mIsIndeterminate; 1.135 + bool mIsHorizontal; 1.136 +} 1.137 + 1.138 +- (void)setValue:(double)value; 1.139 +- (double)value; 1.140 +- (void)setMax:(double)max; 1.141 +- (double)max; 1.142 +- (void)setIndeterminate:(bool)aIndeterminate; 1.143 +- (bool)isIndeterminate; 1.144 +- (void)setHorizontal:(bool)aIsHorizontal; 1.145 +- (bool)isHorizontal; 1.146 +- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView; 1.147 +@end 1.148 + 1.149 +@implementation NSProgressBarCell 1.150 + 1.151 +- (void)setMax:(double)aMax 1.152 +{ 1.153 + mMax = aMax; 1.154 +} 1.155 + 1.156 +- (double)max 1.157 +{ 1.158 + return mMax; 1.159 +} 1.160 + 1.161 +- (void)setValue:(double)aValue 1.162 +{ 1.163 + mValue = aValue; 1.164 +} 1.165 + 1.166 +- (double)value 1.167 +{ 1.168 + return mValue; 1.169 +} 1.170 + 1.171 +- (void)setIndeterminate:(bool)aIndeterminate 1.172 +{ 1.173 + mIsIndeterminate = aIndeterminate; 1.174 +} 1.175 + 1.176 +- (bool)isIndeterminate 1.177 +{ 1.178 + return mIsIndeterminate; 1.179 +} 1.180 + 1.181 +- (void)setHorizontal:(bool)aIsHorizontal 1.182 +{ 1.183 + mIsHorizontal = aIsHorizontal; 1.184 +} 1.185 + 1.186 +- (bool)isHorizontal 1.187 +{ 1.188 + return mIsHorizontal; 1.189 +} 1.190 + 1.191 +- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView 1.192 +{ 1.193 + CGContext* cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 1.194 + 1.195 + HIThemeTrackDrawInfo tdi; 1.196 + 1.197 + tdi.version = 0; 1.198 + tdi.min = 0; 1.199 + 1.200 + tdi.value = INT32_MAX * (mValue / mMax); 1.201 + tdi.max = INT32_MAX; 1.202 + tdi.bounds = NSRectToCGRect(cellFrame); 1.203 + tdi.attributes = mIsHorizontal ? kThemeTrackHorizontal : 0; 1.204 + tdi.enableState = [self controlTint] == NSClearControlTint ? kThemeTrackInactive 1.205 + : kThemeTrackActive; 1.206 + 1.207 + NSControlSize size = [self controlSize]; 1.208 + if (size == NSRegularControlSize) { 1.209 + tdi.kind = mIsIndeterminate ? kThemeLargeIndeterminateBar 1.210 + : kThemeLargeProgressBar; 1.211 + } else { 1.212 + NS_ASSERTION(size == NSSmallControlSize, 1.213 + "We shouldn't have another size than small and regular for the moment"); 1.214 + tdi.kind = mIsIndeterminate ? kThemeMediumIndeterminateBar 1.215 + : kThemeMediumProgressBar; 1.216 + } 1.217 + 1.218 + int32_t stepsPerSecond = mIsIndeterminate ? 60 : 30; 1.219 + int32_t milliSecondsPerStep = 1000 / stepsPerSecond; 1.220 + tdi.trackInfo.progress.phase = uint8_t(PR_IntervalToMilliseconds(PR_IntervalNow()) / 1.221 + milliSecondsPerStep); 1.222 + 1.223 + HIThemeDrawTrack(&tdi, NULL, cgContext, kHIThemeOrientationNormal); 1.224 +} 1.225 + 1.226 +@end 1.227 + 1.228 +@interface ContextAwareSearchFieldCell : NSSearchFieldCell 1.229 +{ 1.230 + nsIFrame* mContext; 1.231 +} 1.232 + 1.233 +// setContext: stores the searchfield nsIFrame so that it can be consulted 1.234 +// during painting. Please reset this by calling setContext:nullptr as soon as 1.235 +// you're done with painting because we don't want to keep a dangling pointer. 1.236 +- (void)setContext:(nsIFrame*)aContext; 1.237 +@end 1.238 + 1.239 +@implementation ContextAwareSearchFieldCell 1.240 + 1.241 +- (id)initTextCell:(NSString*)aString 1.242 +{ 1.243 + if ((self = [super initTextCell:aString])) { 1.244 + mContext = nullptr; 1.245 + } 1.246 + return self; 1.247 +} 1.248 + 1.249 +- (void)setContext:(nsIFrame*)aContext 1.250 +{ 1.251 + mContext = aContext; 1.252 +} 1.253 + 1.254 +static BOOL IsToolbarStyleContainer(nsIFrame* aFrame) 1.255 +{ 1.256 + nsIContent* content = aFrame->GetContent(); 1.257 + if (!content) 1.258 + return NO; 1.259 + 1.260 + if (content->Tag() == nsGkAtoms::toolbar || 1.261 + content->Tag() == nsGkAtoms::toolbox || 1.262 + content->Tag() == nsGkAtoms::statusbar) 1.263 + return YES; 1.264 + 1.265 + switch (aFrame->StyleDisplay()->mAppearance) { 1.266 + case NS_THEME_TOOLBAR: 1.267 + case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: 1.268 + case NS_THEME_STATUSBAR: 1.269 + return YES; 1.270 + default: 1.271 + return NO; 1.272 + } 1.273 +} 1.274 + 1.275 +- (BOOL)_isToolbarMode 1.276 +{ 1.277 + // On 10.7, searchfields have two different styles, depending on whether 1.278 + // the searchfield is on top of of window chrome. This function is called on 1.279 + // 10.7 during drawing in order to determine which style to use. 1.280 + for (nsIFrame* frame = mContext; frame; frame = frame->GetParent()) { 1.281 + if (IsToolbarStyleContainer(frame)) { 1.282 + return YES; 1.283 + } 1.284 + } 1.285 + return NO; 1.286 +} 1.287 + 1.288 +@end 1.289 + 1.290 +// Workaround for Bug 542048 1.291 +// On 64-bit, NSSearchFieldCells don't draw focus rings. 1.292 +#if defined(__x86_64__) 1.293 + 1.294 +static void DrawFocusRing(NSRect rect, float radius) 1.295 +{ 1.296 + NSSetFocusRingStyle(NSFocusRingOnly); 1.297 + NSBezierPath* path = [NSBezierPath bezierPath]; 1.298 + rect = NSInsetRect(rect, radius, radius); 1.299 + [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0]; 1.300 + [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0]; 1.301 + [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle: 0.0 endAngle: 90.0]; 1.302 + [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle: 90.0 endAngle:180.0]; 1.303 + [path closePath]; 1.304 + [path fill]; 1.305 +} 1.306 + 1.307 +@interface SearchFieldCellWithFocusRing : ContextAwareSearchFieldCell {} @end 1.308 + 1.309 +@implementation SearchFieldCellWithFocusRing 1.310 + 1.311 +- (void)drawWithFrame:(NSRect)rect inView:(NSView*)controlView 1.312 +{ 1.313 + [super drawWithFrame:rect inView:controlView]; 1.314 + if ([self showsFirstResponder]) { 1.315 + DrawFocusRing(rect, NSHeight(rect) / 2); 1.316 + } 1.317 +} 1.318 + 1.319 +@end 1.320 + 1.321 +#endif 1.322 + 1.323 +// Copied from nsLookAndFeel.h 1.324 +// Apple hasn't defined a constant for scollbars with two arrows on each end, so we'll use this one. 1.325 +static const int kThemeScrollBarArrowsBoth = 2; 1.326 + 1.327 +#define HITHEME_ORIENTATION kHIThemeOrientationNormal 1.328 +#define MAX_FOCUS_RING_WIDTH 4 1.329 + 1.330 +// These enums are for indexing into the margin array. 1.331 +enum { 1.332 + leopardOS = 0 1.333 +}; 1.334 + 1.335 +enum { 1.336 + miniControlSize, 1.337 + smallControlSize, 1.338 + regularControlSize 1.339 +}; 1.340 + 1.341 +enum { 1.342 + leftMargin, 1.343 + topMargin, 1.344 + rightMargin, 1.345 + bottomMargin 1.346 +}; 1.347 + 1.348 +static int EnumSizeForCocoaSize(NSControlSize cocoaControlSize) { 1.349 + if (cocoaControlSize == NSMiniControlSize) 1.350 + return miniControlSize; 1.351 + else if (cocoaControlSize == NSSmallControlSize) 1.352 + return smallControlSize; 1.353 + else 1.354 + return regularControlSize; 1.355 +} 1.356 + 1.357 +static NSControlSize CocoaSizeForEnum(int32_t enumControlSize) { 1.358 + if (enumControlSize == miniControlSize) 1.359 + return NSMiniControlSize; 1.360 + else if (enumControlSize == smallControlSize) 1.361 + return NSSmallControlSize; 1.362 + else 1.363 + return NSRegularControlSize; 1.364 +} 1.365 + 1.366 +static NSString* CUIControlSizeForCocoaSize(NSControlSize aControlSize) 1.367 +{ 1.368 + if (aControlSize == NSRegularControlSize) 1.369 + return @"regular"; 1.370 + else if (aControlSize == NSSmallControlSize) 1.371 + return @"small"; 1.372 + else 1.373 + return @"mini"; 1.374 +} 1.375 + 1.376 +static void InflateControlRect(NSRect* rect, NSControlSize cocoaControlSize, const float marginSet[][3][4]) 1.377 +{ 1.378 + if (!marginSet) 1.379 + return; 1.380 + 1.381 + static int osIndex = leopardOS; 1.382 + int controlSize = EnumSizeForCocoaSize(cocoaControlSize); 1.383 + const float* buttonMargins = marginSet[osIndex][controlSize]; 1.384 + rect->origin.x -= buttonMargins[leftMargin]; 1.385 + rect->origin.y -= buttonMargins[bottomMargin]; 1.386 + rect->size.width += buttonMargins[leftMargin] + buttonMargins[rightMargin]; 1.387 + rect->size.height += buttonMargins[bottomMargin] + buttonMargins[topMargin]; 1.388 +} 1.389 + 1.390 +static NSWindow* NativeWindowForFrame(nsIFrame* aFrame, 1.391 + nsIWidget** aTopLevelWidget = NULL) 1.392 +{ 1.393 + if (!aFrame) 1.394 + return nil; 1.395 + 1.396 + nsIWidget* widget = aFrame->GetNearestWidget(); 1.397 + if (!widget) 1.398 + return nil; 1.399 + 1.400 + nsIWidget* topLevelWidget = widget->GetTopLevelWidget(); 1.401 + if (aTopLevelWidget) 1.402 + *aTopLevelWidget = topLevelWidget; 1.403 + 1.404 + return (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW); 1.405 +} 1.406 + 1.407 +static NSSize 1.408 +WindowButtonsSize(nsIFrame* aFrame) 1.409 +{ 1.410 + NSWindow* window = NativeWindowForFrame(aFrame); 1.411 + if (!window) { 1.412 + // Return fallback values. 1.413 + if (!nsCocoaFeatures::OnLionOrLater()) 1.414 + return NSMakeSize(57, 16); 1.415 + return NSMakeSize(54, 16); 1.416 + } 1.417 + 1.418 + NSRect buttonBox = NSZeroRect; 1.419 + NSButton* closeButton = [window standardWindowButton:NSWindowCloseButton]; 1.420 + if (closeButton) { 1.421 + buttonBox = NSUnionRect(buttonBox, [closeButton frame]); 1.422 + } 1.423 + NSButton* minimizeButton = [window standardWindowButton:NSWindowMiniaturizeButton]; 1.424 + if (minimizeButton) { 1.425 + buttonBox = NSUnionRect(buttonBox, [minimizeButton frame]); 1.426 + } 1.427 + NSButton* zoomButton = [window standardWindowButton:NSWindowZoomButton]; 1.428 + if (zoomButton) { 1.429 + buttonBox = NSUnionRect(buttonBox, [zoomButton frame]); 1.430 + } 1.431 + return buttonBox.size; 1.432 +} 1.433 + 1.434 +static BOOL FrameIsInActiveWindow(nsIFrame* aFrame) 1.435 +{ 1.436 + nsIWidget* topLevelWidget = NULL; 1.437 + NSWindow* win = NativeWindowForFrame(aFrame, &topLevelWidget); 1.438 + if (!topLevelWidget || !win) 1.439 + return YES; 1.440 + 1.441 + // XUL popups, e.g. the toolbar customization popup, can't become key windows, 1.442 + // but controls in these windows should still get the active look. 1.443 + if (topLevelWidget->WindowType() == eWindowType_popup) 1.444 + return YES; 1.445 + if ([win isSheet]) 1.446 + return [win isKeyWindow]; 1.447 + return [win isMainWindow] && ![win attachedSheet]; 1.448 +} 1.449 + 1.450 +// Toolbar controls and content controls respond to different window 1.451 +// activeness states. 1.452 +static BOOL IsActive(nsIFrame* aFrame, BOOL aIsToolbarControl) 1.453 +{ 1.454 + if (aIsToolbarControl) 1.455 + return [NativeWindowForFrame(aFrame) isMainWindow]; 1.456 + return FrameIsInActiveWindow(aFrame); 1.457 +} 1.458 + 1.459 +NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeCocoa, nsNativeTheme, nsITheme) 1.460 + 1.461 + 1.462 +nsNativeThemeCocoa::nsNativeThemeCocoa() 1.463 +{ 1.464 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.465 + 1.466 + // provide a local autorelease pool, as this is called during startup 1.467 + // before the main event-loop pool is in place 1.468 + nsAutoreleasePool pool; 1.469 + 1.470 + mHelpButtonCell = [[NSButtonCell alloc] initTextCell:nil]; 1.471 + [mHelpButtonCell setBezelStyle:NSHelpButtonBezelStyle]; 1.472 + [mHelpButtonCell setButtonType:NSMomentaryPushInButton]; 1.473 + [mHelpButtonCell setHighlightsBy:NSPushInCellMask]; 1.474 + 1.475 + mPushButtonCell = [[NSButtonCell alloc] initTextCell:nil]; 1.476 + [mPushButtonCell setButtonType:NSMomentaryPushInButton]; 1.477 + [mPushButtonCell setHighlightsBy:NSPushInCellMask]; 1.478 + 1.479 + mRadioButtonCell = [[NSButtonCell alloc] initTextCell:nil]; 1.480 + [mRadioButtonCell setButtonType:NSRadioButton]; 1.481 + 1.482 + mCheckboxCell = [[NSButtonCell alloc] initTextCell:nil]; 1.483 + [mCheckboxCell setButtonType:NSSwitchButton]; 1.484 + [mCheckboxCell setAllowsMixedState:YES]; 1.485 + 1.486 +#if defined(__x86_64__) 1.487 + mSearchFieldCell = [[SearchFieldCellWithFocusRing alloc] initTextCell:@""]; 1.488 +#else 1.489 + mSearchFieldCell = [[ContextAwareSearchFieldCell alloc] initTextCell:@""]; 1.490 +#endif 1.491 + [mSearchFieldCell setBezelStyle:NSTextFieldRoundedBezel]; 1.492 + [mSearchFieldCell setBezeled:YES]; 1.493 + [mSearchFieldCell setEditable:YES]; 1.494 + [mSearchFieldCell setFocusRingType:NSFocusRingTypeExterior]; 1.495 + 1.496 + mDropdownCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]; 1.497 + 1.498 + mComboBoxCell = [[NSComboBoxCell alloc] initTextCell:@""]; 1.499 + [mComboBoxCell setBezeled:YES]; 1.500 + [mComboBoxCell setEditable:YES]; 1.501 + [mComboBoxCell setFocusRingType:NSFocusRingTypeExterior]; 1.502 + 1.503 + mProgressBarCell = [[NSProgressBarCell alloc] init]; 1.504 + 1.505 + mMeterBarCell = [[NSLevelIndicatorCell alloc] 1.506 + initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]; 1.507 + 1.508 + mCellDrawView = [[CellDrawView alloc] init]; 1.509 + 1.510 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.511 +} 1.512 + 1.513 +nsNativeThemeCocoa::~nsNativeThemeCocoa() 1.514 +{ 1.515 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.516 + 1.517 + [mMeterBarCell release]; 1.518 + [mProgressBarCell release]; 1.519 + [mHelpButtonCell release]; 1.520 + [mPushButtonCell release]; 1.521 + [mRadioButtonCell release]; 1.522 + [mCheckboxCell release]; 1.523 + [mSearchFieldCell release]; 1.524 + [mDropdownCell release]; 1.525 + [mComboBoxCell release]; 1.526 + [mCellDrawView release]; 1.527 + 1.528 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.529 +} 1.530 + 1.531 +// Limit on the area of the target rect (in pixels^2) in 1.532 +// DrawCellWithScaling(), DrawButton() and DrawScrollbar(), above which we 1.533 +// don't draw the object into a bitmap buffer. This is to avoid crashes in 1.534 +// [NSGraphicsContext graphicsContextWithGraphicsPort:flipped:] and 1.535 +// CGContextDrawImage(), and also to avoid very poor drawing performance in 1.536 +// CGContextDrawImage() when it scales the bitmap (particularly if xscale or 1.537 +// yscale is less than but near 1 -- e.g. 0.9). This value was determined 1.538 +// by trial and error, on OS X 10.4.11 and 10.5.4, and on systems with 1.539 +// different amounts of RAM. 1.540 +#define BITMAP_MAX_AREA 500000 1.541 + 1.542 +static int 1.543 +GetBackingScaleFactorForRendering(CGContextRef cgContext) 1.544 +{ 1.545 + CGAffineTransform ctm = CGContextGetUserSpaceToDeviceSpaceTransform(cgContext); 1.546 + CGRect transformedUserSpacePixel = CGRectApplyAffineTransform(CGRectMake(0, 0, 1, 1), ctm); 1.547 + float maxScale = std::max(fabs(transformedUserSpacePixel.size.width), 1.548 + fabs(transformedUserSpacePixel.size.height)); 1.549 + return maxScale > 1.0 ? 2 : 1; 1.550 +} 1.551 + 1.552 +/* 1.553 + * Draw the given NSCell into the given cgContext. 1.554 + * 1.555 + * destRect - the size and position of the resulting control rectangle 1.556 + * controlSize - the NSControlSize which will be given to the NSCell before 1.557 + * asking it to render 1.558 + * naturalSize - The natural dimensions of this control. 1.559 + * If the control rect size is not equal to either of these, a scale 1.560 + * will be applied to the context so that rendering the control at the 1.561 + * natural size will result in it filling the destRect space. 1.562 + * If a control has no natural dimensions in either/both axes, pass 0.0f. 1.563 + * minimumSize - The minimum dimensions of this control. 1.564 + * If the control rect size is less than the minimum for a given axis, 1.565 + * a scale will be applied to the context so that the minimum is used 1.566 + * for drawing. If a control has no minimum dimensions in either/both 1.567 + * axes, pass 0.0f. 1.568 + * marginSet - an array of margins; a multidimensional array of [2][3][4], 1.569 + * with the first dimension being the OS version (Tiger or Leopard), 1.570 + * the second being the control size (mini, small, regular), and the third 1.571 + * being the 4 margin values (left, top, right, bottom). 1.572 + * view - The NSView that we're drawing into. As far as I can tell, it doesn't 1.573 + * matter if this is really the right view; it just has to return YES when 1.574 + * asked for isFlipped. Otherwise we'll get drawing bugs on 10.4. 1.575 + * mirrorHorizontal - whether to mirror the cell horizontally 1.576 + */ 1.577 +static void DrawCellWithScaling(NSCell *cell, 1.578 + CGContextRef cgContext, 1.579 + const HIRect& destRect, 1.580 + NSControlSize controlSize, 1.581 + NSSize naturalSize, 1.582 + NSSize minimumSize, 1.583 + const float marginSet[][3][4], 1.584 + NSView* view, 1.585 + BOOL mirrorHorizontal) 1.586 +{ 1.587 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.588 + 1.589 + NSRect drawRect = NSMakeRect(destRect.origin.x, destRect.origin.y, destRect.size.width, destRect.size.height); 1.590 + 1.591 + if (naturalSize.width != 0.0f) 1.592 + drawRect.size.width = naturalSize.width; 1.593 + if (naturalSize.height != 0.0f) 1.594 + drawRect.size.height = naturalSize.height; 1.595 + 1.596 + // Keep aspect ratio when scaling if one dimension is free. 1.597 + if (naturalSize.width == 0.0f && naturalSize.height != 0.0f) 1.598 + drawRect.size.width = destRect.size.width * naturalSize.height / destRect.size.height; 1.599 + if (naturalSize.height == 0.0f && naturalSize.width != 0.0f) 1.600 + drawRect.size.height = destRect.size.height * naturalSize.width / destRect.size.width; 1.601 + 1.602 + // Honor minimum sizes. 1.603 + if (drawRect.size.width < minimumSize.width) 1.604 + drawRect.size.width = minimumSize.width; 1.605 + if (drawRect.size.height < minimumSize.height) 1.606 + drawRect.size.height = minimumSize.height; 1.607 + 1.608 + [NSGraphicsContext saveGraphicsState]; 1.609 + 1.610 + // Only skip the buffer if the area of our cell (in pixels^2) is too large. 1.611 + if (drawRect.size.width * drawRect.size.height > BITMAP_MAX_AREA) { 1.612 + // Inflate the rect Gecko gave us by the margin for the control. 1.613 + InflateControlRect(&drawRect, controlSize, marginSet); 1.614 + 1.615 + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; 1.616 + [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]]; 1.617 + 1.618 + DrawCellIncludingFocusRing(cell, drawRect, view); 1.619 + 1.620 + [NSGraphicsContext setCurrentContext:savedContext]; 1.621 + } 1.622 + else { 1.623 + float w = ceil(drawRect.size.width); 1.624 + float h = ceil(drawRect.size.height); 1.625 + NSRect tmpRect = NSMakeRect(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, w, h); 1.626 + 1.627 + // inflate to figure out the frame we need to tell NSCell to draw in, to get something that's 0,0,w,h 1.628 + InflateControlRect(&tmpRect, controlSize, marginSet); 1.629 + 1.630 + // and then, expand by MAX_FOCUS_RING_WIDTH size to make sure we can capture any focus ring 1.631 + w += MAX_FOCUS_RING_WIDTH * 2.0; 1.632 + h += MAX_FOCUS_RING_WIDTH * 2.0; 1.633 + 1.634 + int backingScaleFactor = GetBackingScaleFactorForRendering(cgContext); 1.635 + CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); 1.636 + CGContextRef ctx = CGBitmapContextCreate(NULL, 1.637 + (int) w * backingScaleFactor, (int) h * backingScaleFactor, 1.638 + 8, (int) w * backingScaleFactor * 4, 1.639 + rgb, kCGImageAlphaPremultipliedFirst); 1.640 + CGColorSpaceRelease(rgb); 1.641 + 1.642 + // We need to flip the image twice in order to avoid drawing bugs on 10.4, see bug 465069. 1.643 + // This is the first flip transform, applied to cgContext. 1.644 + CGContextScaleCTM(cgContext, 1.0f, -1.0f); 1.645 + CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height)); 1.646 + if (mirrorHorizontal) { 1.647 + CGContextScaleCTM(cgContext, -1.0f, 1.0f); 1.648 + CGContextTranslateCTM(cgContext, -(2.0 * destRect.origin.x + destRect.size.width), 0.0f); 1.649 + } 1.650 + 1.651 + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; 1.652 + [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]]; 1.653 + 1.654 + CGContextScaleCTM(ctx, backingScaleFactor, backingScaleFactor); 1.655 + 1.656 + // This is the second flip transform, applied to ctx. 1.657 + CGContextScaleCTM(ctx, 1.0f, -1.0f); 1.658 + CGContextTranslateCTM(ctx, 0.0f, -(2.0 * tmpRect.origin.y + tmpRect.size.height)); 1.659 + 1.660 + DrawCellIncludingFocusRing(cell, tmpRect, view); 1.661 + 1.662 + [NSGraphicsContext setCurrentContext:savedContext]; 1.663 + 1.664 + CGImageRef img = CGBitmapContextCreateImage(ctx); 1.665 + 1.666 + // Drop the image into the original destination rectangle, scaling to fit 1.667 + // Only scale MAX_FOCUS_RING_WIDTH by xscale/yscale when the resulting rect 1.668 + // doesn't extend beyond the overflow rect 1.669 + float xscale = destRect.size.width / drawRect.size.width; 1.670 + float yscale = destRect.size.height / drawRect.size.height; 1.671 + float scaledFocusRingX = xscale < 1.0f ? MAX_FOCUS_RING_WIDTH * xscale : MAX_FOCUS_RING_WIDTH; 1.672 + float scaledFocusRingY = yscale < 1.0f ? MAX_FOCUS_RING_WIDTH * yscale : MAX_FOCUS_RING_WIDTH; 1.673 + CGContextDrawImage(cgContext, CGRectMake(destRect.origin.x - scaledFocusRingX, 1.674 + destRect.origin.y - scaledFocusRingY, 1.675 + destRect.size.width + scaledFocusRingX * 2, 1.676 + destRect.size.height + scaledFocusRingY * 2), 1.677 + img); 1.678 + 1.679 + CGImageRelease(img); 1.680 + CGContextRelease(ctx); 1.681 + } 1.682 + 1.683 + [NSGraphicsContext restoreGraphicsState]; 1.684 + 1.685 +#if DRAW_IN_FRAME_DEBUG 1.686 + CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); 1.687 + CGContextFillRect(cgContext, destRect); 1.688 +#endif 1.689 + 1.690 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.691 +} 1.692 + 1.693 +struct CellRenderSettings { 1.694 + // The natural dimensions of the control. 1.695 + // If a control has no natural dimensions in either/both axes, set to 0.0f. 1.696 + NSSize naturalSizes[3]; 1.697 + 1.698 + // The minimum dimensions of the control. 1.699 + // If a control has no minimum dimensions in either/both axes, set to 0.0f. 1.700 + NSSize minimumSizes[3]; 1.701 + 1.702 + // A three-dimensional array, 1.703 + // with the first dimension being the OS version (only Leopard for the moment), 1.704 + // the second being the control size (mini, small, regular), and the third 1.705 + // being the 4 margin values (left, top, right, bottom). 1.706 + float margins[1][3][4]; 1.707 +}; 1.708 + 1.709 +/* 1.710 + * This is a helper method that returns the required NSControlSize given a size 1.711 + * and the size of the three controls plus a tolerance. 1.712 + * size - The width or the height of the element to draw. 1.713 + * sizes - An array with the all the width/height of the element for its 1.714 + * different sizes. 1.715 + * tolerance - The tolerance as passed to DrawCellWithSnapping. 1.716 + * NOTE: returns NSRegularControlSize if all values in 'sizes' are zero. 1.717 + */ 1.718 +static NSControlSize FindControlSize(CGFloat size, const CGFloat* sizes, CGFloat tolerance) 1.719 +{ 1.720 + for (uint32_t i = miniControlSize; i <= regularControlSize; ++i) { 1.721 + if (sizes[i] == 0) { 1.722 + continue; 1.723 + } 1.724 + 1.725 + CGFloat next = 0; 1.726 + // Find next value. 1.727 + for (uint32_t j = i+1; j <= regularControlSize; ++j) { 1.728 + if (sizes[j] != 0) { 1.729 + next = sizes[j]; 1.730 + break; 1.731 + } 1.732 + } 1.733 + 1.734 + // If it's the latest value, we pick it. 1.735 + if (next == 0) { 1.736 + return CocoaSizeForEnum(i); 1.737 + } 1.738 + 1.739 + if (size <= sizes[i] + tolerance && size < next) { 1.740 + return CocoaSizeForEnum(i); 1.741 + } 1.742 + } 1.743 + 1.744 + // If we are here, that means sizes[] was an array with only empty values 1.745 + // or the algorithm above is wrong. 1.746 + // The former can happen but the later would be wrong. 1.747 + NS_ASSERTION(sizes[0] == 0 && sizes[1] == 0 && sizes[2] == 0, 1.748 + "We found no control! We shouldn't be there!"); 1.749 + return CocoaSizeForEnum(regularControlSize); 1.750 +} 1.751 + 1.752 +/* 1.753 + * Draw the given NSCell into the given cgContext with a nice control size. 1.754 + * 1.755 + * This function is similar to DrawCellWithScaling, but it decides what 1.756 + * control size to use based on the destRect's size. 1.757 + * Scaling is only applied when the difference between the destRect's size 1.758 + * and the next smaller natural size is greater than snapTolerance. Otherwise 1.759 + * it snaps to the next smaller control size without scaling because unscaled 1.760 + * controls look nicer. 1.761 + */ 1.762 +static void DrawCellWithSnapping(NSCell *cell, 1.763 + CGContextRef cgContext, 1.764 + const HIRect& destRect, 1.765 + const CellRenderSettings settings, 1.766 + float verticalAlignFactor, 1.767 + NSView* view, 1.768 + BOOL mirrorHorizontal, 1.769 + float snapTolerance = 2.0f) 1.770 +{ 1.771 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.772 + 1.773 + const float rectWidth = destRect.size.width, rectHeight = destRect.size.height; 1.774 + const NSSize *sizes = settings.naturalSizes; 1.775 + const NSSize miniSize = sizes[EnumSizeForCocoaSize(NSMiniControlSize)]; 1.776 + const NSSize smallSize = sizes[EnumSizeForCocoaSize(NSSmallControlSize)]; 1.777 + const NSSize regularSize = sizes[EnumSizeForCocoaSize(NSRegularControlSize)]; 1.778 + 1.779 + HIRect drawRect = destRect; 1.780 + 1.781 + CGFloat controlWidths[3] = { miniSize.width, smallSize.width, regularSize.width }; 1.782 + NSControlSize controlSizeX = FindControlSize(rectWidth, controlWidths, snapTolerance); 1.783 + CGFloat controlHeights[3] = { miniSize.height, smallSize.height, regularSize.height }; 1.784 + NSControlSize controlSizeY = FindControlSize(rectHeight, controlHeights, snapTolerance); 1.785 + 1.786 + NSControlSize controlSize = NSRegularControlSize; 1.787 + int sizeIndex = 0; 1.788 + 1.789 + // At some sizes, don't scale but snap. 1.790 + const NSControlSize smallerControlSize = 1.791 + EnumSizeForCocoaSize(controlSizeX) < EnumSizeForCocoaSize(controlSizeY) ? 1.792 + controlSizeX : controlSizeY; 1.793 + const int smallerControlSizeIndex = EnumSizeForCocoaSize(smallerControlSize); 1.794 + const NSSize size = sizes[smallerControlSizeIndex]; 1.795 + float diffWidth = size.width ? rectWidth - size.width : 0.0f; 1.796 + float diffHeight = size.height ? rectHeight - size.height : 0.0f; 1.797 + if (diffWidth >= 0.0f && diffHeight >= 0.0f && 1.798 + diffWidth <= snapTolerance && diffHeight <= snapTolerance) { 1.799 + // Snap to the smaller control size. 1.800 + controlSize = smallerControlSize; 1.801 + sizeIndex = smallerControlSizeIndex; 1.802 + // Resize and center the drawRect. 1.803 + if (sizes[sizeIndex].width) { 1.804 + drawRect.origin.x += ceil((destRect.size.width - sizes[sizeIndex].width) / 2); 1.805 + drawRect.size.width = sizes[sizeIndex].width; 1.806 + } 1.807 + if (sizes[sizeIndex].height) { 1.808 + drawRect.origin.y += floor((destRect.size.height - sizes[sizeIndex].height) * verticalAlignFactor); 1.809 + drawRect.size.height = sizes[sizeIndex].height; 1.810 + } 1.811 + } else { 1.812 + // Use the larger control size. 1.813 + controlSize = EnumSizeForCocoaSize(controlSizeX) > EnumSizeForCocoaSize(controlSizeY) ? 1.814 + controlSizeX : controlSizeY; 1.815 + sizeIndex = EnumSizeForCocoaSize(controlSize); 1.816 + } 1.817 + 1.818 + [cell setControlSize:controlSize]; 1.819 + 1.820 + NSSize minimumSize = settings.minimumSizes ? settings.minimumSizes[sizeIndex] : NSZeroSize; 1.821 + DrawCellWithScaling(cell, cgContext, drawRect, controlSize, sizes[sizeIndex], 1.822 + minimumSize, settings.margins, view, mirrorHorizontal); 1.823 + 1.824 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.825 +} 1.826 + 1.827 +static float VerticalAlignFactor(nsIFrame *aFrame) 1.828 +{ 1.829 + if (!aFrame) 1.830 + return 0.5f; // default: center 1.831 + 1.832 + const nsStyleCoord& va = aFrame->StyleTextReset()->mVerticalAlign; 1.833 + uint8_t intval = (va.GetUnit() == eStyleUnit_Enumerated) 1.834 + ? va.GetIntValue() 1.835 + : NS_STYLE_VERTICAL_ALIGN_MIDDLE; 1.836 + switch (intval) { 1.837 + case NS_STYLE_VERTICAL_ALIGN_TOP: 1.838 + case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP: 1.839 + return 0.0f; 1.840 + 1.841 + case NS_STYLE_VERTICAL_ALIGN_SUB: 1.842 + case NS_STYLE_VERTICAL_ALIGN_SUPER: 1.843 + case NS_STYLE_VERTICAL_ALIGN_MIDDLE: 1.844 + case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE: 1.845 + return 0.5f; 1.846 + 1.847 + case NS_STYLE_VERTICAL_ALIGN_BASELINE: 1.848 + case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM: 1.849 + case NS_STYLE_VERTICAL_ALIGN_BOTTOM: 1.850 + return 1.0f; 1.851 + 1.852 + default: 1.853 + NS_NOTREACHED("invalid vertical-align"); 1.854 + return 0.5f; 1.855 + } 1.856 +} 1.857 + 1.858 +// These are the sizes that Gecko needs to request to draw if it wants 1.859 +// to get a standard-sized Aqua radio button drawn. Note that the rects 1.860 +// that draw these are actually a little bigger. 1.861 +static const CellRenderSettings radioSettings = { 1.862 + { 1.863 + NSMakeSize(11, 11), // mini 1.864 + NSMakeSize(13, 13), // small 1.865 + NSMakeSize(16, 16) // regular 1.866 + }, 1.867 + { 1.868 + NSZeroSize, NSZeroSize, NSZeroSize 1.869 + }, 1.870 + { 1.871 + { // Leopard 1.872 + {0, 0, 0, 0}, // mini 1.873 + {0, 1, 1, 1}, // small 1.874 + {0, 0, 0, 0} // regular 1.875 + } 1.876 + } 1.877 +}; 1.878 + 1.879 +static const CellRenderSettings checkboxSettings = { 1.880 + { 1.881 + NSMakeSize(11, 11), // mini 1.882 + NSMakeSize(13, 13), // small 1.883 + NSMakeSize(16, 16) // regular 1.884 + }, 1.885 + { 1.886 + NSZeroSize, NSZeroSize, NSZeroSize 1.887 + }, 1.888 + { 1.889 + { // Leopard 1.890 + {0, 1, 0, 0}, // mini 1.891 + {0, 1, 0, 1}, // small 1.892 + {0, 1, 0, 1} // regular 1.893 + } 1.894 + } 1.895 +}; 1.896 + 1.897 +void 1.898 +nsNativeThemeCocoa::DrawCheckboxOrRadio(CGContextRef cgContext, bool inCheckbox, 1.899 + const HIRect& inBoxRect, bool inSelected, 1.900 + EventStates inState, nsIFrame* aFrame) 1.901 +{ 1.902 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.903 + 1.904 + NSButtonCell *cell = inCheckbox ? mCheckboxCell : mRadioButtonCell; 1.905 + NSCellStateValue state = inSelected ? NSOnState : NSOffState; 1.906 + 1.907 + // Check if we have an indeterminate checkbox 1.908 + if (inCheckbox && GetIndeterminate(aFrame)) 1.909 + state = NSMixedState; 1.910 + 1.911 + [cell setEnabled:!IsDisabled(aFrame, inState)]; 1.912 + [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS)]; 1.913 + [cell setState:state]; 1.914 + [cell setHighlighted:inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)]; 1.915 + [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)]; 1.916 + 1.917 + // Ensure that the control is square. 1.918 + float length = std::min(inBoxRect.size.width, inBoxRect.size.height); 1.919 + HIRect drawRect = CGRectMake(inBoxRect.origin.x + (int)((inBoxRect.size.width - length) / 2.0f), 1.920 + inBoxRect.origin.y + (int)((inBoxRect.size.height - length) / 2.0f), 1.921 + length, length); 1.922 + 1.923 + DrawCellWithSnapping(cell, cgContext, drawRect, 1.924 + inCheckbox ? checkboxSettings : radioSettings, 1.925 + VerticalAlignFactor(aFrame), mCellDrawView, NO); 1.926 + 1.927 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.928 +} 1.929 + 1.930 +static const CellRenderSettings searchFieldSettings = { 1.931 + { 1.932 + NSMakeSize(0, 16), // mini 1.933 + NSMakeSize(0, 19), // small 1.934 + NSMakeSize(0, 22) // regular 1.935 + }, 1.936 + { 1.937 + NSMakeSize(32, 0), // mini 1.938 + NSMakeSize(38, 0), // small 1.939 + NSMakeSize(44, 0) // regular 1.940 + }, 1.941 + { 1.942 + { // Leopard 1.943 + {0, 0, 0, 0}, // mini 1.944 + {0, 0, 0, 0}, // small 1.945 + {0, 0, 0, 0} // regular 1.946 + } 1.947 + } 1.948 +}; 1.949 + 1.950 +void 1.951 +nsNativeThemeCocoa::DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect, 1.952 + nsIFrame* aFrame, EventStates inState) 1.953 +{ 1.954 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.955 + 1.956 + ContextAwareSearchFieldCell* cell = mSearchFieldCell; 1.957 + [cell setContext:aFrame]; 1.958 + [cell setEnabled:!IsDisabled(aFrame, inState)]; 1.959 + // NOTE: this could probably use inState 1.960 + [cell setShowsFirstResponder:IsFocused(aFrame)]; 1.961 + 1.962 + DrawCellWithSnapping(cell, cgContext, inBoxRect, searchFieldSettings, 1.963 + VerticalAlignFactor(aFrame), mCellDrawView, 1.964 + IsFrameRTL(aFrame)); 1.965 + 1.966 + [cell setContext:nullptr]; 1.967 + 1.968 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.969 +} 1.970 + 1.971 +static const NSSize kHelpButtonSize = NSMakeSize(20, 20); 1.972 + 1.973 +static const CellRenderSettings pushButtonSettings = { 1.974 + { 1.975 + NSMakeSize(0, 16), // mini 1.976 + NSMakeSize(0, 19), // small 1.977 + NSMakeSize(0, 22) // regular 1.978 + }, 1.979 + { 1.980 + NSMakeSize(18, 0), // mini 1.981 + NSMakeSize(26, 0), // small 1.982 + NSMakeSize(30, 0) // regular 1.983 + }, 1.984 + { 1.985 + { // Leopard 1.986 + {0, 0, 0, 0}, // mini 1.987 + {4, 0, 4, 1}, // small 1.988 + {5, 0, 5, 2} // regular 1.989 + } 1.990 + } 1.991 +}; 1.992 + 1.993 +// The height at which we start doing square buttons instead of rounded buttons 1.994 +// Rounded buttons look bad if drawn at a height greater than 26, so at that point 1.995 +// we switch over to doing square buttons which looks fine at any size. 1.996 +#define DO_SQUARE_BUTTON_HEIGHT 26 1.997 + 1.998 +void 1.999 +nsNativeThemeCocoa::DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRect, 1.1000 + EventStates inState, uint8_t aWidgetType, 1.1001 + nsIFrame* aFrame) 1.1002 +{ 1.1003 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1004 + 1.1005 + BOOL isActive = FrameIsInActiveWindow(aFrame); 1.1006 + BOOL isDisabled = IsDisabled(aFrame, inState); 1.1007 + 1.1008 + NSButtonCell* cell = (aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON) ? mHelpButtonCell : mPushButtonCell; 1.1009 + [cell setEnabled:!isDisabled]; 1.1010 + [cell setHighlighted:isActive && 1.1011 + inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)]; 1.1012 + [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS) && !isDisabled && isActive]; 1.1013 + 1.1014 + if (aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON) { 1.1015 + DrawCellWithScaling(cell, cgContext, inBoxRect, NSRegularControlSize, 1.1016 + NSZeroSize, kHelpButtonSize, NULL, mCellDrawView, 1.1017 + false); // Don't mirror icon in RTL. 1.1018 + } else { 1.1019 + // If the button is tall enough, draw the square button style so that 1.1020 + // buttons with non-standard content look good. Otherwise draw normal 1.1021 + // rounded aqua buttons. 1.1022 + if (inBoxRect.size.height > DO_SQUARE_BUTTON_HEIGHT) { 1.1023 + [cell setBezelStyle:NSShadowlessSquareBezelStyle]; 1.1024 + DrawCellWithScaling(cell, cgContext, inBoxRect, NSRegularControlSize, 1.1025 + NSZeroSize, NSMakeSize(14, 0), NULL, mCellDrawView, 1.1026 + IsFrameRTL(aFrame)); 1.1027 + } else { 1.1028 + [cell setBezelStyle:NSRoundedBezelStyle]; 1.1029 + DrawCellWithSnapping(cell, cgContext, inBoxRect, pushButtonSettings, 0.5f, 1.1030 + mCellDrawView, IsFrameRTL(aFrame), 1.0f); 1.1031 + } 1.1032 + } 1.1033 + 1.1034 +#if DRAW_IN_FRAME_DEBUG 1.1035 + CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); 1.1036 + CGContextFillRect(cgContext, inBoxRect); 1.1037 +#endif 1.1038 + 1.1039 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1040 +} 1.1041 + 1.1042 +typedef void (*RenderHIThemeControlFunction)(CGContextRef cgContext, const HIRect& aRenderRect, void* aData); 1.1043 + 1.1044 +static void 1.1045 +RenderTransformedHIThemeControl(CGContextRef aCGContext, const HIRect& aRect, 1.1046 + RenderHIThemeControlFunction aFunc, void* aData, 1.1047 + BOOL mirrorHorizontally = NO) 1.1048 +{ 1.1049 + CGAffineTransform savedCTM = CGContextGetCTM(aCGContext); 1.1050 + CGContextTranslateCTM(aCGContext, aRect.origin.x, aRect.origin.y); 1.1051 + 1.1052 + bool drawDirect; 1.1053 + HIRect drawRect = aRect; 1.1054 + drawRect.origin = CGPointZero; 1.1055 + 1.1056 + if (!mirrorHorizontally && savedCTM.a == 1.0f && savedCTM.b == 0.0f && 1.1057 + savedCTM.c == 0.0f && (savedCTM.d == 1.0f || savedCTM.d == -1.0f)) { 1.1058 + drawDirect = TRUE; 1.1059 + } else { 1.1060 + drawDirect = FALSE; 1.1061 + } 1.1062 + 1.1063 + // Fall back to no bitmap buffer if the area of our control (in pixels^2) 1.1064 + // is too large. 1.1065 + if (drawDirect || (aRect.size.width * aRect.size.height > BITMAP_MAX_AREA)) { 1.1066 + aFunc(aCGContext, drawRect, aData); 1.1067 + } else { 1.1068 + // Inflate the buffer to capture focus rings. 1.1069 + int w = ceil(drawRect.size.width) + 2 * MAX_FOCUS_RING_WIDTH; 1.1070 + int h = ceil(drawRect.size.height) + 2 * MAX_FOCUS_RING_WIDTH; 1.1071 + 1.1072 + int backingScaleFactor = GetBackingScaleFactorForRendering(aCGContext); 1.1073 + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 1.1074 + CGContextRef bitmapctx = CGBitmapContextCreate(NULL, 1.1075 + w * backingScaleFactor, 1.1076 + h * backingScaleFactor, 1.1077 + 8, 1.1078 + w * backingScaleFactor * 4, 1.1079 + colorSpace, 1.1080 + kCGImageAlphaPremultipliedFirst); 1.1081 + CGColorSpaceRelease(colorSpace); 1.1082 + 1.1083 + CGContextScaleCTM(bitmapctx, backingScaleFactor, backingScaleFactor); 1.1084 + CGContextTranslateCTM(bitmapctx, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH); 1.1085 + 1.1086 + // HITheme always wants to draw into a flipped context, or things 1.1087 + // get confused. 1.1088 + CGContextTranslateCTM(bitmapctx, 0.0f, aRect.size.height); 1.1089 + CGContextScaleCTM(bitmapctx, 1.0f, -1.0f); 1.1090 + 1.1091 + aFunc(bitmapctx, drawRect, aData); 1.1092 + 1.1093 + CGImageRef bitmap = CGBitmapContextCreateImage(bitmapctx); 1.1094 + 1.1095 + CGAffineTransform ctm = CGContextGetCTM(aCGContext); 1.1096 + 1.1097 + // We need to unflip, so that we can do a DrawImage without getting a flipped image. 1.1098 + CGContextTranslateCTM(aCGContext, 0.0f, aRect.size.height); 1.1099 + CGContextScaleCTM(aCGContext, 1.0f, -1.0f); 1.1100 + 1.1101 + if (mirrorHorizontally) { 1.1102 + CGContextTranslateCTM(aCGContext, aRect.size.width, 0); 1.1103 + CGContextScaleCTM(aCGContext, -1.0f, 1.0f); 1.1104 + } 1.1105 + 1.1106 + HIRect inflatedDrawRect = CGRectMake(-MAX_FOCUS_RING_WIDTH, -MAX_FOCUS_RING_WIDTH, w, h); 1.1107 + CGContextDrawImage(aCGContext, inflatedDrawRect, bitmap); 1.1108 + 1.1109 + CGContextSetCTM(aCGContext, ctm); 1.1110 + 1.1111 + CGImageRelease(bitmap); 1.1112 + CGContextRelease(bitmapctx); 1.1113 + } 1.1114 + 1.1115 + CGContextSetCTM(aCGContext, savedCTM); 1.1116 +} 1.1117 + 1.1118 +static void 1.1119 +RenderButton(CGContextRef cgContext, const HIRect& aRenderRect, void* aData) 1.1120 +{ 1.1121 + HIThemeButtonDrawInfo* bdi = (HIThemeButtonDrawInfo*)aData; 1.1122 + HIThemeDrawButton(&aRenderRect, bdi, cgContext, kHIThemeOrientationNormal, NULL); 1.1123 +} 1.1124 + 1.1125 +void 1.1126 +nsNativeThemeCocoa::DrawButton(CGContextRef cgContext, ThemeButtonKind inKind, 1.1127 + const HIRect& inBoxRect, bool inIsDefault, 1.1128 + ThemeButtonValue inValue, ThemeButtonAdornment inAdornment, 1.1129 + EventStates inState, nsIFrame* aFrame) 1.1130 +{ 1.1131 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1132 + 1.1133 + BOOL isActive = FrameIsInActiveWindow(aFrame); 1.1134 + BOOL isDisabled = IsDisabled(aFrame, inState); 1.1135 + 1.1136 + HIThemeButtonDrawInfo bdi; 1.1137 + bdi.version = 0; 1.1138 + bdi.kind = inKind; 1.1139 + bdi.value = inValue; 1.1140 + bdi.adornment = inAdornment; 1.1141 + 1.1142 + if (isDisabled) { 1.1143 + bdi.state = kThemeStateUnavailable; 1.1144 + } 1.1145 + else if (inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) { 1.1146 + bdi.state = kThemeStatePressed; 1.1147 + } 1.1148 + else { 1.1149 + if (inKind == kThemeArrowButton) 1.1150 + bdi.state = kThemeStateUnavailable; // these are always drawn as unavailable 1.1151 + else if (!isActive && inKind == kThemeListHeaderButton) 1.1152 + bdi.state = kThemeStateInactive; 1.1153 + else 1.1154 + bdi.state = kThemeStateActive; 1.1155 + } 1.1156 + 1.1157 + if (inState.HasState(NS_EVENT_STATE_FOCUS) && isActive) 1.1158 + bdi.adornment |= kThemeAdornmentFocus; 1.1159 + 1.1160 + if (inIsDefault && !isDisabled && isActive && 1.1161 + !inState.HasState(NS_EVENT_STATE_ACTIVE)) { 1.1162 + bdi.adornment |= kThemeAdornmentDefault; 1.1163 + bdi.animation.time.start = 0; 1.1164 + bdi.animation.time.current = CFAbsoluteTimeGetCurrent(); 1.1165 + } 1.1166 + 1.1167 + HIRect drawFrame = inBoxRect; 1.1168 + 1.1169 + if (inKind == kThemePushButton) { 1.1170 + drawFrame.size.height -= 2; 1.1171 + if (inBoxRect.size.height < pushButtonSettings.naturalSizes[smallControlSize].height) { 1.1172 + bdi.kind = kThemePushButtonMini; 1.1173 + } 1.1174 + else if (inBoxRect.size.height < pushButtonSettings.naturalSizes[regularControlSize].height) { 1.1175 + bdi.kind = kThemePushButtonSmall; 1.1176 + drawFrame.origin.y -= 1; 1.1177 + drawFrame.origin.x += 1; 1.1178 + drawFrame.size.width -= 2; 1.1179 + } 1.1180 + } 1.1181 + else if (inKind == kThemeListHeaderButton) { 1.1182 + CGContextClipToRect(cgContext, inBoxRect); 1.1183 + // Always remove the top border. 1.1184 + drawFrame.origin.y -= 1; 1.1185 + drawFrame.size.height += 1; 1.1186 + // Remove the left border in LTR mode and the right border in RTL mode. 1.1187 + drawFrame.size.width += 1; 1.1188 + bool isLast = IsLastTreeHeaderCell(aFrame); 1.1189 + if (isLast) 1.1190 + drawFrame.size.width += 1; // Also remove the other border. 1.1191 + if (!IsFrameRTL(aFrame) || isLast) 1.1192 + drawFrame.origin.x -= 1; 1.1193 + } 1.1194 + 1.1195 + RenderTransformedHIThemeControl(cgContext, drawFrame, RenderButton, &bdi, 1.1196 + IsFrameRTL(aFrame)); 1.1197 + 1.1198 +#if DRAW_IN_FRAME_DEBUG 1.1199 + CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); 1.1200 + CGContextFillRect(cgContext, inBoxRect); 1.1201 +#endif 1.1202 + 1.1203 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1204 +} 1.1205 + 1.1206 +static const CellRenderSettings dropdownSettings = { 1.1207 + { 1.1208 + NSMakeSize(0, 16), // mini 1.1209 + NSMakeSize(0, 19), // small 1.1210 + NSMakeSize(0, 22) // regular 1.1211 + }, 1.1212 + { 1.1213 + NSMakeSize(18, 0), // mini 1.1214 + NSMakeSize(38, 0), // small 1.1215 + NSMakeSize(44, 0) // regular 1.1216 + }, 1.1217 + { 1.1218 + { // Leopard 1.1219 + {1, 1, 2, 1}, // mini 1.1220 + {3, 0, 3, 1}, // small 1.1221 + {3, 0, 3, 0} // regular 1.1222 + } 1.1223 + } 1.1224 +}; 1.1225 + 1.1226 +static const CellRenderSettings editableMenulistSettings = { 1.1227 + { 1.1228 + NSMakeSize(0, 15), // mini 1.1229 + NSMakeSize(0, 18), // small 1.1230 + NSMakeSize(0, 21) // regular 1.1231 + }, 1.1232 + { 1.1233 + NSMakeSize(18, 0), // mini 1.1234 + NSMakeSize(38, 0), // small 1.1235 + NSMakeSize(44, 0) // regular 1.1236 + }, 1.1237 + { 1.1238 + { // Leopard 1.1239 + {0, 0, 2, 2}, // mini 1.1240 + {0, 0, 3, 2}, // small 1.1241 + {0, 1, 3, 3} // regular 1.1242 + } 1.1243 + } 1.1244 +}; 1.1245 + 1.1246 +void 1.1247 +nsNativeThemeCocoa::DrawDropdown(CGContextRef cgContext, const HIRect& inBoxRect, 1.1248 + EventStates inState, uint8_t aWidgetType, 1.1249 + nsIFrame* aFrame) 1.1250 +{ 1.1251 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1252 + 1.1253 + [mDropdownCell setPullsDown:(aWidgetType == NS_THEME_BUTTON)]; 1.1254 + 1.1255 + BOOL isEditable = (aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD); 1.1256 + NSCell* cell = isEditable ? (NSCell*)mComboBoxCell : (NSCell*)mDropdownCell; 1.1257 + 1.1258 + [cell setEnabled:!IsDisabled(aFrame, inState)]; 1.1259 + [cell setShowsFirstResponder:(IsFocused(aFrame) || inState.HasState(NS_EVENT_STATE_FOCUS))]; 1.1260 + [cell setHighlighted:IsOpenButton(aFrame)]; 1.1261 + [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)]; 1.1262 + 1.1263 + const CellRenderSettings& settings = isEditable ? editableMenulistSettings : dropdownSettings; 1.1264 + DrawCellWithSnapping(cell, cgContext, inBoxRect, settings, 1.1265 + 0.5f, mCellDrawView, IsFrameRTL(aFrame)); 1.1266 + 1.1267 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1268 +} 1.1269 + 1.1270 +static const CellRenderSettings spinnerSettings = { 1.1271 + { 1.1272 + NSMakeSize(11, 16), // mini (width trimmed by 2px to reduce blank border) 1.1273 + NSMakeSize(15, 22), // small 1.1274 + NSMakeSize(19, 27) // regular 1.1275 + }, 1.1276 + { 1.1277 + NSMakeSize(11, 16), // mini (width trimmed by 2px to reduce blank border) 1.1278 + NSMakeSize(15, 22), // small 1.1279 + NSMakeSize(19, 27) // regular 1.1280 + }, 1.1281 + { 1.1282 + { // Leopard 1.1283 + {0, 0, 0, 0}, // mini 1.1284 + {0, 0, 0, 0}, // small 1.1285 + {0, 0, 0, 0} // regular 1.1286 + } 1.1287 + } 1.1288 +}; 1.1289 + 1.1290 +void 1.1291 +nsNativeThemeCocoa::DrawSpinButtons(CGContextRef cgContext, ThemeButtonKind inKind, 1.1292 + const HIRect& inBoxRect, ThemeDrawState inDrawState, 1.1293 + ThemeButtonAdornment inAdornment, 1.1294 + EventStates inState, nsIFrame* aFrame) 1.1295 +{ 1.1296 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1297 + 1.1298 + HIThemeButtonDrawInfo bdi; 1.1299 + bdi.version = 0; 1.1300 + bdi.kind = inKind; 1.1301 + bdi.value = kThemeButtonOff; 1.1302 + bdi.adornment = inAdornment; 1.1303 + 1.1304 + if (IsDisabled(aFrame, inState)) 1.1305 + bdi.state = kThemeStateUnavailable; 1.1306 + else 1.1307 + bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive; 1.1308 + 1.1309 + HIThemeDrawButton(&inBoxRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL); 1.1310 + 1.1311 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1312 +} 1.1313 + 1.1314 +void 1.1315 +nsNativeThemeCocoa::DrawSpinButton(CGContextRef cgContext, 1.1316 + ThemeButtonKind inKind, 1.1317 + const HIRect& inBoxRect, 1.1318 + ThemeDrawState inDrawState, 1.1319 + ThemeButtonAdornment inAdornment, 1.1320 + EventStates inState, 1.1321 + nsIFrame* aFrame, 1.1322 + uint8_t aWidgetType) 1.1323 +{ 1.1324 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1325 + 1.1326 + MOZ_ASSERT(aWidgetType == NS_THEME_SPINNER_UP_BUTTON || 1.1327 + aWidgetType == NS_THEME_SPINNER_DOWN_BUTTON); 1.1328 + 1.1329 + HIThemeButtonDrawInfo bdi; 1.1330 + bdi.version = 0; 1.1331 + bdi.kind = inKind; 1.1332 + bdi.value = kThemeButtonOff; 1.1333 + bdi.adornment = inAdornment; 1.1334 + 1.1335 + if (IsDisabled(aFrame, inState)) 1.1336 + bdi.state = kThemeStateUnavailable; 1.1337 + else 1.1338 + bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive; 1.1339 + 1.1340 + // Cocoa only allows kThemeIncDecButton to paint the up and down spin buttons 1.1341 + // together as a single unit (presumably because when one button is active, 1.1342 + // the appearance of both changes (in different ways)). Here we have to paint 1.1343 + // both buttons, using clip to hide the one we don't want to paint. 1.1344 + HIRect drawRect = inBoxRect; 1.1345 + drawRect.size.height *= 2; 1.1346 + if (aWidgetType == NS_THEME_SPINNER_DOWN_BUTTON) { 1.1347 + drawRect.origin.y -= inBoxRect.size.height; 1.1348 + } 1.1349 + 1.1350 + // Shift the drawing a little to the left, since cocoa paints with more 1.1351 + // blank space around the visual buttons than we'd like: 1.1352 + drawRect.origin.x -= 1; 1.1353 + 1.1354 + CGContextSaveGState(cgContext); 1.1355 + CGContextClipToRect(cgContext, inBoxRect); 1.1356 + 1.1357 + HIThemeDrawButton(&drawRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL); 1.1358 + 1.1359 + CGContextRestoreGState(cgContext); 1.1360 + 1.1361 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1362 +} 1.1363 + 1.1364 +void 1.1365 +nsNativeThemeCocoa::DrawFrame(CGContextRef cgContext, HIThemeFrameKind inKind, 1.1366 + const HIRect& inBoxRect, bool inDisabled, 1.1367 + EventStates inState) 1.1368 +{ 1.1369 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1370 + 1.1371 + HIThemeFrameDrawInfo fdi; 1.1372 + fdi.version = 0; 1.1373 + fdi.kind = inKind; 1.1374 + 1.1375 + // We don't ever set an inactive state for this because it doesn't 1.1376 + // look right (see other apps). 1.1377 + fdi.state = inDisabled ? kThemeStateUnavailable : kThemeStateActive; 1.1378 + 1.1379 + // for some reason focus rings on listboxes draw incorrectly 1.1380 + if (inKind == kHIThemeFrameListBox) 1.1381 + fdi.isFocused = 0; 1.1382 + else 1.1383 + fdi.isFocused = inState.HasState(NS_EVENT_STATE_FOCUS); 1.1384 + 1.1385 + // HIThemeDrawFrame takes the rect for the content area of the frame, not 1.1386 + // the bounding rect for the frame. Here we reduce the size of the rect we 1.1387 + // will pass to make it the size of the content. 1.1388 + HIRect drawRect = inBoxRect; 1.1389 + if (inKind == kHIThemeFrameTextFieldSquare) { 1.1390 + SInt32 frameOutset = 0; 1.1391 + ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset); 1.1392 + drawRect.origin.x += frameOutset; 1.1393 + drawRect.origin.y += frameOutset; 1.1394 + drawRect.size.width -= frameOutset * 2; 1.1395 + drawRect.size.height -= frameOutset * 2; 1.1396 + } 1.1397 + else if (inKind == kHIThemeFrameListBox) { 1.1398 + SInt32 frameOutset = 0; 1.1399 + ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset); 1.1400 + drawRect.origin.x += frameOutset; 1.1401 + drawRect.origin.y += frameOutset; 1.1402 + drawRect.size.width -= frameOutset * 2; 1.1403 + drawRect.size.height -= frameOutset * 2; 1.1404 + } 1.1405 + 1.1406 +#if DRAW_IN_FRAME_DEBUG 1.1407 + CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); 1.1408 + CGContextFillRect(cgContext, inBoxRect); 1.1409 +#endif 1.1410 + 1.1411 + HIThemeDrawFrame(&drawRect, &fdi, cgContext, HITHEME_ORIENTATION); 1.1412 + 1.1413 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1414 +} 1.1415 + 1.1416 +static const CellRenderSettings progressSettings[2][2] = { 1.1417 + // Vertical progress bar. 1.1418 + { 1.1419 + // Determined settings. 1.1420 + { 1.1421 + { 1.1422 + NSZeroSize, // mini 1.1423 + NSMakeSize(10, 0), // small 1.1424 + NSMakeSize(16, 0) // regular 1.1425 + }, 1.1426 + { 1.1427 + NSZeroSize, NSZeroSize, NSZeroSize 1.1428 + }, 1.1429 + { 1.1430 + { // Leopard 1.1431 + {0, 0, 0, 0}, // mini 1.1432 + {1, 1, 1, 1}, // small 1.1433 + {1, 1, 1, 1} // regular 1.1434 + } 1.1435 + } 1.1436 + }, 1.1437 + // There is no horizontal margin in regular undetermined size. 1.1438 + { 1.1439 + { 1.1440 + NSZeroSize, // mini 1.1441 + NSMakeSize(10, 0), // small 1.1442 + NSMakeSize(16, 0) // regular 1.1443 + }, 1.1444 + { 1.1445 + NSZeroSize, NSZeroSize, NSZeroSize 1.1446 + }, 1.1447 + { 1.1448 + { // Leopard 1.1449 + {0, 0, 0, 0}, // mini 1.1450 + {1, 1, 1, 1}, // small 1.1451 + {1, 0, 1, 0} // regular 1.1452 + } 1.1453 + } 1.1454 + } 1.1455 + }, 1.1456 + // Horizontal progress bar. 1.1457 + { 1.1458 + // Determined settings. 1.1459 + { 1.1460 + { 1.1461 + NSZeroSize, // mini 1.1462 + NSMakeSize(0, 10), // small 1.1463 + NSMakeSize(0, 16) // regular 1.1464 + }, 1.1465 + { 1.1466 + NSZeroSize, NSZeroSize, NSZeroSize 1.1467 + }, 1.1468 + { 1.1469 + { // Leopard 1.1470 + {0, 0, 0, 0}, // mini 1.1471 + {1, 1, 1, 1}, // small 1.1472 + {1, 1, 1, 1} // regular 1.1473 + } 1.1474 + } 1.1475 + }, 1.1476 + // There is no horizontal margin in regular undetermined size. 1.1477 + { 1.1478 + { 1.1479 + NSZeroSize, // mini 1.1480 + NSMakeSize(0, 10), // small 1.1481 + NSMakeSize(0, 16) // regular 1.1482 + }, 1.1483 + { 1.1484 + NSZeroSize, NSZeroSize, NSZeroSize 1.1485 + }, 1.1486 + { 1.1487 + { // Leopard 1.1488 + {0, 0, 0, 0}, // mini 1.1489 + {1, 1, 1, 1}, // small 1.1490 + {0, 1, 0, 1} // regular 1.1491 + } 1.1492 + } 1.1493 + } 1.1494 + } 1.1495 +}; 1.1496 + 1.1497 +void 1.1498 +nsNativeThemeCocoa::DrawProgress(CGContextRef cgContext, const HIRect& inBoxRect, 1.1499 + bool inIsIndeterminate, bool inIsHorizontal, 1.1500 + double inValue, double inMaxValue, 1.1501 + nsIFrame* aFrame) 1.1502 +{ 1.1503 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1504 + 1.1505 + NSProgressBarCell* cell = mProgressBarCell; 1.1506 + 1.1507 + [cell setValue:inValue]; 1.1508 + [cell setMax:inMaxValue]; 1.1509 + [cell setIndeterminate:inIsIndeterminate]; 1.1510 + [cell setHorizontal:inIsHorizontal]; 1.1511 + [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] 1.1512 + : NSClearControlTint)]; 1.1513 + 1.1514 + DrawCellWithSnapping(cell, cgContext, inBoxRect, 1.1515 + progressSettings[inIsHorizontal][inIsIndeterminate], 1.1516 + VerticalAlignFactor(aFrame), mCellDrawView, 1.1517 + IsFrameRTL(aFrame)); 1.1518 + 1.1519 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1520 +} 1.1521 + 1.1522 +static const CellRenderSettings meterSetting = { 1.1523 + { 1.1524 + NSMakeSize(0, 16), // mini 1.1525 + NSMakeSize(0, 16), // small 1.1526 + NSMakeSize(0, 16) // regular 1.1527 + }, 1.1528 + { 1.1529 + NSZeroSize, NSZeroSize, NSZeroSize 1.1530 + }, 1.1531 + { 1.1532 + { // Leopard 1.1533 + {1, 1, 1, 1}, // mini 1.1534 + {1, 1, 1, 1}, // small 1.1535 + {1, 1, 1, 1} // regular 1.1536 + } 1.1537 + } 1.1538 +}; 1.1539 + 1.1540 +void 1.1541 +nsNativeThemeCocoa::DrawMeter(CGContextRef cgContext, const HIRect& inBoxRect, 1.1542 + nsIFrame* aFrame) 1.1543 +{ 1.1544 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK 1.1545 + 1.1546 + NS_PRECONDITION(aFrame, "aFrame should not be null here!"); 1.1547 + 1.1548 + // When using -moz-meterbar on an non meter element, we will not be able to 1.1549 + // get all the needed information so we just draw an empty meter. 1.1550 + nsIContent* content = aFrame->GetContent(); 1.1551 + if (!(content && content->IsHTML(nsGkAtoms::meter))) { 1.1552 + DrawCellWithSnapping(mMeterBarCell, cgContext, inBoxRect, 1.1553 + meterSetting, VerticalAlignFactor(aFrame), 1.1554 + mCellDrawView, IsFrameRTL(aFrame)); 1.1555 + return; 1.1556 + } 1.1557 + 1.1558 + HTMLMeterElement* meterElement = static_cast<HTMLMeterElement*>(content); 1.1559 + double value = meterElement->Value(); 1.1560 + double min = meterElement->Min(); 1.1561 + double max = meterElement->Max(); 1.1562 + 1.1563 + NSLevelIndicatorCell* cell = mMeterBarCell; 1.1564 + 1.1565 + [cell setMinValue:min]; 1.1566 + [cell setMaxValue:max]; 1.1567 + [cell setDoubleValue:value]; 1.1568 + 1.1569 + /** 1.1570 + * The way HTML and Cocoa defines the meter/indicator widget are different. 1.1571 + * So, we are going to use a trick to get the Cocoa widget showing what we 1.1572 + * are expecting: we set the warningValue or criticalValue to the current 1.1573 + * value when we want to have the widget to be in the warning or critical 1.1574 + * state. 1.1575 + */ 1.1576 + EventStates states = aFrame->GetContent()->AsElement()->State(); 1.1577 + 1.1578 + // Reset previously set warning and critical values. 1.1579 + [cell setWarningValue:max+1]; 1.1580 + [cell setCriticalValue:max+1]; 1.1581 + 1.1582 + if (states.HasState(NS_EVENT_STATE_SUB_OPTIMUM)) { 1.1583 + [cell setWarningValue:value]; 1.1584 + } else if (states.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM)) { 1.1585 + [cell setCriticalValue:value]; 1.1586 + } 1.1587 + 1.1588 + HIRect rect = CGRectStandardize(inBoxRect); 1.1589 + BOOL vertical = IsVerticalMeter(aFrame); 1.1590 + 1.1591 + CGContextSaveGState(cgContext); 1.1592 + 1.1593 + if (vertical) { 1.1594 + /** 1.1595 + * Cocoa doesn't provide a vertical meter bar so to show one, we have to 1.1596 + * show a rotated horizontal meter bar. 1.1597 + * Given that we want to show a vertical meter bar, we assume that the rect 1.1598 + * has vertical dimensions but we can't correctly draw a meter widget inside 1.1599 + * such a rectangle so we need to inverse width and height (and re-position) 1.1600 + * to get a rectangle with horizontal dimensions. 1.1601 + * Finally, we want to show a vertical meter so we want to rotate the result 1.1602 + * so it is vertical. We do that by changing the context. 1.1603 + */ 1.1604 + CGFloat tmp = rect.size.width; 1.1605 + rect.size.width = rect.size.height; 1.1606 + rect.size.height = tmp; 1.1607 + rect.origin.x += rect.size.height / 2.f - rect.size.width / 2.f; 1.1608 + rect.origin.y += rect.size.width / 2.f - rect.size.height / 2.f; 1.1609 + 1.1610 + CGContextTranslateCTM(cgContext, CGRectGetMidX(rect), CGRectGetMidY(rect)); 1.1611 + CGContextRotateCTM(cgContext, -M_PI / 2.f); 1.1612 + CGContextTranslateCTM(cgContext, -CGRectGetMidX(rect), -CGRectGetMidY(rect)); 1.1613 + } 1.1614 + 1.1615 + DrawCellWithSnapping(cell, cgContext, rect, 1.1616 + meterSetting, VerticalAlignFactor(aFrame), 1.1617 + mCellDrawView, !vertical && IsFrameRTL(aFrame)); 1.1618 + 1.1619 + CGContextRestoreGState(cgContext); 1.1620 + 1.1621 + NS_OBJC_END_TRY_ABORT_BLOCK 1.1622 +} 1.1623 + 1.1624 +void 1.1625 +nsNativeThemeCocoa::DrawTabPanel(CGContextRef cgContext, const HIRect& inBoxRect, 1.1626 + nsIFrame* aFrame) 1.1627 +{ 1.1628 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1629 + 1.1630 + HIThemeTabPaneDrawInfo tpdi; 1.1631 + 1.1632 + tpdi.version = 1; 1.1633 + tpdi.state = FrameIsInActiveWindow(aFrame) ? kThemeStateActive : kThemeStateInactive; 1.1634 + tpdi.direction = kThemeTabNorth; 1.1635 + tpdi.size = kHIThemeTabSizeNormal; 1.1636 + tpdi.kind = kHIThemeTabKindNormal; 1.1637 + 1.1638 + HIThemeDrawTabPane(&inBoxRect, &tpdi, cgContext, HITHEME_ORIENTATION); 1.1639 + 1.1640 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1641 +} 1.1642 + 1.1643 +void 1.1644 +nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect, 1.1645 + EventStates inState, bool inIsVertical, 1.1646 + bool inIsReverse, int32_t inCurrentValue, 1.1647 + int32_t inMinValue, int32_t inMaxValue, 1.1648 + nsIFrame* aFrame) 1.1649 +{ 1.1650 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1651 + 1.1652 + HIThemeTrackDrawInfo tdi; 1.1653 + 1.1654 + tdi.version = 0; 1.1655 + tdi.kind = kThemeMediumSlider; 1.1656 + tdi.bounds = inBoxRect; 1.1657 + tdi.min = inMinValue; 1.1658 + tdi.max = inMaxValue; 1.1659 + tdi.value = inCurrentValue; 1.1660 + tdi.attributes = kThemeTrackShowThumb; 1.1661 + if (!inIsVertical) 1.1662 + tdi.attributes |= kThemeTrackHorizontal; 1.1663 + if (inIsReverse) 1.1664 + tdi.attributes |= kThemeTrackRightToLeft; 1.1665 + if (inState.HasState(NS_EVENT_STATE_FOCUS)) 1.1666 + tdi.attributes |= kThemeTrackHasFocus; 1.1667 + if (IsDisabled(aFrame, inState)) 1.1668 + tdi.enableState = kThemeTrackDisabled; 1.1669 + else 1.1670 + tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive; 1.1671 + tdi.trackInfo.slider.thumbDir = kThemeThumbPlain; 1.1672 + tdi.trackInfo.slider.pressState = 0; 1.1673 + 1.1674 + HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION); 1.1675 + 1.1676 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1677 +} 1.1678 + 1.1679 +nsIFrame* 1.1680 +nsNativeThemeCocoa::SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter) 1.1681 +{ 1.1682 + // Usually a separator is drawn by the segment to the right of the 1.1683 + // separator, but pressed and selected segments have higher priority. 1.1684 + if (!aBefore || !aAfter) 1.1685 + return nullptr; 1.1686 + if (IsSelectedButton(aAfter)) 1.1687 + return aAfter; 1.1688 + if (IsSelectedButton(aBefore) || IsPressedButton(aBefore)) 1.1689 + return aBefore; 1.1690 + return aAfter; 1.1691 +} 1.1692 + 1.1693 +CGRect 1.1694 +nsNativeThemeCocoa::SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft, 1.1695 + nsIFrame* aCurrent, nsIFrame* aRight) 1.1696 +{ 1.1697 + // A separator between two segments should always be located in the leftmost 1.1698 + // pixel column of the segment to the right of the separator, regardless of 1.1699 + // who ends up drawing it. 1.1700 + // CoreUI draws the separators inside the drawing rect. 1.1701 + if (aLeft && SeparatorResponsibility(aLeft, aCurrent) == aLeft) { 1.1702 + // The left button draws the separator, so we need to make room for it. 1.1703 + aRect.origin.x += 1; 1.1704 + aRect.size.width -= 1; 1.1705 + } 1.1706 + if (SeparatorResponsibility(aCurrent, aRight) == aCurrent) { 1.1707 + // We draw the right separator, so we need to extend the draw rect into the 1.1708 + // segment to our right. 1.1709 + aRect.size.width += 1; 1.1710 + } 1.1711 + return aRect; 1.1712 +} 1.1713 + 1.1714 +static NSString* ToolbarButtonPosition(BOOL aIsFirst, BOOL aIsLast) 1.1715 +{ 1.1716 + if (aIsFirst) { 1.1717 + if (aIsLast) 1.1718 + return @"kCUISegmentPositionOnly"; 1.1719 + return @"kCUISegmentPositionFirst"; 1.1720 + } 1.1721 + if (aIsLast) 1.1722 + return @"kCUISegmentPositionLast"; 1.1723 + return @"kCUISegmentPositionMiddle"; 1.1724 +} 1.1725 + 1.1726 +struct SegmentedControlRenderSettings { 1.1727 + const CGFloat* heights; 1.1728 + const NSString* widgetName; 1.1729 + const BOOL ignoresPressedWhenSelected; 1.1730 + const BOOL isToolbarControl; 1.1731 +}; 1.1732 + 1.1733 +static const CGFloat tabHeights[3] = { 17, 20, 23 }; 1.1734 + 1.1735 +static const SegmentedControlRenderSettings tabRenderSettings = { 1.1736 + tabHeights, @"tab", YES, NO 1.1737 +}; 1.1738 + 1.1739 +static const CGFloat toolbarButtonHeights[3] = { 15, 18, 22 }; 1.1740 + 1.1741 +static const SegmentedControlRenderSettings toolbarButtonRenderSettings = { 1.1742 + toolbarButtonHeights, @"kCUIWidgetButtonSegmentedSCurve", NO, YES 1.1743 +}; 1.1744 + 1.1745 +void 1.1746 +nsNativeThemeCocoa::DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect, 1.1747 + EventStates inState, nsIFrame* aFrame, 1.1748 + const SegmentedControlRenderSettings& aSettings) 1.1749 +{ 1.1750 + BOOL isActive = IsActive(aFrame, aSettings.isToolbarControl); 1.1751 + BOOL isFocused = inState.HasState(NS_EVENT_STATE_FOCUS); 1.1752 + BOOL isSelected = IsSelectedButton(aFrame); 1.1753 + BOOL isPressed = IsPressedButton(aFrame); 1.1754 + if (isSelected && aSettings.ignoresPressedWhenSelected) { 1.1755 + isPressed = NO; 1.1756 + } 1.1757 + 1.1758 + BOOL isRTL = IsFrameRTL(aFrame); 1.1759 + nsIFrame* left = GetAdjacentSiblingFrameWithSameAppearance(aFrame, isRTL); 1.1760 + nsIFrame* right = GetAdjacentSiblingFrameWithSameAppearance(aFrame, !isRTL); 1.1761 + CGRect drawRect = SeparatorAdjustedRect(inBoxRect, left, aFrame, right); 1.1762 + if (drawRect.size.width * drawRect.size.height > CUIDRAW_MAX_AREA) { 1.1763 + return; 1.1764 + } 1.1765 + BOOL drawLeftSeparator = SeparatorResponsibility(left, aFrame) == aFrame; 1.1766 + BOOL drawRightSeparator = SeparatorResponsibility(aFrame, right) == aFrame; 1.1767 + NSControlSize controlSize = FindControlSize(drawRect.size.height, aSettings.heights, 4.0f); 1.1768 + 1.1769 + CUIDraw([NSWindow coreUIRenderer], drawRect, cgContext, 1.1770 + (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: 1.1771 + aSettings.widgetName, @"widget", 1.1772 + ToolbarButtonPosition(!left, !right), @"kCUIPositionKey", 1.1773 + [NSNumber numberWithBool:drawLeftSeparator], @"kCUISegmentLeadingSeparatorKey", 1.1774 + [NSNumber numberWithBool:drawRightSeparator], @"kCUISegmentTrailingSeparatorKey", 1.1775 + [NSNumber numberWithBool:isSelected], @"value", 1.1776 + (isPressed ? @"pressed" : (isActive ? @"normal" : @"inactive")), @"state", 1.1777 + [NSNumber numberWithBool:isFocused], @"focus", 1.1778 + CUIControlSizeForCocoaSize(controlSize), @"size", 1.1779 + [NSNumber numberWithBool:YES], @"is.flipped", 1.1780 + @"up", @"direction", 1.1781 + nil], 1.1782 + nil); 1.1783 +} 1.1784 + 1.1785 +static inline UInt8 1.1786 +ConvertToPressState(EventStates aButtonState, UInt8 aPressState) 1.1787 +{ 1.1788 + // If the button is pressed, return the press state passed in. Otherwise, return 0. 1.1789 + return aButtonState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER) ? aPressState : 0; 1.1790 +} 1.1791 + 1.1792 +void 1.1793 +nsNativeThemeCocoa::GetScrollbarPressStates(nsIFrame* aFrame, 1.1794 + EventStates aButtonStates[]) 1.1795 +{ 1.1796 + static nsIContent::AttrValuesArray attributeValues[] = { 1.1797 + &nsGkAtoms::scrollbarUpTop, 1.1798 + &nsGkAtoms::scrollbarDownTop, 1.1799 + &nsGkAtoms::scrollbarUpBottom, 1.1800 + &nsGkAtoms::scrollbarDownBottom, 1.1801 + nullptr 1.1802 + }; 1.1803 + 1.1804 + // Get the state of any scrollbar buttons in our child frames 1.1805 + for (nsIFrame *childFrame = aFrame->GetFirstPrincipalChild(); 1.1806 + childFrame; 1.1807 + childFrame = childFrame->GetNextSibling()) { 1.1808 + 1.1809 + nsIContent *childContent = childFrame->GetContent(); 1.1810 + if (!childContent) continue; 1.1811 + int32_t attrIndex = childContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::sbattr, 1.1812 + attributeValues, eCaseMatters); 1.1813 + if (attrIndex < 0) continue; 1.1814 + 1.1815 + aButtonStates[attrIndex] = GetContentState(childFrame, NS_THEME_BUTTON); 1.1816 + } 1.1817 +} 1.1818 + 1.1819 +// Both of the following sets of numbers were derived by loading the testcase in 1.1820 +// bmo bug 380185 in Safari and observing its behavior for various heights of scrollbar. 1.1821 +// These magic numbers are the minimum sizes we can draw a scrollbar and still 1.1822 +// have room for everything to display, including the thumb 1.1823 +#define MIN_SCROLLBAR_SIZE_WITH_THUMB 61 1.1824 +#define MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB 49 1.1825 +// And these are the minimum sizes if we don't draw the thumb 1.1826 +#define MIN_SCROLLBAR_SIZE 56 1.1827 +#define MIN_SMALL_SCROLLBAR_SIZE 46 1.1828 + 1.1829 +void 1.1830 +nsNativeThemeCocoa::GetScrollbarDrawInfo(HIThemeTrackDrawInfo& aTdi, nsIFrame *aFrame, 1.1831 + const CGSize& aSize, bool aShouldGetButtonStates) 1.1832 +{ 1.1833 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1834 + 1.1835 + int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); 1.1836 + int32_t minpos = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0); 1.1837 + int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); 1.1838 + int32_t thumbSize = CheckIntAttr(aFrame, nsGkAtoms::pageincrement, 10); 1.1839 + 1.1840 + bool isHorizontal = aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::orient, 1.1841 + nsGkAtoms::horizontal, eCaseMatters); 1.1842 + bool isSmall = aFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL; 1.1843 + 1.1844 + aTdi.version = 0; 1.1845 + aTdi.kind = isSmall ? kThemeSmallScrollBar : kThemeMediumScrollBar; 1.1846 + aTdi.bounds.origin = CGPointZero; 1.1847 + aTdi.bounds.size = aSize; 1.1848 + aTdi.min = minpos; 1.1849 + aTdi.max = maxpos; 1.1850 + aTdi.value = curpos; 1.1851 + aTdi.attributes = 0; 1.1852 + aTdi.enableState = kThemeTrackActive; 1.1853 + if (isHorizontal) 1.1854 + aTdi.attributes |= kThemeTrackHorizontal; 1.1855 + 1.1856 + aTdi.trackInfo.scrollbar.viewsize = (SInt32)thumbSize; 1.1857 + 1.1858 + // This should be done early on so things like "kThemeTrackNothingToScroll" can 1.1859 + // override the active enable state. 1.1860 + aTdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive; 1.1861 + 1.1862 + /* Only display features if we have enough room for them. 1.1863 + * Gecko still maintains the scrollbar info; this is just a visual issue (bug 380185). 1.1864 + */ 1.1865 + int32_t longSideLength = (int32_t)(isHorizontal ? (aSize.width) : (aSize.height)); 1.1866 + if (longSideLength >= (isSmall ? MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB : MIN_SCROLLBAR_SIZE_WITH_THUMB)) { 1.1867 + aTdi.attributes |= kThemeTrackShowThumb; 1.1868 + } 1.1869 + else if (longSideLength < (isSmall ? MIN_SMALL_SCROLLBAR_SIZE : MIN_SCROLLBAR_SIZE)) { 1.1870 + aTdi.enableState = kThemeTrackNothingToScroll; 1.1871 + return; 1.1872 + } 1.1873 + 1.1874 + aTdi.trackInfo.scrollbar.pressState = 0; 1.1875 + 1.1876 + // Only go get these scrollbar button states if we need it. For example, 1.1877 + // there's no reason to look up scrollbar button states when we're only 1.1878 + // creating a TrackDrawInfo to determine the size of the thumb. There's 1.1879 + // also no reason to do this on Lion or later, whose scrollbars have no 1.1880 + // arrow buttons. 1.1881 + if (aShouldGetButtonStates && !nsCocoaFeatures::OnLionOrLater()) { 1.1882 + EventStates buttonStates[4]; 1.1883 + GetScrollbarPressStates(aFrame, buttonStates); 1.1884 + NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"]; 1.1885 + // It seems that unless all four buttons are showing, kThemeTopOutsideArrowPressed is the correct constant for 1.1886 + // the up scrollbar button. 1.1887 + if ([buttonPlacement isEqualToString:@"DoubleBoth"]) { 1.1888 + aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) | 1.1889 + ConvertToPressState(buttonStates[1], kThemeTopInsideArrowPressed) | 1.1890 + ConvertToPressState(buttonStates[2], kThemeBottomInsideArrowPressed) | 1.1891 + ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed); 1.1892 + } else { 1.1893 + aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) | 1.1894 + ConvertToPressState(buttonStates[1], kThemeBottomOutsideArrowPressed) | 1.1895 + ConvertToPressState(buttonStates[2], kThemeTopOutsideArrowPressed) | 1.1896 + ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed); 1.1897 + } 1.1898 + } 1.1899 + 1.1900 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1901 +} 1.1902 + 1.1903 +static void 1.1904 +RenderScrollbar(CGContextRef cgContext, const HIRect& aRenderRect, void* aData) 1.1905 +{ 1.1906 + HIThemeTrackDrawInfo* tdi = (HIThemeTrackDrawInfo*)aData; 1.1907 + HIThemeDrawTrack(tdi, NULL, cgContext, HITHEME_ORIENTATION); 1.1908 +} 1.1909 + 1.1910 +void 1.1911 +nsNativeThemeCocoa::DrawScrollbar(CGContextRef aCGContext, const HIRect& aBoxRect, nsIFrame *aFrame) 1.1912 +{ 1.1913 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1914 + 1.1915 + HIThemeTrackDrawInfo tdi; 1.1916 + GetScrollbarDrawInfo(tdi, aFrame, aBoxRect.size, true); // True means we want the press states 1.1917 + RenderTransformedHIThemeControl(aCGContext, aBoxRect, RenderScrollbar, &tdi); 1.1918 + 1.1919 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.1920 +} 1.1921 + 1.1922 +nsIFrame* 1.1923 +nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame) 1.1924 +{ 1.1925 + // Walk our parents to find a scrollbar frame 1.1926 + nsIFrame *scrollbarFrame = aFrame; 1.1927 + do { 1.1928 + if (scrollbarFrame->GetType() == nsGkAtoms::scrollbarFrame) break; 1.1929 + } while ((scrollbarFrame = scrollbarFrame->GetParent())); 1.1930 + 1.1931 + // We return null if we can't find a parent scrollbar frame 1.1932 + return scrollbarFrame; 1.1933 +} 1.1934 + 1.1935 +static bool 1.1936 +ToolbarCanBeUnified(CGContextRef cgContext, const HIRect& inBoxRect, NSWindow* aWindow) 1.1937 +{ 1.1938 + if (![aWindow isKindOfClass:[ToolbarWindow class]]) 1.1939 + return false; 1.1940 + 1.1941 + ToolbarWindow* win = (ToolbarWindow*)aWindow; 1.1942 + float unifiedToolbarHeight = [win unifiedToolbarHeight]; 1.1943 + return inBoxRect.origin.x == 0 && 1.1944 + inBoxRect.size.width >= [win frame].size.width && 1.1945 + CGRectGetMaxY(inBoxRect) <= unifiedToolbarHeight; 1.1946 +} 1.1947 + 1.1948 +// By default, kCUIWidgetWindowFrame drawing draws rounded corners in the 1.1949 +// upper corners. Depending on the context type, it fills the background in 1.1950 +// the corners with black or leaves it transparent. Unfortunately, this corner 1.1951 +// rounding interacts poorly with the window corner masking we apply during 1.1952 +// titlebar drawing and results in small remnants of the corner background 1.1953 +// appearing at the rounded edge. 1.1954 +// So we draw square corners. 1.1955 +static void 1.1956 +DrawNativeTitlebarToolbarWithSquareCorners(CGContextRef aContext, const CGRect& aRect, 1.1957 + CGFloat aUnifiedHeight, BOOL aIsMain) 1.1958 +{ 1.1959 + // We extend the draw rect horizontally and clip away the rounded corners. 1.1960 + const CGFloat extendHorizontal = 10; 1.1961 + CGRect drawRect = CGRectInset(aRect, -extendHorizontal, 0); 1.1962 + if (drawRect.size.width * drawRect.size.height <= CUIDRAW_MAX_AREA) { 1.1963 + CGContextSaveGState(aContext); 1.1964 + CGContextClipToRect(aContext, aRect); 1.1965 + 1.1966 + CUIDraw([NSWindow coreUIRenderer], drawRect, aContext, 1.1967 + (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: 1.1968 + @"kCUIWidgetWindowFrame", @"widget", 1.1969 + @"regularwin", @"windowtype", 1.1970 + (aIsMain ? @"normal" : @"inactive"), @"state", 1.1971 + [NSNumber numberWithDouble:aUnifiedHeight], @"kCUIWindowFrameUnifiedTitleBarHeightKey", 1.1972 + [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawTitleSeparatorKey", 1.1973 + [NSNumber numberWithBool:YES], @"is.flipped", 1.1974 + nil], 1.1975 + nil); 1.1976 + 1.1977 + CGContextRestoreGState(aContext); 1.1978 + } 1.1979 +} 1.1980 + 1.1981 +void 1.1982 +nsNativeThemeCocoa::DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect, 1.1983 + NSWindow* aWindow) 1.1984 +{ 1.1985 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.1986 + 1.1987 + CGContextSaveGState(cgContext); 1.1988 + CGContextClipToRect(cgContext, inBoxRect); 1.1989 + 1.1990 + CGFloat unifiedHeight = std::max([(ToolbarWindow*)aWindow unifiedToolbarHeight], 1.1991 + inBoxRect.size.height); 1.1992 + BOOL isMain = [aWindow isMainWindow]; 1.1993 + CGFloat titlebarHeight = unifiedHeight - inBoxRect.size.height; 1.1994 + CGRect drawRect = CGRectMake(inBoxRect.origin.x, inBoxRect.origin.y - titlebarHeight, 1.1995 + inBoxRect.size.width, inBoxRect.size.height + titlebarHeight); 1.1996 + DrawNativeTitlebarToolbarWithSquareCorners(cgContext, drawRect, unifiedHeight, isMain); 1.1997 + 1.1998 + CGContextRestoreGState(cgContext); 1.1999 + 1.2000 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.2001 +} 1.2002 + 1.2003 +void 1.2004 +nsNativeThemeCocoa::DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect, 1.2005 + nsIFrame *aFrame) 1.2006 +{ 1.2007 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.2008 + 1.2009 + if (inBoxRect.size.height < 2.0f) 1.2010 + return; 1.2011 + 1.2012 + CGContextSaveGState(cgContext); 1.2013 + CGContextClipToRect(cgContext, inBoxRect); 1.2014 + 1.2015 + // kCUIWidgetWindowFrame draws a complete window frame with both title bar 1.2016 + // and bottom bar. We only want the bottom bar, so we extend the draw rect 1.2017 + // upwards to make space for the title bar, and then we clip it away. 1.2018 + CGRect drawRect = inBoxRect; 1.2019 + const int extendUpwards = 40; 1.2020 + drawRect.origin.y -= extendUpwards; 1.2021 + drawRect.size.height += extendUpwards; 1.2022 + if (drawRect.size.width * drawRect.size.height <= CUIDRAW_MAX_AREA) { 1.2023 + CUIDraw([NSWindow coreUIRenderer], drawRect, cgContext, 1.2024 + (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: 1.2025 + @"kCUIWidgetWindowFrame", @"widget", 1.2026 + @"regularwin", @"windowtype", 1.2027 + (IsActive(aFrame, YES) ? @"normal" : @"inactive"), @"state", 1.2028 + [NSNumber numberWithInt:inBoxRect.size.height], @"kCUIWindowFrameBottomBarHeightKey", 1.2029 + [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawBottomBarSeparatorKey", 1.2030 + [NSNumber numberWithBool:YES], @"is.flipped", 1.2031 + nil], 1.2032 + nil); 1.2033 + } 1.2034 + 1.2035 + CGContextRestoreGState(cgContext); 1.2036 + 1.2037 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.2038 +} 1.2039 + 1.2040 +void 1.2041 +nsNativeThemeCocoa::DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect, 1.2042 + CGFloat aUnifiedHeight, BOOL aIsMain) 1.2043 +{ 1.2044 + CGFloat unifiedHeight = std::max(aUnifiedHeight, aTitlebarRect.size.height); 1.2045 + DrawNativeTitlebarToolbarWithSquareCorners(aContext, aTitlebarRect, unifiedHeight, aIsMain); 1.2046 +} 1.2047 + 1.2048 +static void 1.2049 +RenderResizer(CGContextRef cgContext, const HIRect& aRenderRect, void* aData) 1.2050 +{ 1.2051 + HIThemeGrowBoxDrawInfo* drawInfo = (HIThemeGrowBoxDrawInfo*)aData; 1.2052 + HIThemeDrawGrowBox(&CGPointZero, drawInfo, cgContext, kHIThemeOrientationNormal); 1.2053 +} 1.2054 + 1.2055 +void 1.2056 +nsNativeThemeCocoa::DrawResizer(CGContextRef cgContext, const HIRect& aRect, 1.2057 + nsIFrame *aFrame) 1.2058 +{ 1.2059 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.2060 + 1.2061 + HIThemeGrowBoxDrawInfo drawInfo; 1.2062 + drawInfo.version = 0; 1.2063 + drawInfo.state = kThemeStateActive; 1.2064 + drawInfo.kind = kHIThemeGrowBoxKindNormal; 1.2065 + drawInfo.direction = kThemeGrowRight | kThemeGrowDown; 1.2066 + drawInfo.size = kHIThemeGrowBoxSizeNormal; 1.2067 + 1.2068 + RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo, 1.2069 + IsFrameRTL(aFrame)); 1.2070 + 1.2071 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.2072 +} 1.2073 + 1.2074 +static bool 1.2075 +IsHiDPIContext(nsDeviceContext* aContext) 1.2076 +{ 1.2077 + return nsPresContext::AppUnitsPerCSSPixel() >= 1.2078 + 2 * aContext->UnscaledAppUnitsPerDevPixel(); 1.2079 +} 1.2080 + 1.2081 +NS_IMETHODIMP 1.2082 +nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext, 1.2083 + nsIFrame* aFrame, 1.2084 + uint8_t aWidgetType, 1.2085 + const nsRect& aRect, 1.2086 + const nsRect& aDirtyRect) 1.2087 +{ 1.2088 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 1.2089 + 1.2090 + // setup to draw into the correct port 1.2091 + int32_t p2a = aContext->AppUnitsPerDevPixel(); 1.2092 + 1.2093 + gfxRect nativeDirtyRect(aDirtyRect.x, aDirtyRect.y, 1.2094 + aDirtyRect.width, aDirtyRect.height); 1.2095 + gfxRect nativeWidgetRect(aRect.x, aRect.y, aRect.width, aRect.height); 1.2096 + nativeWidgetRect.ScaleInverse(gfxFloat(p2a)); 1.2097 + nativeDirtyRect.ScaleInverse(gfxFloat(p2a)); 1.2098 + nativeWidgetRect.Round(); 1.2099 + if (nativeWidgetRect.IsEmpty()) 1.2100 + return NS_OK; // Don't attempt to draw invisible widgets. 1.2101 + 1.2102 + gfxContext* thebesCtx = aContext->ThebesContext(); 1.2103 + if (!thebesCtx) 1.2104 + return NS_ERROR_FAILURE; 1.2105 + 1.2106 + gfxContextMatrixAutoSaveRestore save(thebesCtx); 1.2107 + 1.2108 + bool hidpi = IsHiDPIContext(aContext->DeviceContext()); 1.2109 + if (hidpi) { 1.2110 + // Use high-resolution drawing. 1.2111 + nativeWidgetRect.ScaleInverse(2.0f); 1.2112 + nativeDirtyRect.ScaleInverse(2.0f); 1.2113 + thebesCtx->Scale(2.0f, 2.0f); 1.2114 + } 1.2115 + 1.2116 + gfxQuartzNativeDrawing nativeDrawing(thebesCtx, nativeDirtyRect, 1.2117 + hidpi ? 2.0f : 1.0f); 1.2118 + 1.2119 + CGContextRef cgContext = nativeDrawing.BeginNativeDrawing(); 1.2120 + if (cgContext == nullptr) { 1.2121 + // The Quartz surface handles 0x0 surfaces by internally 1.2122 + // making all operations no-ops; there's no cgcontext created for them. 1.2123 + // Unfortunately, this means that callers that want to render 1.2124 + // directly to the CGContext need to be aware of this quirk. 1.2125 + return NS_OK; 1.2126 + } 1.2127 + 1.2128 +#if 0 1.2129 + if (1 /*aWidgetType == NS_THEME_TEXTFIELD*/) { 1.2130 + fprintf(stderr, "Native theme drawing widget %d [%p] dis:%d in rect [%d %d %d %d]\n", 1.2131 + aWidgetType, aFrame, IsDisabled(aFrame), aRect.x, aRect.y, aRect.width, aRect.height); 1.2132 + fprintf(stderr, "Cairo matrix: [%f %f %f %f %f %f]\n", 1.2133 + mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0); 1.2134 + fprintf(stderr, "Native theme xform[0]: [%f %f %f %f %f %f]\n", 1.2135 + mm0.a, mm0.b, mm0.c, mm0.d, mm0.tx, mm0.ty); 1.2136 + CGAffineTransform mm = CGContextGetCTM(cgContext); 1.2137 + fprintf(stderr, "Native theme xform[1]: [%f %f %f %f %f %f]\n", 1.2138 + mm.a, mm.b, mm.c, mm.d, mm.tx, mm.ty); 1.2139 + } 1.2140 +#endif 1.2141 + 1.2142 + CGRect macRect = CGRectMake(nativeWidgetRect.X(), nativeWidgetRect.Y(), 1.2143 + nativeWidgetRect.Width(), nativeWidgetRect.Height()); 1.2144 + 1.2145 +#if 0 1.2146 + fprintf(stderr, " --> macRect %f %f %f %f\n", 1.2147 + macRect.origin.x, macRect.origin.y, macRect.size.width, macRect.size.height); 1.2148 + CGRect bounds = CGContextGetClipBoundingBox(cgContext); 1.2149 + fprintf(stderr, " --> clip bounds: %f %f %f %f\n", 1.2150 + bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); 1.2151 + 1.2152 + //CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 1.0, 0.1); 1.2153 + //CGContextFillRect(cgContext, bounds); 1.2154 +#endif 1.2155 + 1.2156 + EventStates eventState = GetContentState(aFrame, aWidgetType); 1.2157 + 1.2158 + switch (aWidgetType) { 1.2159 + case NS_THEME_DIALOG: { 1.2160 + HIThemeSetFill(kThemeBrushDialogBackgroundActive, NULL, cgContext, HITHEME_ORIENTATION); 1.2161 + CGContextFillRect(cgContext, macRect); 1.2162 + } 1.2163 + break; 1.2164 + 1.2165 + case NS_THEME_MENUPOPUP: { 1.2166 + HIThemeMenuDrawInfo mdi; 1.2167 + memset(&mdi, 0, sizeof(mdi)); 1.2168 + mdi.version = 0; 1.2169 + mdi.menuType = IsDisabled(aFrame, eventState) ? 1.2170 + static_cast<ThemeMenuType>(kThemeMenuTypeInactive) : 1.2171 + static_cast<ThemeMenuType>(kThemeMenuTypePopUp); 1.2172 + 1.2173 + bool isLeftOfParent = false; 1.2174 + if (IsSubmenu(aFrame, &isLeftOfParent) && !isLeftOfParent) { 1.2175 + mdi.menuType = kThemeMenuTypeHierarchical; 1.2176 + } 1.2177 + 1.2178 + // The rounded corners draw outside the frame. 1.2179 + CGRect deflatedRect = CGRectMake(macRect.origin.x, macRect.origin.y + 4, 1.2180 + macRect.size.width, macRect.size.height - 8); 1.2181 + HIThemeDrawMenuBackground(&deflatedRect, &mdi, cgContext, HITHEME_ORIENTATION); 1.2182 + } 1.2183 + break; 1.2184 + 1.2185 + case NS_THEME_MENUITEM: { 1.2186 + bool isTransparent; 1.2187 + if (thebesCtx->IsCairo()) { 1.2188 + isTransparent = thebesCtx->OriginalSurface()->GetContentType() == gfxContentType::COLOR_ALPHA; 1.2189 + } else { 1.2190 + SurfaceFormat format = thebesCtx->GetDrawTarget()->GetFormat(); 1.2191 + isTransparent = (format == SurfaceFormat::R8G8B8A8) || 1.2192 + (format == SurfaceFormat::B8G8R8A8); 1.2193 + } 1.2194 + if (isTransparent) { 1.2195 + // Clear the background to get correct transparency. 1.2196 + CGContextClearRect(cgContext, macRect); 1.2197 + } 1.2198 + 1.2199 + // maybe use kThemeMenuItemHierBackground or PopUpBackground instead of just Plain? 1.2200 + HIThemeMenuItemDrawInfo drawInfo; 1.2201 + memset(&drawInfo, 0, sizeof(drawInfo)); 1.2202 + drawInfo.version = 0; 1.2203 + drawInfo.itemType = kThemeMenuItemPlain; 1.2204 + drawInfo.state = (IsDisabled(aFrame, eventState) ? 1.2205 + static_cast<ThemeMenuState>(kThemeMenuDisabled) : 1.2206 + CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ? 1.2207 + static_cast<ThemeMenuState>(kThemeMenuSelected) : 1.2208 + static_cast<ThemeMenuState>(kThemeMenuActive)); 1.2209 + 1.2210 + // XXX pass in the menu rect instead of always using the item rect 1.2211 + HIRect ignored; 1.2212 + HIThemeDrawMenuItem(&macRect, &macRect, &drawInfo, cgContext, HITHEME_ORIENTATION, &ignored); 1.2213 + } 1.2214 + break; 1.2215 + 1.2216 + case NS_THEME_MENUSEPARATOR: { 1.2217 + ThemeMenuState menuState; 1.2218 + if (IsDisabled(aFrame, eventState)) { 1.2219 + menuState = kThemeMenuDisabled; 1.2220 + } 1.2221 + else { 1.2222 + menuState = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ? 1.2223 + kThemeMenuSelected : kThemeMenuActive; 1.2224 + } 1.2225 + 1.2226 + HIThemeMenuItemDrawInfo midi = { 0, kThemeMenuItemPlain, menuState }; 1.2227 + HIThemeDrawMenuSeparator(&macRect, &macRect, &midi, cgContext, HITHEME_ORIENTATION); 1.2228 + } 1.2229 + break; 1.2230 + 1.2231 + case NS_THEME_TOOLTIP: 1.2232 + CGContextSetRGBFillColor(cgContext, 0.996, 1.000, 0.792, 0.950); 1.2233 + CGContextFillRect(cgContext, macRect); 1.2234 + break; 1.2235 + 1.2236 + case NS_THEME_CHECKBOX: 1.2237 + case NS_THEME_RADIO: { 1.2238 + bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX); 1.2239 + DrawCheckboxOrRadio(cgContext, isCheckbox, macRect, GetCheckedOrSelected(aFrame, !isCheckbox), 1.2240 + eventState, aFrame); 1.2241 + } 1.2242 + break; 1.2243 + 1.2244 + case NS_THEME_BUTTON: 1.2245 + if (IsDefaultButton(aFrame)) { 1.2246 + if (!IsDisabled(aFrame, eventState) && FrameIsInActiveWindow(aFrame) && 1.2247 + !QueueAnimatedContentForRefresh(aFrame->GetContent(), 10)) { 1.2248 + NS_WARNING("Unable to animate button!"); 1.2249 + } 1.2250 + DrawButton(cgContext, kThemePushButton, macRect, true, 1.2251 + kThemeButtonOff, kThemeAdornmentNone, eventState, aFrame); 1.2252 + } else if (IsButtonTypeMenu(aFrame)) { 1.2253 + DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame); 1.2254 + } else { 1.2255 + DrawPushButton(cgContext, macRect, eventState, aWidgetType, aFrame); 1.2256 + } 1.2257 + break; 1.2258 + 1.2259 + case NS_THEME_MOZ_MAC_HELP_BUTTON: 1.2260 + DrawPushButton(cgContext, macRect, eventState, aWidgetType, aFrame); 1.2261 + break; 1.2262 + 1.2263 + case NS_THEME_BUTTON_BEVEL: 1.2264 + DrawButton(cgContext, kThemeMediumBevelButton, macRect, 1.2265 + IsDefaultButton(aFrame), kThemeButtonOff, kThemeAdornmentNone, 1.2266 + eventState, aFrame); 1.2267 + break; 1.2268 + 1.2269 + case NS_THEME_SPINNER: { 1.2270 + nsIContent* content = aFrame->GetContent(); 1.2271 + if (content->IsHTML()) { 1.2272 + // In HTML the theming for the spin buttons is drawn individually into 1.2273 + // their own backgrounds instead of being drawn into the background of 1.2274 + // their spinner parent as it is for XUL. 1.2275 + break; 1.2276 + } 1.2277 + ThemeDrawState state = kThemeStateActive; 1.2278 + if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::state, 1.2279 + NS_LITERAL_STRING("up"), eCaseMatters)) { 1.2280 + state = kThemeStatePressedUp; 1.2281 + } 1.2282 + else if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::state, 1.2283 + NS_LITERAL_STRING("down"), eCaseMatters)) { 1.2284 + state = kThemeStatePressedDown; 1.2285 + } 1.2286 + 1.2287 + DrawSpinButtons(cgContext, kThemeIncDecButton, macRect, state, 1.2288 + kThemeAdornmentNone, eventState, aFrame); 1.2289 + } 1.2290 + break; 1.2291 + 1.2292 + case NS_THEME_SPINNER_UP_BUTTON: 1.2293 + case NS_THEME_SPINNER_DOWN_BUTTON: { 1.2294 + nsNumberControlFrame* numberControlFrame = 1.2295 + nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame); 1.2296 + if (numberControlFrame) { 1.2297 + ThemeDrawState state = kThemeStateActive; 1.2298 + if (numberControlFrame->SpinnerUpButtonIsDepressed()) { 1.2299 + state = kThemeStatePressedUp; 1.2300 + } else if (numberControlFrame->SpinnerDownButtonIsDepressed()) { 1.2301 + state = kThemeStatePressedDown; 1.2302 + } 1.2303 + DrawSpinButton(cgContext, kThemeIncDecButtonMini, macRect, state, 1.2304 + kThemeAdornmentNone, eventState, aFrame, aWidgetType); 1.2305 + } 1.2306 + } 1.2307 + break; 1.2308 + 1.2309 + case NS_THEME_TOOLBAR_BUTTON: 1.2310 + DrawSegment(cgContext, macRect, eventState, aFrame, toolbarButtonRenderSettings); 1.2311 + break; 1.2312 + 1.2313 + case NS_THEME_TOOLBAR_SEPARATOR: { 1.2314 + HIThemeSeparatorDrawInfo sdi = { 0, kThemeStateActive }; 1.2315 + HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION); 1.2316 + } 1.2317 + break; 1.2318 + 1.2319 + case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: 1.2320 + case NS_THEME_TOOLBAR: { 1.2321 + NSWindow* win = NativeWindowForFrame(aFrame); 1.2322 + if (ToolbarCanBeUnified(cgContext, macRect, win)) { 1.2323 + DrawUnifiedToolbar(cgContext, macRect, win); 1.2324 + break; 1.2325 + } 1.2326 + BOOL isMain = [win isMainWindow]; 1.2327 + CGRect drawRect = macRect; 1.2328 + 1.2329 + // top border 1.2330 + drawRect.size.height = 1.0f; 1.2331 + DrawNativeGreyColorInRect(cgContext, toolbarTopBorderGrey, drawRect, isMain); 1.2332 + 1.2333 + // background 1.2334 + drawRect.origin.y += drawRect.size.height; 1.2335 + drawRect.size.height = macRect.size.height - 2.0f; 1.2336 + DrawNativeGreyColorInRect(cgContext, toolbarFillGrey, drawRect, isMain); 1.2337 + 1.2338 + // bottom border 1.2339 + drawRect.origin.y += drawRect.size.height; 1.2340 + drawRect.size.height = 1.0f; 1.2341 + DrawNativeGreyColorInRect(cgContext, toolbarBottomBorderGrey, drawRect, isMain); 1.2342 + } 1.2343 + break; 1.2344 + 1.2345 + case NS_THEME_WINDOW_TITLEBAR: { 1.2346 + NSWindow* win = NativeWindowForFrame(aFrame); 1.2347 + BOOL isMain = [win isMainWindow]; 1.2348 + float unifiedToolbarHeight = [win isKindOfClass:[ToolbarWindow class]] ? 1.2349 + [(ToolbarWindow*)win unifiedToolbarHeight] : macRect.size.height; 1.2350 + DrawNativeTitlebar(cgContext, macRect, unifiedToolbarHeight, isMain); 1.2351 + } 1.2352 + break; 1.2353 + 1.2354 + case NS_THEME_TOOLBOX: { 1.2355 + HIThemeHeaderDrawInfo hdi = { 0, kThemeStateActive, kHIThemeHeaderKindWindow }; 1.2356 + HIThemeDrawHeader(&macRect, &hdi, cgContext, HITHEME_ORIENTATION); 1.2357 + } 1.2358 + break; 1.2359 + 1.2360 + case NS_THEME_STATUSBAR: 1.2361 + DrawStatusBar(cgContext, macRect, aFrame); 1.2362 + break; 1.2363 + 1.2364 + case NS_THEME_DROPDOWN: 1.2365 + case NS_THEME_DROPDOWN_TEXTFIELD: 1.2366 + DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame); 1.2367 + break; 1.2368 + 1.2369 + case NS_THEME_DROPDOWN_BUTTON: 1.2370 + DrawButton(cgContext, kThemeArrowButton, macRect, false, kThemeButtonOn, 1.2371 + kThemeAdornmentArrowDownArrow, eventState, aFrame); 1.2372 + break; 1.2373 + 1.2374 + case NS_THEME_GROUPBOX: { 1.2375 + HIThemeGroupBoxDrawInfo gdi = { 0, kThemeStateActive, kHIThemeGroupBoxKindPrimary }; 1.2376 + HIThemeDrawGroupBox(&macRect, &gdi, cgContext, HITHEME_ORIENTATION); 1.2377 + break; 1.2378 + } 1.2379 + 1.2380 + case NS_THEME_TEXTFIELD: 1.2381 + case NS_THEME_NUMBER_INPUT: 1.2382 + // HIThemeSetFill is not available on 10.3 1.2383 + CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); 1.2384 + CGContextFillRect(cgContext, macRect); 1.2385 + 1.2386 + // XUL textboxes set the native appearance on the containing box, while 1.2387 + // concrete focus is set on the html:input element within it. We can 1.2388 + // though, check the focused attribute of xul textboxes in this case. 1.2389 + // On Mac, focus rings are always shown for textboxes, so we do not need 1.2390 + // to check the window's focus ring state here 1.2391 + if (aFrame->GetContent()->IsXUL() && IsFocused(aFrame)) { 1.2392 + eventState |= NS_EVENT_STATE_FOCUS; 1.2393 + } 1.2394 + 1.2395 + DrawFrame(cgContext, kHIThemeFrameTextFieldSquare, macRect, 1.2396 + IsDisabled(aFrame, eventState) || IsReadOnly(aFrame), eventState); 1.2397 + break; 1.2398 + 1.2399 + case NS_THEME_SEARCHFIELD: 1.2400 + DrawSearchField(cgContext, macRect, aFrame, eventState); 1.2401 + break; 1.2402 + 1.2403 + case NS_THEME_PROGRESSBAR: 1.2404 + if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) { 1.2405 + NS_WARNING("Unable to animate progressbar!"); 1.2406 + } 1.2407 + DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame, eventState), 1.2408 + aFrame->StyleDisplay()->mOrient != NS_STYLE_ORIENT_VERTICAL, 1.2409 + GetProgressValue(aFrame), GetProgressMaxValue(aFrame), aFrame); 1.2410 + break; 1.2411 + 1.2412 + case NS_THEME_PROGRESSBAR_VERTICAL: 1.2413 + DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame, eventState), 1.2414 + false, GetProgressValue(aFrame), 1.2415 + GetProgressMaxValue(aFrame), aFrame); 1.2416 + break; 1.2417 + 1.2418 + case NS_THEME_METERBAR: 1.2419 + DrawMeter(cgContext, macRect, aFrame); 1.2420 + break; 1.2421 + 1.2422 + case NS_THEME_PROGRESSBAR_CHUNK: 1.2423 + case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: 1.2424 + case NS_THEME_METERBAR_CHUNK: 1.2425 + // Do nothing: progress and meter bars cases will draw chunks. 1.2426 + break; 1.2427 + 1.2428 + case NS_THEME_TREEVIEW_TWISTY: 1.2429 + DrawButton(cgContext, kThemeDisclosureButton, macRect, false, 1.2430 + kThemeDisclosureRight, kThemeAdornmentNone, eventState, aFrame); 1.2431 + break; 1.2432 + 1.2433 + case NS_THEME_TREEVIEW_TWISTY_OPEN: 1.2434 + DrawButton(cgContext, kThemeDisclosureButton, macRect, false, 1.2435 + kThemeDisclosureDown, kThemeAdornmentNone, eventState, aFrame); 1.2436 + break; 1.2437 + 1.2438 + case NS_THEME_TREEVIEW_HEADER_CELL: { 1.2439 + TreeSortDirection sortDirection = GetTreeSortDirection(aFrame); 1.2440 + DrawButton(cgContext, kThemeListHeaderButton, macRect, false, 1.2441 + sortDirection == eTreeSortDirection_Natural ? kThemeButtonOff : kThemeButtonOn, 1.2442 + sortDirection == eTreeSortDirection_Ascending ? 1.2443 + kThemeAdornmentHeaderButtonSortUp : kThemeAdornmentNone, eventState, aFrame); 1.2444 + } 1.2445 + break; 1.2446 + 1.2447 + case NS_THEME_TREEVIEW_TREEITEM: 1.2448 + case NS_THEME_TREEVIEW: 1.2449 + // HIThemeSetFill is not available on 10.3 1.2450 + // HIThemeSetFill(kThemeBrushWhite, NULL, cgContext, HITHEME_ORIENTATION); 1.2451 + CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); 1.2452 + CGContextFillRect(cgContext, macRect); 1.2453 + break; 1.2454 + 1.2455 + case NS_THEME_TREEVIEW_HEADER: 1.2456 + // do nothing, taken care of by individual header cells 1.2457 + case NS_THEME_TREEVIEW_HEADER_SORTARROW: 1.2458 + // do nothing, taken care of by treeview header 1.2459 + case NS_THEME_TREEVIEW_LINE: 1.2460 + // do nothing, these lines don't exist on macos 1.2461 + break; 1.2462 + 1.2463 + case NS_THEME_SCALE_HORIZONTAL: 1.2464 + case NS_THEME_SCALE_VERTICAL: { 1.2465 + int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); 1.2466 + int32_t minpos = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0); 1.2467 + int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); 1.2468 + if (!maxpos) 1.2469 + maxpos = 100; 1.2470 + 1.2471 + bool reverse = aFrame->GetContent()-> 1.2472 + AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, 1.2473 + NS_LITERAL_STRING("reverse"), eCaseMatters); 1.2474 + DrawScale(cgContext, macRect, eventState, 1.2475 + (aWidgetType == NS_THEME_SCALE_VERTICAL), reverse, 1.2476 + curpos, minpos, maxpos, aFrame); 1.2477 + } 1.2478 + break; 1.2479 + 1.2480 + case NS_THEME_SCALE_THUMB_HORIZONTAL: 1.2481 + case NS_THEME_SCALE_THUMB_VERTICAL: 1.2482 + // do nothing, drawn by scale 1.2483 + break; 1.2484 + 1.2485 + case NS_THEME_RANGE: { 1.2486 + nsRangeFrame *rangeFrame = do_QueryFrame(aFrame); 1.2487 + if (!rangeFrame) { 1.2488 + break; 1.2489 + } 1.2490 + // DrawScale requires integer min, max and value. This is purely for 1.2491 + // drawing, so we normalize to a range 0-1000 here. 1.2492 + int32_t value = int32_t(rangeFrame->GetValueAsFractionOfRange() * 1000); 1.2493 + int32_t min = 0; 1.2494 + int32_t max = 1000; 1.2495 + bool isVertical = !IsRangeHorizontal(aFrame); 1.2496 + bool reverseDir = 1.2497 + isVertical || 1.2498 + rangeFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; 1.2499 + DrawScale(cgContext, macRect, eventState, isVertical, reverseDir, 1.2500 + value, min, max, aFrame); 1.2501 + break; 1.2502 + } 1.2503 + 1.2504 + case NS_THEME_SCROLLBAR_SMALL: 1.2505 + case NS_THEME_SCROLLBAR: 1.2506 + if (!nsLookAndFeel::UseOverlayScrollbars()) { 1.2507 + DrawScrollbar(cgContext, macRect, aFrame); 1.2508 + } 1.2509 + break; 1.2510 + case NS_THEME_SCROLLBAR_THUMB_VERTICAL: 1.2511 + case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: 1.2512 + if (nsLookAndFeel::UseOverlayScrollbars()) { 1.2513 + BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL); 1.2514 + BOOL isRolledOver = CheckBooleanAttr(GetParentScrollbarFrame(aFrame), 1.2515 + nsGkAtoms::hover); 1.2516 + if (!nsCocoaFeatures::OnMountainLionOrLater() || !isRolledOver) { 1.2517 + if (isHorizontal) { 1.2518 + macRect.origin.y += 4; 1.2519 + macRect.size.height -= 4; 1.2520 + } else { 1.2521 + if (aFrame->StyleVisibility()->mDirection != 1.2522 + NS_STYLE_DIRECTION_RTL) { 1.2523 + macRect.origin.x += 4; 1.2524 + } 1.2525 + macRect.size.width -= 4; 1.2526 + } 1.2527 + } 1.2528 + const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame); 1.2529 + CUIDraw([NSWindow coreUIRenderer], macRect, cgContext, 1.2530 + (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: 1.2531 + @"kCUIWidgetOverlayScrollBar", @"widget", 1.2532 + @"regular", @"size", 1.2533 + (isRolledOver ? @"rollover" : @""), @"state", 1.2534 + (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey", 1.2535 + (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey", 1.2536 + [NSNumber numberWithBool:YES], @"indiconly", 1.2537 + [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey", 1.2538 + [NSNumber numberWithBool:YES], @"is.flipped", 1.2539 + nil], 1.2540 + nil); 1.2541 + } 1.2542 + break; 1.2543 + case NS_THEME_SCROLLBAR_BUTTON_UP: 1.2544 + case NS_THEME_SCROLLBAR_BUTTON_LEFT: 1.2545 +#if SCROLLBARS_VISUAL_DEBUG 1.2546 + CGContextSetRGBFillColor(cgContext, 1.0, 0, 0, 0.6); 1.2547 + CGContextFillRect(cgContext, macRect); 1.2548 +#endif 1.2549 + break; 1.2550 + case NS_THEME_SCROLLBAR_BUTTON_DOWN: 1.2551 + case NS_THEME_SCROLLBAR_BUTTON_RIGHT: 1.2552 +#if SCROLLBARS_VISUAL_DEBUG 1.2553 + CGContextSetRGBFillColor(cgContext, 0, 1.0, 0, 0.6); 1.2554 + CGContextFillRect(cgContext, macRect); 1.2555 +#endif 1.2556 + break; 1.2557 + case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: 1.2558 + case NS_THEME_SCROLLBAR_TRACK_VERTICAL: 1.2559 + if (nsLookAndFeel::UseOverlayScrollbars() && 1.2560 + CheckBooleanAttr(GetParentScrollbarFrame(aFrame), nsGkAtoms::hover)) { 1.2561 + BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL); 1.2562 + if (!nsCocoaFeatures::OnMountainLionOrLater()) { 1.2563 + // On OSX 10.7, scrollbars don't grow when hovered. The adjustments 1.2564 + // below were obtained by trial and error. 1.2565 + if (isHorizontal) { 1.2566 + macRect.origin.y += 2.0; 1.2567 + } else { 1.2568 + if (aFrame->StyleVisibility()->mDirection != 1.2569 + NS_STYLE_DIRECTION_RTL) { 1.2570 + macRect.origin.x += 3.0; 1.2571 + } else { 1.2572 + macRect.origin.x -= 1.0; 1.2573 + } 1.2574 + } 1.2575 + } 1.2576 + const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame); 1.2577 + CUIDraw([NSWindow coreUIRenderer], macRect, cgContext, 1.2578 + (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: 1.2579 + @"kCUIWidgetOverlayScrollBar", @"widget", 1.2580 + @"regular", @"size", 1.2581 + (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey", 1.2582 + (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey", 1.2583 + [NSNumber numberWithBool:YES], @"noindicator", 1.2584 + [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey", 1.2585 + [NSNumber numberWithBool:YES], @"is.flipped", 1.2586 + nil], 1.2587 + nil); 1.2588 + } 1.2589 + break; 1.2590 + 1.2591 + case NS_THEME_TEXTFIELD_MULTILINE: { 1.2592 + // we have to draw this by hand because there is no HITheme value for it 1.2593 + CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); 1.2594 + 1.2595 + CGContextFillRect(cgContext, macRect); 1.2596 + 1.2597 + CGContextSetLineWidth(cgContext, 1.0); 1.2598 + CGContextSetShouldAntialias(cgContext, false); 1.2599 + 1.2600 + // stroke everything but the top line of the text area 1.2601 + CGContextSetRGBStrokeColor(cgContext, 0.6, 0.6, 0.6, 1.0); 1.2602 + CGContextBeginPath(cgContext); 1.2603 + CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1); 1.2604 + CGContextAddLineToPoint(cgContext, macRect.origin.x, macRect.origin.y + macRect.size.height); 1.2605 + CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + macRect.size.height); 1.2606 + CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1); 1.2607 + CGContextStrokePath(cgContext); 1.2608 + 1.2609 + // stroke the line across the top of the text area 1.2610 + CGContextSetRGBStrokeColor(cgContext, 0.4510, 0.4510, 0.4510, 1.0); 1.2611 + CGContextBeginPath(cgContext); 1.2612 + CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1); 1.2613 + CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1); 1.2614 + CGContextStrokePath(cgContext); 1.2615 + 1.2616 + // draw a focus ring 1.2617 + if (eventState.HasState(NS_EVENT_STATE_FOCUS)) { 1.2618 + // We need to bring the rectangle in by 1 pixel on each side. 1.2619 + CGRect cgr = CGRectMake(macRect.origin.x + 1, 1.2620 + macRect.origin.y + 1, 1.2621 + macRect.size.width - 2, 1.2622 + macRect.size.height - 2); 1.2623 + HIThemeDrawFocusRect(&cgr, true, cgContext, kHIThemeOrientationNormal); 1.2624 + } 1.2625 + } 1.2626 + break; 1.2627 + 1.2628 + case NS_THEME_LISTBOX: { 1.2629 + // We have to draw this by hand because kHIThemeFrameListBox drawing 1.2630 + // is buggy on 10.5, see bug 579259. 1.2631 + CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); 1.2632 + CGContextFillRect(cgContext, macRect); 1.2633 + 1.2634 + // #8E8E8E for the top border, #BEBEBE for the rest. 1.2635 + float x = macRect.origin.x, y = macRect.origin.y; 1.2636 + float w = macRect.size.width, h = macRect.size.height; 1.2637 + CGContextSetRGBFillColor(cgContext, 0.557, 0.557, 0.557, 1.0); 1.2638 + CGContextFillRect(cgContext, CGRectMake(x, y, w, 1)); 1.2639 + CGContextSetRGBFillColor(cgContext, 0.745, 0.745, 0.745, 1.0); 1.2640 + CGContextFillRect(cgContext, CGRectMake(x, y + 1, 1, h - 1)); 1.2641 + CGContextFillRect(cgContext, CGRectMake(x + w - 1, y + 1, 1, h - 1)); 1.2642 + CGContextFillRect(cgContext, CGRectMake(x + 1, y + h - 1, w - 2, 1)); 1.2643 + } 1.2644 + break; 1.2645 + 1.2646 + case NS_THEME_TAB: 1.2647 + DrawSegment(cgContext, macRect, eventState, aFrame, tabRenderSettings); 1.2648 + break; 1.2649 + 1.2650 + case NS_THEME_TAB_PANELS: 1.2651 + DrawTabPanel(cgContext, macRect, aFrame); 1.2652 + break; 1.2653 + 1.2654 + case NS_THEME_RESIZER: 1.2655 + DrawResizer(cgContext, macRect, aFrame); 1.2656 + break; 1.2657 + } 1.2658 + 1.2659 + nativeDrawing.EndNativeDrawing(); 1.2660 + 1.2661 + return NS_OK; 1.2662 + 1.2663 + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 1.2664 +} 1.2665 + 1.2666 +nsIntMargin 1.2667 +nsNativeThemeCocoa::RTLAwareMargin(const nsIntMargin& aMargin, nsIFrame* aFrame) 1.2668 +{ 1.2669 + if (IsFrameRTL(aFrame)) { 1.2670 + // Return a copy of aMargin w/ right & left reversed: 1.2671 + return nsIntMargin(aMargin.top, aMargin.left, 1.2672 + aMargin.bottom, aMargin.right); 1.2673 + } 1.2674 + 1.2675 + return aMargin; 1.2676 +} 1.2677 + 1.2678 +static const nsIntMargin kAquaDropdownBorder(1, 22, 2, 5); 1.2679 +static const nsIntMargin kAquaComboboxBorder(3, 20, 3, 4); 1.2680 +static const nsIntMargin kAquaSearchfieldBorder(3, 5, 2, 19); 1.2681 + 1.2682 +NS_IMETHODIMP 1.2683 +nsNativeThemeCocoa::GetWidgetBorder(nsDeviceContext* aContext, 1.2684 + nsIFrame* aFrame, 1.2685 + uint8_t aWidgetType, 1.2686 + nsIntMargin* aResult) 1.2687 +{ 1.2688 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 1.2689 + 1.2690 + aResult->SizeTo(0, 0, 0, 0); 1.2691 + 1.2692 + switch (aWidgetType) { 1.2693 + case NS_THEME_BUTTON: 1.2694 + { 1.2695 + if (IsButtonTypeMenu(aFrame)) { 1.2696 + *aResult = RTLAwareMargin(kAquaDropdownBorder, aFrame); 1.2697 + } else { 1.2698 + aResult->SizeTo(1, 7, 3, 7); 1.2699 + } 1.2700 + break; 1.2701 + } 1.2702 + 1.2703 + case NS_THEME_TOOLBAR_BUTTON: 1.2704 + { 1.2705 + aResult->SizeTo(1, 4, 1, 4); 1.2706 + break; 1.2707 + } 1.2708 + 1.2709 + case NS_THEME_CHECKBOX: 1.2710 + case NS_THEME_RADIO: 1.2711 + { 1.2712 + // nsFormControlFrame::GetIntrinsicWidth and nsFormControlFrame::GetIntrinsicHeight 1.2713 + // assume a border width of 2px. 1.2714 + aResult->SizeTo(2, 2, 2, 2); 1.2715 + break; 1.2716 + } 1.2717 + 1.2718 + case NS_THEME_DROPDOWN: 1.2719 + case NS_THEME_DROPDOWN_BUTTON: 1.2720 + *aResult = RTLAwareMargin(kAquaDropdownBorder, aFrame); 1.2721 + break; 1.2722 + 1.2723 + case NS_THEME_DROPDOWN_TEXTFIELD: 1.2724 + *aResult = RTLAwareMargin(kAquaComboboxBorder, aFrame); 1.2725 + break; 1.2726 + 1.2727 + case NS_THEME_NUMBER_INPUT: 1.2728 + case NS_THEME_TEXTFIELD: 1.2729 + { 1.2730 + SInt32 frameOutset = 0; 1.2731 + ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset); 1.2732 + 1.2733 + SInt32 textPadding = 0; 1.2734 + ::GetThemeMetric(kThemeMetricEditTextWhitespace, &textPadding); 1.2735 + 1.2736 + frameOutset += textPadding; 1.2737 + 1.2738 + aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset); 1.2739 + break; 1.2740 + } 1.2741 + 1.2742 + case NS_THEME_TEXTFIELD_MULTILINE: 1.2743 + aResult->SizeTo(1, 1, 1, 1); 1.2744 + break; 1.2745 + 1.2746 + case NS_THEME_SEARCHFIELD: 1.2747 + *aResult = RTLAwareMargin(kAquaSearchfieldBorder, aFrame); 1.2748 + break; 1.2749 + 1.2750 + case NS_THEME_LISTBOX: 1.2751 + { 1.2752 + SInt32 frameOutset = 0; 1.2753 + ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset); 1.2754 + aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset); 1.2755 + break; 1.2756 + } 1.2757 + 1.2758 + case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: 1.2759 + case NS_THEME_SCROLLBAR_TRACK_VERTICAL: 1.2760 + { 1.2761 + bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL); 1.2762 + 1.2763 + // On Lion and later, scrollbars have no arrows. 1.2764 + if (!nsCocoaFeatures::OnLionOrLater()) { 1.2765 + // There's only an endcap to worry about when both arrows are on the bottom 1.2766 + NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"]; 1.2767 + if (!buttonPlacement || [buttonPlacement isEqualToString:@"DoubleMax"]) { 1.2768 + nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); 1.2769 + if (!scrollbarFrame) return NS_ERROR_FAILURE; 1.2770 + bool isSmall = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL); 1.2771 + 1.2772 + // There isn't a metric for this, so just hardcode a best guess at the value. 1.2773 + // This value is even less exact due to the fact that the endcap is partially concave. 1.2774 + int32_t endcapSize = isSmall ? 5 : 6; 1.2775 + 1.2776 + if (isHorizontal) 1.2777 + aResult->SizeTo(0, 0, 0, endcapSize); 1.2778 + else 1.2779 + aResult->SizeTo(endcapSize, 0, 0, 0); 1.2780 + } 1.2781 + } 1.2782 + 1.2783 + if (nsLookAndFeel::UseOverlayScrollbars()) { 1.2784 + if (isHorizontal) { 1.2785 + aResult->SizeTo(2, 1, 1, 1); 1.2786 + } else { 1.2787 + aResult->SizeTo(1, 1, 1, 2); 1.2788 + } 1.2789 + } 1.2790 + 1.2791 + break; 1.2792 + } 1.2793 + 1.2794 + case NS_THEME_STATUSBAR: 1.2795 + aResult->SizeTo(1, 0, 0, 0); 1.2796 + break; 1.2797 + } 1.2798 + 1.2799 + if (IsHiDPIContext(aContext)) { 1.2800 + *aResult = *aResult + *aResult; // doubled 1.2801 + } 1.2802 + 1.2803 + return NS_OK; 1.2804 + 1.2805 + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 1.2806 +} 1.2807 + 1.2808 +// Return false here to indicate that CSS padding values should be used. There is 1.2809 +// no reason to make a distinction between padding and border values, just specify 1.2810 +// whatever values you want in GetWidgetBorder and only use this to return true 1.2811 +// if you want to override CSS padding values. 1.2812 +bool 1.2813 +nsNativeThemeCocoa::GetWidgetPadding(nsDeviceContext* aContext, 1.2814 + nsIFrame* aFrame, 1.2815 + uint8_t aWidgetType, 1.2816 + nsIntMargin* aResult) 1.2817 +{ 1.2818 + // We don't want CSS padding being used for certain widgets. 1.2819 + // See bug 381639 for an example of why. 1.2820 + switch (aWidgetType) { 1.2821 + case NS_THEME_BUTTON: 1.2822 + // Radios and checkboxes return a fixed size in GetMinimumWidgetSize 1.2823 + // and have a meaningful baseline, so they can't have 1.2824 + // author-specified padding. 1.2825 + case NS_THEME_CHECKBOX: 1.2826 + case NS_THEME_RADIO: 1.2827 + aResult->SizeTo(0, 0, 0, 0); 1.2828 + return true; 1.2829 + } 1.2830 + return false; 1.2831 +} 1.2832 + 1.2833 +bool 1.2834 +nsNativeThemeCocoa::GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFrame, 1.2835 + uint8_t aWidgetType, nsRect* aOverflowRect) 1.2836 +{ 1.2837 + int32_t p2a = aContext->AppUnitsPerDevPixel(); 1.2838 + switch (aWidgetType) { 1.2839 + case NS_THEME_BUTTON: 1.2840 + case NS_THEME_MOZ_MAC_HELP_BUTTON: 1.2841 + case NS_THEME_TOOLBAR_BUTTON: 1.2842 + case NS_THEME_NUMBER_INPUT: 1.2843 + case NS_THEME_TEXTFIELD: 1.2844 + case NS_THEME_TEXTFIELD_MULTILINE: 1.2845 + case NS_THEME_SEARCHFIELD: 1.2846 + case NS_THEME_LISTBOX: 1.2847 + case NS_THEME_DROPDOWN: 1.2848 + case NS_THEME_DROPDOWN_BUTTON: 1.2849 + case NS_THEME_DROPDOWN_TEXTFIELD: 1.2850 + case NS_THEME_CHECKBOX: 1.2851 + case NS_THEME_RADIO: 1.2852 + case NS_THEME_TAB: 1.2853 + { 1.2854 + // We assume that the above widgets can draw a focus ring that will be less than 1.2855 + // or equal to 4 pixels thick. 1.2856 + nsIntMargin extraSize = nsIntMargin(MAX_FOCUS_RING_WIDTH, 1.2857 + MAX_FOCUS_RING_WIDTH, 1.2858 + MAX_FOCUS_RING_WIDTH, 1.2859 + MAX_FOCUS_RING_WIDTH); 1.2860 + nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a), 1.2861 + NSIntPixelsToAppUnits(extraSize.right, p2a), 1.2862 + NSIntPixelsToAppUnits(extraSize.bottom, p2a), 1.2863 + NSIntPixelsToAppUnits(extraSize.left, p2a)); 1.2864 + aOverflowRect->Inflate(m); 1.2865 + return true; 1.2866 + } 1.2867 + case NS_THEME_PROGRESSBAR: 1.2868 + { 1.2869 + // Progress bars draw a 2 pixel white shadow under their progress indicators 1.2870 + nsMargin m(0, 0, NSIntPixelsToAppUnits(2, p2a), 0); 1.2871 + aOverflowRect->Inflate(m); 1.2872 + return true; 1.2873 + } 1.2874 + } 1.2875 + 1.2876 + return false; 1.2877 +} 1.2878 + 1.2879 +static const int32_t kRegularScrollbarThumbMinSize = 26; 1.2880 +static const int32_t kSmallScrollbarThumbMinSize = 26; 1.2881 + 1.2882 +NS_IMETHODIMP 1.2883 +nsNativeThemeCocoa::GetMinimumWidgetSize(nsRenderingContext* aContext, 1.2884 + nsIFrame* aFrame, 1.2885 + uint8_t aWidgetType, 1.2886 + nsIntSize* aResult, 1.2887 + bool* aIsOverridable) 1.2888 +{ 1.2889 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 1.2890 + 1.2891 + aResult->SizeTo(0,0); 1.2892 + *aIsOverridable = true; 1.2893 + 1.2894 + switch (aWidgetType) { 1.2895 + case NS_THEME_BUTTON: 1.2896 + { 1.2897 + aResult->SizeTo(pushButtonSettings.minimumSizes[miniControlSize].width, 1.2898 + pushButtonSettings.naturalSizes[miniControlSize].height); 1.2899 + break; 1.2900 + } 1.2901 + 1.2902 + case NS_THEME_MOZ_MAC_HELP_BUTTON: 1.2903 + { 1.2904 + aResult->SizeTo(kHelpButtonSize.width, kHelpButtonSize.height); 1.2905 + *aIsOverridable = false; 1.2906 + break; 1.2907 + } 1.2908 + 1.2909 + case NS_THEME_TOOLBAR_BUTTON: 1.2910 + { 1.2911 + aResult->SizeTo(0, toolbarButtonHeights[miniControlSize]); 1.2912 + break; 1.2913 + } 1.2914 + 1.2915 + case NS_THEME_SPINNER: 1.2916 + case NS_THEME_SPINNER_UP_BUTTON: 1.2917 + case NS_THEME_SPINNER_DOWN_BUTTON: 1.2918 + { 1.2919 + SInt32 buttonHeight = 0, buttonWidth = 0; 1.2920 + if (aFrame->GetContent()->IsXUL()) { 1.2921 + ::GetThemeMetric(kThemeMetricLittleArrowsWidth, &buttonWidth); 1.2922 + ::GetThemeMetric(kThemeMetricLittleArrowsHeight, &buttonHeight); 1.2923 + } else { 1.2924 + NSSize size = 1.2925 + spinnerSettings.minimumSizes[EnumSizeForCocoaSize(NSMiniControlSize)]; 1.2926 + buttonWidth = size.width; 1.2927 + buttonHeight = size.height; 1.2928 + if (aWidgetType != NS_THEME_SPINNER) { 1.2929 + // the buttons are half the height of the spinner 1.2930 + buttonHeight /= 2; 1.2931 + } 1.2932 + } 1.2933 + aResult->SizeTo(buttonWidth, buttonHeight); 1.2934 + *aIsOverridable = true; 1.2935 + break; 1.2936 + } 1.2937 + 1.2938 + case NS_THEME_DROPDOWN: 1.2939 + case NS_THEME_DROPDOWN_BUTTON: 1.2940 + { 1.2941 + SInt32 popupHeight = 0; 1.2942 + ::GetThemeMetric(kThemeMetricPopupButtonHeight, &popupHeight); 1.2943 + aResult->SizeTo(0, popupHeight); 1.2944 + break; 1.2945 + } 1.2946 + 1.2947 + case NS_THEME_NUMBER_INPUT: 1.2948 + case NS_THEME_TEXTFIELD: 1.2949 + case NS_THEME_TEXTFIELD_MULTILINE: 1.2950 + case NS_THEME_SEARCHFIELD: 1.2951 + { 1.2952 + // at minimum, we should be tall enough for 9pt text. 1.2953 + // I'm using hardcoded values here because the appearance manager 1.2954 + // values for the frame size are incorrect. 1.2955 + aResult->SizeTo(0, (2 + 2) /* top */ + 9 + (1 + 1) /* bottom */); 1.2956 + break; 1.2957 + } 1.2958 + 1.2959 + case NS_THEME_WINDOW_BUTTON_BOX: { 1.2960 + NSSize size = WindowButtonsSize(aFrame); 1.2961 + aResult->SizeTo(size.width, size.height); 1.2962 + *aIsOverridable = false; 1.2963 + break; 1.2964 + } 1.2965 + 1.2966 + case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON: { 1.2967 + if ([NativeWindowForFrame(aFrame) respondsToSelector:@selector(toggleFullScreen:)]) { 1.2968 + // This value is hardcoded because it's needed before we can measure the 1.2969 + // position and size of the fullscreen button. 1.2970 + aResult->SizeTo(16, 17); 1.2971 + } 1.2972 + *aIsOverridable = false; 1.2973 + break; 1.2974 + } 1.2975 + 1.2976 + case NS_THEME_PROGRESSBAR: 1.2977 + { 1.2978 + SInt32 barHeight = 0; 1.2979 + ::GetThemeMetric(kThemeMetricNormalProgressBarThickness, &barHeight); 1.2980 + aResult->SizeTo(0, barHeight); 1.2981 + break; 1.2982 + } 1.2983 + 1.2984 + case NS_THEME_TREEVIEW_TWISTY: 1.2985 + case NS_THEME_TREEVIEW_TWISTY_OPEN: 1.2986 + { 1.2987 + SInt32 twistyHeight = 0, twistyWidth = 0; 1.2988 + ::GetThemeMetric(kThemeMetricDisclosureButtonWidth, &twistyWidth); 1.2989 + ::GetThemeMetric(kThemeMetricDisclosureButtonHeight, &twistyHeight); 1.2990 + aResult->SizeTo(twistyWidth, twistyHeight); 1.2991 + *aIsOverridable = false; 1.2992 + break; 1.2993 + } 1.2994 + 1.2995 + case NS_THEME_TREEVIEW_HEADER: 1.2996 + case NS_THEME_TREEVIEW_HEADER_CELL: 1.2997 + { 1.2998 + SInt32 headerHeight = 0; 1.2999 + ::GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight); 1.3000 + aResult->SizeTo(0, headerHeight - 1); // We don't need the top border. 1.3001 + break; 1.3002 + } 1.3003 + 1.3004 + case NS_THEME_TAB: 1.3005 + { 1.3006 + aResult->SizeTo(0, tabHeights[miniControlSize]); 1.3007 + break; 1.3008 + } 1.3009 + 1.3010 + case NS_THEME_RANGE: 1.3011 + { 1.3012 + // The Mac Appearance Manager API (the old API we're currently using) 1.3013 + // doesn't define constants to obtain a minimum size for sliders. We use 1.3014 + // the "thickness" of a slider that has default dimensions for both the 1.3015 + // minimum width and height to get something sane and so that paint 1.3016 + // invalidation works. 1.3017 + SInt32 size = 0; 1.3018 + if (IsRangeHorizontal(aFrame)) { 1.3019 + ::GetThemeMetric(kThemeMetricHSliderHeight, &size); 1.3020 + } else { 1.3021 + ::GetThemeMetric(kThemeMetricVSliderWidth, &size); 1.3022 + } 1.3023 + aResult->SizeTo(size, size); 1.3024 + *aIsOverridable = true; 1.3025 + break; 1.3026 + } 1.3027 + 1.3028 + case NS_THEME_RANGE_THUMB: 1.3029 + { 1.3030 + SInt32 width = 0; 1.3031 + SInt32 height = 0; 1.3032 + ::GetThemeMetric(kThemeMetricSliderMinThumbWidth, &width); 1.3033 + ::GetThemeMetric(kThemeMetricSliderMinThumbHeight, &height); 1.3034 + aResult->SizeTo(width, height); 1.3035 + *aIsOverridable = false; 1.3036 + break; 1.3037 + } 1.3038 + 1.3039 + case NS_THEME_SCALE_HORIZONTAL: 1.3040 + { 1.3041 + SInt32 scaleHeight = 0; 1.3042 + ::GetThemeMetric(kThemeMetricHSliderHeight, &scaleHeight); 1.3043 + aResult->SizeTo(scaleHeight, scaleHeight); 1.3044 + *aIsOverridable = false; 1.3045 + break; 1.3046 + } 1.3047 + 1.3048 + case NS_THEME_SCALE_VERTICAL: 1.3049 + { 1.3050 + SInt32 scaleWidth = 0; 1.3051 + ::GetThemeMetric(kThemeMetricVSliderWidth, &scaleWidth); 1.3052 + aResult->SizeTo(scaleWidth, scaleWidth); 1.3053 + *aIsOverridable = false; 1.3054 + break; 1.3055 + } 1.3056 + 1.3057 + case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: 1.3058 + case NS_THEME_SCROLLBAR_THUMB_VERTICAL: 1.3059 + { 1.3060 + // Find our parent scrollbar frame in order to find out whether we're in 1.3061 + // a small or a large scrollbar. 1.3062 + nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); 1.3063 + if (!scrollbarFrame) 1.3064 + return NS_ERROR_FAILURE; 1.3065 + 1.3066 + bool isSmall = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL); 1.3067 + bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL); 1.3068 + int32_t& minSize = isHorizontal ? aResult->width : aResult->height; 1.3069 + minSize = isSmall ? kSmallScrollbarThumbMinSize : kRegularScrollbarThumbMinSize; 1.3070 + break; 1.3071 + } 1.3072 + 1.3073 + case NS_THEME_SCROLLBAR: 1.3074 + case NS_THEME_SCROLLBAR_SMALL: 1.3075 + case NS_THEME_SCROLLBAR_TRACK_VERTICAL: 1.3076 + case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: 1.3077 + { 1.3078 + *aIsOverridable = false; 1.3079 + 1.3080 + if (nsLookAndFeel::UseOverlayScrollbars()) { 1.3081 + nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); 1.3082 + if (scrollbarFrame && 1.3083 + scrollbarFrame->StyleDisplay()->mAppearance == 1.3084 + NS_THEME_SCROLLBAR_SMALL) { 1.3085 + aResult->SizeTo(14, 14); 1.3086 + } 1.3087 + else { 1.3088 + aResult->SizeTo(16, 16); 1.3089 + } 1.3090 + break; 1.3091 + } 1.3092 + 1.3093 + // yeah, i know i'm cheating a little here, but i figure that it 1.3094 + // really doesn't matter if the scrollbar is vertical or horizontal 1.3095 + // and the width metric is a really good metric for every piece 1.3096 + // of the scrollbar. 1.3097 + 1.3098 + nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); 1.3099 + if (!scrollbarFrame) return NS_ERROR_FAILURE; 1.3100 + 1.3101 + int32_t themeMetric = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ? 1.3102 + kThemeMetricSmallScrollBarWidth : 1.3103 + kThemeMetricScrollBarWidth; 1.3104 + SInt32 scrollbarWidth = 0; 1.3105 + ::GetThemeMetric(themeMetric, &scrollbarWidth); 1.3106 + aResult->SizeTo(scrollbarWidth, scrollbarWidth); 1.3107 + break; 1.3108 + } 1.3109 + 1.3110 + case NS_THEME_SCROLLBAR_NON_DISAPPEARING: 1.3111 + { 1.3112 + int32_t themeMetric = kThemeMetricScrollBarWidth; 1.3113 + 1.3114 + if (aFrame) { 1.3115 + nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); 1.3116 + if (scrollbarFrame && 1.3117 + scrollbarFrame->StyleDisplay()->mAppearance == 1.3118 + NS_THEME_SCROLLBAR_SMALL) { 1.3119 + // XXX We're interested in the width of non-disappearing scrollbars 1.3120 + // to leave enough space for a dropmarker in non-native styled 1.3121 + // comboboxes (bug 869314). It isn't clear to me if comboboxes can 1.3122 + // ever have small scrollbars. 1.3123 + themeMetric = kThemeMetricSmallScrollBarWidth; 1.3124 + } 1.3125 + } 1.3126 + 1.3127 + SInt32 scrollbarWidth = 0; 1.3128 + ::GetThemeMetric(themeMetric, &scrollbarWidth); 1.3129 + aResult->SizeTo(scrollbarWidth, scrollbarWidth); 1.3130 + break; 1.3131 + } 1.3132 + 1.3133 + case NS_THEME_SCROLLBAR_BUTTON_UP: 1.3134 + case NS_THEME_SCROLLBAR_BUTTON_DOWN: 1.3135 + case NS_THEME_SCROLLBAR_BUTTON_LEFT: 1.3136 + case NS_THEME_SCROLLBAR_BUTTON_RIGHT: 1.3137 + { 1.3138 + nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); 1.3139 + if (!scrollbarFrame) return NS_ERROR_FAILURE; 1.3140 + 1.3141 + // Since there is no NS_THEME_SCROLLBAR_BUTTON_UP_SMALL we need to ask the parent what appearance style it has. 1.3142 + int32_t themeMetric = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ? 1.3143 + kThemeMetricSmallScrollBarWidth : 1.3144 + kThemeMetricScrollBarWidth; 1.3145 + SInt32 scrollbarWidth = 0; 1.3146 + ::GetThemeMetric(themeMetric, &scrollbarWidth); 1.3147 + 1.3148 + // It seems that for both sizes of scrollbar, the buttons are one pixel "longer". 1.3149 + if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT || aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) 1.3150 + aResult->SizeTo(scrollbarWidth+1, scrollbarWidth); 1.3151 + else 1.3152 + aResult->SizeTo(scrollbarWidth, scrollbarWidth+1); 1.3153 + 1.3154 + *aIsOverridable = false; 1.3155 + break; 1.3156 + } 1.3157 + case NS_THEME_RESIZER: 1.3158 + { 1.3159 + HIThemeGrowBoxDrawInfo drawInfo; 1.3160 + drawInfo.version = 0; 1.3161 + drawInfo.state = kThemeStateActive; 1.3162 + drawInfo.kind = kHIThemeGrowBoxKindNormal; 1.3163 + drawInfo.direction = kThemeGrowRight | kThemeGrowDown; 1.3164 + drawInfo.size = kHIThemeGrowBoxSizeNormal; 1.3165 + HIPoint pnt = { 0, 0 }; 1.3166 + HIRect bounds; 1.3167 + HIThemeGetGrowBoxBounds(&pnt, &drawInfo, &bounds); 1.3168 + aResult->SizeTo(bounds.size.width, bounds.size.height); 1.3169 + *aIsOverridable = false; 1.3170 + } 1.3171 + } 1.3172 + 1.3173 + if (IsHiDPIContext(aContext->DeviceContext())) { 1.3174 + *aResult = *aResult * 2; 1.3175 + } 1.3176 + 1.3177 + return NS_OK; 1.3178 + 1.3179 + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 1.3180 +} 1.3181 + 1.3182 +NS_IMETHODIMP 1.3183 +nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 1.3184 + nsIAtom* aAttribute, bool* aShouldRepaint) 1.3185 +{ 1.3186 + // Some widget types just never change state. 1.3187 + switch (aWidgetType) { 1.3188 + case NS_THEME_WINDOW_TITLEBAR: 1.3189 + case NS_THEME_TOOLBOX: 1.3190 + case NS_THEME_TOOLBAR: 1.3191 + case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: 1.3192 + case NS_THEME_STATUSBAR: 1.3193 + case NS_THEME_STATUSBAR_PANEL: 1.3194 + case NS_THEME_STATUSBAR_RESIZER_PANEL: 1.3195 + case NS_THEME_TOOLTIP: 1.3196 + case NS_THEME_TAB_PANELS: 1.3197 + case NS_THEME_TAB_PANEL: 1.3198 + case NS_THEME_DIALOG: 1.3199 + case NS_THEME_MENUPOPUP: 1.3200 + case NS_THEME_GROUPBOX: 1.3201 + case NS_THEME_PROGRESSBAR_CHUNK: 1.3202 + case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: 1.3203 + case NS_THEME_PROGRESSBAR: 1.3204 + case NS_THEME_PROGRESSBAR_VERTICAL: 1.3205 + case NS_THEME_METERBAR: 1.3206 + case NS_THEME_METERBAR_CHUNK: 1.3207 + *aShouldRepaint = false; 1.3208 + return NS_OK; 1.3209 + } 1.3210 + 1.3211 + // XXXdwh Not sure what can really be done here. Can at least guess for 1.3212 + // specific widgets that they're highly unlikely to have certain states. 1.3213 + // For example, a toolbar doesn't care about any states. 1.3214 + if (!aAttribute) { 1.3215 + // Hover/focus/active changed. Always repaint. 1.3216 + *aShouldRepaint = true; 1.3217 + } else { 1.3218 + // Check the attribute to see if it's relevant. 1.3219 + // disabled, checked, dlgtype, default, etc. 1.3220 + *aShouldRepaint = false; 1.3221 + if (aAttribute == nsGkAtoms::disabled || 1.3222 + aAttribute == nsGkAtoms::checked || 1.3223 + aAttribute == nsGkAtoms::selected || 1.3224 + aAttribute == nsGkAtoms::menuactive || 1.3225 + aAttribute == nsGkAtoms::sortDirection || 1.3226 + aAttribute == nsGkAtoms::focused || 1.3227 + aAttribute == nsGkAtoms::_default || 1.3228 + aAttribute == nsGkAtoms::open || 1.3229 + aAttribute == nsGkAtoms::hover) 1.3230 + *aShouldRepaint = true; 1.3231 + } 1.3232 + 1.3233 + return NS_OK; 1.3234 +} 1.3235 + 1.3236 +NS_IMETHODIMP 1.3237 +nsNativeThemeCocoa::ThemeChanged() 1.3238 +{ 1.3239 + // This is unimplemented because we don't care if gecko changes its theme 1.3240 + // and Mac OS X doesn't have themes. 1.3241 + return NS_OK; 1.3242 +} 1.3243 + 1.3244 +bool 1.3245 +nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame, 1.3246 + uint8_t aWidgetType) 1.3247 +{ 1.3248 + // We don't have CSS set up to render non-native scrollbars on Mac OS X so we 1.3249 + // render natively even if native theme support is disabled. 1.3250 + if (aWidgetType != NS_THEME_SCROLLBAR && 1.3251 + aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled()) 1.3252 + return false; 1.3253 + 1.3254 + // if this is a dropdown button in a combobox the answer is always no 1.3255 + if (aWidgetType == NS_THEME_DROPDOWN_BUTTON) { 1.3256 + nsIFrame* parentFrame = aFrame->GetParent(); 1.3257 + if (parentFrame && (parentFrame->GetType() == nsGkAtoms::comboboxControlFrame)) 1.3258 + return false; 1.3259 + } 1.3260 + 1.3261 + switch (aWidgetType) { 1.3262 + case NS_THEME_LISTBOX: 1.3263 + 1.3264 + case NS_THEME_DIALOG: 1.3265 + case NS_THEME_WINDOW: 1.3266 + case NS_THEME_WINDOW_BUTTON_BOX: 1.3267 + case NS_THEME_WINDOW_TITLEBAR: 1.3268 + case NS_THEME_MENUPOPUP: 1.3269 + case NS_THEME_MENUITEM: 1.3270 + case NS_THEME_MENUSEPARATOR: 1.3271 + case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON: 1.3272 + case NS_THEME_TOOLTIP: 1.3273 + 1.3274 + case NS_THEME_CHECKBOX: 1.3275 + case NS_THEME_CHECKBOX_CONTAINER: 1.3276 + case NS_THEME_RADIO: 1.3277 + case NS_THEME_RADIO_CONTAINER: 1.3278 + case NS_THEME_GROUPBOX: 1.3279 + case NS_THEME_MOZ_MAC_HELP_BUTTON: 1.3280 + case NS_THEME_BUTTON: 1.3281 + case NS_THEME_BUTTON_BEVEL: 1.3282 + case NS_THEME_TOOLBAR_BUTTON: 1.3283 + case NS_THEME_SPINNER: 1.3284 + case NS_THEME_SPINNER_UP_BUTTON: 1.3285 + case NS_THEME_SPINNER_DOWN_BUTTON: 1.3286 + case NS_THEME_TOOLBAR: 1.3287 + case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: 1.3288 + case NS_THEME_STATUSBAR: 1.3289 + case NS_THEME_NUMBER_INPUT: 1.3290 + case NS_THEME_TEXTFIELD: 1.3291 + case NS_THEME_TEXTFIELD_MULTILINE: 1.3292 + case NS_THEME_SEARCHFIELD: 1.3293 + //case NS_THEME_TOOLBOX: 1.3294 + //case NS_THEME_TOOLBAR_BUTTON: 1.3295 + case NS_THEME_PROGRESSBAR: 1.3296 + case NS_THEME_PROGRESSBAR_VERTICAL: 1.3297 + case NS_THEME_PROGRESSBAR_CHUNK: 1.3298 + case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: 1.3299 + case NS_THEME_METERBAR: 1.3300 + case NS_THEME_METERBAR_CHUNK: 1.3301 + case NS_THEME_TOOLBAR_SEPARATOR: 1.3302 + 1.3303 + case NS_THEME_TAB_PANELS: 1.3304 + case NS_THEME_TAB: 1.3305 + 1.3306 + case NS_THEME_TREEVIEW_TWISTY: 1.3307 + case NS_THEME_TREEVIEW_TWISTY_OPEN: 1.3308 + case NS_THEME_TREEVIEW: 1.3309 + case NS_THEME_TREEVIEW_HEADER: 1.3310 + case NS_THEME_TREEVIEW_HEADER_CELL: 1.3311 + case NS_THEME_TREEVIEW_HEADER_SORTARROW: 1.3312 + case NS_THEME_TREEVIEW_TREEITEM: 1.3313 + case NS_THEME_TREEVIEW_LINE: 1.3314 + 1.3315 + case NS_THEME_RANGE: 1.3316 + 1.3317 + case NS_THEME_SCALE_HORIZONTAL: 1.3318 + case NS_THEME_SCALE_THUMB_HORIZONTAL: 1.3319 + case NS_THEME_SCALE_VERTICAL: 1.3320 + case NS_THEME_SCALE_THUMB_VERTICAL: 1.3321 + 1.3322 + case NS_THEME_SCROLLBAR: 1.3323 + case NS_THEME_SCROLLBAR_SMALL: 1.3324 + case NS_THEME_SCROLLBAR_BUTTON_UP: 1.3325 + case NS_THEME_SCROLLBAR_BUTTON_DOWN: 1.3326 + case NS_THEME_SCROLLBAR_BUTTON_LEFT: 1.3327 + case NS_THEME_SCROLLBAR_BUTTON_RIGHT: 1.3328 + case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: 1.3329 + case NS_THEME_SCROLLBAR_THUMB_VERTICAL: 1.3330 + case NS_THEME_SCROLLBAR_TRACK_VERTICAL: 1.3331 + case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: 1.3332 + case NS_THEME_SCROLLBAR_NON_DISAPPEARING: 1.3333 + 1.3334 + case NS_THEME_DROPDOWN: 1.3335 + case NS_THEME_DROPDOWN_BUTTON: 1.3336 + case NS_THEME_DROPDOWN_TEXT: 1.3337 + case NS_THEME_DROPDOWN_TEXTFIELD: 1.3338 + return !IsWidgetStyled(aPresContext, aFrame, aWidgetType); 1.3339 + break; 1.3340 + 1.3341 + case NS_THEME_RESIZER: 1.3342 + { 1.3343 + nsIFrame* parentFrame = aFrame->GetParent(); 1.3344 + if (!parentFrame || parentFrame->GetType() != nsGkAtoms::scrollFrame) 1.3345 + return true; 1.3346 + 1.3347 + // Note that IsWidgetStyled is not called for resizers on Mac. This is 1.3348 + // because for scrollable containers, the native resizer looks better 1.3349 + // when (non-overlay) scrollbars are present even when the style is 1.3350 + // overriden, and the custom transparent resizer looks better when 1.3351 + // scrollbars are not present. 1.3352 + nsIScrollableFrame* scrollFrame = do_QueryFrame(parentFrame); 1.3353 + return (!nsLookAndFeel::UseOverlayScrollbars() && 1.3354 + scrollFrame && scrollFrame->GetScrollbarVisibility()); 1.3355 + break; 1.3356 + } 1.3357 + } 1.3358 + 1.3359 + return false; 1.3360 +} 1.3361 + 1.3362 +bool 1.3363 +nsNativeThemeCocoa::WidgetIsContainer(uint8_t aWidgetType) 1.3364 +{ 1.3365 + // flesh this out at some point 1.3366 + switch (aWidgetType) { 1.3367 + case NS_THEME_DROPDOWN_BUTTON: 1.3368 + case NS_THEME_RADIO: 1.3369 + case NS_THEME_CHECKBOX: 1.3370 + case NS_THEME_PROGRESSBAR: 1.3371 + case NS_THEME_METERBAR: 1.3372 + case NS_THEME_RANGE: 1.3373 + case NS_THEME_MOZ_MAC_HELP_BUTTON: 1.3374 + return false; 1.3375 + break; 1.3376 + } 1.3377 + return true; 1.3378 +} 1.3379 + 1.3380 +bool 1.3381 +nsNativeThemeCocoa::ThemeDrawsFocusForWidget(uint8_t aWidgetType) 1.3382 +{ 1.3383 + if (aWidgetType == NS_THEME_DROPDOWN || 1.3384 + aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD || 1.3385 + aWidgetType == NS_THEME_BUTTON || 1.3386 + aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON || 1.3387 + aWidgetType == NS_THEME_RADIO || 1.3388 + aWidgetType == NS_THEME_RANGE || 1.3389 + aWidgetType == NS_THEME_CHECKBOX) 1.3390 + return true; 1.3391 + 1.3392 + return false; 1.3393 +} 1.3394 + 1.3395 +bool 1.3396 +nsNativeThemeCocoa::ThemeNeedsComboboxDropmarker() 1.3397 +{ 1.3398 + return false; 1.3399 +} 1.3400 + 1.3401 +bool 1.3402 +nsNativeThemeCocoa::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType) 1.3403 +{ 1.3404 + switch (aWidgetType) { 1.3405 + case NS_THEME_DIALOG: 1.3406 + case NS_THEME_GROUPBOX: 1.3407 + case NS_THEME_TAB_PANELS: 1.3408 + case NS_THEME_MENUPOPUP: 1.3409 + case NS_THEME_MENUITEM: 1.3410 + case NS_THEME_MENUSEPARATOR: 1.3411 + case NS_THEME_TOOLTIP: 1.3412 + case NS_THEME_SPINNER: 1.3413 + case NS_THEME_SPINNER_UP_BUTTON: 1.3414 + case NS_THEME_SPINNER_DOWN_BUTTON: 1.3415 + case NS_THEME_TOOLBAR_SEPARATOR: 1.3416 + case NS_THEME_TOOLBOX: 1.3417 + case NS_THEME_NUMBER_INPUT: 1.3418 + case NS_THEME_TEXTFIELD: 1.3419 + case NS_THEME_TREEVIEW: 1.3420 + case NS_THEME_TREEVIEW_LINE: 1.3421 + case NS_THEME_TEXTFIELD_MULTILINE: 1.3422 + case NS_THEME_LISTBOX: 1.3423 + case NS_THEME_RESIZER: 1.3424 + return false; 1.3425 + default: 1.3426 + return true; 1.3427 + } 1.3428 +} 1.3429 + 1.3430 +nsITheme::Transparency 1.3431 +nsNativeThemeCocoa::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType) 1.3432 +{ 1.3433 + switch (aWidgetType) { 1.3434 + case NS_THEME_MENUPOPUP: 1.3435 + case NS_THEME_TOOLTIP: 1.3436 + return eTransparent; 1.3437 + 1.3438 + case NS_THEME_SCROLLBAR_SMALL: 1.3439 + case NS_THEME_SCROLLBAR: 1.3440 + return nsLookAndFeel::UseOverlayScrollbars() ? eTransparent : eOpaque; 1.3441 + 1.3442 + case NS_THEME_STATUSBAR: 1.3443 + // Knowing that scrollbars and statusbars are opaque improves 1.3444 + // performance, because we create layers for them. 1.3445 + return eOpaque; 1.3446 + 1.3447 + case NS_THEME_TOOLBAR: 1.3448 + case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: 1.3449 + return eOpaque; 1.3450 + 1.3451 + default: 1.3452 + return eUnknownTransparency; 1.3453 + } 1.3454 +}