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 |