Index: chrome/browser/ui/views/website_settings/chooser_bubble_ui.cc |
diff --git a/chrome/browser/ui/views/website_settings/chooser_bubble_ui.cc b/chrome/browser/ui/views/website_settings/chooser_bubble_ui.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..25f604287a13b31ddfb3da506ce54ff9bd5c32f8 |
--- /dev/null |
+++ b/chrome/browser/ui/views/website_settings/chooser_bubble_ui.cc |
@@ -0,0 +1,346 @@ |
+// Copyright 2015 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/website_settings/chooser_bubble_ui.h" |
+ |
+#include <string> |
+ |
+#include "base/prefs/pref_service.h" |
+#include "base/strings/string16.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "chrome/browser/profiles/profile.h" |
+#include "chrome/browser/ui/browser.h" |
+#include "chrome/browser/ui/views/exclusive_access_bubble_views.h" |
+#include "chrome/browser/ui/views/frame/browser_view.h" |
+#include "chrome/browser/ui/views/frame/top_container_view.h" |
+#include "chrome/browser/ui/views/location_bar/location_bar_view.h" |
+#include "chrome/browser/ui/views/location_bar/location_icon_view.h" |
+#include "chrome/browser/usb/web_usb_permission_bubble_request.h" |
+#include "chrome/common/pref_names.h" |
+#include "chrome/grit/generated_resources.h" |
+#include "ui/accessibility/ax_view_state.h" |
+#include "ui/base/l10n/l10n_util.h" |
+#include "ui/base/resource/resource_bundle.h" |
+#include "ui/gfx/paint_vector_icon.h" |
+#include "ui/gfx/text_constants.h" |
+#include "ui/gfx/vector_icons_public.h" |
+#include "ui/views/bubble/bubble_delegate.h" |
+#include "ui/views/bubble/bubble_frame_view.h" |
+#include "ui/views/controls/button/label_button.h" |
+#include "ui/views/controls/button/label_button_border.h" |
+#include "ui/views/controls/label.h" |
+#include "ui/views/controls/table/table_view.h" |
+#include "ui/views/controls/table/table_view_observer.h" |
+#include "ui/views/layout/box_layout.h" |
+#include "ui/views/layout/grid_layout.h" |
+ |
+namespace { |
+ |
+// chooser permission bubble width |
+const int kChooserPermissionBubbleWidth = 300; |
+ |
+// chooser permission bubble height |
+const int kChooserPermissionBubbleHeight = 200; |
+ |
+// text label height when no devices found |
+const int kLabelHeight = 30; |
+ |
+// Spacing constant for outer margin. This is added to the |
+// bubble margin itself to equalize the margins at 13px. |
+const int kBubbleOuterMargin = 5; |
+ |
+// Spacing between major items should be 9px. |
+const int kItemMajorSpacing = 9; |
+ |
+// Button border size, draws inside the spacing distance. |
+const int kButtonBorderSize = 2; |
+ |
+} // namespace |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+// View implementation for the chooser bubble. |
+class ChooserBubbleUiDelegate : public views::BubbleDelegateView, |
+ public views::ButtonListener, |
+ public views::TableViewObserver { |
+ public: |
+ ChooserBubbleUiDelegate(views::View* anchor_view, |
+ views::BubbleBorder::Arrow anchor_arrow, |
+ ChooserBubbleUi* owner, |
+ const WebUsbPermissionBubbleRequest* request); |
+ ~ChooserBubbleUiDelegate() override; |
+ |
+ void Close(); |
+ |
+ // BubbleDelegateView: |
+ bool ShouldShowCloseButton() const override; |
+ bool ShouldShowWindowTitle() const override; |
+ base::string16 GetWindowTitle() const override; |
+ void OnWidgetDestroying(views::Widget* widget) override; |
+ |
+ // ButtonListener: |
+ void ButtonPressed(views::Button* button, const ui::Event& event) override; |
+ |
+ // views::TableViewObserver: |
+ void OnSelectionChanged() override; |
+ |
+ // Updates the anchor's arrow and view. Also repositions the bubble so it's |
+ // displayed in the correct location. |
+ void UpdateAnchor(views::View* anchor_view, |
+ views::BubbleBorder::Arrow anchor_arrow); |
+ |
+ private: |
+ ChooserBubbleUi* owner_; |
+ views::Button* connect_; |
+ views::Button* cancel_; |
+ views::TableView* table_view_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ChooserBubbleUiDelegate); |
+}; |
+ |
+ui::TableColumn ChooserTableColumn(int id, const std::string& title) { |
+ ui::TableColumn column; |
+ column.id = id; |
+ column.title = base::ASCIIToUTF16(title.c_str()); |
+ return column; |
+} |
+ |
+class ChooserTableModel : public ui::TableModel { |
+ public: |
+ explicit ChooserTableModel(const std::vector<std::string>& device_names) |
+ : observer_(nullptr) { |
+ device_names_ = device_names; |
+ row_count_ = static_cast<int>(device_names_.size()); |
+ } |
+ |
+ // ui::TableModel: |
+ int RowCount() override { return row_count_; } |
+ |
+ base::string16 GetText(int row, int column_id) override { |
+ if (row >= 0 && row < row_count_) { |
+ return base::ASCIIToUTF16(device_names_[row]); |
+ } else { |
+ return base::string16(); |
+ } |
+ } |
+ |
+ void SetObserver(ui::TableModelObserver* observer) override { |
+ observer_ = observer; |
+ } |
+ |
+ private: |
+ ui::TableModelObserver* observer_; |
+ std::vector<std::string> device_names_; |
+ int row_count_; |
+}; |
+ |
+ChooserBubbleUiDelegate::ChooserBubbleUiDelegate( |
+ views::View* anchor_view, |
+ views::BubbleBorder::Arrow anchor_arrow, |
+ ChooserBubbleUi* owner, |
+ const WebUsbPermissionBubbleRequest* request) |
+ : views::BubbleDelegateView(anchor_view, anchor_arrow), |
+ owner_(owner), |
+ connect_(nullptr), |
+ cancel_(nullptr) { |
+ views::GridLayout* layout = new views::GridLayout(this); |
+ SetLayoutManager(layout); |
+ |
+ views::ColumnSet* column_set = layout->AddColumnSet(0); |
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
+ views::GridLayout::USE_PREF, 0, 0); |
+ |
+ layout->StartRow(1, 0); |
+ |
+ const std::vector<std::string>& device_names = request->device_names(); |
+ if (!device_names.empty()) { |
+ // create a table view and list the device names into it. |
+ std::vector<ui::TableColumn> table_columns; |
+ table_columns.push_back(ChooserTableColumn( |
+ 0, "" /* empty string makes the column title invisible */)); |
+ table_view_ = new views::TableView(new ChooserTableModel(device_names), |
+ table_columns, views::TEXT_ONLY, true); |
+ table_view_->SetObserver(this); |
+ layout->AddView(table_view_->CreateParentIfNecessary(), 1, 1, |
+ views::GridLayout::FILL, views::GridLayout::FILL, |
+ kChooserPermissionBubbleWidth, |
+ kChooserPermissionBubbleHeight); |
+ } else { |
+ // show a text label saying no devices found. |
+ views::Label* label = new views::Label(l10n_util::GetStringUTF16( |
+ IDS_WEBUSB_PERMISSIONS_BUBBLE_NO_DEVICES_FOUND_PROMPT)); |
+ label->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
+ layout->AddView(label, 1, 1, views::GridLayout::FILL, |
+ views::GridLayout::FILL, kChooserPermissionBubbleWidth, |
+ kLabelHeight); |
+ } |
+ |
+ layout->AddPaddingRow(0, kItemMajorSpacing); |
+ |
+ views::View* button_row = new views::View(); |
+ views::GridLayout* button_layout = new views::GridLayout(button_row); |
+ views::ColumnSet* button_columns = button_layout->AddColumnSet(0); |
+ button_row->SetLayoutManager(button_layout); |
+ layout->StartRow(1, 0); |
+ layout->AddView(button_row); |
+ |
+ // lay out the Connect/Cancel buttons. |
+ button_columns->AddColumn(views::GridLayout::TRAILING, |
+ views::GridLayout::FILL, 100, |
+ views::GridLayout::USE_PREF, 0, 0); |
+ button_columns->AddPaddingColumn(0, |
+ kItemMajorSpacing - (2 * kButtonBorderSize)); |
+ button_columns->AddColumn(views::GridLayout::TRAILING, |
+ views::GridLayout::FILL, 0, |
+ views::GridLayout::USE_PREF, 0, 0); |
+ button_layout->StartRow(0, 0); |
+ |
+ base::string16 connect_text = base::ASCIIToUTF16("Connect"); |
+ views::LabelButton* connect_button = |
+ new views::LabelButton(this, connect_text); |
+ connect_button->SetStyle(views::Button::STYLE_BUTTON); |
+ // disable the connect button at the beginning since no device selected yet. |
+ connect_button->SetEnabled(false); |
+ button_layout->AddView(connect_button); |
+ connect_ = connect_button; |
+ |
+ base::string16 cancel_text = base::ASCIIToUTF16("Cancel"); |
+ views::LabelButton* cancel_button = new views::LabelButton(this, cancel_text); |
+ cancel_button->SetStyle(views::Button::STYLE_BUTTON); |
+ button_layout->AddView(cancel_button); |
+ cancel_ = cancel_button; |
+ |
+ button_layout->AddPaddingRow(0, kBubbleOuterMargin); |
+} |
+ |
+ChooserBubbleUiDelegate::~ChooserBubbleUiDelegate() { |
+ RemoveAllChildViews(true); |
+ if (owner_) |
+ owner_->Close(); |
+} |
+ |
+void ChooserBubbleUiDelegate::Close() { |
+ owner_ = nullptr; |
+ GetWidget()->Close(); |
+} |
+ |
+bool ChooserBubbleUiDelegate::ShouldShowCloseButton() const { |
+ return true; |
+} |
+ |
+bool ChooserBubbleUiDelegate::ShouldShowWindowTitle() const { |
+ return true; |
+} |
+ |
+base::string16 ChooserBubbleUiDelegate::GetWindowTitle() const { |
+ return l10n_util::GetStringUTF16(IDS_WEBUSB_PERMISSIONS_BUBBLE_PROMPT); |
+} |
+ |
+void ChooserBubbleUiDelegate::OnWidgetDestroying(views::Widget* widget) { |
+ views::BubbleDelegateView::OnWidgetDestroying(widget); |
+ if (owner_) { |
+ owner_->Close(); |
+ owner_ = nullptr; |
+ } |
+} |
+ |
+void ChooserBubbleUiDelegate::ButtonPressed(views::Button* button, |
+ const ui::Event& event) { |
+ if (!owner_) |
+ return; |
+ |
+ if (button == connect_) |
+ owner_->Connect(table_view_->selection_model().active()); |
+ else if (button == cancel_) |
+ owner_->Cancel(); |
+} |
+ |
+void ChooserBubbleUiDelegate::OnSelectionChanged() { |
+ // enable the connect button since user has selected an item. |
+ connect_->SetEnabled(true); |
+} |
+ |
+void ChooserBubbleUiDelegate::UpdateAnchor( |
+ views::View* anchor_view, |
+ views::BubbleBorder::Arrow anchor_arrow) { |
+ if (GetAnchorView() == anchor_view && arrow() == anchor_arrow) |
+ return; |
+ |
+ set_arrow(anchor_arrow); |
+ |
+ // Update the border in the bubble: will either add or remove the arrow. |
+ views::BubbleFrameView* frame = |
+ views::BubbleDelegateView::GetBubbleFrameView(); |
+ views::BubbleBorder::Arrow adjusted_arrow = anchor_arrow; |
+ if (base::i18n::IsRTL()) |
+ adjusted_arrow = views::BubbleBorder::horizontal_mirror(adjusted_arrow); |
+ frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>( |
+ new views::BubbleBorder(adjusted_arrow, shadow(), color()))); |
+ |
+ // Reposition the bubble based on the updated arrow and view. |
+ SetAnchorView(anchor_view); |
+} |
+ |
+////////////////////////////////////////////////////////////////////////////// |
+// ChooserBubbleUi |
+ |
+ChooserBubbleUi::ChooserBubbleUi(Browser* browser, |
+ WebUsbPermissionBubbleRequest* request) |
+ : browser_(browser), request_(request), bubble_delegate_(nullptr) { |
+ DCHECK(browser); |
+} |
+ |
+ChooserBubbleUi::~ChooserBubbleUi() {} |
+ |
+void ChooserBubbleUi::Show(BubbleReference bubble_reference) { |
+ if (bubble_delegate_) |
+ bubble_delegate_->Close(); |
+ bubble_delegate_ = new ChooserBubbleUiDelegate( |
+ GetAnchorView(), GetAnchorArrow(), this, request_); |
+ |
+ // Set |parent_window| because some valid anchors can become hidden. |
+ views::Widget* widget = views::Widget::GetWidgetForNativeWindow( |
+ browser_->window()->GetNativeWindow()); |
+ bubble_delegate_->set_parent_window(widget->GetNativeView()); |
+ |
+ views::BubbleDelegateView::CreateBubble(bubble_delegate_)->Show(); |
+} |
+ |
+void ChooserBubbleUi::Close() { |
+ if (bubble_delegate_) { |
+ bubble_delegate_->Close(); |
+ bubble_delegate_ = nullptr; |
+ } |
+} |
+ |
+void ChooserBubbleUi::UpdateAnchorPosition() { |
+ bubble_delegate_->UpdateAnchor(GetAnchorView(), GetAnchorArrow()); |
+} |
+ |
+void ChooserBubbleUi::Connect(int index) { |
+ request_->Connect(index); |
+ Close(); |
+} |
+ |
+void ChooserBubbleUi::Cancel() { |
+ request_->Cancel(); |
+ Close(); |
+} |
+ |
+views::View* ChooserBubbleUi::GetAnchorView() { |
+ BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_); |
+ |
+ if (browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)) |
+ return browser_view->GetLocationBarView()->location_icon_view(); |
+ |
+ if (browser_view->IsFullscreenBubbleVisible()) |
+ return browser_view->exclusive_access_bubble()->GetView(); |
+ |
+ return browser_view->top_container(); |
+} |
+ |
+views::BubbleBorder::Arrow ChooserBubbleUi::GetAnchorArrow() { |
+ if (browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)) |
+ return views::BubbleBorder::TOP_LEFT; |
+ return views::BubbleBorder::NONE; |
+} |