Home | History | Annotate | Download | only in cocoa
      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 <Cocoa/Cocoa.h>
      6 
      7 #import "chrome/browser/ui/cocoa/theme_install_bubble_view.h"
      8 
      9 #include "base/memory/scoped_nsobject.h"
     10 #include "content/common/notification_service.h"
     11 #include "grit/generated_resources.h"
     12 #include "ui/base/l10n/l10n_util_mac.h"
     13 
     14 namespace {
     15 
     16 // The alpha of the bubble.
     17 static const float kBubbleAlpha = 0.75;
     18 
     19 // The roundedness of the edges of our bubble.
     20 static const int kBubbleCornerRadius = 4;
     21 
     22 // Padding around text in popup box.
     23 static const int kTextHorizPadding = 90;
     24 static const int kTextVertPadding = 45;
     25 
     26 // Point size of the text in the box.
     27 static const int kLoadingTextSize = 24;
     28 
     29 }
     30 
     31 // static
     32 ThemeInstallBubbleView* ThemeInstallBubbleView::view_ = NULL;
     33 
     34 // The Cocoa view to draw a gray rounded rect with "Loading..." in it.
     35 @interface ThemeInstallBubbleViewCocoa : NSView {
     36  @private
     37   scoped_nsobject<NSAttributedString> message_;
     38 
     39   NSRect grayRect_;
     40   NSRect textRect_;
     41 }
     42 
     43 - (id)init;
     44 
     45 // The size of the gray rect that will be drawn.
     46 - (NSSize)preferredSize;
     47 // Forces size calculations of where everything will be drawn.
     48 - (void)layout;
     49 
     50 @end
     51 
     52 ThemeInstallBubbleView::ThemeInstallBubbleView(NSWindow* window)
     53     : cocoa_view_([[ThemeInstallBubbleViewCocoa alloc] init]),
     54       num_loads_extant_(1) {
     55   DCHECK(window);
     56 
     57   NSView* parent_view = [window contentView];
     58   NSRect parent_bounds = [parent_view bounds];
     59   if (parent_bounds.size.height < [cocoa_view_ preferredSize].height)
     60     Close();
     61 
     62   // Close when theme has been installed.
     63   registrar_.Add(
     64       this,
     65       NotificationType::BROWSER_THEME_CHANGED,
     66       NotificationService::AllSources());
     67 
     68   // Close when we are installing an extension, not a theme.
     69   registrar_.Add(
     70       this,
     71       NotificationType::NO_THEME_DETECTED,
     72       NotificationService::AllSources());
     73   registrar_.Add(
     74       this,
     75       NotificationType::EXTENSION_INSTALLED,
     76       NotificationService::AllSources());
     77   registrar_.Add(
     78       this,
     79       NotificationType::EXTENSION_INSTALL_ERROR,
     80       NotificationService::AllSources());
     81 
     82   // Don't let the bubble overlap the confirm dialog.
     83   registrar_.Add(
     84       this,
     85       NotificationType::EXTENSION_WILL_SHOW_CONFIRM_DIALOG,
     86       NotificationService::AllSources());
     87 
     88   // Add the view.
     89   [cocoa_view_ setFrame:parent_bounds];
     90   [cocoa_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
     91   [parent_view addSubview:cocoa_view_
     92                positioned:NSWindowAbove
     93                relativeTo:nil];
     94   [cocoa_view_ layout];
     95 }
     96 
     97 ThemeInstallBubbleView::~ThemeInstallBubbleView() {
     98   // Need to delete self; the real work happens in Close().
     99 }
    100 
    101 void ThemeInstallBubbleView::Close() {
    102   --num_loads_extant_;
    103   if (num_loads_extant_ < 1) {
    104     registrar_.RemoveAll();
    105     if (cocoa_view_ && [cocoa_view_ superview]) {
    106       [cocoa_view_ removeFromSuperview];
    107       [cocoa_view_ release];
    108     }
    109     view_ = NULL;
    110     delete this;
    111     // this is deleted; nothing more!
    112   }
    113 }
    114 
    115 void ThemeInstallBubbleView::Observe(NotificationType type,
    116                                      const NotificationSource& source,
    117                                      const NotificationDetails& details) {
    118   Close();
    119 }
    120 
    121 // static
    122 void ThemeInstallBubbleView::Show(NSWindow* window) {
    123   if (view_)
    124     ++view_->num_loads_extant_;
    125   else
    126     view_ = new ThemeInstallBubbleView(window);
    127 }
    128 
    129 @implementation ThemeInstallBubbleViewCocoa
    130 
    131 - (id)init {
    132   self = [super initWithFrame:NSZeroRect];
    133   if (self) {
    134     NSString* loadingString =
    135         l10n_util::GetNSStringWithFixup(IDS_THEME_LOADING_TITLE);
    136     NSFont* loadingFont = [NSFont systemFontOfSize:kLoadingTextSize];
    137     NSColor* textColor = [NSColor whiteColor];
    138     NSDictionary* loadingAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
    139                                   loadingFont, NSFontAttributeName,
    140                                   textColor, NSForegroundColorAttributeName,
    141                                   nil];
    142     message_.reset([[NSAttributedString alloc] initWithString:loadingString
    143                                                    attributes:loadingAttrs]);
    144 
    145     // TODO(avi): find a white-on-black spinner
    146   }
    147   return self;
    148 }
    149 
    150 - (NSSize)preferredSize {
    151   NSSize size = [message_.get() size];
    152   size.width += kTextHorizPadding;
    153   size.height += kTextVertPadding;
    154   return size;
    155 }
    156 
    157 // Update the layout to keep the view centered when the window is resized.
    158 - (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize {
    159   [super resizeWithOldSuperviewSize:oldBoundsSize];
    160   [self layout];
    161 }
    162 
    163 - (void)layout {
    164   NSRect bounds = [self bounds];
    165 
    166   grayRect_.size = [self preferredSize];
    167   grayRect_.origin.x = (bounds.size.width - grayRect_.size.width) / 2;
    168   grayRect_.origin.y = bounds.size.height / 2;
    169 
    170   textRect_.size = [message_.get() size];
    171   textRect_.origin.x = (bounds.size.width - [message_.get() size].width) / 2;
    172   textRect_.origin.y = (bounds.size.height + kTextVertPadding) / 2;
    173 }
    174 
    175 - (void)drawRect:(NSRect)dirtyRect {
    176   [[NSColor clearColor] set];
    177   NSRectFillUsingOperation([self bounds], NSCompositeSourceOver);
    178 
    179   [[[NSColor blackColor] colorWithAlphaComponent:kBubbleAlpha] set];
    180   [[NSBezierPath bezierPathWithRoundedRect:grayRect_
    181                                    xRadius:kBubbleCornerRadius
    182                                    yRadius:kBubbleCornerRadius] fill];
    183 
    184   [message_.get() drawInRect:textRect_];
    185 }
    186 
    187 @end
    188