Chromium Code Reviews| 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,301 @@ |
| +// Copyright (c) 2011 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" |
| +#include "chrome/browser/ui/confirm_bubble_model.h" |
| +#import "chrome/browser/ui/cocoa/confirm_bubble_controller.h" |
| +#include "grit/theme_resources.h" |
| +#import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| +#include "ui/gfx/image/image.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 |
| + |
| +// 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 |
| + |
|
Robert Sesek
2011/11/28 15:55:39
nit: remove
Hironori Bono
2011/12/09 11:14:32
Done.
|
| +@end |
| + |
| +@implementation ConfirmBubbleTextView |
| + |
| +- (BOOL)acceptsFirstResponder { |
| + return NO; |
| +} |
| + |
| +@end |
| + |
| +// Private Methods |
| +@interface ConfirmBubbleView(Private) |
| +- (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.
|
| +- (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 initBubble. |
| + NSRect bounds; |
| + bounds.origin.x = bounds.origin.y = 0; |
| + bounds.size.width = kMaxMessageWidth + kButtonHEdgeMargin * 2; |
| + bounds.size.height = 0; |
| + if (self = [super initWithFrame:bounds]) { |
| + parent_ = parent; |
| + controller_ = controller; |
| + [self initBubble]; |
| + } |
| + return self; |
| +} |
| + |
| +- (void)dealloc { |
|
Mark Mentovai
2011/11/28 14:33:06
Not needed.
Hironori Bono
2011/12/09 11:14:32
Done.
|
| + [super dealloc]; |
| +} |
| + |
| +- (void)drawRect:(NSRect)dirtyRect { |
| + // Fill the background rectangle in white and draw its edge. |
| + float topLeftRadius = kBubbleCornerRadius; |
| + float topRightRadius = kBubbleCornerRadius; |
| + float bottomLeftRadius = kBubbleCornerRadius; |
| + float bottomRightRadius = kBubbleCornerRadius; |
| + |
| + NSRect bounds = [self bounds]; |
| + bounds = NSInsetRect(bounds, 0.5, 0.5); |
| + NSBezierPath* border = |
| + [NSBezierPath gtm_bezierPathWithRoundRect:bounds |
| + topLeftCornerRadius:topLeftRadius |
| + topRightCornerRadius:topRightRadius |
| + bottomLeftCornerRadius:bottomLeftRadius |
| + bottomRightCornerRadius:bottomRightRadius]; |
| + |
| + [[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)initBubble { |
| + 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; |
| + CGFloat bottom = kButtonVEdgeMargin; |
| + CGFloat height = 0; |
| + if ([controller_ hasCancelButton]) { |
| + cancelButton_.reset([[NSButton alloc] |
| + initWithFrame:NSMakeRect(0, bottom, 0, 0)]); |
| + [cancelButton_.get() setBezelStyle:NSRoundedBezelStyle]; |
| + [cancelButton_.get() setTitle:[controller_ cancelButton]]; |
| + [cancelButton_.get() setTarget:self]; |
| + [cancelButton_.get() setAction:@selector(cancel:)]; |
| + [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
|
| + 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); |
| + } |
| + if ([controller_ hasOkButton]) { |
| + okButton_.reset([[NSButton alloc] |
| + initWithFrame:NSMakeRect(0, bottom, 0, 0)]); |
| + [okButton_.get() setBezelStyle:NSRoundedBezelStyle]; |
| + [okButton_.get() setTitle:[controller_ okButton]]; |
| + [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); |
| + } |
| + |
| + // 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, the title label and the close button to the third row. We put |
| + // the icon and the close button first so the title can use all remaining |
| + // space. |
| + 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); |
| + } |
| + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| + NSImage* closeImage = |
| + bundle.GetImageNamed(IDR_INFO_BUBBLE_CLOSE).ToNSImage(); |
| + if (closeImage) { |
| + closeButton_.reset([[NSButton alloc] initWithFrame:NSMakeRect( |
| + left, bottom, [closeImage size].width, [closeImage size].height)]); |
| + [closeButton_.get() setImage:closeImage]; |
| + [closeButton_.get() setTarget:self]; |
| + [closeButton_.get() setAction:@selector(close:)]; |
| + [closeButton_.get() sizeToFit]; |
| + NSRect closeButtonRect = [closeButton_.get() frame]; |
| + right -= closeButtonRect.size.width + kButtonHEdgeMargin; |
| + closeButtonRect.origin.x = right; |
| + [closeButton_.get() setFrame:closeButtonRect]; |
| + [self addSubview:closeButton_.get()]; |
| + height = std::max(height, closeButtonRect.size.height); |
| + right -= kUnrelatedControlHorizontalSpacing; |
| + } |
| + 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]; |
| + |
| + // Set this view to the first responder so we can get mouse events. (We will |
| + // resign the first responder and send events to the next responder.) |
| + [[parent_ window] makeFirstResponder:self]; |
| +} |
| + |
| +// Closes this bubble and releases all resources. This function just sets 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 |
| Property changes on: chrome/browser/ui/cocoa/confirm_bubble_view.mm |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |