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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/ui/cocoa/confirm_bubble_view.mm
===================================================================
--- chrome/browser/ui/cocoa/confirm_bubble_view.mm (revision 0)
+++ chrome/browser/ui/cocoa/confirm_bubble_view.mm (revision 0)
@@ -0,0 +1,295 @@
+// Copyright (c) 2012 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.
+
+#import "chrome/browser/ui/cocoa/confirm_bubble_view.h"
+
+#include "base/string16.h"
+#import "base/mac/cocoa_protocols.h"
+#include "chrome/browser/themes/theme_service.h"
+#import "chrome/browser/ui/cocoa/confirm_bubble_controller.h"
+#include "chrome/browser/ui/confirm_bubble_model.h"
+#import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/point.h"
+
+namespace {
+
+// The width for the message text. We break lines so the specified message fits
+// into this width.
+const int kMaxMessageWidth = 400;
+
+// The corner redius of this bubble view.
+const int kBubbleCornerRadius = 3;
+
+// The color for the border of this bubble view.
+const float kBubbleWindowEdge = 0.7f;
+
+// Constants used for layouting controls. These variables are copied from
+// "ui/views/layout/layout_constants.h".
+// Vertical spacing between a label and some control.
+const int kLabelToControlVerticalSpacing = 8;
+
+// Horizontal spacing between controls that are logically related.
+const int kRelatedControlHorizontalSpacing = 8;
+
+// Vertical spacing between controls that are logically related.
+const int kRelatedControlVerticalSpacing = 8;
+
+// Horizontal spacing between controls that are logically unrelated.
+const int kUnrelatedControlHorizontalSpacing = 12;
+
+// Vertical spacing between the edge of the window and the
+// top or bottom of a button.
+const int kButtonVEdgeMargin = 6;
+
+// Horizontal spacing between the edge of the window and the
+// left or right of a button.
+const int kButtonHEdgeMargin = 7;
+
+} // namespace
+
+void ConfirmBubbleModel::Show(gfx::NativeView view,
+ const gfx::Point& origin,
+ ConfirmBubbleModel* model) {
+ // Create a custom NSViewController that manages a bubble view, and add it to
+ // a child to the specified view. This controller will be automatically
+ // deleted when it loses first-responder status.
+ ConfirmBubbleController* controller =
+ [[ConfirmBubbleController alloc] initWithParent:view
+ origin:origin.ToCGPoint()
+ model:model];
+ [view addSubview:[controller view]
+ positioned:NSWindowAbove
+ relativeTo:nil];
+ [[view window] makeFirstResponder:[controller view]];
+}
+
+// An interface that is derived from NSTextView and does not accept
+// first-responder status, i.e. a NSTextView-derived class that never becomes
+// the first responder. When we click a NSTextView object, it becomes the first
+// responder. Unfortunately, we delete the ConfirmBubbleView object anytime when
+// it loses first-responder status not to prevent disturbing other responders.
+// To prevent text views in this ConfirmBubbleView object from stealing the
+// first-responder status, we use this view in the ConfirmBubbleView object.
+@interface ConfirmBubbleTextView : NSTextView
+@end
+
+@implementation ConfirmBubbleTextView
+
+- (BOOL)acceptsFirstResponder {
+ return NO;
+}
+
+@end
+
+// Private Methods
+@interface ConfirmBubbleView (Private)
+- (void)performLayout;
+- (void)closeBubble;
+@end
+
+@implementation ConfirmBubbleView
+
+- (id)initWithParent:(NSView*)parent
+ controller:(ConfirmBubbleController*)controller {
+ // Create a NSView and set its width. We will set its position and height
+ // after finish layouting controls in performLayout:.
+ NSRect bounds =
+ NSMakeRect(0, 0, kMaxMessageWidth + kButtonHEdgeMargin * 2, 0);
+ if (self = [super initWithFrame:bounds]) {
+ parent_ = parent;
+ controller_ = controller;
+ [self performLayout];
+ }
+ return self;
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+ // Fill the background rectangle in white and draw its edge.
+ NSRect bounds = [self bounds];
+ bounds = NSInsetRect(bounds, 0.5, 0.5);
+ NSBezierPath* border =
+ [NSBezierPath gtm_bezierPathWithRoundRect:bounds
+ topLeftCornerRadius:kBubbleCornerRadius
+ topRightCornerRadius:kBubbleCornerRadius
+ bottomLeftCornerRadius:kBubbleCornerRadius
+ bottomRightCornerRadius:kBubbleCornerRadius];
+ [[NSColor colorWithDeviceWhite:1.0f alpha:1.0f] set];
+ [border fill];
+ [[NSColor colorWithDeviceWhite:kBubbleWindowEdge alpha:1.0f] set];
+ [border stroke];
+}
+
+// An NSResponder method.
+- (BOOL)resignFirstResponder {
+ // We do not only accept this request but also close this bubble when we are
+ // asked to resign the first responder. This bubble should be displayed only
+ // while it is the first responder.
+ [self closeBubble];
+ return YES;
+}
+
+// NSControl action handlers. These handlers are called when we click a cancel
+// button, a close icon, and an OK button, respectively.
+- (IBAction)cancel:(id)sender {
+ [controller_ cancel];
+ [self closeBubble];
+}
+
+- (IBAction)close:(id)sender {
+ [self closeBubble];
+}
+
+- (IBAction)ok:(id)sender {
+ [controller_ accept];
+ [self closeBubble];
+}
+
+// An NSTextViewDelegate method. This function is called when we click a link in
+// this bubble.
+- (BOOL)textView:(NSTextView*)textView
+ clickedOnLink:(id)link
+ atIndex:(NSUInteger)charIndex {
+ [controller_ linkClicked];
+ [self closeBubble];
+ return YES;
+}
+
+// Initializes controls specified by the ConfirmBubbleModel object and layouts
+// them into this bubble. This function retrieves text and images from the
+// ConfirmBubbleModel object (via the ConfirmBubbleController object) and
+// layouts them programmatically. This function layouts controls in the botom-up
+// order since NSView uses bottom-up coordinate.
+- (void)performLayout {
+ NSRect frameRect = [self frame];
+
+ // Add the ok button and the cancel button to the first row if we have either
+ // of them.
+ CGFloat left = kButtonHEdgeMargin;
+ CGFloat right = frameRect.size.width - kButtonHEdgeMargin;
+ CGFloat bottom = kButtonVEdgeMargin;
+ CGFloat height = 0;
+ if ([controller_ hasOkButton]) {
+ okButton_.reset([[NSButton alloc]
+ initWithFrame:NSMakeRect(0, bottom, 0, 0)]);
+ [okButton_.get() setBezelStyle:NSRoundedBezelStyle];
+ [okButton_.get() setTitle:[controller_ okButtonText]];
+ [okButton_.get() setTarget:self];
+ [okButton_.get() setAction:@selector(ok:)];
+ [okButton_.get() sizeToFit];
+ NSRect okButtonRect = [okButton_.get() frame];
+ right -= okButtonRect.size.width;
+ okButtonRect.origin.x = right;
+ [okButton_.get() setFrame:okButtonRect];
+ [self addSubview:okButton_.get()];
+ height = std::max(height, okButtonRect.size.height);
+ }
+ if ([controller_ hasCancelButton]) {
+ cancelButton_.reset([[NSButton alloc]
+ initWithFrame:NSMakeRect(0, bottom, 0, 0)]);
+ [cancelButton_.get() setBezelStyle:NSRoundedBezelStyle];
+ [cancelButton_.get() setTitle:[controller_ cancelButtonText]];
+ [cancelButton_.get() setTarget:self];
+ [cancelButton_.get() setAction:@selector(cancel:)];
+ [cancelButton_.get() sizeToFit];
+ NSRect cancelButtonRect = [cancelButton_.get() frame];
+ right -= cancelButtonRect.size.width + kButtonHEdgeMargin;
+ cancelButtonRect.origin.x = right;
+ [cancelButton_.get() setFrame:cancelButtonRect];
+ [self addSubview:cancelButton_.get()];
+ height = std::max(height, cancelButtonRect.size.height);
+ }
+
+ // Add the message label (and the link label) to the second row.
+ left = kButtonHEdgeMargin;
+ right = frameRect.size.width;
+ bottom += height + kRelatedControlVerticalSpacing;
+ height = 0;
+ messageLabel_.reset([[ConfirmBubbleTextView alloc]
+ initWithFrame:NSMakeRect(left, bottom, kMaxMessageWidth, 0)]);
+ NSString* messageText = [controller_ messageText];
+ NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
+ scoped_nsobject<NSMutableAttributedString> attributedMessage(
+ [[NSMutableAttributedString alloc] initWithString:messageText
+ attributes:attributes]);
+ NSString* linkText = [controller_ linkText];
+ if (linkText) {
+ [attributes setObject:[NSString string]
+ forKey:NSLinkAttributeName];
+ scoped_nsobject<NSAttributedString> attributedLink(
+ [[NSAttributedString alloc] initWithString:linkText
+ attributes:attributes]);
+ [attributedMessage.get() appendAttributedString:attributedLink.get()];
+ }
+ [[messageLabel_.get() textStorage] setAttributedString:attributedMessage];
+ [messageLabel_.get() setHorizontallyResizable:NO];
+ [messageLabel_.get() setVerticallyResizable:YES];
+ [messageLabel_.get() setEditable:NO];
+ [messageLabel_.get() setDrawsBackground:NO];
+ [messageLabel_.get() setDelegate:self];
+ [messageLabel_.get() sizeToFit];
+ height = [messageLabel_.get() frame].size.height;
+ [self addSubview:messageLabel_.get()];
+
+ // Add the icon and the title label to the third row.
+ left = kButtonHEdgeMargin;
+ right = frameRect.size.width;
+ bottom += height + kLabelToControlVerticalSpacing;
+ height = 0;
+ NSImage* iconImage = [controller_ icon];
+ if (iconImage) {
+ icon_.reset([[NSImageView alloc] initWithFrame:NSMakeRect(
+ left, bottom, [iconImage size].width, [iconImage size].height)]);
+ [icon_.get() setImage:iconImage];
+ [self addSubview:icon_.get()];
+ left += [icon_.get() frame].size.width + kRelatedControlHorizontalSpacing;
+ height = std::max(height, [icon_.get() frame].size.height);
+ }
+ titleLabel_.reset([[NSTextView alloc]
+ initWithFrame:NSMakeRect(left, bottom, right - left, 0)]);
+ [titleLabel_.get() setString:[controller_ title]];
+ [titleLabel_.get() setHorizontallyResizable:NO];
+ [titleLabel_.get() setVerticallyResizable:YES];
+ [titleLabel_.get() setEditable:NO];
+ [titleLabel_.get() setSelectable:NO];
+ [titleLabel_.get() setDrawsBackground:NO];
+ [titleLabel_.get() sizeToFit];
+ [self addSubview:titleLabel_.get()];
+ height = std::max(height, [titleLabel_.get() frame].size.height);
+
+ // Adjust the frame rectangle of this bubble so we can show all controls.
+ NSRect parentRect = [parent_ frame];
+ frameRect.size.height = bottom + height + kButtonVEdgeMargin;
+ frameRect.origin.x = (parentRect.size.width - frameRect.size.width) / 2;
+ frameRect.origin.y = parentRect.size.height - frameRect.size.height;
+ [self setFrame:frameRect];
+}
+
+// Closes this bubble and releases all resources. This function just puts the
+// owner ConfirmBubbleController object to the current autorelease pool. (This
+// view will be deleted when the owner object is deleted.)
+- (void)closeBubble {
+ [self removeFromSuperview];
+ [controller_ autorelease];
+ parent_ = nil;
+ controller_ = nil;
+}
+
+@end
+
+@implementation ConfirmBubbleView (ExposedForUnitTesting)
+
+- (void)clickOk {
+ [self ok:self];
+}
+
+- (void)clickCancel {
+ [self cancel:self];
+}
+
+- (void)clickLink {
+ [self textView:messageLabel_.get() clickedOnLink:nil atIndex:0];
+}
+
+@end
Property changes on: chrome/browser/ui/cocoa/confirm_bubble_view.mm
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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