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/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" 16 17@implementation StyledTextFieldCell 18 19- (CGFloat)topTextFrameOffset { 20 return 0.0; 21} 22 23- (CGFloat)bottomTextFrameOffset { 24 return 0.0; 25} 26 27- (CGFloat)cornerRadius { 28 return 0.0; 29} 30 31- (rect_path_utils::RoundedCornerFlags)roundedCornerFlags { 32 return rect_path_utils::RoundedCornerAll; 33} 34 35- (BOOL)shouldDrawBezel { 36 return NO; 37} 38 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]; 44 return textFrame; 45} 46 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]; 51} 52 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]; 57} 58 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] 63 inView:controlView]; 64} 65 66// For NSTextFieldCell this is the area within the borders. For our 67// purposes, we count the info decorations as being part of the 68// border. 69- (NSRect)drawingRectForBounds:(NSRect)theRect { 70 return [super drawingRectForBounds:[self textFrameForFrame:theRect]]; 71} 72 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; 80 81 DCHECK([controlView isFlipped]); 82 rect_path_utils::RoundedCornerFlags roundedCornerFlags = 83 [self roundedCornerFlags]; 84 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]; 89 90 // Paint button background image if there is one (otherwise the border won't 91 // look right). 92 ThemeService* themeProvider = 93 static_cast<ThemeService*>([[controlView window] themeProvider]); 94 if (themeProvider) { 95 NSColor* backgroundImageColor = nil; 96 if (themeProvider->HasCustomImage(IDR_THEME_BUTTON_BACKGROUND)) { 97 backgroundImageColor = 98 themeProvider->GetNSImageColorNamed(IDR_THEME_BUTTON_BACKGROUND); 99 } 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]; 106 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); 113 } 114 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); 122 } 123 124 // Fill interior with background color. 125 rect_path_utils::FillRectWithInset(roundedCornerFlags, frame, lineWidth, 126 lineWidth, radius, 127 [self backgroundColor]); 128 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). 134 { 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, 145 shadowShade); 146 } 147 148 // Draw optional bezel below bottom stroke. 149 if ([self shouldDrawBezel] && themeProvider && 150 themeProvider->UsingDefaultTheme()) { 151 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, 157 NSWidth(cellFrame), 158 lineWidth); 159 bezelRect = NSInsetRect(bezelRect, radius - halfLineWidth, 0.0); 160 NSRectFillUsingOperation(bezelRect, NSCompositeSourceOver); 161 } 162 163 // Draw the interior before the focus ring, to make sure nothing overlaps it. 164 [self drawInteriorWithFrame:cellFrame inView:controlView]; 165 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); 172 } 173} 174 175@end 176