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