| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/views/extensions/bookmark_app_bubble_view.h" |
| |
| #include "base/numerics/safe_conversions.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/ui/host_desktop.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/common/constants.h" |
| #include "grit/components_strings.h" |
| #include "ui/accessibility/ax_view_state.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/events/keycodes/keyboard_codes.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_skia_source.h" |
| #include "ui/views/controls/button/checkbox.h" |
| #include "ui/views/controls/button/label_button.h" |
| #include "ui/views/controls/image_view.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/controls/textfield/textfield.h" |
| #include "ui/views/layout/grid_layout.h" |
| #include "ui/views/layout/layout_constants.h" |
| #include "ui/views/widget/widget.h" |
| |
| #if defined(OS_WIN) |
| #include "base/win/shortcut.h" |
| #endif // defined(OS_WIN) |
| |
| using views::ColumnSet; |
| using views::GridLayout; |
| |
| namespace { |
| |
| // Minimum width of the the bubble. |
| const int kMinBubbleWidth = 300; |
| // Minimum width of the the textfield. |
| const int kMinTextfieldWidth = 200; |
| // Size of the icon. |
| const int kIconSize = extension_misc::EXTENSION_ICON_MEDIUM; |
| |
| class WebAppInfoImageSource : public gfx::ImageSkiaSource { |
| public: |
| WebAppInfoImageSource(int dip_size, const WebApplicationInfo& info) |
| : dip_size_(dip_size), info_(info) {} |
| ~WebAppInfoImageSource() override {} |
| |
| private: |
| gfx::ImageSkiaRep GetImageForScale(float scale) override { |
| int size = base::saturated_cast<int>(dip_size_ * scale); |
| for (const auto& icon_info : info_.icons) { |
| if (icon_info.width == size) { |
| return gfx::ImageSkiaRep(icon_info.data, scale); |
| } |
| } |
| return gfx::ImageSkiaRep(); |
| } |
| |
| int dip_size_; |
| WebApplicationInfo info_; |
| }; |
| |
| } // namespace |
| |
| BookmarkAppBubbleView::~BookmarkAppBubbleView() { |
| } |
| |
| // static |
| void BookmarkAppBubbleView::ShowBubble( |
| views::View* anchor_view, |
| const WebApplicationInfo& web_app_info, |
| const BrowserWindow::ShowBookmarkAppBubbleCallback& callback) { |
| // |bookmark_app_bubble| becomes owned by the BubbleDelegateView through the |
| // views system, and is freed when the BubbleDelegateView is closed and |
| // subsequently destroyed. |
| BookmarkAppBubbleView* bookmark_app_bubble = |
| new BookmarkAppBubbleView(anchor_view, web_app_info, callback); |
| views::BubbleDelegateView::CreateBubble(bookmark_app_bubble)->Show(); |
| // Select the entire title textfield contents when the bubble is first shown. |
| bookmark_app_bubble->title_tf_->SelectAll(true); |
| bookmark_app_bubble->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); |
| } |
| |
| BookmarkAppBubbleView::BookmarkAppBubbleView( |
| views::View* anchor_view, |
| const WebApplicationInfo& web_app_info, |
| const BrowserWindow::ShowBookmarkAppBubbleCallback& callback) |
| : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), |
| web_app_info_(web_app_info), |
| user_accepted_(false), |
| callback_(callback), |
| add_button_(NULL), |
| cancel_button_(NULL), |
| open_as_window_checkbox_(NULL), |
| title_tf_(NULL) { |
| const SkColor background_color = GetNativeTheme()->GetSystemColor( |
| ui::NativeTheme::kColorId_DialogBackground); |
| set_arrow(views::BubbleBorder::TOP_CENTER); |
| set_color(background_color); |
| set_background(views::Background::CreateSolidBackground(background_color)); |
| set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0)); |
| } |
| |
| void BookmarkAppBubbleView::Init() { |
| views::Label* title_label = new views::Label( |
| l10n_util::GetStringUTF16(TitleStringId())); |
| ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); |
| title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::MediumFont)); |
| title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| |
| add_button_ = |
| new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_ADD)); |
| add_button_->SetStyle(views::Button::STYLE_BUTTON); |
| add_button_->SetIsDefault(true); |
| |
| cancel_button_ = |
| new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL)); |
| cancel_button_->SetStyle(views::Button::STYLE_BUTTON); |
| |
| GridLayout* layout = new GridLayout(this); |
| SetLayoutManager(layout); |
| |
| // Column sets used in the layout of the bubble. |
| enum ColumnSetID { |
| TITLE_COLUMN_SET_ID, |
| TITLE_TEXT_COLUMN_SET_ID, |
| CONTENT_COLUMN_SET_ID |
| }; |
| |
| // The column layout used for the title and checkbox. |
| ColumnSet* cs = layout->AddColumnSet(TITLE_COLUMN_SET_ID); |
| cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); |
| cs->AddColumn( |
| GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); |
| cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); |
| |
| // The column layout used for the icon and text box. |
| cs = layout->AddColumnSet(TITLE_TEXT_COLUMN_SET_ID); |
| cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); |
| cs->AddColumn(GridLayout::LEADING, |
| GridLayout::CENTER, |
| 0, |
| GridLayout::USE_PREF, |
| 0, |
| 0); |
| cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); |
| cs->AddColumn(GridLayout::FILL, |
| GridLayout::CENTER, |
| 1, |
| GridLayout::USE_PREF, |
| 0, |
| kMinTextfieldWidth); |
| cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); |
| |
| // The column layout used for the row with buttons. |
| cs = layout->AddColumnSet(CONTENT_COLUMN_SET_ID); |
| cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); |
| cs->AddColumn( |
| GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); |
| cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing); |
| cs->AddColumn( |
| GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); |
| cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing); |
| cs->AddColumn( |
| GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); |
| cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); |
| |
| layout->StartRow(0, TITLE_COLUMN_SET_ID); |
| layout->AddView(title_label); |
| layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); |
| |
| layout->StartRow(0, TITLE_TEXT_COLUMN_SET_ID); |
| icon_image_view_ = new views::ImageView(); |
| |
| gfx::Size image_size(kIconSize, kIconSize); |
| gfx::ImageSkia image(new WebAppInfoImageSource(kIconSize, web_app_info_), |
| image_size); |
| icon_image_view_->SetImageSize(image_size); |
| icon_image_view_->SetImage(image); |
| layout->AddView(icon_image_view_); |
| |
| title_tf_ = new views::Textfield(); |
| title_tf_->SetText(web_app_info_.title); |
| title_tf_->SetAccessibleName(l10n_util::GetStringUTF16( |
| IDS_BOOKMARK_APP_AX_BUBBLE_NAME_LABEL)); |
| title_tf_->set_controller(this); |
| layout->AddView(title_tf_); |
| layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); |
| |
| layout->StartRow(0, CONTENT_COLUMN_SET_ID); |
| open_as_window_checkbox_ = new views::Checkbox( |
| l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_WINDOW)); |
| open_as_window_checkbox_->SetChecked(web_app_info_.open_as_window); |
| layout->AddView(open_as_window_checkbox_); |
| layout->AddView(add_button_); |
| layout->AddView(cancel_button_); |
| layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); |
| |
| AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); |
| UpdateAddButtonState(); |
| } |
| |
| views::View* BookmarkAppBubbleView::GetInitiallyFocusedView() { |
| return title_tf_; |
| } |
| |
| void BookmarkAppBubbleView::WindowClosing() { |
| callback_.Run(user_accepted_, web_app_info_); |
| } |
| |
| bool BookmarkAppBubbleView::AcceleratorPressed( |
| const ui::Accelerator& accelerator) { |
| if (accelerator.key_code() == ui::VKEY_RETURN) { |
| HandleButtonPressed(add_button_); |
| } |
| |
| return BubbleDelegateView::AcceleratorPressed(accelerator); |
| } |
| |
| void BookmarkAppBubbleView::GetAccessibleState(ui::AXViewState* state) { |
| views::BubbleDelegateView::GetAccessibleState(state); |
| state->name = l10n_util::GetStringUTF16(TitleStringId()); |
| } |
| |
| gfx::Size BookmarkAppBubbleView::GetMinimumSize() const { |
| gfx::Size size(views::BubbleDelegateView::GetPreferredSize()); |
| size.SetToMax(gfx::Size(kMinBubbleWidth, 0)); |
| return size; |
| } |
| |
| void BookmarkAppBubbleView::ButtonPressed(views::Button* sender, |
| const ui::Event& event) { |
| HandleButtonPressed(sender); |
| } |
| |
| void BookmarkAppBubbleView::ContentsChanged( |
| views::Textfield* sender, |
| const base::string16& new_contents) { |
| DCHECK_EQ(title_tf_, sender); |
| UpdateAddButtonState(); |
| } |
| |
| void BookmarkAppBubbleView::HandleButtonPressed(views::Button* sender) { |
| if (sender == add_button_) { |
| user_accepted_ = true; |
| web_app_info_.title = GetTrimmedTitle(); |
| web_app_info_.open_as_window = open_as_window_checkbox_->checked(); |
| } |
| |
| GetWidget()->Close(); |
| } |
| |
| void BookmarkAppBubbleView::UpdateAddButtonState() { |
| add_button_->SetEnabled(!GetTrimmedTitle().empty()); |
| } |
| |
| int BookmarkAppBubbleView::TitleStringId() { |
| int string_id = IDS_ADD_TO_DESKTOP_BUBBLE_TITLE; |
| #if defined(USE_ASH) |
| if (chrome::GetHostDesktopTypeForNativeWindow( |
| anchor_widget()->GetNativeWindow()) == |
| chrome::HOST_DESKTOP_TYPE_ASH) { |
| string_id = IDS_ADD_TO_SHELF_BUBBLE_TITLE; |
| } |
| #endif |
| return string_id; |
| } |
| |
| base::string16 BookmarkAppBubbleView::GetTrimmedTitle() { |
| base::string16 title(title_tf_->text()); |
| base::TrimWhitespace(title, base::TRIM_ALL, &title); |
| return title; |
| } |