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