1// Copyright 2013 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 "ui/app_list/cocoa/app_list_pager_view.h"
6
7#include "base/mac/mac_util.h"
8#include "skia/ext/skia_utils_mac.h"
9#include "ui/app_list/app_list_constants.h"
10#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
11
12namespace {
13
14const CGFloat kButtonHeight = 6;
15const CGFloat kButtonCornerRadius = 2;
16const CGFloat kButtonStripPadding = 20;
17
18}  // namespace
19
20@interface AppListPagerView ()
21
22@property(assign, nonatomic) NSInteger hoveredSegment;
23
24- (NSInteger)segmentUnderLocation:(NSPoint)locationInWindow;
25
26@end
27
28@interface AppListPagerCell : NSSegmentedCell;
29@end
30
31@implementation AppListPagerView
32
33@synthesize hoveredSegment = hoveredSegment_;
34
35+ (Class)cellClass {
36  return [AppListPagerCell class];
37}
38
39- (id)init {
40  if ((self = [super initWithFrame:NSZeroRect])) {
41    trackingArea_.reset(
42        [[CrTrackingArea alloc] initWithRect:NSZeroRect
43                                     options:NSTrackingInVisibleRect |
44                                             NSTrackingMouseEnteredAndExited |
45                                             NSTrackingMouseMoved |
46                                             NSTrackingActiveInKeyWindow
47                                       owner:self
48                                    userInfo:nil]);
49    [self addTrackingArea:trackingArea_.get()];
50    hoveredSegment_ = -1;
51  }
52  return self;
53}
54
55- (NSInteger)findAndHighlightSegmentAtLocation:(NSPoint)locationInWindow {
56  NSInteger segment = [self segmentUnderLocation:locationInWindow];
57  [self setHoveredSegment:segment];
58  return segment;
59}
60
61- (void)setHoveredSegment:(NSInteger)segment {
62  if (segment == hoveredSegment_)
63    return;
64
65  hoveredSegment_ = segment;
66  [self setNeedsDisplay:YES];
67  return;
68}
69
70- (NSInteger)segmentUnderLocation:(NSPoint)locationInWindow {
71  if ([self segmentCount] == 0)
72    return -1;
73
74  NSPoint pointInView = [self convertPoint:locationInWindow
75                                  fromView:nil];
76  if (![self mouse:pointInView inRect:[self bounds]])
77    return -1;
78
79  CGFloat segmentWidth = [self bounds].size.width / [self segmentCount];
80  return pointInView.x / segmentWidth;
81}
82
83- (BOOL)isHoveredForSegment:(NSInteger)segment {
84  return segment == hoveredSegment_;
85}
86
87- (void)mouseExited:(NSEvent*)theEvent {
88  [self setHoveredSegment:-1];
89}
90
91- (void)mouseMoved:(NSEvent*)theEvent {
92  [self findAndHighlightSegmentAtLocation:[theEvent locationInWindow]];
93}
94
95- (void)mouseDown:(NSEvent*)theEvent {
96  // Temporarily clear the highlight to give feedback.
97  [self setHoveredSegment:-1];
98}
99
100// The stock NSSegmentedControl ignores any clicks outside the non-default
101// control height, so process all clicks here.
102- (void)mouseUp:(NSEvent*)theEvent {
103  [self findAndHighlightSegmentAtLocation:[theEvent locationInWindow]];
104  if (hoveredSegment_ < 0)
105    return;
106
107  [self setSelectedSegment:hoveredSegment_];
108  [[self target] performSelector:[self action]
109                      withObject:self];
110}
111
112@end
113
114@implementation AppListPagerCell
115
116- (void)drawWithFrame:(NSRect)cellFrame
117               inView:(NSView*)controlView {
118  // 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::SkColorToSRGBNSColor(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::SkColorToSRGBNSColor(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