blob: c1f53b2cc173ad54d389aa6b3dcb64ae20e8b0f8 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00008#include "chrome/browser/themes/theme_properties.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00009#include "chrome/browser/themes/theme_service.h"
10#import "chrome/browser/ui/cocoa/nsview_additions.h"
11#import "chrome/browser/ui/cocoa/themed_window.h"
12#include "grit/theme_resources.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010013#import "ui/base/cocoa/nsgraphics_context_additions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014#include "ui/base/resource/resource_bundle.h"
15#include "ui/gfx/font.h"
16#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
17
Torne (Richard Coles)58218062012-11-14 11:43:16 +000018@implementation StyledTextFieldCell
19
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010020- (CGFloat)topTextFrameOffset {
21 return 0.0;
22}
23
24- (CGFloat)bottomTextFrameOffset {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000025 return 0.0;
26}
27
28- (CGFloat)cornerRadius {
29 return 0.0;
30}
31
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000032- (rect_path_utils::RoundedCornerFlags)roundedCornerFlags {
33 return rect_path_utils::RoundedCornerAll;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000034}
35
36- (BOOL)shouldDrawBezel {
37 return NO;
38}
39
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010040- (NSRect)textFrameForFrameInternal:(NSRect)cellFrame {
41 CGFloat topOffset = [self topTextFrameOffset];
42 NSRect textFrame = cellFrame;
43 textFrame.origin.y += topOffset;
44 textFrame.size.height -= topOffset + [self bottomTextFrameOffset];
45 return textFrame;
46}
47
Torne (Richard Coles)58218062012-11-14 11:43:16 +000048// Returns the same value as textCursorFrameForFrame, but does not call it
49// directly to avoid potential infinite loops.
50- (NSRect)textFrameForFrame:(NSRect)cellFrame {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010051 return [self textFrameForFrameInternal:cellFrame];
Torne (Richard Coles)58218062012-11-14 11:43:16 +000052}
53
54// Returns the same value as textFrameForFrame, but does not call it directly to
55// avoid potential infinite loops.
56- (NSRect)textCursorFrameForFrame:(NSRect)cellFrame {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010057 return [self textFrameForFrameInternal:cellFrame];
Torne (Richard Coles)58218062012-11-14 11:43:16 +000058}
59
60// Override to show the I-beam cursor only in the area given by
61// |textCursorFrameForFrame:|.
62- (void)resetCursorRect:(NSRect)cellFrame inView:(NSView *)controlView {
63 [super resetCursorRect:[self textCursorFrameForFrame:cellFrame]
64 inView:controlView];
65}
66
67// For NSTextFieldCell this is the area within the borders. For our
68// purposes, we count the info decorations as being part of the
69// border.
70- (NSRect)drawingRectForBounds:(NSRect)theRect {
71 return [super drawingRectForBounds:[self textFrameForFrame:theRect]];
72}
73
74// TODO(shess): This code is manually drawing the cell's border area,
75// but otherwise the cell assumes -setBordered:YES for purposes of
76// calculating things like the editing area. This is probably
77// incorrect. I know that this affects -drawingRectForBounds:.
78- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
79 const CGFloat lineWidth = [controlView cr_lineWidth];
80 const CGFloat halfLineWidth = lineWidth / 2.0;
81
82 DCHECK([controlView isFlipped]);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000083 rect_path_utils::RoundedCornerFlags roundedCornerFlags =
84 [self roundedCornerFlags];
Torne (Richard Coles)58218062012-11-14 11:43:16 +000085
86 // TODO(shess): This inset is also reflected by |kFieldVisualInset|
87 // in omnibox_popup_view_mac.mm.
88 const NSRect frame = NSInsetRect(cellFrame, 0, lineWidth);
89 const CGFloat radius = [self cornerRadius];
90
91 // Paint button background image if there is one (otherwise the border won't
92 // look right).
93 ThemeService* themeProvider =
94 static_cast<ThemeService*>([[controlView window] themeProvider]);
95 if (themeProvider) {
Ben Murdochbb1529c2013-08-08 10:24:53 +010096 NSColor* backgroundImageColor = nil;
97 if (themeProvider->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND)) {
98 backgroundImageColor =
99 themeProvider->GetNSImageColorNamed(IDR_THEME_BUTTON_BACKGROUND);
100 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000101 if (backgroundImageColor) {
102 // Set the phase to match window.
103 NSRect trueRect = [controlView convertRect:cellFrame toView:nil];
104 NSPoint midPoint = NSMakePoint(NSMinX(trueRect), NSMaxY(trueRect));
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100105 [[NSGraphicsContext currentContext] cr_setPatternPhase:midPoint
106 forView:controlView];
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000107
108 // NOTE(shess): This seems like it should be using a 0.0 inset,
109 // but AFAICT using a halfLineWidth inset is important in mixing the
110 // toolbar background and the omnibox background.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000111 rect_path_utils::FillRectWithInset(roundedCornerFlags, frame,
112 halfLineWidth, halfLineWidth, radius,
113 backgroundImageColor);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000114 }
115
116 // Draw the outer stroke (over the background).
117 BOOL active = [[controlView window] isMainWindow];
118 NSColor* strokeColor = themeProvider->GetNSColor(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000119 active ? ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE :
Ben Murdochbb1529c2013-08-08 10:24:53 +0100120 ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000121 rect_path_utils::FrameRectWithInset(roundedCornerFlags, frame, 0.0, 0.0,
122 radius, lineWidth, strokeColor);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000123 }
124
125 // Fill interior with background color.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000126 rect_path_utils::FillRectWithInset(roundedCornerFlags, frame, lineWidth,
127 lineWidth, radius,
128 [self backgroundColor]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000129
130 // Draw the shadow. For the rounded-rect case, the shadow needs to
131 // slightly turn in at the corners. |shadowFrame| is at the same
132 // midline as the inner border line on the top and left, but at the
133 // outer border line on the bottom and right. The clipping change
134 // will clip the bottom and right edges (and corner).
135 {
136 gfx::ScopedNSGraphicsContextSaveGState state;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000137 [rect_path_utils::RectPathWithInset(roundedCornerFlags, frame, lineWidth,
138 lineWidth, radius) addClip];
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000139 const NSRect shadowFrame =
140 NSOffsetRect(frame, halfLineWidth, halfLineWidth);
141 NSColor* shadowShade = [NSColor colorWithCalibratedWhite:0.0
142 alpha:0.05 / lineWidth];
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000143 rect_path_utils::FrameRectWithInset(roundedCornerFlags, shadowFrame,
144 halfLineWidth, halfLineWidth,
145 radius - halfLineWidth, lineWidth,
146 shadowShade);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000147 }
148
149 // Draw optional bezel below bottom stroke.
150 if ([self shouldDrawBezel] && themeProvider &&
151 themeProvider->UsingDefaultTheme()) {
152
153 NSColor* bezelColor = themeProvider->GetNSColor(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100154 ThemeProperties::COLOR_TOOLBAR_BEZEL);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000155 [[bezelColor colorWithAlphaComponent:0.5 / lineWidth] set];
156 NSRect bezelRect = NSMakeRect(cellFrame.origin.x,
157 NSMaxY(cellFrame) - lineWidth,
158 NSWidth(cellFrame),
159 lineWidth);
160 bezelRect = NSInsetRect(bezelRect, radius - halfLineWidth, 0.0);
161 NSRectFillUsingOperation(bezelRect, NSCompositeSourceOver);
162 }
163
164 // Draw the interior before the focus ring, to make sure nothing overlaps it.
165 [self drawInteriorWithFrame:cellFrame inView:controlView];
166
167 // Draw the focus ring if needed.
168 if ([self showsFirstResponder]) {
169 NSColor* color = [[NSColor keyboardFocusIndicatorColor]
170 colorWithAlphaComponent:0.5 / lineWidth];
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000171 rect_path_utils::FrameRectWithInset(roundedCornerFlags, frame, 0.0, 0.0,
172 radius, lineWidth * 2, color);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000173 }
174}
175
176@end