зеркало из https://github.com/mozilla/gecko-dev.git
Bug 450800 - Add -moz-appearance: searchfield on Mac OS X; general NSCell drawing cleanup. r=smichaud sr=roc
This commit is contained in:
Родитель
9315b9bda1
Коммит
53d5c006d9
|
@ -156,6 +156,9 @@
|
|||
// A multiline text field
|
||||
#define NS_THEME_TEXTFIELD_MULTILINE 97
|
||||
|
||||
// A searchfield
|
||||
#define NS_THEME_SEARCHFIELD 98
|
||||
|
||||
// A dropdown list.
|
||||
#define NS_THEME_DROPDOWN 101
|
||||
|
||||
|
|
|
@ -544,6 +544,7 @@ CSS_KEY(scrollbarthumb-vertical, scrollbarthumb_vertical)
|
|||
CSS_KEY(textfield, textfield)
|
||||
CSS_KEY(textfield-multiline, textfield_multiline)
|
||||
CSS_KEY(caret, caret)
|
||||
CSS_KEY(searchfield, searchfield)
|
||||
CSS_KEY(menubar, menubar)
|
||||
CSS_KEY(menupopup, menupopup)
|
||||
CSS_KEY(menuitem, menuitem)
|
||||
|
|
|
@ -291,6 +291,7 @@ const PRInt32 nsCSSProps::kAppearanceKTable[] = {
|
|||
eCSSKeyword_textfield, NS_THEME_TEXTFIELD,
|
||||
eCSSKeyword_textfield_multiline, NS_THEME_TEXTFIELD_MULTILINE,
|
||||
eCSSKeyword_caret, NS_THEME_TEXTFIELD_CARET,
|
||||
eCSSKeyword_searchfield, NS_THEME_SEARCHFIELD,
|
||||
eCSSKeyword_menulist, NS_THEME_DROPDOWN,
|
||||
eCSSKeyword_menulistbutton, NS_THEME_DROPDOWN_BUTTON,
|
||||
eCSSKeyword_menulisttext, NS_THEME_DROPDOWN_TEXT,
|
||||
|
|
|
@ -2873,6 +2873,22 @@ NSEvent* gLastDragEvent = nil;
|
|||
}
|
||||
|
||||
|
||||
// Needed to deal with the consequences of calling [NSCell
|
||||
// drawWithFrame:inView:] with a ChildView object as the inView parameter
|
||||
// (this can happen in nsNativeThemeCocoa.mm): drawWithFrame:inView:
|
||||
// expects an NSControl as its inView parameter, and may call [NSControl
|
||||
// currentEditor] on it. But since a ChildView object (like an NSView object)
|
||||
// isn't a control, it doesn't have a "current editor", or a currentEditor
|
||||
// method. So calling currentEditor on it will trigger a Objective-C
|
||||
// "unrecognized selector" exception. To prevent this, ChildView needs its
|
||||
// own currentEditor method. Since a ChildView object never has a "current
|
||||
// editor", it should always return nil.
|
||||
- (NSText*)currentEditor
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (void)scrollRect:(NSRect)aRect by:(NSSize)offset
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
|
|
@ -113,6 +113,7 @@ protected:
|
|||
void DrawCheckboxOrRadio(CGContextRef cgContext, PRBool inCheckbox,
|
||||
const HIRect& inBoxRect, PRBool inSelected,
|
||||
PRBool inDisabled, PRInt32 inState, nsIFrame* aFrame);
|
||||
void DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect, nsIFrame* aFrame);
|
||||
void DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRect, PRBool inIsDefault,
|
||||
PRBool inDisabled, PRInt32 inState, nsIFrame* aFrame);
|
||||
void DrawButton(CGContextRef context, ThemeButtonKind inKind,
|
||||
|
@ -138,6 +139,7 @@ private:
|
|||
NSButtonCell* mPushButtonCell;
|
||||
NSButtonCell* mRadioButtonCell;
|
||||
NSButtonCell* mCheckboxCell;
|
||||
NSSearchFieldCell* mSearchFieldCell;
|
||||
};
|
||||
|
||||
#endif // nsNativeThemeCocoa_h_
|
||||
|
|
|
@ -81,6 +81,30 @@ extern "C" {
|
|||
- (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;
|
||||
|
@ -129,6 +153,18 @@ static void InflateControlRect(NSRect* rect, NSControlSize cocoaControlSize, con
|
|||
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)
|
||||
{
|
||||
|
@ -181,6 +217,12 @@ nsNativeThemeCocoa::nsNativeThemeCocoa()
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -191,6 +233,7 @@ nsNativeThemeCocoa::~nsNativeThemeCocoa()
|
|||
[mPushButtonCell release];
|
||||
[mRadioButtonCell release];
|
||||
[mCheckboxCell release];
|
||||
[mSearchFieldCell release];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
@ -227,9 +270,9 @@ nsNativeThemeCocoa::~nsNativeThemeCocoa()
|
|||
* 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,
|
||||
|
@ -238,8 +281,7 @@ static void DrawCellWithScaling(NSCell *cell,
|
|||
NSSize naturalSize,
|
||||
NSSize minimumSize,
|
||||
const float marginSet[][3][4],
|
||||
PRBool flip,
|
||||
PRBool needsBuffer = PR_TRUE)
|
||||
NSView* view)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
|
@ -264,38 +306,22 @@ static void DrawCellWithScaling(NSCell *cell,
|
|||
|
||||
[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);
|
||||
|
@ -311,15 +337,19 @@ static void DrawCellWithScaling(NSCell *cell,
|
|||
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];
|
||||
|
||||
|
@ -383,9 +413,8 @@ 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;
|
||||
|
||||
|
@ -416,8 +445,9 @@ static void DrawCellWithSnapping(NSCell *cell,
|
|||
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.
|
||||
|
@ -443,7 +473,7 @@ static void DrawCellWithSnapping(NSCell *cell,
|
|||
|
||||
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;
|
||||
}
|
||||
|
@ -546,8 +576,49 @@ nsNativeThemeCocoa::DrawCheckboxOrRadio(CGContextRef cgContext, PRBool inCheckbo
|
|||
|
||||
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;
|
||||
}
|
||||
|
@ -605,7 +676,7 @@ nsNativeThemeCocoa::DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRe
|
|||
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];
|
||||
|
||||
|
@ -630,8 +701,8 @@ nsNativeThemeCocoa::DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRe
|
|||
|
||||
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
|
||||
|
@ -1503,6 +1574,10 @@ nsNativeThemeCocoa::DrawWidgetBackground(nsIRenderingContext* aContext, nsIFrame
|
|||
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),
|
||||
|
@ -1713,6 +1788,10 @@ nsNativeThemeCocoa::GetWidgetBorder(nsIDeviceContext* aContext,
|
|||
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;
|
||||
|
@ -1791,6 +1870,7 @@ nsNativeThemeCocoa::GetWidgetOverflow(nsIDeviceContext* aContext, nsIFrame* aFra
|
|||
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:
|
||||
|
@ -1855,6 +1935,7 @@ nsNativeThemeCocoa::GetMinimumWidgetSize(nsIRenderingContext* aContext,
|
|||
|
||||
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
|
||||
|
@ -2126,6 +2207,7 @@ nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* a
|
|||
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:
|
||||
|
|
Загрузка…
Ссылка в новой задаче