- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / browser / zoom_bubble_controller.mm
1 // Copyright (c) 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 #include "chrome/browser/ui/cocoa/browser/zoom_bubble_controller.h"
6
7 #include "base/mac/foundation_util.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "chrome/browser/chrome_page_zoom.h"
10 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
11 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
12 #include "chrome/browser/ui/zoom/zoom_controller.h"
13 #include "content/public/common/page_zoom.h"
14 #include "grit/generated_resources.h"
15 #include "skia/ext/skia_utils_mac.h"
16 #import "ui/base/cocoa/hover_button.h"
17 #import "ui/base/cocoa/window_size_constants.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/native_theme/native_theme.h"
20
21 @interface ZoomBubbleController (Private)
22 - (void)performLayout;
23 - (void)autoCloseBubble;
24 - (NSAttributedString*)attributedStringWithString:(NSString*)string
25                                          fontSize:(CGFloat)fontSize;
26 // Adds a new zoom button to the bubble.
27 - (NSButton*)addButtonWithTitleID:(int)titleID
28                          fontSize:(CGFloat)fontSize
29                            action:(SEL)action;
30 - (NSTextField*)addZoomPercentTextField;
31 - (void)updateAutoCloseTimer;
32 @end
33
34 // Button that highlights the background on mouse over.
35 @interface ZoomHoverButton : HoverButton
36 @end
37
38 namespace {
39
40 // The amount of time to wait before the bubble automatically closes.
41 // Should keep in sync with kBubbleCloseDelay in
42 // src/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc.
43 NSTimeInterval gAutoCloseDelay = 1.5;
44
45 // The height of the window.
46 const CGFloat kWindowHeight = 29.0;
47
48 // Width of the zoom in and zoom out buttons.
49 const CGFloat kZoomInOutButtonWidth = 44.0;
50
51 // Width of zoom label.
52 const CGFloat kZoomLabelWidth = 55.0;
53
54 // Horizontal margin for the reset zoom button.
55 const CGFloat kResetZoomMargin = 9.0;
56
57 // The font size text shown in the bubble.
58 const CGFloat kTextFontSize = 12.0;
59
60 // The font size of the zoom in and zoom out buttons.
61 const CGFloat kZoomInOutButtonFontSize = 16.0;
62
63 }  // namespace
64
65 namespace chrome {
66
67 void SetZoomBubbleAutoCloseDelayForTesting(NSTimeInterval time_interval) {
68   gAutoCloseDelay = time_interval;
69 }
70
71 }  // namespace chrome
72
73 @implementation ZoomBubbleController
74
75 - (id)initWithParentWindow:(NSWindow*)parentWindow
76              closeObserver:(void(^)(ZoomBubbleController*))closeObserver {
77   base::scoped_nsobject<InfoBubbleWindow> window(
78       [[InfoBubbleWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 100)
79                                           styleMask:NSBorderlessWindowMask
80                                             backing:NSBackingStoreBuffered
81                                               defer:NO]);
82   if ((self = [super initWithWindow:window
83                        parentWindow:parentWindow
84                          anchoredAt:NSZeroPoint])) {
85     [window setCanBecomeKeyWindow:NO];
86     closeObserver_.reset(Block_copy(closeObserver));
87
88     ui::NativeTheme* nativeTheme = ui::NativeTheme::instance();
89     [[self bubble] setAlignment:info_bubble::kAlignRightEdgeToAnchorEdge];
90     [[self bubble] setArrowLocation:info_bubble::kNoArrow];
91     [[self bubble] setBackgroundColor:
92         gfx::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor(
93             ui::NativeTheme::kColorId_DialogBackground))];
94
95     [self performLayout];
96
97     trackingArea_.reset([[CrTrackingArea alloc]
98         initWithRect:NSZeroRect
99              options:NSTrackingMouseEnteredAndExited |
100                      NSTrackingActiveAlways |
101                      NSTrackingInVisibleRect
102                owner:self
103             userInfo:nil]);
104     [trackingArea_.get() clearOwnerWhenWindowWillClose:[self window]];
105     [[[self window] contentView] addTrackingArea:trackingArea_.get()];
106   }
107   return self;
108 }
109
110 - (void)showForWebContents:(content::WebContents*)contents
111                 anchoredAt:(NSPoint)anchorPoint
112                  autoClose:(BOOL)autoClose {
113   contents_ = contents;
114   [self onZoomChanged];
115   InfoBubbleWindow* window =
116       base::mac::ObjCCastStrict<InfoBubbleWindow>([self window]);
117   [window setAllowedAnimations:autoClose
118       ? info_bubble::kAnimateOrderIn | info_bubble::kAnimateOrderOut
119       : info_bubble::kAnimateNone];
120
121   self.anchorPoint = anchorPoint;
122   [self showWindow:nil];
123
124   autoClose_ = autoClose;
125   [self updateAutoCloseTimer];
126 }
127
128 - (void)onZoomChanged {
129   if (!contents_)
130     return;  // NULL in tests.
131
132   ZoomController* zoomController = ZoomController::FromWebContents(contents_);
133   if (!zoomController)
134     return;
135
136   int percent = zoomController->zoom_percent();
137   NSString* string =
138       l10n_util::GetNSStringF(IDS_ZOOM_PERCENT, base::IntToString16(percent));
139   [zoomPercent_ setAttributedStringValue:
140       [self attributedStringWithString:string
141                               fontSize:kTextFontSize]];
142
143   [self updateAutoCloseTimer];
144 }
145
146 - (void)resetToDefault:(id)sender {
147   chrome_page_zoom::Zoom(contents_, content::PAGE_ZOOM_RESET);
148 }
149
150 - (void)zoomIn:(id)sender {
151   chrome_page_zoom::Zoom(contents_, content::PAGE_ZOOM_IN);
152 }
153
154 - (void)zoomOut:(id)sender {
155   chrome_page_zoom::Zoom(contents_, content::PAGE_ZOOM_OUT);
156 }
157
158 - (void)closeWithoutAnimation {
159   InfoBubbleWindow* window =
160       base::mac::ObjCCastStrict<InfoBubbleWindow>([self window]);
161   [window setAllowedAnimations:info_bubble::kAnimateNone];
162   [self close];
163 }
164
165 - (void)windowWillClose:(NSNotification*)notification {
166   contents_ = NULL;
167   closeObserver_.get()(self);
168   [NSObject cancelPreviousPerformRequestsWithTarget:self
169                                            selector:@selector(autoCloseBubble)
170                                              object:nil];
171   [super windowWillClose:notification];
172 }
173
174 - (void)mouseEntered:(NSEvent*)theEvent {
175   isMouseInside_ = YES;
176   [self updateAutoCloseTimer];
177 }
178
179 - (void)mouseExited:(NSEvent*)theEvent {
180   isMouseInside_ = NO;
181   [self updateAutoCloseTimer];
182 }
183
184 // Private /////////////////////////////////////////////////////////////////////
185
186 - (void)performLayout {
187   // Zoom out button.
188   NSButton* zoomOutButton = [self addButtonWithTitleID:IDS_ZOOM_MINUS2
189                                               fontSize:kZoomInOutButtonFontSize
190                                                 action:@selector(zoomOut:)];
191   NSRect rect = NSMakeRect(0, 0, kZoomInOutButtonWidth, kWindowHeight);
192   [zoomOutButton setFrame:rect];
193
194   // Zoom label.
195   zoomPercent_.reset([[self addZoomPercentTextField] retain]);
196   rect.origin.x += NSWidth(rect);
197   rect.size.width = kZoomLabelWidth;
198   [zoomPercent_ sizeToFit];
199   NSRect zoomRect = rect;
200   zoomRect.size.height = NSHeight([zoomPercent_ frame]);
201   zoomRect.origin.y = roundf((NSHeight(rect) - NSHeight(zoomRect)) / 2.0);
202   [zoomPercent_ setFrame:zoomRect];
203
204   // Zoom in button.
205   NSButton* zoomInButton = [self addButtonWithTitleID:IDS_ZOOM_PLUS2
206                                              fontSize:kZoomInOutButtonFontSize
207                                                action:@selector(zoomIn:)];
208   rect.origin.x += NSWidth(rect);
209   rect.size.width = kZoomInOutButtonWidth;
210   [zoomInButton setFrame:rect];
211
212   // Separator view.
213   rect.origin.x += NSWidth(rect);
214   rect.size.width = 1;
215   base::scoped_nsobject<NSBox> separatorView(
216       [[NSBox alloc] initWithFrame:rect]);
217   [separatorView setBoxType:NSBoxCustom];
218   ui::NativeTheme* nativeTheme = ui::NativeTheme::instance();
219   [separatorView setBorderColor:
220       gfx::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor(
221           ui::NativeTheme::kColorId_MenuSeparatorColor))];
222   [[[self window] contentView] addSubview:separatorView];
223
224   // Reset zoom button.
225   NSButton* resetButton =
226       [self addButtonWithTitleID:IDS_ZOOM_SET_DEFAULT_SHORT
227                         fontSize:kTextFontSize
228                           action:@selector(resetToDefault:)];
229   rect.origin.x += NSWidth(rect);
230   rect.size.width =
231       [[resetButton attributedTitle] size].width + kResetZoomMargin * 2.0;
232   [resetButton setFrame:rect];
233
234   // Update window frame.
235   NSRect windowFrame = [[self window] frame];
236   windowFrame.size.height = NSHeight(rect);
237   windowFrame.size.width = NSMaxX(rect);
238   [[self window] setFrame:windowFrame display:YES];
239 }
240
241 - (void)autoCloseBubble {
242   if (!autoClose_)
243     return;
244   [self close];
245 }
246
247 - (NSAttributedString*)attributedStringWithString:(NSString*)string
248                                            fontSize:(CGFloat)fontSize {
249   base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
250       [[NSMutableParagraphStyle alloc] init]);
251   [paragraphStyle setAlignment:NSCenterTextAlignment];
252   NSDictionary* attributes = @{
253       NSFontAttributeName:
254       [NSFont systemFontOfSize:fontSize],
255       NSForegroundColorAttributeName:
256       [NSColor colorWithCalibratedWhite:0.58 alpha:1.0],
257       NSParagraphStyleAttributeName:
258       paragraphStyle.get()
259   };
260   return [[[NSAttributedString alloc]
261       initWithString:string
262           attributes:attributes] autorelease];
263 }
264
265 - (NSButton*)addButtonWithTitleID:(int)titleID
266                          fontSize:(CGFloat)fontSize
267                            action:(SEL)action {
268   base::scoped_nsobject<NSButton> button(
269       [[ZoomHoverButton alloc] initWithFrame:NSZeroRect]);
270   NSString* title = l10n_util::GetNSStringWithFixup(titleID);
271   [button setAttributedTitle:[self attributedStringWithString:title
272                                                      fontSize:fontSize]];
273   [[button cell] setBordered:NO];
274   [button setTarget:self];
275   [button setAction:action];
276   [[[self window] contentView] addSubview:button];
277   return button.autorelease();
278 }
279
280 - (NSTextField*)addZoomPercentTextField {
281   base::scoped_nsobject<NSTextField> textField(
282       [[NSTextField alloc] initWithFrame:NSZeroRect]);
283   [textField setEditable:NO];
284   [textField setBordered:NO];
285   [textField setDrawsBackground:NO];
286   [[[self window] contentView] addSubview:textField];
287   return textField.autorelease();
288 }
289
290 - (void)updateAutoCloseTimer {
291   [NSObject cancelPreviousPerformRequestsWithTarget:self
292                                            selector:@selector(autoCloseBubble)
293                                              object:nil];
294   if (autoClose_ && !isMouseInside_) {
295     [self performSelector:@selector(autoCloseBubble)
296                withObject:nil
297                afterDelay:gAutoCloseDelay];
298   }
299 }
300
301 @end
302
303 @implementation ZoomHoverButton
304
305 - (void)drawRect:(NSRect)rect {
306   NSRect bounds = [self bounds];
307   NSAttributedString* title = [self attributedTitle];
308   if ([self hoverState] != kHoverStateNone) {
309     ui::NativeTheme* nativeTheme = ui::NativeTheme::instance();
310     [gfx::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor(
311         ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor)) set];
312     NSRectFillUsingOperation(bounds, NSCompositeSourceOver);
313
314     // Change the title color.
315     base::scoped_nsobject<NSMutableAttributedString> selectedTitle(
316         [[NSMutableAttributedString alloc] initWithAttributedString:title]);
317     NSColor* selectedTitleColor =
318         gfx::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor(
319             ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor));
320     [selectedTitle addAttribute:NSForegroundColorAttributeName
321                           value:selectedTitleColor
322                           range:NSMakeRange(0, [title length])];
323     title = selectedTitle.autorelease();
324   }
325
326   [[self cell] drawTitle:title
327                withFrame:bounds
328                   inView:self];
329 }
330
331 @end