app_list_pager_view.mm revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file. 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#import "ui/app_list/cocoa/app_list_pager_view.h" 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/mac/mac_util.h" 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "skia/ext/skia_utils_mac.h" 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/app_list/app_list_constants.h" 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)namespace { 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const CGFloat kButtonHeight = 6; 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const CGFloat kButtonCornerRadius = 2; 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const CGFloat kButtonStripPadding = 20; 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} // namespace 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@interface AppListPagerView () 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@property(assign, nonatomic) NSInteger hoveredSegment; 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (NSInteger)segmentUnderLocation:(NSPoint)locationInWindow; 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@end 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@interface AppListPagerCell : NSSegmentedCell; 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@end 30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@implementation AppListPagerView 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@synthesize hoveredSegment = hoveredSegment_; 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)+ (Class)cellClass { 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return [AppListPagerCell class]; 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (id)init { 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((self = [super initWithFrame:NSZeroRect])) { 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) trackingArea_.reset( 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [[CrTrackingArea alloc] initWithRect:NSZeroRect 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) options:NSTrackingInVisibleRect | 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NSTrackingMouseEnteredAndExited | 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NSTrackingMouseMoved | 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NSTrackingActiveInKeyWindow 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) owner:self 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) userInfo:nil]); 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [self addTrackingArea:trackingArea_.get()]; 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) hoveredSegment_ = -1; 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return self; 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (NSInteger)findAndHighlightSegmentAtLocation:(NSPoint)locationInWindow { 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NSInteger segment = [self segmentUnderLocation:locationInWindow]; 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [self setHoveredSegment:segment]; 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return segment; 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (void)setHoveredSegment:(NSInteger)segment { 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (segment == hoveredSegment_) 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) hoveredSegment_ = segment; 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [self setNeedsDisplay:YES]; 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (NSInteger)segmentUnderLocation:(NSPoint)locationInWindow { 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ([self segmentCount] == 0) 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return -1; 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NSPoint pointInView = [self convertPoint:locationInWindow 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) fromView:nil]; 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (![self mouse:pointInView inRect:[self bounds]]) 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return -1; 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CGFloat segmentWidth = [self bounds].size.width / [self segmentCount]; 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return pointInView.x / segmentWidth; 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (BOOL)isHoveredForSegment:(NSInteger)segment { 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return segment == hoveredSegment_; 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (void)mouseExited:(NSEvent*)theEvent { 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [self setHoveredSegment:-1]; 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (void)mouseMoved:(NSEvent*)theEvent { 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [self findAndHighlightSegmentAtLocation:[theEvent locationInWindow]]; 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (void)mouseDown:(NSEvent*)theEvent { 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Temporarily clear the highlight to give feedback. 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [self setHoveredSegment:-1]; 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// The stock NSSegmentedControl ignores any clicks outside the non-default 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// control height, so process all clicks here. 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (void)mouseUp:(NSEvent*)theEvent { 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [self findAndHighlightSegmentAtLocation:[theEvent locationInWindow]]; 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (hoveredSegment_ < 0) 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [self setSelectedSegment:hoveredSegment_]; 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) [[self target] performSelector:[self action] 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) withObject:self]; 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@end 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)@implementation AppListPagerCell 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)- (void)drawWithFrame:(NSRect)cellFrame 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) inView:(NSView*)controlView { 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Draw nothing if there are less than two segments. 119 if ([self segmentCount] < 2) 120 return; 121 122 cellFrame.size.width /= [self segmentCount]; 123 for (NSInteger i = 0; i < [self segmentCount]; ++i) { 124 [self drawSegment:i 125 inFrame:cellFrame 126 withView:controlView]; 127 cellFrame.origin.x += cellFrame.size.width; 128 } 129} 130 131- (void)drawSegment:(NSInteger)segment 132 inFrame:(NSRect)frame 133 withView:(NSView*)controlView { 134 gfx::ScopedNSGraphicsContextSaveGState context; 135 NSRect clipRect = NSMakeRect( 136 frame.origin.x + kButtonStripPadding / 2, 137 floor(frame.origin.y + (frame.size.height - kButtonHeight) / 2), 138 frame.size.width - kButtonStripPadding, 139 kButtonHeight); 140 [[NSBezierPath bezierPathWithRoundedRect:clipRect 141 xRadius:kButtonCornerRadius 142 yRadius:kButtonCornerRadius] addClip]; 143 144 AppListPagerView* pagerControl = 145 base::mac::ObjCCastStrict<AppListPagerView>(controlView); 146 SkColor backgroundColor = [pagerControl hoveredSegment] == segment ? 147 app_list::kPagerHoverColor : 148 app_list::kPagerNormalColor; 149 150 [gfx::SkColorToCalibratedNSColor(backgroundColor) set]; 151 NSRectFill(frame); 152 153 if (![[self target] conformsToProtocol:@protocol(AppListPagerDelegate)]) 154 return; 155 156 CGFloat selectedRatio = [[self target] visiblePortionOfPage:segment]; 157 if (selectedRatio == 0.0) 158 return; 159 160 [gfx::SkColorToCalibratedNSColor(app_list::kPagerSelectedColor) set]; 161 if (selectedRatio < 0) 162 frame.origin.x += frame.size.width + frame.size.width * selectedRatio; 163 frame.size.width *= fabs(selectedRatio); 164 NSRectFill(frame); 165} 166 167@end 168