--- a/widget/src/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/src/cocoa/nsNativeThemeCocoa.mm
@@ -76,16 +76,40 @@ extern "C" {
// Workaround for NSCell control tint drawing
// Without this workaround, NSCells are always drawn with the clear control tint
// as long as they're not attached to an NSControl which is a subview of an active window.
// XXXmstange Why doesn't Webkit need this?
@implementation NSCell (ControlTintWorkaround)
- (int)_realControlTint { return [self controlTint]; }
@end
+// On 10.4, NSSearchFieldCells can't draw focus rings.
+@interface SearchFieldCellWithFocusRing : NSSearchFieldCell {} @end
+
+@implementation SearchFieldCellWithFocusRing
+
+- (void) drawWithFrame:(NSRect)rect inView:(NSView*)controlView
+{
+ [super drawWithFrame:rect inView:controlView];
+ if (!nsToolkit::OnLeopardOrLater() && [self showsFirstResponder]) {
+ NSSetFocusRingStyle(NSFocusRingOnly);
+ NSBezierPath* path = [NSBezierPath bezierPath];
+ float radius = NSHeight(rect) / 2;
+ rect = NSInsetRect(rect, radius, radius);
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle: 0.0 endAngle: 90.0];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle: 90.0 endAngle:180.0];
+ [path closePath];
+ [path fill];
+ }
+}
+
+@end
+
// Copied from nsLookAndFeel.h
// Apple hasn't defined a constant for scollbars with two arrows on each end, so we'll use this one.
static const int kThemeScrollBarArrowsBoth = 2;
#define HITHEME_ORIENTATION kHIThemeOrientationNormal
#define MAX_FOCUS_RING_WIDTH 4
// These enums are for indexing into the margin array.
@@ -124,16 +148,28 @@ static void InflateControlRect(NSRect* r
int controlSize = EnumSizeForCocoaSize(cocoaControlSize);
const float* buttonMargins = marginSet[osIndex][controlSize];
rect->origin.x -= buttonMargins[leftMargin];
rect->origin.y -= buttonMargins[bottomMargin];
rect->size.width += buttonMargins[leftMargin] + buttonMargins[rightMargin];
rect->size.height += buttonMargins[bottomMargin] + buttonMargins[topMargin];
}
+static NSView* NativeViewForFrame(nsIFrame* aFrame)
+{
+ if (!aFrame)
+ return nil;
+
+ nsIWidget* widget = aFrame->GetWindow();
+ if (!widget)
+ return nil;
+
+ return (NSView*)widget->GetNativeData(NS_NATIVE_WIDGET);
+}
+
static NSWindow* NativeWindowForFrame(nsIFrame* aFrame, int* aLevelsUp = NULL,
nsIWidget** aTopLevelWidget = NULL)
{
if (!aFrame)
return nil;
nsIWidget* widget = aFrame->GetWindow();
if (!widget)
@@ -176,26 +212,33 @@ nsNativeThemeCocoa::nsNativeThemeCocoa()
[mPushButtonCell setHighlightsBy:NSPushInCellMask];
mRadioButtonCell = [[NSButtonCell alloc] initTextCell:nil];
[mRadioButtonCell setButtonType:NSRadioButton];
mCheckboxCell = [[NSButtonCell alloc] initTextCell:nil];
[mCheckboxCell setButtonType:NSSwitchButton];
+ mSearchFieldCell = [[SearchFieldCellWithFocusRing alloc] initTextCell:@""];
+ [mSearchFieldCell setBezelStyle:NSTextFieldRoundedBezel];
+ [mSearchFieldCell setBezeled:YES];
+ [mSearchFieldCell setEditable:YES];
+ [mSearchFieldCell setFocusRingType:NSFocusRingTypeExterior];
+
NS_OBJC_END_TRY_ABORT_BLOCK;
}
nsNativeThemeCocoa::~nsNativeThemeCocoa()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
[mPushButtonCell release];
[mRadioButtonCell release];
[mCheckboxCell release];
+ [mSearchFieldCell release];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// Limit on the area of the target rect (in pixels^2) in
// DrawCellWithScaling(), DrawButton() and DrawScrollbar(), above which we
// don't draw the object into a bitmap buffer. This is to avoid crashes in
// [NSGraphicsContext graphicsContextWithGraphicsPort:flipped:] and
@@ -222,29 +265,28 @@ nsNativeThemeCocoa::~nsNativeThemeCocoa(
* a scale will be applied to the context so that the minimum is used
* for drawing. If a control has no minimum dimensions in either/both
* axes, pass 0.0f.
* marginSet - an array of margins; a multidimensional array of [2][3][4],
* with the first dimension being the OS version (Tiger or Leopard),
* the second being the control size (mini, small, regular), and the third
* being the 4 margin values (left, top, right, bottom).
* flip - Whether to draw the control mirrored
- * needsBuffer - Set this to false if no buffer should be used. Bypassing the
- * buffer is faster but it can lead to painting problems with accumulating
- * focus rings.
+ * view - The NSView that we're drawing into. As far as I can tell, it doesn't
+ * matter if this is really the right view; it just has to return YES when
+ * asked for isFlipped. Otherwise we'll get drawing bugs on 10.4.
*/
static void DrawCellWithScaling(NSCell *cell,
CGContextRef cgContext,
const HIRect& destRect,
NSControlSize controlSize,
NSSize naturalSize,
NSSize minimumSize,
const float marginSet[][3][4],
- PRBool flip,
- PRBool needsBuffer = PR_TRUE)
+ NSView* view)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
NSRect drawRect = NSMakeRect(destRect.origin.x, destRect.origin.y, destRect.size.width, destRect.size.height);
if (naturalSize.width != 0.0f)
drawRect.size.width = naturalSize.width;
if (naturalSize.height != 0.0f)
@@ -259,72 +301,60 @@ static void DrawCellWithScaling(NSCell *
// Honor minimum sizes.
if (drawRect.size.width < minimumSize.width)
drawRect.size.width = minimumSize.width;
if (drawRect.size.height < minimumSize.height)
drawRect.size.height = minimumSize.height;
[NSGraphicsContext saveGraphicsState];
- if (flip) {
- // This flips the image in place and is necessary to work around a bug in the way
- // NSButtonCell draws buttons.
- CGContextScaleCTM(cgContext, 1.0f, -1.0f);
- CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height));
- }
-
- // Fall back to no bitmap buffer (and no scaling) if the area of our cell
- // (in pixels^2) is too large.
- BOOL noBufferOverride = (drawRect.size.width * drawRect.size.height > BITMAP_MAX_AREA);
-
- if ((!needsBuffer && drawRect.size.width == destRect.size.width &&
- drawRect.size.height == destRect.size.height) || noBufferOverride) {
+ // Only skip the buffer if the area of our cell (in pixels^2) is too large.
+ if (drawRect.size.width * drawRect.size.height > BITMAP_MAX_AREA) {
// Inflate the rect Gecko gave us by the margin for the control.
InflateControlRect(&drawRect, controlSize, marginSet);
NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
-
- // Set up the graphics context we've been asked to draw to.
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]];
- // [NSView focusView] may return nil here, but
- // [NSCell drawWithFrame:inView:] can deal with that.
- [cell drawWithFrame:drawRect inView:[NSView focusView]];
+ [cell drawWithFrame:drawRect inView:view];
[NSGraphicsContext setCurrentContext:savedContext];
}
else {
float w = ceil(drawRect.size.width);
float h = ceil(drawRect.size.height);
-
- NSRect tmpRect = NSMakeRect(0.0f, 0.0f, w, h);
+ NSRect tmpRect = NSMakeRect(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, w, h);
// inflate to figure out the frame we need to tell NSCell to draw in, to get something that's 0,0,w,h
InflateControlRect(&tmpRect, controlSize, marginSet);
// and then, expand by MAX_FOCUS_RING_WIDTH size to make sure we can capture any focus ring
w += MAX_FOCUS_RING_WIDTH * 2.0;
h += MAX_FOCUS_RING_WIDTH * 2.0;
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(NULL,
(int) w, (int) h,
8, (int) w * 4,
rgb, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(rgb);
- CGContextTranslateCTM(ctx, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH);
+ // We need to flip the image twice in order to avoid drawing bugs on 10.4, see bug 465069.
+ // This is the first flip transform, applied to cgContext.
+ CGContextScaleCTM(cgContext, 1.0f, -1.0f);
+ CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height));
NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
-
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]];
- // [NSView focusView] may return nil here, but
- // [NSCell drawWithFrame:inView:] can deal with that.
- [cell drawWithFrame:tmpRect inView:[NSView focusView]];
+ // This is the second flip transform, applied to ctx.
+ CGContextScaleCTM(ctx, 1.0f, -1.0f);
+ CGContextTranslateCTM(ctx, 0.0f, -(2.0 * tmpRect.origin.y + tmpRect.size.height));
+
+ [cell drawWithFrame:tmpRect inView:view];
[NSGraphicsContext setCurrentContext:savedContext];
CGImageRef img = CGBitmapContextCreateImage(ctx);
// Drop the image into the original destination rectangle, scaling to fit
// Only scale MAX_FOCUS_RING_WIDTH by xscale/yscale when the resulting rect
// doesn't extend beyond the overflow rect
@@ -378,19 +408,18 @@ struct CellRenderSettings {
* it snaps to the next smaller control size without scaling because unscaled
* controls look nicer.
*/
static const float sSnapTolerance = 1.0f;
static void DrawCellWithSnapping(NSCell *cell,
CGContextRef cgContext,
const HIRect& destRect,
const CellRenderSettings settings,
- PRBool flip,
float verticalAlignFactor,
- PRBool needsBuffer = PR_TRUE)
+ NSView* view)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
const float rectWidth = destRect.size.width, rectHeight = destRect.size.height;
const NSSize *sizes = settings.naturalSizes;
const NSSize miniSize = sizes[EnumSizeForCocoaSize(NSMiniControlSize)];
const NSSize smallSize = sizes[EnumSizeForCocoaSize(NSSmallControlSize)];
const NSSize regularSize = sizes[EnumSizeForCocoaSize(NSRegularControlSize)];
@@ -411,18 +440,19 @@ static void DrawCellWithSnapping(NSCell
NSControlSize controlSize = NSRegularControlSize;
int sizeIndex = 0;
// At some sizes, don't scale but snap.
const NSControlSize smallerControlSize =
EnumSizeForCocoaSize(controlSizeX) < EnumSizeForCocoaSize(controlSizeY) ?
controlSizeX : controlSizeY;
const int smallerControlSizeIndex = EnumSizeForCocoaSize(smallerControlSize);
- float diffWidth = rectWidth - sizes[smallerControlSizeIndex].width;
- float diffHeight = rectHeight - sizes[smallerControlSizeIndex].height;
+ const NSSize size = sizes[smallerControlSizeIndex];
+ float diffWidth = size.width ? rectWidth - size.width : 0.0f;
+ float diffHeight = size.height ? rectHeight - size.height : 0.0f;
if (diffWidth >= 0.0f && diffHeight >= 0.0f &&
diffWidth <= sSnapTolerance && diffHeight <= sSnapTolerance) {
// Snap to the smaller control size.
controlSize = smallerControlSize;
sizeIndex = smallerControlSizeIndex;
// Resize and center the drawRect.
if (sizes[sizeIndex].width) {
drawRect.origin.x += ceil((destRect.size.width - sizes[sizeIndex].width) / 2);
@@ -438,17 +468,17 @@ static void DrawCellWithSnapping(NSCell
controlSizeX : controlSizeY;
sizeIndex = EnumSizeForCocoaSize(controlSize);
}
[cell setControlSize:controlSize];
NSSize minimumSize = settings.minimumSizes ? settings.minimumSizes[sizeIndex] : NSZeroSize;
DrawCellWithScaling(cell, cgContext, drawRect, controlSize, sizes[sizeIndex],
- minimumSize, settings.margins, flip, needsBuffer);
+ minimumSize, settings.margins, view);
NS_OBJC_END_TRY_ABORT_BLOCK;
}
static float VerticalAlignFactor(nsIFrame *aFrame)
{
if (!aFrame)
return 0.5f; // default: center
@@ -541,18 +571,59 @@ nsNativeThemeCocoa::DrawCheckboxOrRadio(
[cell setEnabled:!inDisabled];
[cell setShowsFirstResponder:(inState & NS_EVENT_STATE_FOCUS)];
[cell setState:(inSelected ? NSOnState : NSOffState)];
[cell setHighlighted:((inState & NS_EVENT_STATE_ACTIVE) && (inState & NS_EVENT_STATE_HOVER))];
[cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)];
DrawCellWithSnapping(cell, cgContext, inBoxRect,
inCheckbox ? checkboxSettings : radioSettings,
- nsToolkit::OnLeopardOrLater(), // Tiger doesn't need flipping
- VerticalAlignFactor(aFrame));
+ VerticalAlignFactor(aFrame),
+ NativeViewForFrame(aFrame));
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+static const CellRenderSettings searchFieldSettings = {
+ {
+ NSMakeSize(0, 16), // mini
+ NSMakeSize(0, 19), // small
+ NSMakeSize(0, 22) // regular
+ },
+ {
+ NSMakeSize(32, 0), // mini
+ NSMakeSize(38, 0), // small
+ NSMakeSize(44, 0) // regular
+ },
+ {
+ { // Tiger
+ {0, 0, 0, 0}, // mini
+ {0, 0, 0, 0}, // small
+ {0, 0, 0, 0} // regular
+ },
+ { // Leopard
+ {0, 0, 0, 0}, // mini
+ {0, 0, 0, 0}, // small
+ {0, 0, 0, 0} // regular
+ }
+ }
+};
+
+void
+nsNativeThemeCocoa::DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect,
+ nsIFrame* aFrame)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ NSSearchFieldCell* cell = mSearchFieldCell;
+ [cell setEnabled:!IsDisabled(aFrame)];
+ [cell setShowsFirstResponder:IsFocused(aFrame)];
+
+ DrawCellWithSnapping(cell, cgContext, inBoxRect, searchFieldSettings,
+ VerticalAlignFactor(aFrame), NativeViewForFrame(aFrame));
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// These are the sizes that Gecko needs to request to draw if it wants
// to get a standard-sized Aqua rounded bevel button drawn. Note that
// the rects that draw these are actually a little bigger.
@@ -600,17 +671,17 @@ nsNativeThemeCocoa::DrawPushButton(CGCon
isActive)];
[mPushButtonCell setShowsFirstResponder:(inState & NS_EVENT_STATE_FOCUS) && !inDisabled && isActive];
// If the button is tall enough, draw the square button style so that buttons with
// non-standard content look good. Otherwise draw normal rounded aqua buttons.
if (drawRect.size.height > DO_SQUARE_BUTTON_HEIGHT) {
[mPushButtonCell setBezelStyle:NSShadowlessSquareBezelStyle];
DrawCellWithScaling(mPushButtonCell, cgContext, inBoxRect, NSRegularControlSize,
- NSZeroSize, NSMakeSize(14, 0), NULL, PR_TRUE);
+ NSZeroSize, NSMakeSize(14, 0), NULL, NativeViewForFrame(aFrame));
} else {
[mPushButtonCell setBezelStyle:NSRoundedBezelStyle];
// Figure out what size cell control we're going to draw and grab its
// natural height and min width.
NSControlSize controlSize = NSRegularControlSize;
float naturalHeight = NATURAL_REGULAR_ROUNDED_BUTTON_HEIGHT;
float minWidth = NATURAL_REGULAR_ROUNDED_BUTTON_MIN_WIDTH;
@@ -625,18 +696,18 @@ nsNativeThemeCocoa::DrawPushButton(CGCon
controlSize = NSSmallControlSize;
naturalHeight = NATURAL_SMALL_ROUNDED_BUTTON_HEIGHT;
minWidth = NATURAL_SMALL_ROUNDED_BUTTON_MIN_WIDTH;
}
[mPushButtonCell setControlSize:controlSize];
DrawCellWithScaling(mPushButtonCell, cgContext, inBoxRect, controlSize,
NSMakeSize(0.0f, naturalHeight),
- NSMakeSize(minWidth, 0.0f),
- pushButtonMargins, PR_TRUE);
+ NSMakeSize(minWidth, 0.0f), pushButtonMargins,
+ NativeViewForFrame(aFrame));
}
#if DRAW_IN_FRAME_DEBUG
CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
CGContextFillRect(cgContext, inBoxRect);
#endif
NS_OBJC_END_TRY_ABORT_BLOCK;
@@ -1498,16 +1569,20 @@ nsNativeThemeCocoa::DrawWidgetBackground
IsFocused(aFrame)) {
eventState |= NS_EVENT_STATE_FOCUS;
}
DrawFrame(cgContext, kHIThemeFrameTextFieldSquare,
macRect, (IsDisabled(aFrame) || IsReadOnly(aFrame)), eventState);
break;
+ case NS_THEME_SEARCHFIELD:
+ DrawSearchField(cgContext, macRect, aFrame);
+ break;
+
case NS_THEME_PROGRESSBAR:
DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame),
PR_TRUE, GetProgressValue(aFrame),
GetProgressMaxValue(aFrame), aFrame);
break;
case NS_THEME_PROGRESSBAR_VERTICAL:
DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame),
@@ -1708,16 +1783,20 @@ nsNativeThemeCocoa::GetWidgetBorder(nsID
aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
break;
}
case NS_THEME_TEXTFIELD_MULTILINE:
aResult->SizeTo(1, 1, 1, 1);
break;
+ case NS_THEME_SEARCHFIELD:
+ aResult->SizeTo(4, 2, 4, 2);
+ break;
+
case NS_THEME_LISTBOX:
{
SInt32 frameOutset = 0;
::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset);
aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
break;
}
@@ -1786,16 +1865,17 @@ nsNativeThemeCocoa::GetWidgetPadding(nsI
PRBool
nsNativeThemeCocoa::GetWidgetOverflow(nsIDeviceContext* aContext, nsIFrame* aFrame,
PRUint8 aWidgetType, nsRect* aOverflowRect)
{
switch (aWidgetType) {
case NS_THEME_BUTTON:
case NS_THEME_TEXTFIELD:
case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_SEARCHFIELD:
case NS_THEME_LISTBOX:
case NS_THEME_DROPDOWN:
case NS_THEME_DROPDOWN_BUTTON:
case NS_THEME_CHECKBOX:
case NS_THEME_RADIO:
case NS_THEME_TAB:
{
// We assume that the above widgets can draw a focus ring that will be less than
@@ -1850,16 +1930,17 @@ nsNativeThemeCocoa::GetMinimumWidgetSize
SInt32 popupHeight = 0;
::GetThemeMetric(kThemeMetricPopupButtonHeight, &popupHeight);
aResult->SizeTo(0, popupHeight);
break;
}
case NS_THEME_TEXTFIELD:
case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_SEARCHFIELD:
{
// at minimum, we should be tall enough for 9pt text.
// I'm using hardcoded values here because the appearance manager
// values for the frame size are incorrect.
aResult->SizeTo(0, (2 + 2) /* top */ + 9 + (1 + 1) /* bottom */);
break;
}
@@ -2121,16 +2202,17 @@ nsNativeThemeCocoa::ThemeSupportsWidget(
case NS_THEME_BUTTON:
case NS_THEME_BUTTON_BEVEL:
case NS_THEME_SPINNER:
case NS_THEME_TOOLBAR:
case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
case NS_THEME_STATUSBAR:
case NS_THEME_TEXTFIELD:
case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_SEARCHFIELD:
//case NS_THEME_TOOLBOX:
//case NS_THEME_TOOLBAR_BUTTON:
case NS_THEME_PROGRESSBAR:
case NS_THEME_PROGRESSBAR_VERTICAL:
case NS_THEME_PROGRESSBAR_CHUNK:
case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
case NS_THEME_TOOLBAR_SEPARATOR: