| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/views/extensions/extension_installed_bubble_view.h" | 5 #include "chrome/browser/ui/views/extensions/extension_installed_bubble_view.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "chrome/browser/extensions/extension_action_manager.h" | 10 #include "chrome/browser/extensions/extension_action_manager.h" |
| 11 #include "chrome/browser/profiles/profile.h" | 11 #include "chrome/browser/profiles/profile.h" |
| 12 #include "chrome/browser/ui/browser.h" | 12 #include "chrome/browser/ui/browser.h" |
| 13 #include "chrome/browser/ui/browser_window.h" | 13 #include "chrome/browser/ui/browser_window.h" |
| 14 #include "chrome/browser/ui/chrome_pages.h" | 14 #include "chrome/browser/ui/chrome_pages.h" |
| 15 #include "chrome/browser/ui/singleton_tabs.h" | 15 #include "chrome/browser/ui/singleton_tabs.h" |
| 16 #include "chrome/browser/ui/sync/sync_promo_ui.h" | 16 #include "chrome/browser/ui/sync/sync_promo_ui.h" |
| 17 #include "chrome/browser/ui/views/frame/browser_view.h" | 17 #include "chrome/browser/ui/views/frame/browser_view.h" |
| 18 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" | 18 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" |
| 19 #include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h" | 19 #include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h" |
| 20 #include "chrome/browser/ui/views/sync/bubble_sync_promo_view.h" |
| 20 #include "chrome/browser/ui/views/toolbar/app_menu_button.h" | 21 #include "chrome/browser/ui/views/toolbar/app_menu_button.h" |
| 21 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h" | 22 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h" |
| 22 #include "chrome/browser/ui/views/toolbar/toolbar_view.h" | 23 #include "chrome/browser/ui/views/toolbar/toolbar_view.h" |
| 23 #include "chrome/common/extensions/sync_helper.h" | 24 #include "chrome/common/extensions/sync_helper.h" |
| 24 #include "chrome/common/url_constants.h" | 25 #include "chrome/common/url_constants.h" |
| 25 #include "chrome/grit/chromium_strings.h" | 26 #include "chrome/grit/chromium_strings.h" |
| 26 #include "chrome/grit/generated_resources.h" | 27 #include "chrome/grit/generated_resources.h" |
| 27 #include "components/bubble/bubble_controller.h" | 28 #include "components/bubble/bubble_controller.h" |
| 28 #include "components/bubble/bubble_ui.h" | 29 #include "components/bubble/bubble_ui.h" |
| 29 #include "extensions/common/extension.h" | 30 #include "extensions/common/extension.h" |
| 30 #include "extensions/common/feature_switch.h" | 31 #include "extensions/common/feature_switch.h" |
| 31 #include "ui/base/l10n/l10n_util.h" | 32 #include "ui/base/l10n/l10n_util.h" |
| 32 #include "ui/base/resource/resource_bundle.h" | 33 #include "ui/base/resource/resource_bundle.h" |
| 33 #include "ui/gfx/render_text.h" | 34 #include "ui/gfx/render_text.h" |
| 34 #include "ui/gfx/text_elider.h" | 35 #include "ui/gfx/text_elider.h" |
| 35 #include "ui/resources/grit/ui_resources.h" | 36 #include "ui/resources/grit/ui_resources.h" |
| 37 #include "ui/views/bubble/bubble_frame_view.h" |
| 36 #include "ui/views/controls/button/image_button.h" | 38 #include "ui/views/controls/button/image_button.h" |
| 37 #include "ui/views/controls/image_view.h" | 39 #include "ui/views/controls/image_view.h" |
| 38 #include "ui/views/controls/label.h" | 40 #include "ui/views/controls/label.h" |
| 39 #include "ui/views/controls/link.h" | 41 #include "ui/views/controls/link.h" |
| 40 #include "ui/views/controls/link_listener.h" | 42 #include "ui/views/controls/link_listener.h" |
| 43 #include "ui/views/layout/box_layout.h" |
| 41 #include "ui/views/layout/fill_layout.h" | 44 #include "ui/views/layout/fill_layout.h" |
| 45 #include "ui/views/layout/grid_layout.h" |
| 42 #include "ui/views/layout/layout_constants.h" | 46 #include "ui/views/layout/layout_constants.h" |
| 43 | 47 |
| 44 using extensions::Extension; | 48 using extensions::Extension; |
| 45 | 49 |
| 46 namespace { | 50 namespace { |
| 47 | 51 |
| 48 const int kIconSize = 43; | 52 const int kIconSize = 43; |
| 49 | 53 |
| 50 const int kRightColumnWidth = 285; | 54 const int kRightColumnWidth = 285; |
| 51 | 55 |
| 52 // The Bubble uses a BubbleBorder which adds about 6 pixels of whitespace | 56 views::Label* CreateLabel(const base::string16& text, |
| 53 // around the content view. We compensate by reducing our outer borders by this | 57 const gfx::FontList& font) { |
| 54 // amount + 4px. | 58 views::Label* label = new views::Label(text, font); |
| 55 const int kOuterMarginInset = 10; | 59 label->SetMultiLine(true); |
| 56 const int kHorizOuterMargin = views::kPanelHorizMargin - kOuterMarginInset; | 60 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| 57 const int kVertOuterMargin = views::kPanelVertMargin - kOuterMarginInset; | 61 return label; |
| 58 | 62 } |
| 59 // Interior vertical margin is 8px smaller than standard | |
| 60 const int kVertInnerMargin = views::kPanelVertMargin - 8; | |
| 61 | |
| 62 // We want to shift the right column (which contains the header and text) up | |
| 63 // 4px to align with icon. | |
| 64 const int kRightcolumnVerticalShift = -4; | |
| 65 | 63 |
| 66 } // namespace | 64 } // namespace |
| 67 | 65 |
| 68 ExtensionInstalledBubbleView::ExtensionInstalledBubbleView( | 66 ExtensionInstalledBubbleView::ExtensionInstalledBubbleView( |
| 69 ExtensionInstalledBubble* bubble, | 67 ExtensionInstalledBubble* bubble, |
| 70 BubbleReference bubble_reference) | 68 BubbleReference bubble_reference) |
| 71 : bubble_reference_(bubble_reference), | 69 : bubble_reference_(bubble_reference), |
| 72 extension_(bubble->extension()), | 70 extension_(bubble->extension()), |
| 73 browser_(bubble->browser()), | 71 browser_(bubble->browser()), |
| 74 type_(bubble->type()) {} | 72 type_(bubble->type()), |
| 73 options_(NONE), |
| 74 sync_promo_(nullptr), |
| 75 close_(nullptr), |
| 76 manage_shortcut_(nullptr) {} |
| 75 | 77 |
| 76 ExtensionInstalledBubbleView::~ExtensionInstalledBubbleView() {} | 78 ExtensionInstalledBubbleView::~ExtensionInstalledBubbleView() {} |
| 77 | 79 |
| 78 void ExtensionInstalledBubbleView::UpdateAnchorView() { | 80 void ExtensionInstalledBubbleView::UpdateAnchorView() { |
| 79 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_); | 81 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_); |
| 80 | 82 |
| 81 views::View* reference_view = nullptr; | 83 views::View* reference_view = nullptr; |
| 82 if (type_ == ExtensionInstalledBubble::BROWSER_ACTION || | 84 if (type_ == ExtensionInstalledBubble::BROWSER_ACTION || |
| 83 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) { | 85 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) { |
| 84 BrowserActionsContainer* container = | 86 BrowserActionsContainer* container = |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 bool ExtensionInstalledBubbleView::AcceleratorPressed( | 160 bool ExtensionInstalledBubbleView::AcceleratorPressed( |
| 159 const ui::Accelerator& accelerator) { | 161 const ui::Accelerator& accelerator) { |
| 160 if (!close_on_esc() || accelerator.key_code() != ui::VKEY_ESCAPE) | 162 if (!close_on_esc() || accelerator.key_code() != ui::VKEY_ESCAPE) |
| 161 return false; | 163 return false; |
| 162 DCHECK(bubble_reference_); | 164 DCHECK(bubble_reference_); |
| 163 bool did_close = bubble_reference_->CloseBubble(BUBBLE_CLOSE_USER_DISMISSED); | 165 bool did_close = bubble_reference_->CloseBubble(BUBBLE_CLOSE_USER_DISMISSED); |
| 164 DCHECK(did_close); | 166 DCHECK(did_close); |
| 165 return true; | 167 return true; |
| 166 } | 168 } |
| 167 | 169 |
| 168 // Views specific implementation. | 170 void ExtensionInstalledBubbleView::OnSignInLinkClicked() { |
| 169 bool ExtensionInstalledBubble::ShouldShow() { | 171 GetWidget()->Close(); |
| 170 if (type() == BROWSER_ACTION || | 172 chrome::ShowBrowserSignin( |
| 171 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) { | 173 browser_, |
| 172 BrowserActionsContainer* container = | 174 signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE); |
| 173 BrowserView::GetBrowserViewForBrowser(browser()) | |
| 174 ->GetToolbarView() | |
| 175 ->browser_actions(); | |
| 176 return !container->animating(); | |
| 177 } | |
| 178 return true; | |
| 179 } | 175 } |
| 180 | 176 |
| 181 class ExtensionInstalledBubbleUi : public BubbleUi { | 177 void ExtensionInstalledBubbleView::ButtonPressed(views::Button* sender, |
| 182 public: | 178 const ui::Event& event) { |
| 183 explicit ExtensionInstalledBubbleUi(ExtensionInstalledBubble* bubble); | 179 DCHECK_EQ(sender, close_); |
| 184 ~ExtensionInstalledBubbleUi() override; | 180 GetWidget()->Close(); |
| 185 | |
| 186 private: | |
| 187 // BubbleUi: | |
| 188 void Show(BubbleReference bubble_reference) override; | |
| 189 void Close() override; | |
| 190 void UpdateAnchorPosition() override; | |
| 191 | |
| 192 ExtensionInstalledBubble* bubble_; | |
| 193 ExtensionInstalledBubbleView* delegate_view_; | |
| 194 | |
| 195 DISALLOW_COPY_AND_ASSIGN(ExtensionInstalledBubbleUi); | |
| 196 }; | |
| 197 | |
| 198 // Implemented here to create the platform specific instance of the BubbleUi. | |
| 199 scoped_ptr<BubbleUi> ExtensionInstalledBubble::BuildBubbleUi() { | |
| 200 return make_scoped_ptr(new ExtensionInstalledBubbleUi(this)); | |
| 201 } | 181 } |
| 202 | 182 |
| 203 // InstalledBubbleContent is the content view which is placed in the | 183 void ExtensionInstalledBubbleView::LinkClicked(views::Link* source, |
| 204 // ExtensionInstalledBubbleView. It displays the install icon and explanatory | 184 int event_flags) { |
| 205 // text about the installed extension. | 185 DCHECK_EQ(manage_shortcut_, source); |
| 206 class InstalledBubbleContent : public views::View, | 186 GetWidget()->Close(); |
| 207 public views::ButtonListener, | |
| 208 public views::LinkListener { | |
| 209 public: | |
| 210 InstalledBubbleContent(const ExtensionInstalledBubble& bubble, | |
| 211 const BubbleReference& bubble_reference, | |
| 212 Browser* browser); | |
| 213 | 187 |
| 214 // Overridden from views::ButtonListener. | 188 std::string configure_url = chrome::kChromeUIExtensionsURL; |
| 215 void ButtonPressed(views::Button* sender, const ui::Event& event) override; | 189 configure_url += chrome::kExtensionConfigureCommandsSubPage; |
| 190 chrome::NavigateParams params( |
| 191 chrome::GetSingletonTabNavigateParams(browser_, GURL(configure_url))); |
| 192 chrome::Navigate(¶ms); |
| 193 } |
| 216 | 194 |
| 217 // Overriden from views::LinkListener. | 195 void ExtensionInstalledBubbleView::InitLayout( |
| 218 void LinkClicked(views::Link* source, int event_flags) override; | 196 const ExtensionInstalledBubble& bubble) { |
| 219 | |
| 220 private: | |
| 221 enum Flavors { | |
| 222 NONE = 0, | |
| 223 HOW_TO_USE = 1 << 0, | |
| 224 HOW_TO_MANAGE = 1 << 1, | |
| 225 SHOW_KEYBINDING = 1 << 2, | |
| 226 SIGN_IN_PROMO = 1 << 3, | |
| 227 }; | |
| 228 | |
| 229 // Layout the signin promo at coordinates |offset_x| and |offset_y|. Returns | |
| 230 // the height (in pixels) of the promo UI. | |
| 231 int LayoutSigninPromo(int offset_x, int offset_y); | |
| 232 | |
| 233 // Overriden from views::View. | |
| 234 gfx::Size GetPreferredSize() const override; | |
| 235 void Layout() override; | |
| 236 void OnPaint(gfx::Canvas* canvas) override; | |
| 237 | |
| 238 // The browser we're associated with. | |
| 239 Browser* browser_; | |
| 240 | |
| 241 // A reference to the bubble to send close events to. | |
| 242 BubbleReference bubble_reference_; | |
| 243 | |
| 244 // The string that contains the link text at the beginning of the sign-in | |
| 245 // promo text. | |
| 246 base::string16 signin_promo_link_text_; | |
| 247 // The remaining text of the sign-in promo text. | |
| 248 base::string16 signin_promo_text_; | |
| 249 | |
| 250 // A vector of RenderText objects representing the full sign-in promo | |
| 251 // paragraph as layed out within the bubble, but has the text of the link | |
| 252 // whited out so the link can be drawn in its place. | |
| 253 ScopedVector<gfx::RenderText> sign_in_promo_lines_; | |
| 254 | |
| 255 // A bitmask containing the various flavors of bubble sections to show. | |
| 256 int flavors_; | |
| 257 | |
| 258 // The height, in pixels, of the sign-in promo. | |
| 259 size_t height_of_signin_promo_; | |
| 260 | |
| 261 views::ImageView* icon_; | |
| 262 views::Label* heading_; | |
| 263 views::Label* how_to_use_; | |
| 264 views::Link* sign_in_link_; | |
| 265 views::Label* manage_; | |
| 266 views::Link* manage_shortcut_; | |
| 267 views::ImageButton* close_button_; | |
| 268 | |
| 269 DISALLOW_COPY_AND_ASSIGN(InstalledBubbleContent); | |
| 270 }; | |
| 271 | |
| 272 InstalledBubbleContent::InstalledBubbleContent( | |
| 273 const ExtensionInstalledBubble& bubble, | |
| 274 const BubbleReference& bubble_reference, | |
| 275 Browser* browser) | |
| 276 : browser_(browser), | |
| 277 bubble_reference_(bubble_reference), | |
| 278 flavors_(NONE), | |
| 279 height_of_signin_promo_(0u), | |
| 280 how_to_use_(nullptr), | |
| 281 sign_in_link_(nullptr), | |
| 282 manage_(nullptr), | |
| 283 manage_shortcut_(nullptr) { | |
| 284 // The Extension Installed bubble takes on various forms, depending on the | 197 // The Extension Installed bubble takes on various forms, depending on the |
| 285 // type of extension installed. In general, though, they are all similar: | 198 // type of extension installed. In general, though, they are all similar: |
| 286 // | 199 // |
| 287 // ------------------------- | 200 // ------------------------- |
| 288 // | | Heading [X] | | 201 // | | Heading [X] | |
| 289 // | Icon | Info | | 202 // | Icon | Info | |
| 290 // | | Extra info | | 203 // | | Extra info | |
| 291 // ------------------------- | 204 // ------------------------- |
| 292 // | 205 // |
| 293 // Icon and Heading are always shown (as well as the close button). | 206 // Icon and Heading are always shown (as well as the close button). |
| 294 // Info is shown for browser actions, page actions and Omnibox keyword | 207 // Info is shown for browser actions, page actions and Omnibox keyword |
| 295 // extensions and might list keyboard shorcut for the former two types. | 208 // extensions and might list keyboard shorcut for the former two types. |
| 296 // Extra info is... | 209 // Extra info is... |
| 297 // ... for other types, either a description of how to manage the extension | 210 // ... for other types, either a description of how to manage the extension |
| 298 // or a link to configure the keybinding shortcut (if one exists). | 211 // or a link to configure the keybinding shortcut (if one exists). |
| 299 // Extra info can include a promo for signing into sync. | 212 // Extra info can include a promo for signing into sync. |
| 300 | 213 |
| 301 const Extension* extension = bubble.extension(); | 214 set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0)); |
| 302 if (extensions::sync_helper::IsSyncable(extension) && | |
| 303 SyncPromoUI::ShouldShowSyncPromo(browser->profile())) | |
| 304 flavors_ |= SIGN_IN_PROMO; | |
| 305 | 215 |
| 306 // Determine the bubble flavor we want, based on the extension type. | 216 if (extensions::sync_helper::IsSyncable(extension_) && |
| 307 switch (bubble.type()) { | 217 SyncPromoUI::ShouldShowSyncPromo(browser_->profile())) |
| 218 options_ |= SIGN_IN_PROMO; |
| 219 |
| 220 // The number of rows in the content section of the bubble. |
| 221 int main_content_row_count = 1; |
| 222 // Determine the bubble option we want, based on the extension type. |
| 223 switch (type_) { |
| 308 case ExtensionInstalledBubble::BROWSER_ACTION: | 224 case ExtensionInstalledBubble::BROWSER_ACTION: |
| 309 case ExtensionInstalledBubble::PAGE_ACTION: | 225 case ExtensionInstalledBubble::PAGE_ACTION: |
| 310 flavors_ |= HOW_TO_USE; | 226 options_ |= HOW_TO_USE; |
| 311 if (bubble.has_command_keybinding()) { | 227 if (bubble.has_command_keybinding()) { |
| 312 flavors_ |= SHOW_KEYBINDING; | 228 options_ |= SHOW_KEYBINDING; |
| 313 } else { | 229 } else { |
| 314 // The How-To-Use text makes the bubble seem a little crowded when the | 230 // The How-To-Use text makes the bubble seem a little crowded when the |
| 315 // extension has a keybinding, so the How-To-Manage text is not shown | 231 // extension has a keybinding, so the How-To-Manage text is not shown |
| 316 // in those cases. | 232 // in those cases. |
| 317 flavors_ |= HOW_TO_MANAGE; | 233 options_ |= HOW_TO_MANAGE; |
| 318 } | 234 } |
| 235 main_content_row_count += 2; |
| 319 break; | 236 break; |
| 320 case ExtensionInstalledBubble::OMNIBOX_KEYWORD: | 237 case ExtensionInstalledBubble::OMNIBOX_KEYWORD: |
| 321 flavors_ |= HOW_TO_USE | HOW_TO_MANAGE; | 238 options_ |= HOW_TO_USE | HOW_TO_MANAGE; |
| 239 main_content_row_count += 2; |
| 322 break; | 240 break; |
| 323 case ExtensionInstalledBubble::GENERIC: | 241 case ExtensionInstalledBubble::GENERIC: |
| 324 break; | 242 break; |
| 325 default: | 243 default: |
| 326 // When adding a new bubble type, the flavor needs to be set. | 244 // When adding a new bubble type, the option needs to be set. |
| 327 static_assert(ExtensionInstalledBubble::GENERIC == 3, | 245 static_assert(ExtensionInstalledBubble::GENERIC == 3, |
| 328 "kBubbleType enum has changed, this switch statement must " | 246 "kBubbleType enum has changed, this switch statement must " |
| 329 "be updateed"); | 247 "be updateed"); |
| 330 break; | 248 break; |
| 331 } | 249 } |
| 332 | 250 |
| 251 views::GridLayout* layout = new views::GridLayout(this); |
| 252 SetLayoutManager(layout); |
| 253 |
| 254 enum ColumnSetId { |
| 255 MAIN_COLUMN_SET = 0, |
| 256 SYNC_PROMO_COLUMN_SET, |
| 257 }; |
| 258 |
| 259 views::ColumnSet* main_cs = layout->AddColumnSet(MAIN_COLUMN_SET); |
| 260 // Note: the left padding column is set to kUnrelatedControlHorizontalSpacing |
| 261 // so that the distance between the left edge and the icon matches the |
| 262 // distance between the icon and the content. |
| 263 main_cs->AddPaddingColumn(0 /* not resizable */, |
| 264 views::kUnrelatedControlHorizontalSpacing); |
| 265 // Icon column. |
| 266 main_cs->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING, 0, |
| 267 views::GridLayout::USE_PREF, 0, 0); |
| 268 main_cs->AddPaddingColumn(0, views::kUnrelatedControlHorizontalSpacing); |
| 269 // Heading column: |
| 270 main_cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0, |
| 271 views::GridLayout::FIXED, kRightColumnWidth, 0); |
| 272 main_cs->AddPaddingColumn(0 /* not resizable */, |
| 273 views::kUnrelatedControlHorizontalSpacing); |
| 274 |
| 333 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 275 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 334 const gfx::FontList& font_list = | 276 const gfx::FontList& font_list = rb.GetFontList(ui::ResourceBundle::BaseFont); |
| 335 rb.GetFontList(ui::ResourceBundle::BaseFont); | |
| 336 | 277 |
| 337 const SkBitmap& icon = bubble.icon(); | 278 const SkBitmap& bitmap = bubble.icon(); |
| 338 // Add the icon (for all flavors). | 279 // Add the icon (for all options). |
| 339 // Scale down to 43x43, but allow smaller icons (don't scale up). | 280 // Scale down to 43x43, but allow smaller icons (don't scale up). |
| 340 gfx::Size size(icon.width(), icon.height()); | 281 gfx::Size size(bitmap.width(), bitmap.height()); |
| 341 if (size.width() > kIconSize || size.height() > kIconSize) | 282 if (size.width() > kIconSize || size.height() > kIconSize) |
| 342 size = gfx::Size(kIconSize, kIconSize); | 283 size = gfx::Size(kIconSize, kIconSize); |
| 343 icon_ = new views::ImageView(); | 284 views::ImageView* icon = new views::ImageView(); |
| 344 icon_->SetImageSize(size); | 285 icon->SetImageSize(size); |
| 345 icon_->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(icon)); | 286 icon->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); |
| 346 AddChildView(icon_); | |
| 347 | 287 |
| 348 // Add the heading (for all flavors). | 288 layout->StartRow(0, MAIN_COLUMN_SET); |
| 349 base::string16 extension_name = base::UTF8ToUTF16(extension->name()); | 289 layout->AddView(icon, 1, main_content_row_count); |
| 290 |
| 291 // Add the heading (for all options). |
| 292 base::string16 extension_name = base::UTF8ToUTF16(extension_->name()); |
| 350 base::i18n::AdjustStringForLocaleDirection(&extension_name); | 293 base::i18n::AdjustStringForLocaleDirection(&extension_name); |
| 351 heading_ = new views::Label(l10n_util::GetStringFUTF16( | 294 views::Label* heading = |
| 352 IDS_EXTENSION_INSTALLED_HEADING, extension_name)); | 295 CreateLabel(l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALLED_HEADING, |
| 353 heading_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont)); | 296 extension_name), |
| 354 heading_->SetMultiLine(true); | 297 rb.GetFontList(ui::ResourceBundle::MediumFont)); |
| 355 heading_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 356 AddChildView(heading_); | |
| 357 | 298 |
| 358 if (flavors_ & HOW_TO_USE) { | 299 close_ = views::BubbleFrameView::CreateCloseButton(this); |
| 359 how_to_use_ = new views::Label(bubble.GetHowToUseDescription()); | 300 |
| 360 how_to_use_->SetFontList(font_list); | 301 views::View* heading_and_close = new views::View(); |
| 361 how_to_use_->SetMultiLine(true); | 302 views::BoxLayout* heading_and_close_layout = |
| 362 how_to_use_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | 303 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, |
| 363 AddChildView(how_to_use_); | 304 views::kUnrelatedControlHorizontalSpacing); |
| 305 heading_and_close_layout->set_cross_axis_alignment( |
| 306 views::BoxLayout::CROSS_AXIS_ALIGNMENT_START); |
| 307 heading_and_close->SetLayoutManager(heading_and_close_layout); |
| 308 heading_and_close->AddChildView(heading); |
| 309 heading_and_close->AddChildView(close_); |
| 310 |
| 311 layout->AddView(heading_and_close); |
| 312 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); |
| 313 |
| 314 auto add_content_view = [&layout](views::View* view) { |
| 315 layout->StartRow(0, MAIN_COLUMN_SET); |
| 316 // Skip the icon column. |
| 317 layout->SkipColumns(1); |
| 318 layout->AddView(view); |
| 319 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); |
| 320 }; |
| 321 |
| 322 if (options_ & HOW_TO_USE) { |
| 323 add_content_view(CreateLabel(bubble.GetHowToUseDescription(), font_list)); |
| 364 } | 324 } |
| 365 | 325 |
| 366 if (flavors_ & SHOW_KEYBINDING) { | 326 if (options_ & SHOW_KEYBINDING) { |
| 367 manage_shortcut_ = new views::Link( | 327 manage_shortcut_ = new views::Link( |
| 368 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_MANAGE_SHORTCUTS)); | 328 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_MANAGE_SHORTCUTS)); |
| 369 manage_shortcut_->set_listener(this); | 329 manage_shortcut_->set_listener(this); |
| 370 AddChildView(manage_shortcut_); | 330 manage_shortcut_->SetUnderline(false); |
| 331 add_content_view(manage_shortcut_); |
| 371 } | 332 } |
| 372 | 333 |
| 373 if (flavors_ & HOW_TO_MANAGE) { | 334 if (options_ & HOW_TO_MANAGE) { |
| 374 manage_ = new views::Label(l10n_util::GetStringUTF16( | 335 add_content_view(CreateLabel( |
| 375 IDS_EXTENSION_INSTALLED_MANAGE_INFO)); | 336 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_MANAGE_INFO), |
| 376 manage_->SetFontList(font_list); | 337 font_list)); |
| 377 manage_->SetMultiLine(true); | |
| 378 manage_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 379 AddChildView(manage_); | |
| 380 } | 338 } |
| 381 | 339 |
| 382 if (flavors_ & SIGN_IN_PROMO) { | 340 if (options_ & SIGN_IN_PROMO) { |
| 383 signin_promo_text_ = | 341 views::ColumnSet* sync_cs = layout->AddColumnSet(SYNC_PROMO_COLUMN_SET); |
| 384 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_SIGNIN_PROMO); | 342 sync_cs->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
| 385 | 343 views::GridLayout::USE_PREF, 0, 0); |
| 386 signin_promo_link_text_ = | 344 layout->StartRow(0, SYNC_PROMO_COLUMN_SET); |
| 387 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_SIGNIN_PROMO_LINK); | 345 sync_promo_ = new BubbleSyncPromoView( |
| 388 sign_in_link_ = new views::Link(signin_promo_link_text_); | 346 this, IDS_EXTENSION_INSTALLED_SYNC_PROMO_LINK_NEW, |
| 389 sign_in_link_->SetFontList(font_list); | 347 IDS_EXTENSION_INSTALLED_SYNC_PROMO_NEW); |
| 390 sign_in_link_->set_listener(this); | 348 layout->AddView(sync_promo_); |
| 391 AddChildView(sign_in_link_); | |
| 392 } | 349 } |
| 393 | |
| 394 // Add the Close button (for all flavors). | |
| 395 close_button_ = new views::ImageButton(this); | |
| 396 close_button_->SetImage(views::CustomButton::STATE_NORMAL, | |
| 397 rb.GetImageSkiaNamed(IDR_CLOSE_2)); | |
| 398 close_button_->SetImage(views::CustomButton::STATE_HOVERED, | |
| 399 rb.GetImageSkiaNamed(IDR_CLOSE_2_H)); | |
| 400 close_button_->SetImage(views::CustomButton::STATE_PRESSED, | |
| 401 rb.GetImageSkiaNamed(IDR_CLOSE_2_P)); | |
| 402 AddChildView(close_button_); | |
| 403 } | 350 } |
| 404 | 351 |
| 405 void InstalledBubbleContent::ButtonPressed(views::Button* sender, | 352 // Views specific implementation. |
| 406 const ui::Event& event) { | 353 bool ExtensionInstalledBubble::ShouldShow() { |
| 407 DCHECK_EQ(sender, close_button_); | 354 if (type() == BROWSER_ACTION || |
| 408 DCHECK(bubble_reference_); | 355 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) { |
| 409 bool did_close = bubble_reference_->CloseBubble(BUBBLE_CLOSE_USER_DISMISSED); | 356 BrowserActionsContainer* container = |
| 410 DCHECK(did_close); | 357 BrowserView::GetBrowserViewForBrowser(browser()) |
| 358 ->GetToolbarView() |
| 359 ->browser_actions(); |
| 360 return !container->animating(); |
| 361 } |
| 362 return true; |
| 411 } | 363 } |
| 412 | 364 |
| 413 void InstalledBubbleContent::LinkClicked(views::Link* source, int event_flags) { | 365 class ExtensionInstalledBubbleUi : public BubbleUi { |
| 414 DCHECK(bubble_reference_); | 366 public: |
| 415 bool did_close = bubble_reference_->CloseBubble(BUBBLE_CLOSE_ACCEPTED); | 367 explicit ExtensionInstalledBubbleUi(ExtensionInstalledBubble* bubble); |
| 416 DCHECK(did_close); | 368 ~ExtensionInstalledBubbleUi() override; |
| 417 | 369 |
| 418 if (source == sign_in_link_) { | 370 private: |
| 419 #if defined(OS_ANDROID) | 371 // BubbleUi: |
| 420 // TODO(bshe): Figure out what to do on Android platform. See | 372 void Show(BubbleReference bubble_reference) override; |
| 421 // crbug.com/559340. | 373 void Close() override; |
| 422 NOTIMPLEMENTED(); | 374 void UpdateAnchorPosition() override; |
| 423 #else | |
| 424 chrome::ShowBrowserSignin( | |
| 425 browser_, | |
| 426 signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE); | |
| 427 #endif | |
| 428 return; | |
| 429 } | |
| 430 | 375 |
| 431 DCHECK_EQ(manage_shortcut_, source); | 376 ExtensionInstalledBubble* bubble_; |
| 377 ExtensionInstalledBubbleView* delegate_view_; |
| 432 | 378 |
| 433 std::string configure_url = chrome::kChromeUIExtensionsURL; | 379 DISALLOW_COPY_AND_ASSIGN(ExtensionInstalledBubbleUi); |
| 434 configure_url += chrome::kExtensionConfigureCommandsSubPage; | 380 }; |
| 435 chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams( | |
| 436 browser_, GURL(configure_url))); | |
| 437 chrome::Navigate(¶ms); | |
| 438 } | |
| 439 | 381 |
| 440 int InstalledBubbleContent::LayoutSigninPromo(int offset_x, int offset_y) { | 382 // Implemented here to create the platform specific instance of the BubbleUi. |
| 441 sign_in_promo_lines_.clear(); | 383 scoped_ptr<BubbleUi> ExtensionInstalledBubble::BuildBubbleUi() { |
| 442 int height = 0; | 384 return make_scoped_ptr(new ExtensionInstalledBubbleUi(this)); |
| 443 gfx::Rect contents_area = GetContentsBounds(); | |
| 444 if (contents_area.IsEmpty()) | |
| 445 return height; | |
| 446 contents_area.set_width(kRightColumnWidth); | |
| 447 | |
| 448 base::string16 full_text = signin_promo_link_text_ + signin_promo_text_; | |
| 449 | |
| 450 // The link is the first item in the text. | |
| 451 const gfx::Size link_size = sign_in_link_->GetPreferredSize(); | |
| 452 sign_in_link_->SetBounds( | |
| 453 offset_x, offset_y, link_size.width(), link_size.height()); | |
| 454 | |
| 455 // Word-wrap the full label text. | |
| 456 const gfx::FontList font_list; | |
| 457 std::vector<base::string16> lines; | |
| 458 gfx::ElideRectangleText(full_text, font_list, contents_area.width(), | |
| 459 contents_area.height(), gfx::ELIDE_LONG_WORDS, | |
| 460 &lines); | |
| 461 | |
| 462 gfx::Point position = gfx::Point( | |
| 463 contents_area.origin().x() + offset_x, | |
| 464 contents_area.origin().y() + offset_y + 1); | |
| 465 if (base::i18n::IsRTL()) { | |
| 466 position -= gfx::Vector2d( | |
| 467 2 * views::kPanelHorizMargin + kHorizOuterMargin, 0); | |
| 468 } | |
| 469 | |
| 470 // Loop through the lines, creating a renderer for each. | |
| 471 for (std::vector<base::string16>::const_iterator it = lines.begin(); | |
| 472 it != lines.end(); ++it) { | |
| 473 gfx::RenderText* line = gfx::RenderText::CreateInstance(); | |
| 474 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI); | |
| 475 line->SetText(*it); | |
| 476 const gfx::Size size(contents_area.width(), | |
| 477 line->GetStringSize().height()); | |
| 478 line->SetDisplayRect(gfx::Rect(position, size)); | |
| 479 position.set_y(position.y() + size.height()); | |
| 480 sign_in_promo_lines_.push_back(line); | |
| 481 height += size.height(); | |
| 482 } | |
| 483 | |
| 484 // The link is drawn separately; make it transparent here to only draw once. | |
| 485 // The link always leads other text and is assumed to fit on the first line. | |
| 486 sign_in_promo_lines_.front()->ApplyColor(SK_ColorTRANSPARENT, | |
| 487 gfx::Range(0, signin_promo_link_text_.size())); | |
| 488 | |
| 489 return height; | |
| 490 } | |
| 491 | |
| 492 gfx::Size InstalledBubbleContent::GetPreferredSize() const { | |
| 493 int width = kHorizOuterMargin; | |
| 494 width += kIconSize; | |
| 495 width += views::kPanelHorizMargin; | |
| 496 width += kRightColumnWidth; | |
| 497 width += 2 * views::kPanelHorizMargin; | |
| 498 width += kHorizOuterMargin; | |
| 499 | |
| 500 int height = kVertOuterMargin; | |
| 501 height += heading_->GetHeightForWidth(kRightColumnWidth); | |
| 502 height += kVertInnerMargin; | |
| 503 | |
| 504 if (flavors_ & HOW_TO_USE) { | |
| 505 height += how_to_use_->GetHeightForWidth(kRightColumnWidth); | |
| 506 height += kVertInnerMargin; | |
| 507 } | |
| 508 | |
| 509 if (flavors_ & HOW_TO_MANAGE) { | |
| 510 height += manage_->GetHeightForWidth(kRightColumnWidth); | |
| 511 height += kVertInnerMargin; | |
| 512 } | |
| 513 | |
| 514 if (flavors_ & SIGN_IN_PROMO && height_of_signin_promo_ > 0u) { | |
| 515 height += height_of_signin_promo_; | |
| 516 height += kVertInnerMargin; | |
| 517 } | |
| 518 | |
| 519 if (flavors_ & SHOW_KEYBINDING) { | |
| 520 height += manage_shortcut_->GetHeightForWidth(kRightColumnWidth); | |
| 521 height += kVertInnerMargin; | |
| 522 } | |
| 523 | |
| 524 return gfx::Size(width, std::max(height, kIconSize + 2 * kVertOuterMargin)); | |
| 525 } | |
| 526 | |
| 527 void InstalledBubbleContent::Layout() { | |
| 528 int x = kHorizOuterMargin; | |
| 529 int y = kVertOuterMargin; | |
| 530 | |
| 531 icon_->SetBounds(x, y, kIconSize, kIconSize); | |
| 532 x += kIconSize; | |
| 533 x += views::kPanelHorizMargin; | |
| 534 | |
| 535 y += kRightcolumnVerticalShift; | |
| 536 heading_->SizeToFit(kRightColumnWidth); | |
| 537 heading_->SetX(x); | |
| 538 heading_->SetY(y); | |
| 539 y += heading_->height(); | |
| 540 y += kVertInnerMargin; | |
| 541 | |
| 542 if (flavors_ & HOW_TO_USE) { | |
| 543 how_to_use_->SizeToFit(kRightColumnWidth); | |
| 544 how_to_use_->SetX(x); | |
| 545 how_to_use_->SetY(y); | |
| 546 y += how_to_use_->height(); | |
| 547 y += kVertInnerMargin; | |
| 548 } | |
| 549 | |
| 550 if (flavors_ & HOW_TO_MANAGE) { | |
| 551 manage_->SizeToFit(kRightColumnWidth); | |
| 552 manage_->SetX(x); | |
| 553 manage_->SetY(y); | |
| 554 y += manage_->height(); | |
| 555 y += kVertInnerMargin; | |
| 556 } | |
| 557 | |
| 558 if (flavors_ & SIGN_IN_PROMO) { | |
| 559 height_of_signin_promo_ = LayoutSigninPromo(x, y); | |
| 560 y += height_of_signin_promo_; | |
| 561 y += kVertInnerMargin; | |
| 562 } | |
| 563 | |
| 564 if (flavors_ & SHOW_KEYBINDING) { | |
| 565 gfx::Size sz = manage_shortcut_->GetPreferredSize(); | |
| 566 manage_shortcut_->SetBounds(width() - 2 * kHorizOuterMargin - sz.width(), | |
| 567 y, | |
| 568 sz.width(), | |
| 569 sz.height()); | |
| 570 y += manage_shortcut_->height(); | |
| 571 y += kVertInnerMargin; | |
| 572 } | |
| 573 | |
| 574 gfx::Size sz; | |
| 575 x += kRightColumnWidth + 2 * views::kPanelHorizMargin + kHorizOuterMargin - | |
| 576 close_button_->GetPreferredSize().width(); | |
| 577 y = kVertOuterMargin; | |
| 578 sz = close_button_->GetPreferredSize(); | |
| 579 // x-1 & y-1 is just slop to get the close button visually aligned with the | |
| 580 // title text and bubble arrow. | |
| 581 close_button_->SetBounds(x - 1, y - 1, sz.width(), sz.height()); | |
| 582 } | |
| 583 | |
| 584 void InstalledBubbleContent::OnPaint(gfx::Canvas* canvas) { | |
| 585 for (ScopedVector<gfx::RenderText>::const_iterator it = | |
| 586 sign_in_promo_lines_.begin(); | |
| 587 it != sign_in_promo_lines_.end(); ++it) | |
| 588 (*it)->Draw(canvas); | |
| 589 | |
| 590 views::View::OnPaint(canvas); | |
| 591 } | 385 } |
| 592 | 386 |
| 593 ExtensionInstalledBubbleUi::ExtensionInstalledBubbleUi( | 387 ExtensionInstalledBubbleUi::ExtensionInstalledBubbleUi( |
| 594 ExtensionInstalledBubble* bubble) | 388 ExtensionInstalledBubble* bubble) |
| 595 : bubble_(bubble), delegate_view_(nullptr) { | 389 : bubble_(bubble), delegate_view_(nullptr) { |
| 596 DCHECK(bubble_); | 390 DCHECK(bubble_); |
| 597 } | 391 } |
| 598 | 392 |
| 599 ExtensionInstalledBubbleUi::~ExtensionInstalledBubbleUi() {} | 393 ExtensionInstalledBubbleUi::~ExtensionInstalledBubbleUi() {} |
| 600 | 394 |
| 601 void ExtensionInstalledBubbleUi::Show(BubbleReference bubble_reference) { | 395 void ExtensionInstalledBubbleUi::Show(BubbleReference bubble_reference) { |
| 602 // Owned by widget. | 396 // Owned by widget. |
| 603 delegate_view_ = new ExtensionInstalledBubbleView(bubble_, bubble_reference); | 397 delegate_view_ = new ExtensionInstalledBubbleView(bubble_, bubble_reference); |
| 604 delegate_view_->UpdateAnchorView(); | 398 delegate_view_->UpdateAnchorView(); |
| 605 | 399 |
| 606 delegate_view_->set_arrow(bubble_->type() == bubble_->OMNIBOX_KEYWORD | 400 delegate_view_->set_arrow(bubble_->type() == bubble_->OMNIBOX_KEYWORD |
| 607 ? views::BubbleBorder::TOP_LEFT | 401 ? views::BubbleBorder::TOP_LEFT |
| 608 : views::BubbleBorder::TOP_RIGHT); | 402 : views::BubbleBorder::TOP_RIGHT); |
| 609 delegate_view_->SetLayoutManager(new views::FillLayout()); | 403 |
| 610 delegate_view_->AddChildView(new InstalledBubbleContent( | 404 delegate_view_->InitLayout(*bubble_); |
| 611 *bubble_, bubble_reference, bubble_->browser())); | |
| 612 | 405 |
| 613 views::BubbleDelegateView::CreateBubble(delegate_view_)->Show(); | 406 views::BubbleDelegateView::CreateBubble(delegate_view_)->Show(); |
| 614 } | 407 } |
| 615 | 408 |
| 616 void ExtensionInstalledBubbleUi::Close() { | 409 void ExtensionInstalledBubbleUi::Close() { |
| 617 if (delegate_view_) { | 410 if (delegate_view_) { |
| 618 delegate_view_->GetWidget()->Close(); | 411 delegate_view_->GetWidget()->Close(); |
| 619 delegate_view_ = nullptr; | 412 delegate_view_ = nullptr; |
| 620 } | 413 } |
| 621 } | 414 } |
| 622 | 415 |
| 623 void ExtensionInstalledBubbleUi::UpdateAnchorPosition() { | 416 void ExtensionInstalledBubbleUi::UpdateAnchorPosition() { |
| 624 DCHECK(delegate_view_); | 417 DCHECK(delegate_view_); |
| 625 delegate_view_->UpdateAnchorView(); | 418 delegate_view_->UpdateAnchorView(); |
| 626 } | 419 } |
| OLD | NEW |