Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1061)

Unified Diff: chrome/browser/ui/views/intent_picker_bubble_view.cc

Issue 2229943003: Reusing Ok/Cancel buttons for intent picker (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: For testing we don't create a widget to contain the class, so DialogButtons are not in place, this … Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/views/intent_picker_bubble_view.cc
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index 0d46d817d4822b33e4f3f64474e5f87bd3a8bc7d..f4dd5db61819820d28c734a918f979c540a55284 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -15,7 +15,9 @@
#include "content/public/browser/navigation_handle.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/material_design/material_design_controller.h"
#include "ui/gfx/canvas.h"
+#include "ui/views/animation/ink_drop_host_view.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/scroll_view.h"
@@ -31,23 +33,47 @@ constexpr size_t kMaxAppResults = arc::ArcNavigationThrottle::kMaxAppResults;
// Main components sizes
constexpr int kRowHeight = 40;
constexpr int kMaxWidth = 320;
-constexpr int kHeaderHeight = 60;
-constexpr int kFooterHeight = 68;
-// Inter components padding
-constexpr int kButtonSeparation = 8;
-constexpr int kLabelImageSeparation = 12;
// UI position wrt the Top Container
constexpr int kTopContainerMerge = 3;
constexpr size_t kAppTagNoneSelected = static_cast<size_t>(-1);
-// Arbitrary negative values to use as tags on the |always_button_| and
-// |just_once_button_|. These are negative to differentiate from the app's tags
-// which are always >= 0.
-enum class Option : int { ALWAYS = -2, JUST_ONCE };
-
} // namespace
+// IntentPickerMenuButton
+
+// This is a convenience class for the buttons on the intent picker so we can
+// manipulate the internal hovering behavior.
+class IntentPickerMenuButton : public views::LabelButton {
+ public:
+ IntentPickerMenuButton(views::ButtonListener* listener,
+ const base::string16& title)
+ : LabelButton(listener, title) {
+ SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ SetInkDropMode(InkDropMode::ON);
+ }
+
+ SkColor GetInkDropBaseColor() const override { return SK_ColorBLACK; }
+
+ void MarkAsUnselected(const ui::Event& event) {
+ AnimateInkDrop(views::InkDropState::DEACTIVATED,
+ ui::LocatedEvent::FromIfValid(&event));
+ }
+
+ void MarkAsSelected(const ui::Event& event) {
+ AnimateInkDrop(views::InkDropState::ACTIVATED,
+ ui::LocatedEvent::FromIfValid(&event));
+ }
+
+ views::InkDropState GetTargetInkDropState() {
+ return ink_drop()->GetTargetInkDropState();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IntentPickerMenuButton);
+};
+
// static
void IntentPickerBubbleView::ShowBubble(
content::NavigationHandle* handle,
@@ -55,18 +81,12 @@ void IntentPickerBubbleView::ShowBubble(
const ThrottleCallback& throttle_cb) {
Browser* browser =
chrome::FindBrowserWithWebContents(handle->GetWebContents());
- if (!browser) {
+ if (!browser || !BrowserView::GetBrowserViewForBrowser(browser)) {
throttle_cb.Run(kAppTagNoneSelected,
arc::ArcNavigationThrottle::CloseReason::ERROR);
return;
}
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
- if (!browser_view) {
- throttle_cb.Run(kAppTagNoneSelected,
- arc::ArcNavigationThrottle::CloseReason::ERROR);
- return;
- }
-
IntentPickerBubbleView* delegate = new IntentPickerBubbleView(
app_info, throttle_cb, handle->GetWebContents());
delegate->set_margins(gfx::Insets());
@@ -101,86 +121,69 @@ IntentPickerBubbleView::CreateBubbleView(
return bubble;
}
-void IntentPickerBubbleView::Init() {
- SkColor button_text_color = SkColorSetRGB(0x42, 0x85, 0xf4);
+bool IntentPickerBubbleView::Accept() {
+ if (!throttle_cb_.is_null()) {
+ auto callback = throttle_cb_;
+ throttle_cb_.Reset();
+ callback.Run(selected_app_tag_,
+ arc::ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED);
+ }
+ return true;
+}
+
+bool IntentPickerBubbleView::Cancel() {
+ if (!throttle_cb_.is_null()) {
+ auto callback = throttle_cb_;
+ throttle_cb_.Reset();
+ callback.Run(selected_app_tag_,
+ arc::ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED);
+ }
+ return true;
+}
+bool IntentPickerBubbleView::Close() {
+ // Whenever closing the bubble without pressing |JUST ONCE| or |ALWAYS| we
+ // need to report back that the user didn't select anything.
+ if (!throttle_cb_.is_null()) {
+ auto callback = throttle_cb_;
+ throttle_cb_.Reset();
+ callback.Run(kAppTagNoneSelected,
+ arc::ArcNavigationThrottle::CloseReason::DIALOG_DEACTIVATED);
+ }
+ return true;
+}
+
+int IntentPickerBubbleView::GetDefaultDialogButton() const {
+ return ui::DIALOG_BUTTON_NONE;
+}
+
+bool IntentPickerBubbleView::IsDialogButtonEnabled(
+ ui::DialogButton button) const {
+ // Both buttons are enabled only when the user selected one of the apps.
+ return selected_app_tag_ != kAppTagNoneSelected;
+}
+
+void IntentPickerBubbleView::Init() {
views::BoxLayout* general_layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
SetLayoutManager(general_layout);
- // Create a view for the upper part of the UI, managed by a GridLayout to
- // allow precise padding.
- View* header = new View();
- views::GridLayout* header_layout = new views::GridLayout(header);
- header->SetLayoutManager(header_layout);
- views::Label* open_with = new views::Label(
- l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_OPEN_WITH));
- open_with->SetHorizontalAlignment(gfx::ALIGN_LEFT);
- open_with->SetFontList(gfx::FontList("Roboto-medium, 15px"));
-
- views::ColumnSet* column_set = header_layout->AddColumnSet(0);
- column_set->AddPaddingColumn(0, 16);
- column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
- views::GridLayout::FIXED, kMaxWidth, kMaxWidth);
- column_set->AddPaddingColumn(0, 16);
- // Tell the GridLayout where to start, then proceed to place the views.
- header_layout->AddPaddingRow(0.0, 21);
- header_layout->StartRow(0, 0);
- header_layout->AddView(open_with);
- header_layout->AddPaddingRow(0.0, 24);
-
- always_button_ = new views::LabelButton(
- this, l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_ALWAYS));
- always_button_->SetFocusBehavior(View::FocusBehavior::ALWAYS);
- always_button_->SetFontList(gfx::FontList("Roboto-medium, 13px"));
- always_button_->set_tag(static_cast<int>(Option::ALWAYS));
- always_button_->SetMinSize(gfx::Size(80, 32));
- always_button_->SetTextColor(views::Button::STATE_DISABLED, SK_ColorGRAY);
- always_button_->SetTextColor(views::Button::STATE_NORMAL, button_text_color);
- always_button_->SetTextColor(views::Button::STATE_HOVERED, button_text_color);
- always_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
- always_button_->SetState(views::Button::STATE_DISABLED);
- always_button_->SetBorder(views::Border::CreateEmptyBorder(gfx::Insets(16)));
-
- just_once_button_ = new views::LabelButton(
- this, l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_JUST_ONCE));
- just_once_button_->SetFocusBehavior(View::FocusBehavior::ALWAYS);
- just_once_button_->SetFontList(gfx::FontList("Roboto-medium, 13px"));
- just_once_button_->set_tag(static_cast<int>(Option::JUST_ONCE));
- just_once_button_->SetMinSize(gfx::Size(80, 32));
- just_once_button_->SetState(views::Button::STATE_DISABLED);
- just_once_button_->SetTextColor(views::Button::STATE_DISABLED, SK_ColorGRAY);
- just_once_button_->SetTextColor(views::Button::STATE_NORMAL,
- button_text_color);
- just_once_button_->SetTextColor(views::Button::STATE_HOVERED,
- button_text_color);
- just_once_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
- just_once_button_->SetBorder(
- views::Border::CreateEmptyBorder(gfx::Insets(16)));
-
- header_layout->StartRow(0, 0);
- AddChildView(header);
-
// Creates a view to hold the views for each app.
views::View* scrollable_view = new views::View();
views::BoxLayout* scrollable_layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
scrollable_view->SetLayoutManager(scrollable_layout);
for (size_t i = 0; i < app_info_.size(); ++i) {
- views::LabelButton* tmp_label = new views::LabelButton(
+ IntentPickerMenuButton* app_button = new IntentPickerMenuButton(
this, base::UTF8ToUTF16(base::StringPiece(app_info_[i].first)));
- tmp_label->SetFocusBehavior(View::FocusBehavior::ALWAYS);
- tmp_label->SetFontList(gfx::FontList("Roboto-regular, 13px"));
- tmp_label->SetImageLabelSpacing(kLabelImageSeparation);
- tmp_label->SetMinSize(gfx::Size(kMaxWidth, kRowHeight));
- tmp_label->SetMaxSize(gfx::Size(kMaxWidth, kRowHeight));
- tmp_label->set_tag(i);
+ app_button->SetMinSize(gfx::Size(kMaxWidth, kRowHeight));
+ app_button->set_tag(i);
if (!app_info_[i].second.IsEmpty()) {
- tmp_label->SetImage(views::ImageButton::STATE_NORMAL,
- *app_info_[i].second.ToImageSkia());
+ app_button->SetImage(views::ImageButton::STATE_NORMAL,
+ *app_info_[i].second.ToImageSkia());
}
- tmp_label->SetBorder(views::Border::CreateEmptyBorder(10, 16, 10, 0));
- scrollable_view->AddChildViewAt(tmp_label, i);
+ app_button->SetBorder(views::Border::CreateEmptyBorder(10, 16, 10, 0));
+ scrollable_view->AddChildViewAt(app_button, i);
}
scroll_view_ = new views::ScrollView();
@@ -199,19 +202,20 @@ void IntentPickerBubbleView::Init() {
}
AddChildView(scroll_view_);
- // The lower part of the Picker contains only the 2 buttons
- // |just_once_button_| and |always_button_|.
- View* footer = new View();
- footer->SetBorder(views::Border::CreateEmptyBorder(24, 0, 12, 12));
- views::BoxLayout* buttons_layout = new views::BoxLayout(
- views::BoxLayout::kHorizontal, 0, 0, kButtonSeparation);
- footer->SetLayoutManager(buttons_layout);
-
- buttons_layout->set_main_axis_alignment(
- views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
- footer->AddChildView(just_once_button_);
- footer->AddChildView(always_button_);
- AddChildView(footer);
+ SetPaintToLayer(true);
bruthig 2016/09/08 15:13:12 I meant the contents of that if block as well. i.
djacobo_ 2016/09/08 21:27:06 They are, otherwise there is a bad behavior while
bruthig 2016/09/12 17:33:11 The purpose of this code is to clip child views th
+ layer()->SetMasksToBounds(true);
+ layer()->SetFillsBoundsOpaquely(false);
+}
+
+base::string16 IntentPickerBubbleView::GetWindowTitle() const {
+ return l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_OPEN_WITH);
+}
+
+base::string16 IntentPickerBubbleView::GetDialogButtonLabel(
+ ui::DialogButton button) const {
+ if (button == ui::DIALOG_BUTTON_OK)
+ return l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_JUST_ONCE);
+ return l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_ALWAYS);
}
IntentPickerBubbleView::IntentPickerBubbleView(
@@ -221,11 +225,8 @@ IntentPickerBubbleView::IntentPickerBubbleView(
: views::BubbleDialogDelegateView(nullptr /* anchor_view */,
views::BubbleBorder::TOP_CENTER),
WebContentsObserver(web_contents),
- was_callback_run_(false),
throttle_cb_(throttle_cb),
selected_app_tag_(kAppTagNoneSelected),
- always_button_(nullptr),
- just_once_button_(nullptr),
scroll_view_(nullptr),
app_info_(app_info) {}
@@ -236,47 +237,28 @@ IntentPickerBubbleView::~IntentPickerBubbleView() {
// If the widget gets closed without an app being selected we still need to use
// the callback so the caller can Resume the navigation.
void IntentPickerBubbleView::OnWidgetDestroying(views::Widget* widget) {
- if (!was_callback_run_) {
- throttle_cb_.Run(
- kAppTagNoneSelected,
- arc::ArcNavigationThrottle::CloseReason::DIALOG_DEACTIVATED);
- was_callback_run_ = true;
+ if (!throttle_cb_.is_null()) {
+ auto callback = throttle_cb_;
+ throttle_cb_.Reset();
+ callback.Run(kAppTagNoneSelected,
+ arc::ArcNavigationThrottle::CloseReason::DIALOG_DEACTIVATED);
}
}
-int IntentPickerBubbleView::GetDialogButtons() const {
- return ui::DIALOG_BUTTON_NONE;
-}
-
void IntentPickerBubbleView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
- switch (sender->tag()) {
- case static_cast<int>(Option::ALWAYS):
- throttle_cb_.Run(selected_app_tag_,
- arc::ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED);
- was_callback_run_ = true;
- GetWidget()->Close();
- break;
- case static_cast<int>(Option::JUST_ONCE):
- throttle_cb_.Run(
- selected_app_tag_,
- arc::ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED);
- was_callback_run_ = true;
- GetWidget()->Close();
- break;
- default:
- // The selected app must be a value in the range [0, app_info_.size()-1].
- DCHECK(static_cast<size_t>(sender->tag()) < app_info_.size());
- // The user cannot click on the |always_button_| or |just_once_button_|
- // unless an explicit app has been selected.
- if (selected_app_tag_ != kAppTagNoneSelected)
- SetLabelButtonBackgroundColor(selected_app_tag_, SK_ColorWHITE);
- selected_app_tag_ = sender->tag();
- SetLabelButtonBackgroundColor(selected_app_tag_,
- SkColorSetRGB(0xeb, 0xeb, 0xeb));
- always_button_->SetState(views::Button::STATE_NORMAL);
- just_once_button_->SetState(views::Button::STATE_NORMAL);
- }
+ // The selected app must be a value in the range [0, app_info_.size()-1].
+ DCHECK(static_cast<size_t>(sender->tag()) < app_info_.size());
+ if (selected_app_tag_ != kAppTagNoneSelected)
+ GetIntentPickerMenuButtonAt(selected_app_tag_)->MarkAsUnselected(event);
+
+ selected_app_tag_ = sender->tag();
+ GetIntentPickerMenuButtonAt(selected_app_tag_)->MarkAsSelected(event);
+
+ // This may not have the integrated |Accept| and |Cancel| buttons because of
+ // how the class gets created for testing.
+ if (GetWidget())
+ GetDialogClientView()->UpdateDialogButtons();
}
gfx::Size IntentPickerBubbleView::GetPreferredSize() const {
@@ -290,29 +272,41 @@ gfx::Size IntentPickerBubbleView::GetPreferredSize() const {
} else {
apps_height *= kRowHeight;
}
- ps.set_height(apps_height + kHeaderHeight + kFooterHeight);
+ ps.set_height(apps_height);
return ps;
}
// If the actual web_contents gets destroyed in the middle of the process we
// should inform the caller about this error.
void IntentPickerBubbleView::WebContentsDestroyed() {
- if (!was_callback_run_) {
- throttle_cb_.Run(kAppTagNoneSelected,
- arc::ArcNavigationThrottle::CloseReason::ERROR);
- was_callback_run_ = true;
+ if (!throttle_cb_.is_null()) {
+ auto callback = throttle_cb_;
+ throttle_cb_.Reset();
+ callback.Run(kAppTagNoneSelected,
+ arc::ArcNavigationThrottle::CloseReason::ERROR);
}
GetWidget()->Close();
}
-views::LabelButton* IntentPickerBubbleView::GetLabelButtonAt(size_t index) {
+IntentPickerMenuButton* IntentPickerBubbleView::GetIntentPickerMenuButtonAt(
+ size_t index) {
views::View* temp_contents = scroll_view_->contents();
- return static_cast<views::LabelButton*>(temp_contents->child_at(index));
+ return static_cast<IntentPickerMenuButton*>(temp_contents->child_at(index));
+}
+
+gfx::ImageSkia IntentPickerBubbleView::GetAppImageForTesting(size_t index) {
+ return GetIntentPickerMenuButtonAt(index)->GetImage(
+ views::Button::ButtonState::STATE_NORMAL);
+}
+
+views::InkDropState IntentPickerBubbleView::GetInkDropStateForTesting(
+ size_t index) {
+ return GetIntentPickerMenuButtonAt(index)->GetTargetInkDropState();
}
-void IntentPickerBubbleView::SetLabelButtonBackgroundColor(size_t index,
- SkColor color) {
- views::LabelButton* temp_lb = GetLabelButtonAt(index);
- temp_lb->set_background(views::Background::CreateSolidBackground(color));
- temp_lb->SchedulePaint();
+void IntentPickerBubbleView::PressButtonForTesting(size_t index,
+ const ui::Event& event) {
+ views::Button* button =
+ static_cast<views::Button*>(GetIntentPickerMenuButtonAt(index));
+ ButtonPressed(button, event);
}

Powered by Google App Engine
This is Rietveld 408576698