• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4
5#import "chrome/browser/ui/cocoa/styled_text_field_cell.h"
6
7#include "base/logging.h"
8#include "chrome/browser/themes/theme_service.h"
9#import "chrome/browser/ui/cocoa/nsview_additions.h"
10#import "chrome/browser/ui/cocoa/themed_window.h"
11#include "grit/theme_resources.h"
12#import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h"
13#include "ui/base/resource/resource_bundle.h"
14#include "ui/gfx/font.h"
15
16namespace {
17
18NSBezierPath* RectPathWithInset(StyledTextFieldCellRoundedFlags roundedFlags,
19                                const NSRect frame,
20                                const CGFloat inset,
21                                const CGFloat outerRadius) {
22  NSRect insetFrame = NSInsetRect(frame, inset, inset);
23
24  if (outerRadius > 0.0) {
25    CGFloat leftRadius = outerRadius - inset;
26    CGFloat rightRadius =
27        (roundedFlags == StyledTextFieldCellRoundedLeft) ? 0 : leftRadius;
28
29    return [NSBezierPath gtm_bezierPathWithRoundRect:insetFrame
30                                 topLeftCornerRadius:leftRadius
31                                topRightCornerRadius:rightRadius
32                              bottomLeftCornerRadius:leftRadius
33                             bottomRightCornerRadius:rightRadius];
34  } else {
35    return [NSBezierPath bezierPathWithRect:insetFrame];
36  }
37}
38
39// Similar to |NSRectFill()|, additionally sets |color| as the fill
40// color.  |outerRadius| greater than 0.0 uses rounded corners, with
41// inset backed out of the radius.
42void FillRectWithInset(StyledTextFieldCellRoundedFlags roundedFlags,
43                       const NSRect frame,
44                       const CGFloat inset,
45                       const CGFloat outerRadius,
46                       NSColor* color) {
47  NSBezierPath* path =
48      RectPathWithInset(roundedFlags, frame, inset, outerRadius);
49  [color setFill];
50  [path fill];
51}
52
53// Similar to |NSFrameRectWithWidth()|, additionally sets |color| as
54// the stroke color (as opposed to the fill color).  |outerRadius|
55// greater than 0.0 uses rounded corners, with inset backed out of the
56// radius.
57void FrameRectWithInset(StyledTextFieldCellRoundedFlags roundedFlags,
58                        const NSRect frame,
59                        const CGFloat inset,
60                        const CGFloat outerRadius,
61                        const CGFloat lineWidth,
62                        NSColor* color) {
63  const CGFloat finalInset = inset + (lineWidth / 2.0);
64  NSBezierPath* path =
65      RectPathWithInset(roundedFlags, frame, finalInset, outerRadius);
66  [color setStroke];
67  [path setLineWidth:lineWidth];
68  [path stroke];
69}
70
71// TODO(shess): Maybe we need a |cocoa_util.h|?
72class ScopedSaveGraphicsState {
73 public:
74  ScopedSaveGraphicsState()
75      : context_([NSGraphicsContext currentContext]) {
76    [context_ saveGraphicsState];
77  }
78  explicit ScopedSaveGraphicsState(NSGraphicsContext* context)
79      : context_(context) {
80    [context_ saveGraphicsState];
81  }
82  ~ScopedSaveGraphicsState() {
83    [context_ restoreGraphicsState];
84  }
85
86private:
87  NSGraphicsContext* context_;
88};
89
90}  // namespace
91
92@implementation StyledTextFieldCell
93
94- (CGFloat)baselineAdjust {
95  return 0.0;
96}
97
98- (CGFloat)cornerRadius {
99  return 0.0;
100}
101
102- (StyledTextFieldCellRoundedFlags)roundedFlags {
103  return StyledTextFieldCellRoundedAll;
104}
105
106- (BOOL)shouldDrawBezel {
107  return NO;
108}
109
110// Returns the same value as textCursorFrameForFrame, but does not call it
111// directly to avoid potential infinite loops.
112- (NSRect)textFrameForFrame:(NSRect)cellFrame {
113  return NSInsetRect(cellFrame, 0, [self baselineAdjust]);
114}
115
116// Returns the same value as textFrameForFrame, but does not call it directly to
117// avoid potential infinite loops.
118- (NSRect)textCursorFrameForFrame:(NSRect)cellFrame {
119  return NSInsetRect(cellFrame, 0, [self baselineAdjust]);
120}
121
122// Override to show the I-beam cursor only in the area given by
123// |textCursorFrameForFrame:|.
124- (void)resetCursorRect:(NSRect)cellFrame inView:(NSView *)controlView {
125  [super resetCursorRect:[self textCursorFrameForFrame:cellFrame]
126                  inView:controlView];
127}
128
129// For NSTextFieldCell this is the area within the borders.  For our
130// purposes, we count the info decorations as being part of the
131// border.
132- (NSRect)drawingRectForBounds:(NSRect)theRect {
133  return [super drawingRectForBounds:[self textFrameForFrame:theRect]];
134}
135
136// TODO(shess): This code is manually drawing the cell's border area,
137// but otherwise the cell assumes -setBordered:YES for purposes of
138// calculating things like the editing area.  This is probably
139// incorrect.  I know that this affects -drawingRectForBounds:.
140- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
141  const CGFloat lineWidth = [controlView cr_lineWidth];
142  const CGFloat halfLineWidth = lineWidth / 2.0;
143
144  DCHECK([controlView isFlipped]);
145  StyledTextFieldCellRoundedFlags roundedFlags = [self roundedFlags];
146
147  // TODO(shess): This inset is also reflected by |kFieldVisualInset|
148  // in autocomplete_popup_view_mac.mm.
149  const NSRect frame = NSInsetRect(cellFrame, 0, lineWidth);
150  const CGFloat radius = [self cornerRadius];
151
152  // Paint button background image if there is one (otherwise the border won't
153  // look right).
154  ThemeService* themeProvider =
155      static_cast<ThemeService*>([[controlView window] themeProvider]);
156  if (themeProvider) {
157    NSColor* backgroundImageColor =
158        themeProvider->GetNSImageColorNamed(IDR_THEME_BUTTON_BACKGROUND, false);
159    if (backgroundImageColor) {
160      // Set the phase to match window.
161      NSRect trueRect = [controlView convertRect:cellFrame toView:nil];
162      NSPoint midPoint = NSMakePoint(NSMinX(trueRect), NSMaxY(trueRect));
163      [[NSGraphicsContext currentContext] setPatternPhase:midPoint];
164
165      // NOTE(shess): This seems like it should be using a 0.0 inset,
166      // but AFAICT using a halfLineWidth inset is important in mixing the
167      // toolbar background and the omnibox background.
168      FillRectWithInset(roundedFlags, frame, halfLineWidth, radius,
169                        backgroundImageColor);
170    }
171
172    // Draw the outer stroke (over the background).
173    BOOL active = [[controlView window] isMainWindow];
174    NSColor* strokeColor = themeProvider->GetNSColor(
175        active ? ThemeService::COLOR_TOOLBAR_BUTTON_STROKE :
176                 ThemeService::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE,
177        true);
178    FrameRectWithInset(roundedFlags, frame, 0.0, radius, lineWidth,
179                       strokeColor);
180  }
181
182  // Fill interior with background color.
183  FillRectWithInset(roundedFlags, frame, lineWidth, radius,
184                    [self backgroundColor]);
185
186  // Draw the shadow.  For the rounded-rect case, the shadow needs to
187  // slightly turn in at the corners.  |shadowFrame| is at the same
188  // midline as the inner border line on the top and left, but at the
189  // outer border line on the bottom and right.  The clipping change
190  // will clip the bottom and right edges (and corner).
191  {
192    ScopedSaveGraphicsState state;
193    [RectPathWithInset(roundedFlags, frame, lineWidth, radius) addClip];
194    const NSRect shadowFrame =
195        NSOffsetRect(frame, halfLineWidth, halfLineWidth);
196    NSColor* shadowShade = [NSColor colorWithCalibratedWhite:0.0 alpha:0.05];
197    FrameRectWithInset(roundedFlags, shadowFrame, halfLineWidth,
198                       radius - halfLineWidth, lineWidth, shadowShade);
199  }
200
201  // Draw optional bezel below bottom stroke.
202  if ([self shouldDrawBezel] && themeProvider &&
203      themeProvider->UsingDefaultTheme()) {
204
205    NSColor* bezelColor = themeProvider->GetNSColor(
206        ThemeService::COLOR_TOOLBAR_BEZEL, true);
207    [[bezelColor colorWithAlphaComponent:0.5] set];
208    NSRect bezelRect = NSMakeRect(cellFrame.origin.x,
209                                  NSMaxY(cellFrame) - lineWidth,
210                                  NSWidth(cellFrame),
211                                  lineWidth);
212    bezelRect = NSInsetRect(bezelRect, radius - halfLineWidth, 0.0);
213    NSRectFillUsingOperation(bezelRect, NSCompositeSourceOver);
214  }
215
216  // Draw the focus ring if needed.
217  if ([self showsFirstResponder]) {
218    NSColor* color =
219        [[NSColor keyboardFocusIndicatorColor] colorWithAlphaComponent:0.5];
220    FrameRectWithInset(roundedFlags, frame, 0.0, radius, lineWidth * 2, color);
221  }
222
223  [self drawInteriorWithFrame:cellFrame inView:controlView];
224}
225
226@end
227