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 #include "chrome/browser/ui/views/theme_install_bubble_view.h" 6 7 #include "base/utf_string_conversions.h" 8 #include "content/browser/tab_contents/tab_contents.h" 9 #include "content/common/notification_service.h" 10 #include "grit/generated_resources.h" 11 #include "ui/base/l10n/l10n_util.h" 12 #include "ui/base/resource/resource_bundle.h" 13 #include "ui/gfx/canvas_skia.h" 14 #include "views/widget/widget.h" 15 16 namespace { 17 18 // The roundedness of the edges of our bubble. 19 static const int kBubbleCornerRadius = 4; 20 21 // Padding around text in popup box. 22 static const int kTextHorizPadding = 90; 23 static const int kTextVertPadding = 45; 24 25 // Multiple loads can be started at once. Only show one bubble, and keep 26 // track of number of loads happening. Close bubble when num_loads < 1. 27 static int num_loads_extant_ = 0; 28 29 } // namespace 30 31 ThemeInstallBubbleView::ThemeInstallBubbleView(TabContents* tab_contents) 32 : popup_(NULL) { 33 if (!tab_contents) 34 Close(); 35 36 text_ = l10n_util::GetStringUTF16(IDS_THEME_LOADING_TITLE); 37 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 38 gfx::Font font(rb.GetFont(ResourceBundle::LargeFont)); 39 SetFont(font); 40 41 // We can't check for the size of tab_contents before we've generated 42 // the string and the font that determine the size of the bubble. 43 tab_contents->GetContainerBounds(&tab_contents_bounds_); 44 if (tab_contents_bounds_.height() < GetPreferredSize().height()) 45 Close(); 46 47 // Close when theme has been installed. 48 registrar_.Add( 49 this, 50 NotificationType::BROWSER_THEME_CHANGED, 51 NotificationService::AllSources()); 52 53 // Close when we are installing an extension, not a theme. 54 registrar_.Add( 55 this, 56 NotificationType::NO_THEME_DETECTED, 57 NotificationService::AllSources()); 58 registrar_.Add( 59 this, 60 NotificationType::EXTENSION_INSTALLED, 61 NotificationService::AllSources()); 62 registrar_.Add( 63 this, 64 NotificationType::EXTENSION_INSTALL_ERROR, 65 NotificationService::AllSources()); 66 67 // Don't let the bubble overlap the confirm dialog. 68 registrar_.Add( 69 this, 70 NotificationType::EXTENSION_WILL_SHOW_CONFIRM_DIALOG, 71 NotificationService::AllSources()); 72 73 gfx::Rect rc(0, 0, 0, 0); 74 views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP); 75 params.transparent = true; 76 params.accept_events = false; 77 popup_ = views::Widget::CreateWidget(params); 78 popup_->SetOpacity(0xCC); 79 popup_->Init(tab_contents->GetNativeView(), rc); 80 popup_->SetContentsView(this); 81 Reposition(); 82 popup_->Show(); 83 84 SchedulePaint(); 85 } 86 87 ThemeInstallBubbleView::~ThemeInstallBubbleView() { 88 num_loads_extant_ = 0; 89 } 90 91 gfx::Size ThemeInstallBubbleView::GetPreferredSize() { 92 return gfx::Size(views::Label::font().GetStringWidth(text_) + 93 kTextHorizPadding, 94 ResourceBundle::GetSharedInstance().GetFont( 95 ResourceBundle::LargeFont).GetHeight() + kTextVertPadding); 96 } 97 98 void ThemeInstallBubbleView::Reposition() { 99 if (!popup_) 100 Close(); 101 102 gfx::Size size = GetPreferredSize(); 103 int mid_x = tab_contents_bounds_.x() + 104 (tab_contents_bounds_.right() - tab_contents_bounds_.x()) / 2; 105 106 int x = base::i18n::IsRTL() ? 107 mid_x + size.width() / 2 : mid_x - size.width() / 2; 108 int y = static_cast<int>(tab_contents_bounds_.y() + 109 (tab_contents_bounds_.bottom() - tab_contents_bounds_.y()) / 2 - 110 size.height() / 2); 111 112 popup_->SetBounds(gfx::Rect(x, y, size.width(), size.height())); 113 } 114 115 void ThemeInstallBubbleView::OnPaint(gfx::Canvas* canvas) { 116 SkScalar rad[8]; 117 for (int i = 0; i < 8; ++i) 118 rad[i] = SkIntToScalar(kBubbleCornerRadius); 119 120 SkPaint paint; 121 paint.setStyle(SkPaint::kFill_Style); 122 paint.setFlags(SkPaint::kAntiAlias_Flag); 123 paint.setColor(SK_ColorBLACK); 124 125 SkRect rect; 126 rect.set(0, 0, 127 SkIntToScalar(width()), 128 SkIntToScalar(height())); 129 SkPath path; 130 path.addRoundRect(rect, rad, SkPath::kCW_Direction); 131 canvas->AsCanvasSkia()->drawPath(path, paint); 132 133 int text_width = views::Label::font().GetStringWidth(text_); 134 gfx::Rect body_bounds(kTextHorizPadding / 2, 0, text_width, height()); 135 body_bounds.set_x(GetMirroredXForRect(body_bounds)); 136 137 SkColor text_color = SK_ColorWHITE; 138 canvas->DrawStringInt(text_, 139 views::Label::font(), 140 text_color, 141 body_bounds.x(), 142 body_bounds.y(), 143 body_bounds.width(), 144 body_bounds.height()); 145 } 146 147 void ThemeInstallBubbleView::Close() { 148 --num_loads_extant_; 149 if (!popup_) { 150 num_loads_extant_ = 0; 151 return; 152 } 153 if (num_loads_extant_ < 1) { 154 registrar_.RemoveAll(); 155 popup_->Close(); 156 } 157 } 158 159 void ThemeInstallBubbleView::Observe(NotificationType type, 160 const NotificationSource& source, 161 const NotificationDetails& details) { 162 Close(); 163 } 164 165 // static 166 void ThemeInstallBubbleView::Show(TabContents* tab_contents) { 167 ++num_loads_extant_; 168 if (num_loads_extant_ < 2) 169 new ThemeInstallBubbleView(tab_contents); 170 } 171