Standardize usage of virtual/override/final in chrome/browser/ui/
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / validation_message_bubble_cocoa.mm
blobef43064e58e5edc8052121cc326c06467e890b6c
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.
5 #include "base/mac/mac_util.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/strings/sys_string_conversions.h"
8 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
9 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
10 #import "chrome/browser/ui/cocoa/validation_message_bubble_controller.h"
11 #include "chrome/browser/ui/validation_message_bubble.h"
12 #include "content/public/browser/render_widget_host.h"
13 #include "content/public/browser/render_widget_host_view.h"
14 #include "grit/theme_resources.h"
15 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
16 #import "ui/base/cocoa/base_view.h"
17 #import "ui/base/cocoa/flipped_view.h"
18 #include "ui/base/resource/resource_bundle.h"
20 const CGFloat kWindowInitialWidth = 200;
21 const CGFloat kWindowInitialHeight = 100;
22 const CGFloat kWindowMinWidth = 64;
23 const CGFloat kWindowMaxWidth = 256;
24 const CGFloat kWindowPadding = 8;
25 const CGFloat kIconTextMargin = 4;
26 const CGFloat kTextVerticalMargin = 4;
28 @implementation ValidationMessageBubbleController
30 - (id)init:(NSWindow*)parentWindow
31 anchoredAt:(NSPoint)anchorPoint
32   mainText:(const base::string16&)mainText
33    subText:(const base::string16&)subText {
35   base::scoped_nsobject<InfoBubbleWindow> window(
36       [[InfoBubbleWindow alloc] initWithContentRect:
37               NSMakeRect(0, 0, kWindowInitialWidth, kWindowInitialHeight)
38                                           styleMask:NSBorderlessWindowMask
39                                             backing:NSBackingStoreBuffered
40                                               defer:NO]);
41   if ((self = [super initWithWindow:window.get()
42                        parentWindow:parentWindow
43                          anchoredAt:anchorPoint])) {
44     [[self bubble] setArrowLocation:info_bubble::kTopLeft];
45     self.shouldOpenAsKeyWindow = NO;
47     NSView* contentView = [ValidationMessageBubbleController
48         constructContentView:mainText subText:subText];
49     [[window contentView] addSubview:contentView];
50     NSRect contentFrame = [contentView frame];
51     NSRect windowFrame = [window frame];
52     windowFrame.size.width = NSWidth(contentFrame) + kWindowPadding * 2;
53     windowFrame.size.height = NSHeight(contentFrame) + kWindowPadding * 2
54         + info_bubble::kBubbleArrowHeight;
55     [window setFrame:windowFrame display:NO];
57     [self showWindow:nil];
58   }
59   return self;
62 + (NSView*)constructContentView:(const base::string16&)mainText
63                         subText:(const base::string16&)subText {
64   NSRect contentFrame = NSMakeRect(kWindowPadding, kWindowPadding, 0, 0);
65   FlippedView* contentView = [[FlippedView alloc] initWithFrame:contentFrame];
67   NSImage* image = ResourceBundle::GetSharedInstance()
68       .GetNativeImageNamed(IDR_INPUT_ALERT).ToNSImage();
69   base::scoped_nsobject<NSImageView> imageView([[NSImageView alloc]
70       initWithFrame:NSMakeRect(0, 0, image.size.width, image.size.height)]);
71   [imageView setImageFrameStyle:NSImageFrameNone];
72   [imageView setImage:image];
73   [contentView addSubview:imageView];
74   contentFrame.size.height = image.size.height;
76   const CGFloat textX = NSWidth([imageView frame]) + kIconTextMargin;
77   NSRect textFrame = NSMakeRect(textX, 0, NSWidth(contentFrame) - textX, 0);
78   base::scoped_nsobject<NSTextField> text(
79       [[NSTextField alloc] initWithFrame:textFrame]);
80   [text setStringValue:base::SysUTF16ToNSString(mainText)];
81   [text setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
82   [text setEditable:NO];
83   [text setBezeled:NO];
84   const CGFloat minTextWidth = kWindowMinWidth - kWindowPadding * 2 - textX;
85   const CGFloat maxTextWidth = kWindowMaxWidth - kWindowPadding * 2 - textX;
86   [text sizeToFit];
87   [contentView addSubview:text];
88   textFrame = [text frame];
89   if (NSWidth(textFrame) < minTextWidth) {
90     textFrame.size.width = minTextWidth;
91     [text setFrame:textFrame];
92   } else if (NSWidth(textFrame) > maxTextWidth) {
93     textFrame.size.width = maxTextWidth;
94     textFrame.size.height = 0;
95     [text setFrame:textFrame];
96     [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:text];
97     textFrame = [text frame];
98   }
99   contentFrame.size.width = NSMaxX(textFrame);
100   contentFrame.size.height =
101       std::max(NSHeight(contentFrame), NSHeight(textFrame));
103   if (!subText.empty()) {
104     NSRect subTextFrame = NSMakeRect(
105         textX, NSMaxY(textFrame) + kTextVerticalMargin,
106         NSWidth(textFrame), 0);
107     base::scoped_nsobject<NSTextField> text2(
108         [[NSTextField alloc] initWithFrame:subTextFrame]);
109     [text2 setStringValue:base::SysUTF16ToNSString(subText)];
110     [text2 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
111     [text2 setEditable:NO];
112     [text2 setBezeled:NO];
113     [text2 sizeToFit];
114     subTextFrame = [text2 frame];
115     if (NSWidth(subTextFrame) > maxTextWidth) {
116       subTextFrame.size.width = maxTextWidth;
117       [text2 setFrame:subTextFrame];
118       [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:text2];
119       subTextFrame = [text2 frame];
120     }
121     [contentView addSubview:text2];
122     contentFrame.size.width =
123         std::max(NSWidth(contentFrame), NSMaxX(subTextFrame));
124     contentFrame.size.height =
125         std::max(NSHeight(contentFrame), NSMaxY(subTextFrame));
126   }
128   [contentView setFrame:contentFrame];
129   return contentView;
133 @end  // implementation ValidationMessageBubbleCocoa
135 // ----------------------------------------------------------------
137 namespace {
139 // Converts |anchor_in_root_view| in rwhv coordinates to cocoa screen
140 // coordinates, and returns an NSPoint at the center of the bottom side of the
141 // converted rectangle.
142 NSPoint GetAnchorPoint(content::RenderWidgetHost* widget_host,
143                        const gfx::Rect& anchor_in_root_view) {
144   BaseView* view = base::mac::ObjCCastStrict<BaseView>(
145       widget_host->GetView()->GetNativeView());
146   NSRect cocoaRect = [view flipRectToNSRect:anchor_in_root_view];
147   NSRect windowRect = [view convertRect:cocoaRect toView:nil];
148   NSPoint point = NSMakePoint(NSMidX(windowRect), NSMinY(windowRect));
149   return [[view window] convertBaseToScreen:point];
152 class ValidationMessageBubbleCocoa : public chrome::ValidationMessageBubble {
153  public:
154   ValidationMessageBubbleCocoa(content::RenderWidgetHost* widget_host,
155                                const gfx::Rect& anchor_in_root_view,
156                                const base::string16& main_text,
157                                const base::string16& sub_text) {
158     controller_.reset([[[ValidationMessageBubbleController alloc]
159                         init:[widget_host->GetView()->GetNativeView() window]
160                   anchoredAt:GetAnchorPoint(widget_host, anchor_in_root_view)
161                     mainText:main_text
162                      subText:sub_text] retain]);
163   }
165   ~ValidationMessageBubbleCocoa() override { [controller_.get() close]; }
167   void SetPositionRelativeToAnchor(
168       content::RenderWidgetHost* widget_host,
169       const gfx::Rect& anchor_in_root_view) override {
170     [controller_.get()
171         setAnchorPoint:GetAnchorPoint(widget_host, anchor_in_root_view)];
172   }
174  private:
175   base::scoped_nsobject<ValidationMessageBubbleController> controller_;
180 // ----------------------------------------------------------------
182 namespace chrome {
184 scoped_ptr<ValidationMessageBubble> ValidationMessageBubble::CreateAndShow(
185     content::RenderWidgetHost* widget_host,
186     const gfx::Rect& anchor_in_root_view,
187     const base::string16& main_text,
188     const base::string16& sub_text) {
189   return scoped_ptr<ValidationMessageBubble>(new ValidationMessageBubbleCocoa(
190       widget_host, anchor_in_root_view, main_text, sub_text)).Pass();