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