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_properties.h" 9#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" 13#import "ui/base/cocoa/nsgraphics_context_additions.h" 14#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 18@implementation StyledTextFieldCell 19 20- (CGFloat)topTextFrameOffset { 21 return 0.0; 22} 23 24- (CGFloat)bottomTextFrameOffset { 25 return 0.0; 26} 27 28- (CGFloat)cornerRadius { 29 return 0.0; 30} 31 32- (rect_path_utils::RoundedCornerFlags)roundedCornerFlags { 33 return rect_path_utils::RoundedCornerAll; 34} 35 36- (BOOL)shouldDrawBezel { 37 return NO; 38} 39 40- (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 48// Returns the same value as textCursorFrameForFrame, but does not call it 49// directly to avoid potential infinite loops. 50- (NSRect)textFrameForFrame:(NSRect)cellFrame { 51 return [self textFrameForFrameInternal:cellFrame]; 52} 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 { 57 return [self textFrameForFrameInternal:cellFrame]; 58} 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]); 83 rect_path_utils::RoundedCornerFlags roundedCornerFlags = 84 [self roundedCornerFlags]; 85 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) { 96 NSColor* backgroundImageColor = nil; 97 if (themeProvider->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND)) { 98 backgroundImageColor = 99 themeProvider->GetNSImageColorNamed(IDR_THEME_BUTTON_BACKGROUND); 100 } 101 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)); 105 [[NSGraphicsContext currentContext] cr_setPatternPhase:midPoint 106 forView:controlView]; 107 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. 111 rect_path_utils::FillRectWithInset(roundedCornerFlags, frame, 112 halfLineWidth, halfLineWidth, radius, 113 backgroundImageColor); 114 } 115 116 // Draw the outer stroke (over the background). 117 BOOL active = [[controlView window] isMainWindow]; 118 NSColor* strokeColor = themeProvider->GetNSColor( 119 active ? ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE : 120 ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE); 121 rect_path_utils::FrameRectWithInset(roundedCornerFlags, frame, 0.0, 0.0, 122 radius, lineWidth, strokeColor); 123 } 124 125 // Fill interior with background color. 126 rect_path_utils::FillRectWithInset(roundedCornerFlags, frame, lineWidth, 127 lineWidth, radius, 128 [self backgroundColor]); 129 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; 137 [rect_path_utils::RectPathWithInset(roundedCornerFlags, frame, lineWidth, 138 lineWidth, radius) addClip]; 139 const NSRect shadowFrame = 140 NSOffsetRect(frame, halfLineWidth, halfLineWidth); 141 NSColor* shadowShade = [NSColor colorWithCalibratedWhite:0.0 142 alpha:0.05 / lineWidth]; 143 rect_path_utils::FrameRectWithInset(roundedCornerFlags, shadowFrame, 144 halfLineWidth, halfLineWidth, 145 radius - halfLineWidth, lineWidth, 146 shadowShade); 147 } 148 149 // Draw optional bezel below bottom stroke. 150 if ([self shouldDrawBezel] && themeProvider && 151 themeProvider->UsingDefaultTheme()) { 152 153 NSColor* bezelColor = themeProvider->GetNSColor( 154 ThemeProperties::COLOR_TOOLBAR_BEZEL); 155 [[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]; 171 rect_path_utils::FrameRectWithInset(roundedCornerFlags, frame, 0.0, 0.0, 172 radius, lineWidth * 2, color); 173 } 174} 175 176@end 177