• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
14namespace {
15
16// The alpha of the bubble.
17static const float kBubbleAlpha = 0.75;
18
19// The roundedness of the edges of our bubble.
20static const int kBubbleCornerRadius = 4;
21
22// Padding around text in popup box.
23static const int kTextHorizPadding = 90;
24static const int kTextVertPadding = 45;
25
26// Point size of the text in the box.
27static const int kLoadingTextSize = 24;
28
29}
30
31// static
32ThemeInstallBubbleView* 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
52ThemeInstallBubbleView::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
97ThemeInstallBubbleView::~ThemeInstallBubbleView() {
98  // Need to delete self; the real work happens in Close().
99}
100
101void 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
115void ThemeInstallBubbleView::Observe(NotificationType type,
116                                     const NotificationSource& source,
117                                     const NotificationDetails& details) {
118  Close();
119}
120
121// static
122void 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