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

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

Powered by Google App Engine
This is Rietveld 408576698