1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/styled_text_field_cell.h"
7 #include "base/logging.h"
8 #include "chrome/browser/themes/theme_properties.h"
9 #include "chrome/browser/themes/theme_service.h"
10 #import "chrome/browser/ui/cocoa/themed_window.h"
11 #include "grit/theme_resources.h"
12 #import "ui/base/cocoa/nsgraphics_context_additions.h"
13 #import "ui/base/cocoa/nsview_additions.h"
14 #include "ui/gfx/font.h"
15 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
17 @implementation StyledTextFieldCell
19 - (CGFloat)topTextFrameOffset {
23 - (CGFloat)bottomTextFrameOffset {
27 - (CGFloat)cornerRadius {
31 - (rect_path_utils::RoundedCornerFlags)roundedCornerFlags {
32 return rect_path_utils::RoundedCornerAll;
35 - (BOOL)shouldDrawBezel {
39 - (NSRect)textFrameForFrameInternal:(NSRect)cellFrame {
40 CGFloat topOffset = [self topTextFrameOffset];
41 NSRect textFrame = cellFrame;
42 textFrame.origin.y += topOffset;
43 textFrame.size.height -= topOffset + [self bottomTextFrameOffset];
47 // Returns the same value as textCursorFrameForFrame, but does not call it
48 // directly to avoid potential infinite loops.
49 - (NSRect)textFrameForFrame:(NSRect)cellFrame {
50 return [self textFrameForFrameInternal:cellFrame];
53 // Returns the same value as textFrameForFrame, but does not call it directly to
54 // avoid potential infinite loops.
55 - (NSRect)textCursorFrameForFrame:(NSRect)cellFrame {
56 return [self textFrameForFrameInternal:cellFrame];
59 // Override to show the I-beam cursor only in the area given by
60 // |textCursorFrameForFrame:|.
61 - (void)resetCursorRect:(NSRect)cellFrame inView:(NSView *)controlView {
62 [super resetCursorRect:[self textCursorFrameForFrame:cellFrame]
66 // For NSTextFieldCell this is the area within the borders. For our
67 // purposes, we count the info decorations as being part of the
69 - (NSRect)drawingRectForBounds:(NSRect)theRect {
70 return [super drawingRectForBounds:[self textFrameForFrame:theRect]];
73 // TODO(shess): This code is manually drawing the cell's border area,
74 // but otherwise the cell assumes -setBordered:YES for purposes of
75 // calculating things like the editing area. This is probably
76 // incorrect. I know that this affects -drawingRectForBounds:.
77 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
78 const CGFloat lineWidth = [controlView cr_lineWidth];
79 const CGFloat halfLineWidth = lineWidth / 2.0;
81 DCHECK([controlView isFlipped]);
82 rect_path_utils::RoundedCornerFlags roundedCornerFlags =
83 [self roundedCornerFlags];
85 // TODO(shess): This inset is also reflected by |kFieldVisualInset|
86 // in omnibox_popup_view_mac.mm.
87 const NSRect frame = NSInsetRect(cellFrame, 0, lineWidth);
88 const CGFloat radius = [self cornerRadius];
90 // Paint button background image if there is one (otherwise the border won't
92 ThemeService* themeProvider =
93 static_cast<ThemeService*>([[controlView window] themeProvider]);
95 NSColor* backgroundImageColor = nil;
96 if (themeProvider->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND)) {
97 backgroundImageColor =
98 themeProvider->GetNSImageColorNamed(IDR_THEME_BUTTON_BACKGROUND);
100 if (backgroundImageColor) {
101 // Set the phase to match window.
102 NSRect trueRect = [controlView convertRect:cellFrame toView:nil];
103 NSPoint midPoint = NSMakePoint(NSMinX(trueRect), NSMaxY(trueRect));
104 [[NSGraphicsContext currentContext] cr_setPatternPhase:midPoint
105 forView:controlView];
107 // NOTE(shess): This seems like it should be using a 0.0 inset,
108 // but AFAICT using a halfLineWidth inset is important in mixing the
109 // toolbar background and the omnibox background.
110 rect_path_utils::FillRectWithInset(roundedCornerFlags, frame,
111 halfLineWidth, halfLineWidth, radius,
112 backgroundImageColor);
115 // Draw the outer stroke (over the background).
116 BOOL active = [[controlView window] isMainWindow];
117 NSColor* strokeColor = themeProvider->GetNSColor(
118 active ? ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE :
119 ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE);
120 rect_path_utils::FrameRectWithInset(roundedCornerFlags, frame, 0.0, 0.0,
121 radius, lineWidth, strokeColor);
124 // Fill interior with background color.
125 rect_path_utils::FillRectWithInset(roundedCornerFlags, frame, lineWidth,
127 [self backgroundColor]);
129 // Draw the shadow. For the rounded-rect case, the shadow needs to
130 // slightly turn in at the corners. |shadowFrame| is at the same
131 // midline as the inner border line on the top and left, but at the
132 // outer border line on the bottom and right. The clipping change
133 // will clip the bottom and right edges (and corner).
135 gfx::ScopedNSGraphicsContextSaveGState state;
136 [rect_path_utils::RectPathWithInset(roundedCornerFlags, frame, lineWidth,
137 lineWidth, radius) addClip];
138 const NSRect shadowFrame =
139 NSOffsetRect(frame, halfLineWidth, halfLineWidth);
140 NSColor* shadowShade = [NSColor colorWithCalibratedWhite:0.0
141 alpha:0.05 / lineWidth];
142 rect_path_utils::FrameRectWithInset(roundedCornerFlags, shadowFrame,
143 halfLineWidth, halfLineWidth,
144 radius - halfLineWidth, lineWidth,
148 // Draw optional bezel below bottom stroke.
149 if ([self shouldDrawBezel] && themeProvider &&
150 themeProvider->UsingDefaultTheme()) {
152 NSColor* bezelColor = themeProvider->GetNSColor(
153 ThemeProperties::COLOR_TOOLBAR_BEZEL);
154 [[bezelColor colorWithAlphaComponent:0.5 / lineWidth] set];
155 NSRect bezelRect = NSMakeRect(cellFrame.origin.x,
156 NSMaxY(cellFrame) - lineWidth,
159 bezelRect = NSInsetRect(bezelRect, radius - halfLineWidth, 0.0);
160 NSRectFillUsingOperation(bezelRect, NSCompositeSourceOver);
163 // Draw the interior before the focus ring, to make sure nothing overlaps it.
164 [self drawInteriorWithFrame:cellFrame inView:controlView];
166 // Draw the focus ring if needed.
167 if ([self showsFirstResponder]) {
168 NSColor* color = [[NSColor keyboardFocusIndicatorColor]
169 colorWithAlphaComponent:0.5 / lineWidth];
170 rect_path_utils::FrameRectWithInset(roundedCornerFlags, frame, 0.0, 0.0,
171 radius, lineWidth * 2, color);