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

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 #include "chrome/browser/ui/confirm_bubble_model.h"
11 #import "chrome/browser/ui/cocoa/confirm_bubble_controller.h"
12 #include "grit/theme_resources.h"
13 #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/gfx/image/image.h"
16
17 namespace {
18
19 // The width for the message text. We break lines so the specified message fits
20 // into this width.
21 const int kMaxMessageWidth = 400;
22
23 // The corner redius of this bubble view
24 const int kBubbleCornerRadius = 3;
25
26 // The color for the border of this bubble view.
27 const float kBubbleWindowEdge = 0.7f;
28
29 // Constants used for layouting controls. These variables are copied from
30 // "ui/views/layout/layout_constants.h".
31 // Vertical spacing between a label and some control.
32 const int kLabelToControlVerticalSpacing = 8;
33
34 // Horizontal spacing between controls that are logically related.
35 const int kRelatedControlHorizontalSpacing = 8;
36
37 // Vertical spacing between controls that are logically related.
38 const int kRelatedControlVerticalSpacing = 8;
39
40 // Horizontal spacing between controls that are logically unrelated.
41 const int kUnrelatedControlHorizontalSpacing = 12;
42
43 // Vertical spacing between the edge of the window and the
44 // top or bottom of a button.
45 const int kButtonVEdgeMargin = 6;
46
47 // Horizontal spacing between the edge of the window and the
48 // left or right of a button.
49 const int kButtonHEdgeMargin = 7;
50
51 } // namespace
52
53 // An interface that is derived from NSTextView and does not accept
54 // first-responder status, i.e. a NSTextView-derived class that never becomes
55 // the first responder. When we click a NSTextView object, it becomes the first
56 // responder. Unfortunately, we delete the ConfirmBubbleView object anytime when
57 // it loses first-responder status not to prevent disturbing other responders.
58 // To prevent text views in this ConfirmBubbleView object from stealing the
59 // first-responder status, we use this view in the ConfirmBubbleView object.
60 @interface ConfirmBubbleTextView : NSTextView
61
Robert Sesek 2011/11/28 15:55:39 nit: remove
Hironori Bono 2011/12/09 11:14:32 Done.
62 @end
63
64 @implementation ConfirmBubbleTextView
65
66 - (BOOL)acceptsFirstResponder {
67 return NO;
68 }
69
70 @end
71
72 // Private Methods
73 @interface ConfirmBubbleView(Private)
74 - (void)initBubble;
Robert Sesek 2011/11/28 15:55:39 In other code, we name this method |-performLayout
Hironori Bono 2011/12/09 11:14:32 Done. Thanks for your correction.
75 - (void)closeBubble;
76 @end
77
78 @implementation ConfirmBubbleView
79
80 - (id)initWithParent:(NSView*)parent
81 controller:(ConfirmBubbleController*)controller {
82 // Create a NSView and set its width. We will set its position and height
83 // after finish layouting controls in initBubble.
84 NSRect bounds;
85 bounds.origin.x = bounds.origin.y = 0;
86 bounds.size.width = kMaxMessageWidth + kButtonHEdgeMargin * 2;
87 bounds.size.height = 0;
88 if (self = [super initWithFrame:bounds]) {
89 parent_ = parent;
90 controller_ = controller;
91 [self initBubble];
92 }
93 return self;
94 }
95
96 - (void)dealloc {
Mark Mentovai 2011/11/28 14:33:06 Not needed.
Hironori Bono 2011/12/09 11:14:32 Done.
97 [super dealloc];
98 }
99
100 - (void)drawRect:(NSRect)dirtyRect {
101 // Fill the background rectangle in white and draw its edge.
102 float topLeftRadius = kBubbleCornerRadius;
103 float topRightRadius = kBubbleCornerRadius;
104 float bottomLeftRadius = kBubbleCornerRadius;
105 float bottomRightRadius = kBubbleCornerRadius;
106
107 NSRect bounds = [self bounds];
108 bounds = NSInsetRect(bounds, 0.5, 0.5);
109 NSBezierPath* border =
110 [NSBezierPath gtm_bezierPathWithRoundRect:bounds
111 topLeftCornerRadius:topLeftRadius
112 topRightCornerRadius:topRightRadius
113 bottomLeftCornerRadius:bottomLeftRadius
114 bottomRightCornerRadius:bottomRightRadius];
115
116 [[NSColor colorWithDeviceWhite:1.0f alpha:1.0f] set];
117 [border fill];
118 [[NSColor colorWithDeviceWhite:kBubbleWindowEdge alpha:1.0f] set];
119 [border stroke];
120 }
121
122 // An NSResponder method.
123 - (BOOL)resignFirstResponder {
124 // We do not only accept this request but also close this bubble when we are
125 // asked to resign the first responder. This bubble should be displayed only
126 // while it is the first responder.
127 [self closeBubble];
128 return YES;
129 }
130
131 // NSControl action handlers. These handlers are called when we click a cancel
132 // button, a close icon, and an OK button, respectively.
133 - (IBAction)cancel:(id)sender {
134 [controller_ cancel];
135 [self closeBubble];
136 }
137
138 - (IBAction)close:(id)sender {
139 [self closeBubble];
140 }
141
142 - (IBAction)ok:(id)sender {
143 [controller_ accept];
144 [self closeBubble];
145 }
146
147 // An NSTextViewDelegate method. This function is called when we click a link in
148 // this bubble.
149 - (BOOL)textView:(NSTextView*)textView
150 clickedOnLink:(id)link
151 atIndex:(NSUInteger)charIndex {
152 [controller_ linkClicked];
153 [self closeBubble];
154 return YES;
155 }
156
157 // Initializes controls specified by the ConfirmBubbleModel object and layouts
158 // them into this bubble. This function retrieves text and images from the
159 // ConfirmBubbleModel object (via the ConfirmBubbleController object) and
160 // layouts them programmatically. This function layouts controls in the botom-up
161 // order since NSView uses bottom-up coordinate.
162 - (void)initBubble {
163 NSRect frameRect = [self frame];
164
165 // Add the ok button and the cancel button to the first row if we have either
166 // of them.
167 CGFloat left = kButtonHEdgeMargin;
168 CGFloat right = frameRect.size.width;
169 CGFloat bottom = kButtonVEdgeMargin;
170 CGFloat height = 0;
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_ cancelButton]];
176 [cancelButton_.get() setTarget:self];
177 [cancelButton_.get() setAction:@selector(cancel:)];
178 [cancelButton_.get() sizeToFit];
Robert Sesek 2011/11/28 15:55:39 Does this look OK? You may want to use -[GTMUILoca
Hironori Bono 2011/12/09 11:14:32 Thanks for your suggestion. Even though this looks
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 if ([controller_ hasOkButton]) {
187 okButton_.reset([[NSButton alloc]
188 initWithFrame:NSMakeRect(0, bottom, 0, 0)]);
189 [okButton_.get() setBezelStyle:NSRoundedBezelStyle];
190 [okButton_.get() setTitle:[controller_ okButton]];
191 [okButton_.get() setTarget:self];
192 [okButton_.get() setAction:@selector(ok:)];
193 [okButton_.get() sizeToFit];
194 NSRect okButtonRect = [okButton_.get() frame];
195 right -= okButtonRect.size.width;
196 okButtonRect.origin.x = right;
197 [okButton_.get() setFrame:okButtonRect];
198 [self addSubview:okButton_.get()];
199 height = std::max(height, okButtonRect.size.height);
200 }
201
202 // Add the message label (and the link label) to the second row.
203 left = kButtonHEdgeMargin;
204 right = frameRect.size.width;
205 bottom += height + kRelatedControlVerticalSpacing;
206 height = 0;
207 messageLabel_.reset([[ConfirmBubbleTextView alloc]
208 initWithFrame:NSMakeRect(left, bottom, kMaxMessageWidth, 0)]);
209 NSString* messageText = [controller_ messageText];
210 NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
211 scoped_nsobject<NSMutableAttributedString> attributedMessage(
212 [[NSMutableAttributedString alloc] initWithString:messageText
213 attributes:attributes]);
214 NSString* linkText = [controller_ linkText];
215 if (linkText) {
216 [attributes setObject:[NSString string]
217 forKey:NSLinkAttributeName];
218 scoped_nsobject<NSAttributedString> attributedLink(
219 [[NSAttributedString alloc] initWithString:linkText
220 attributes:attributes]);
221 [attributedMessage.get() appendAttributedString:attributedLink.get()];
222 }
223 [[messageLabel_.get() textStorage] setAttributedString:attributedMessage];
224 [messageLabel_.get() setHorizontallyResizable:NO];
225 [messageLabel_.get() setVerticallyResizable:YES];
226 [messageLabel_.get() setEditable:NO];
227 [messageLabel_.get() setDrawsBackground:NO];
228 [messageLabel_.get() setDelegate:self];
229 [messageLabel_.get() sizeToFit];
230 height = [messageLabel_.get() frame].size.height;
231 [self addSubview:messageLabel_.get()];
232
233 // Add the icon, the title label and the close button to the third row. We put
234 // the icon and the close button first so the title can use all remaining
235 // space.
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 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
250 NSImage* closeImage =
251 bundle.GetImageNamed(IDR_INFO_BUBBLE_CLOSE).ToNSImage();
252 if (closeImage) {
253 closeButton_.reset([[NSButton alloc] initWithFrame:NSMakeRect(
254 left, bottom, [closeImage size].width, [closeImage size].height)]);
255 [closeButton_.get() setImage:closeImage];
256 [closeButton_.get() setTarget:self];
257 [closeButton_.get() setAction:@selector(close:)];
258 [closeButton_.get() sizeToFit];
259 NSRect closeButtonRect = [closeButton_.get() frame];
260 right -= closeButtonRect.size.width + kButtonHEdgeMargin;
261 closeButtonRect.origin.x = right;
262 [closeButton_.get() setFrame:closeButtonRect];
263 [self addSubview:closeButton_.get()];
264 height = std::max(height, closeButtonRect.size.height);
265 right -= kUnrelatedControlHorizontalSpacing;
266 }
267 titleLabel_.reset([[NSTextView alloc]
268 initWithFrame:NSMakeRect(left, bottom, right - left, 0)]);
269 [titleLabel_.get() setString:[controller_ title]];
270 [titleLabel_.get() setHorizontallyResizable:NO];
271 [titleLabel_.get() setVerticallyResizable:YES];
272 [titleLabel_.get() setEditable:NO];
273 [titleLabel_.get() setSelectable:NO];
274 [titleLabel_.get() setDrawsBackground:NO];
275 [titleLabel_.get() sizeToFit];
276 [self addSubview:titleLabel_.get()];
277 height = std::max(height, [titleLabel_.get() frame].size.height);
278
279 // Adjust the frame rectangle of this bubble so we can show all controls.
280 NSRect parentRect = [parent_ frame];
281 frameRect.size.height = bottom + height + kButtonVEdgeMargin;
282 frameRect.origin.x = (parentRect.size.width - frameRect.size.width) / 2;
283 frameRect.origin.y = parentRect.size.height - frameRect.size.height;
284 [self setFrame:frameRect];
285
286 // Set this view to the first responder so we can get mouse events. (We will
287 // resign the first responder and send events to the next responder.)
288 [[parent_ window] makeFirstResponder:self];
289 }
290
291 // Closes this bubble and releases all resources. This function just sets the
292 // owner ConfirmBubbleController object to the current autorelease pool. (This
293 // view will be deleted when the owner object is deleted.)
294 - (void)closeBubble {
295 [self removeFromSuperview];
296 [controller_ autorelease];
297 parent_ = nil;
298 controller_ = nil;
299 }
300
301 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698