Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(754)

Side by Side Diff: chrome/browser/ui/cocoa/confirm_bubble_view.mm

Issue 8697001: Implement confirm bubble for Mac. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "chrome/browser/ui/cocoa/confirm_bubble_view.h"
6
7 #include "base/string16.h"
8 #import "base/mac/cocoa_protocols.h"
9 #include "chrome/browser/themes/theme_service.h"
10 #import "chrome/browser/ui/cocoa/confirm_bubble_controller.h"
11 #include "chrome/browser/ui/confirm_bubble_model.h"
12 #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h"
13 #include "ui/gfx/image/image.h"
14 #include "ui/gfx/point.h"
15
16 namespace {
17
18 // The width for the message text. We break lines so the specified message fits
19 // into this width.
20 const int kMaxMessageWidth = 400;
21
22 // The corner redius of this bubble view.
23 const int kBubbleCornerRadius = 3;
24
25 // The color for the border of this bubble view.
26 const float kBubbleWindowEdge = 0.7f;
27
28 // Constants used for layouting controls. These variables are copied from
29 // "ui/views/layout/layout_constants.h".
30 // Vertical spacing between a label and some control.
31 const int kLabelToControlVerticalSpacing = 8;
32
33 // Horizontal spacing between controls that are logically related.
34 const int kRelatedControlHorizontalSpacing = 8;
35
36 // Vertical spacing between controls that are logically related.
37 const int kRelatedControlVerticalSpacing = 8;
38
39 // Horizontal spacing between controls that are logically unrelated.
40 const int kUnrelatedControlHorizontalSpacing = 12;
41
42 // Vertical spacing between the edge of the window and the
43 // top or bottom of a button.
44 const int kButtonVEdgeMargin = 6;
45
46 // Horizontal spacing between the edge of the window and the
47 // left or right of a button.
48 const int kButtonHEdgeMargin = 7;
49
50 } // namespace
51
52 void ConfirmBubbleModel::Show(gfx::NativeView view,
53 const gfx::Point& origin,
54 ConfirmBubbleModel* model) {
55 // Create a custom NSViewController that manages a bubble view, and add it to
56 // a child to the specified view. This controller will be automatically
57 // deleted when it loses first-responder status.
58 ConfirmBubbleController* controller =
59 [[ConfirmBubbleController alloc] initWithParent:view
60 origin:origin.ToCGPoint()
61 model:model];
62 [view addSubview:[controller view]
63 positioned:NSWindowAbove
64 relativeTo:nil];
65 [[view window] makeFirstResponder:[controller view]];
66 }
67
68 // An interface that is derived from NSTextView and does not accept
69 // first-responder status, i.e. a NSTextView-derived class that never becomes
70 // the first responder. When we click a NSTextView object, it becomes the first
71 // responder. Unfortunately, we delete the ConfirmBubbleView object anytime when
72 // it loses first-responder status not to prevent disturbing other responders.
73 // To prevent text views in this ConfirmBubbleView object from stealing the
74 // first-responder status, we use this view in the ConfirmBubbleView object.
75 @interface ConfirmBubbleTextView : NSTextView
76 @end
77
78 @implementation ConfirmBubbleTextView
79
80 - (BOOL)acceptsFirstResponder {
81 return NO;
82 }
83
84 @end
85
86 // Private Methods
87 @interface ConfirmBubbleView (Private)
88 - (void)performLayout;
89 - (void)closeBubble;
90 @end
91
92 @implementation ConfirmBubbleView
93
94 - (id)initWithParent:(NSView*)parent
95 controller:(ConfirmBubbleController*)controller {
96 // Create a NSView and set its width. We will set its position and height
97 // after finish layouting controls in performLayout:.
98 NSRect bounds =
99 NSMakeRect(0, 0, kMaxMessageWidth + kButtonHEdgeMargin * 2, 0);
100 if (self = [super initWithFrame:bounds]) {
101 parent_ = parent;
102 controller_ = controller;
103 [self performLayout];
104 }
105 return self;
106 }
107
108 - (void)drawRect:(NSRect)dirtyRect {
109 // Fill the background rectangle in white and draw its edge.
110 NSRect bounds = [self bounds];
111 bounds = NSInsetRect(bounds, 0.5, 0.5);
112 NSBezierPath* border =
113 [NSBezierPath gtm_bezierPathWithRoundRect:bounds
114 topLeftCornerRadius:kBubbleCornerRadius
115 topRightCornerRadius:kBubbleCornerRadius
116 bottomLeftCornerRadius:kBubbleCornerRadius
117 bottomRightCornerRadius:kBubbleCornerRadius];
118 [[NSColor colorWithDeviceWhite:1.0f alpha:1.0f] set];
119 [border fill];
120 [[NSColor colorWithDeviceWhite:kBubbleWindowEdge alpha:1.0f] set];
121 [border stroke];
122 }
123
124 // An NSResponder method.
125 - (BOOL)resignFirstResponder {
126 // We do not only accept this request but also close this bubble when we are
127 // asked to resign the first responder. This bubble should be displayed only
128 // while it is the first responder.
129 [self closeBubble];
130 return YES;
131 }
132
133 // NSControl action handlers. These handlers are called when we click a cancel
134 // button, a close icon, and an OK button, respectively.
135 - (IBAction)cancel:(id)sender {
136 [controller_ cancel];
137 [self closeBubble];
138 }
139
140 - (IBAction)close:(id)sender {
141 [self closeBubble];
142 }
143
144 - (IBAction)ok:(id)sender {
145 [controller_ accept];
146 [self closeBubble];
147 }
148
149 // An NSTextViewDelegate method. This function is called when we click a link in
150 // this bubble.
151 - (BOOL)textView:(NSTextView*)textView
152 clickedOnLink:(id)link
153 atIndex:(NSUInteger)charIndex {
154 [controller_ linkClicked];
155 [self closeBubble];
156 return YES;
157 }
158
159 // Initializes controls specified by the ConfirmBubbleModel object and layouts
160 // them into this bubble. This function retrieves text and images from the
161 // ConfirmBubbleModel object (via the ConfirmBubbleController object) and
162 // layouts them programmatically. This function layouts controls in the botom-up
163 // order since NSView uses bottom-up coordinate.
164 - (void)performLayout {
165 NSRect frameRect = [self frame];
166
167 // Add the ok button and the cancel button to the first row if we have either
168 // of them.
169 CGFloat left = kButtonHEdgeMargin;
170 CGFloat right = frameRect.size.width - kButtonHEdgeMargin;
171 CGFloat bottom = kButtonVEdgeMargin;
172 CGFloat height = 0;
173 if ([controller_ hasOkButton]) {
174 okButton_.reset([[NSButton alloc]
175 initWithFrame:NSMakeRect(0, bottom, 0, 0)]);
176 [okButton_.get() setBezelStyle:NSRoundedBezelStyle];
177 [okButton_.get() setTitle:[controller_ okButtonText]];
178 [okButton_.get() setTarget:self];
179 [okButton_.get() setAction:@selector(ok:)];
180 [okButton_.get() sizeToFit];
181 NSRect okButtonRect = [okButton_.get() frame];
182 right -= okButtonRect.size.width;
183 okButtonRect.origin.x = right;
184 [okButton_.get() setFrame:okButtonRect];
185 [self addSubview:okButton_.get()];
186 height = std::max(height, okButtonRect.size.height);
187 }
188 if ([controller_ hasCancelButton]) {
189 cancelButton_.reset([[NSButton alloc]
190 initWithFrame:NSMakeRect(0, bottom, 0, 0)]);
191 [cancelButton_.get() setBezelStyle:NSRoundedBezelStyle];
192 [cancelButton_.get() setTitle:[controller_ cancelButtonText]];
193 [cancelButton_.get() setTarget:self];
194 [cancelButton_.get() setAction:@selector(cancel:)];
195 [cancelButton_.get() sizeToFit];
196 NSRect cancelButtonRect = [cancelButton_.get() frame];
197 right -= cancelButtonRect.size.width + kButtonHEdgeMargin;
198 cancelButtonRect.origin.x = right;
199 [cancelButton_.get() setFrame:cancelButtonRect];
200 [self addSubview:cancelButton_.get()];
201 height = std::max(height, cancelButtonRect.size.height);
202 }
203
204 // Add the message label (and the link label) to the second row.
205 left = kButtonHEdgeMargin;
206 right = frameRect.size.width;
207 bottom += height + kRelatedControlVerticalSpacing;
208 height = 0;
209 messageLabel_.reset([[ConfirmBubbleTextView alloc]
210 initWithFrame:NSMakeRect(left, bottom, kMaxMessageWidth, 0)]);
211 NSString* messageText = [controller_ messageText];
212 NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
213 scoped_nsobject<NSMutableAttributedString> attributedMessage(
214 [[NSMutableAttributedString alloc] initWithString:messageText
215 attributes:attributes]);
216 NSString* linkText = [controller_ linkText];
217 if (linkText) {
218 [attributes setObject:[NSString string]
219 forKey:NSLinkAttributeName];
220 scoped_nsobject<NSAttributedString> attributedLink(
221 [[NSAttributedString alloc] initWithString:linkText
222 attributes:attributes]);
223 [attributedMessage.get() appendAttributedString:attributedLink.get()];
224 }
225 [[messageLabel_.get() textStorage] setAttributedString:attributedMessage];
226 [messageLabel_.get() setHorizontallyResizable:NO];
227 [messageLabel_.get() setVerticallyResizable:YES];
228 [messageLabel_.get() setEditable:NO];
229 [messageLabel_.get() setDrawsBackground:NO];
230 [messageLabel_.get() setDelegate:self];
231 [messageLabel_.get() sizeToFit];
232 height = [messageLabel_.get() frame].size.height;
233 [self addSubview:messageLabel_.get()];
234
235 // Add the icon and the title label to the third row.
236 left = kButtonHEdgeMargin;
237 right = frameRect.size.width;
238 bottom += height + kLabelToControlVerticalSpacing;
239 height = 0;
240 NSImage* iconImage = [controller_ icon];
241 if (iconImage) {
242 icon_.reset([[NSImageView alloc] initWithFrame:NSMakeRect(
243 left, bottom, [iconImage size].width, [iconImage size].height)]);
244 [icon_.get() setImage:iconImage];
245 [self addSubview:icon_.get()];
246 left += [icon_.get() frame].size.width + kRelatedControlHorizontalSpacing;
247 height = std::max(height, [icon_.get() frame].size.height);
248 }
249 titleLabel_.reset([[NSTextView alloc]
250 initWithFrame:NSMakeRect(left, bottom, right - left, 0)]);
251 [titleLabel_.get() setString:[controller_ title]];
252 [titleLabel_.get() setHorizontallyResizable:NO];
253 [titleLabel_.get() setVerticallyResizable:YES];
254 [titleLabel_.get() setEditable:NO];
255 [titleLabel_.get() setSelectable:NO];
256 [titleLabel_.get() setDrawsBackground:NO];
257 [titleLabel_.get() sizeToFit];
258 [self addSubview:titleLabel_.get()];
259 height = std::max(height, [titleLabel_.get() frame].size.height);
260
261 // Adjust the frame rectangle of this bubble so we can show all controls.
262 NSRect parentRect = [parent_ frame];
263 frameRect.size.height = bottom + height + kButtonVEdgeMargin;
264 frameRect.origin.x = (parentRect.size.width - frameRect.size.width) / 2;
265 frameRect.origin.y = parentRect.size.height - frameRect.size.height;
266 [self setFrame:frameRect];
267 }
268
269 // Closes this bubble and releases all resources. This function just puts the
270 // owner ConfirmBubbleController object to the current autorelease pool. (This
271 // view will be deleted when the owner object is deleted.)
272 - (void)closeBubble {
273 [self removeFromSuperview];
274 [controller_ autorelease];
275 parent_ = nil;
276 controller_ = nil;
277 }
278
279 @end
280
281 @implementation ConfirmBubbleView (ExposedForUnitTesting)
282
283 - (void)clickOk {
284 [self ok:self];
285 }
286
287 - (void)clickCancel {
288 [self cancel:self];
289 }
290
291 - (void)clickLink {
292 [self textView:messageLabel_.get() clickedOnLink:nil atIndex:0];
293 }
294
295 @end
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/confirm_bubble_view.h ('k') | chrome/browser/ui/confirm_bubble_model.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698