// 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 <Cocoa/Cocoa.h> #import "chrome/browser/ui/cocoa/theme_install_bubble_view.h" #include "base/memory/scoped_nsobject.h" #include "content/common/notification_service.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util_mac.h" namespace { // The alpha of the bubble. static const float kBubbleAlpha = 0.75; // The roundedness of the edges of our bubble. static const int kBubbleCornerRadius = 4; // Padding around text in popup box. static const int kTextHorizPadding = 90; static const int kTextVertPadding = 45; // Point size of the text in the box. static const int kLoadingTextSize = 24; } // static ThemeInstallBubbleView* ThemeInstallBubbleView::view_ = NULL; // The Cocoa view to draw a gray rounded rect with "Loading..." in it. @interface ThemeInstallBubbleViewCocoa : NSView { @private scoped_nsobject<NSAttributedString> message_; NSRect grayRect_; NSRect textRect_; } - (id)init; // The size of the gray rect that will be drawn. - (NSSize)preferredSize; // Forces size calculations of where everything will be drawn. - (void)layout; @end ThemeInstallBubbleView::ThemeInstallBubbleView(NSWindow* window) : cocoa_view_([[ThemeInstallBubbleViewCocoa alloc] init]), num_loads_extant_(1) { DCHECK(window); NSView* parent_view = [window contentView]; NSRect parent_bounds = [parent_view bounds]; if (parent_bounds.size.height < [cocoa_view_ preferredSize].height) Close(); // Close when theme has been installed. registrar_.Add( this, NotificationType::BROWSER_THEME_CHANGED, NotificationService::AllSources()); // Close when we are installing an extension, not a theme. registrar_.Add( this, NotificationType::NO_THEME_DETECTED, NotificationService::AllSources()); registrar_.Add( this, NotificationType::EXTENSION_INSTALLED, NotificationService::AllSources()); registrar_.Add( this, NotificationType::EXTENSION_INSTALL_ERROR, NotificationService::AllSources()); // Don't let the bubble overlap the confirm dialog. registrar_.Add( this, NotificationType::EXTENSION_WILL_SHOW_CONFIRM_DIALOG, NotificationService::AllSources()); // Add the view. [cocoa_view_ setFrame:parent_bounds]; [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [parent_view addSubview:cocoa_view_ positioned:NSWindowAbove relativeTo:nil]; [cocoa_view_ layout]; } ThemeInstallBubbleView::~ThemeInstallBubbleView() { // Need to delete self; the real work happens in Close(). } void ThemeInstallBubbleView::Close() { --num_loads_extant_; if (num_loads_extant_ < 1) { registrar_.RemoveAll(); if (cocoa_view_ && [cocoa_view_ superview]) { [cocoa_view_ removeFromSuperview]; [cocoa_view_ release]; } view_ = NULL; delete this; // this is deleted; nothing more! } } void ThemeInstallBubbleView::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { Close(); } // static void ThemeInstallBubbleView::Show(NSWindow* window) { if (view_) ++view_->num_loads_extant_; else view_ = new ThemeInstallBubbleView(window); } @implementation ThemeInstallBubbleViewCocoa - (id)init { self = [super initWithFrame:NSZeroRect]; if (self) { NSString* loadingString = l10n_util::GetNSStringWithFixup(IDS_THEME_LOADING_TITLE); NSFont* loadingFont = [NSFont systemFontOfSize:kLoadingTextSize]; NSColor* textColor = [NSColor whiteColor]; NSDictionary* loadingAttrs = [NSDictionary dictionaryWithObjectsAndKeys: loadingFont, NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil]; message_.reset([[NSAttributedString alloc] initWithString:loadingString attributes:loadingAttrs]); // TODO(avi): find a white-on-black spinner } return self; } - (NSSize)preferredSize { NSSize size = [message_.get() size]; size.width += kTextHorizPadding; size.height += kTextVertPadding; return size; } // Update the layout to keep the view centered when the window is resized. - (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize { [super resizeWithOldSuperviewSize:oldBoundsSize]; [self layout]; } - (void)layout { NSRect bounds = [self bounds]; grayRect_.size = [self preferredSize]; grayRect_.origin.x = (bounds.size.width - grayRect_.size.width) / 2; grayRect_.origin.y = bounds.size.height / 2; textRect_.size = [message_.get() size]; textRect_.origin.x = (bounds.size.width - [message_.get() size].width) / 2; textRect_.origin.y = (bounds.size.height + kTextVertPadding) / 2; } - (void)drawRect:(NSRect)dirtyRect { [[NSColor clearColor] set]; NSRectFillUsingOperation([self bounds], NSCompositeSourceOver); [[[NSColor blackColor] colorWithAlphaComponent:kBubbleAlpha] set]; [[NSBezierPath bezierPathWithRoundedRect:grayRect_ xRadius:kBubbleCornerRadius yRadius:kBubbleCornerRadius] fill]; [message_.get() drawInRect:textRect_]; } @end