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

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

Issue 2134293002: Adding a ScrollView for IntentPickerBubbleView (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Adding const for GetAppInfoSizeForTesting() Created 4 years, 5 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 c5f007a5ca7746472d5fe094fadf0bfffa071754..83c800039fe3cea380a03decd93a39a55664cc92 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -4,8 +4,6 @@
#include "chrome/browser/ui/views/intent_picker_bubble_view.h"
-#include <algorithm>
-
#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
@@ -18,16 +16,22 @@
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas.h"
+#include "ui/views/border.h"
#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/scroll_view.h"
+#include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/window/dialog_client_view.h"
+namespace content {
+class WebContents;
+} // namespace content
+
namespace {
-// TODO(djacobo): Add a scroll bar and remove kMaxAppResults.
-// Discriminating app results when the list is longer than |kMaxAppResults|
-constexpr size_t kMaxAppResults = 3;
+// Using |kMaxAppResults| as a measure of how many apps we want to show.
+constexpr size_t kMaxAppResults = arc::ArcNavigationThrottle::kMaxAppResults;
// Main components sizes
constexpr int kRowHeight = 40;
constexpr int kMaxWidth = 320;
@@ -39,7 +43,7 @@ constexpr int kLabelImageSeparation = 12;
// UI position wrt the Top Container
constexpr int kTopContainerMerge = 3;
-constexpr int kAppTagNoneSelected = -1;
+constexpr size_t kAppTagNoneSelected = -1;
Luis Héctor Chávez 2016/07/21 21:55:34 ok, just do an explicit cast: static_cast<size_t>(
djacobo_ 2016/07/22 01:22:07 Done.
// 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
@@ -89,6 +93,18 @@ void IntentPickerBubbleView::ShowBubble(
widget->Show();
}
+// static
+std::unique_ptr<IntentPickerBubbleView>
+IntentPickerBubbleView::CreateBubbleForTesting(
+ const std::vector<NameAndIcon>& app_info,
+ const ThrottleCallback& throttle_cb,
+ content::WebContents* web_contents) {
+ std::unique_ptr<IntentPickerBubbleView> bubble(
+ new IntentPickerBubbleView(app_info, throttle_cb, web_contents));
+ bubble->Init();
+ return bubble;
+}
+
void IntentPickerBubbleView::Init() {
SkColor button_text_color = SkColorSetRGB(0x42, 0x85, 0xf4);
@@ -128,6 +144,8 @@ void IntentPickerBubbleView::Init() {
always_button_->SetTextColor(views::Button::STATE_HOVERED, button_text_color);
always_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
always_button_->SetState(views::Button::STATE_DISABLED);
+ // Insets in the format top, left, bottom, right.
sky 2016/07/21 22:57:06 This comment isn't particularly useful when all th
djacobo_ 2016/07/22 01:22:07 Nice shorthand, applied the same to |just_once_but
+ always_button_->SetBorder(views::Border::CreateEmptyBorder(16, 16, 16, 16));
just_once_button_ = new views::LabelButton(
this, l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_JUST_ONCE));
@@ -142,8 +160,19 @@ void IntentPickerBubbleView::Init() {
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(16, 16, 16, 16));
+
+ // Adding the |open_with| label to the picker.
sky 2016/07/21 22:57:06 Is this comment relevant here?
djacobo_ 2016/07/22 01:22:07 I believe is easy to see what's going on by the co
+ header_layout->StartRow(0, 0);
+ AddChildViewAt(header, static_cast<int>(ViewsId::HEADER));
sky 2016/07/21 22:57:06 Based on the name I would expect ViewId to corresp
djacobo_ 2016/07/22 01:22:07 You are right and the idea sounds better, modifica
- for (size_t i = 0; i < std::min(app_info_.size(), kMaxAppResults); ++i) {
+ // Setting a view and a layout for a ScrollView for the apps' list.
sky 2016/07/21 22:57:06 This comment is confusing. I think you mean someth
djacobo_ 2016/07/22 01:22:07 Oh wow I messed up pretty badly indeed, Done.
+ 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(
this, base::UTF8ToUTF16(base::StringPiece(app_info_[i].first)));
tmp_label->SetFocusBehavior(View::FocusBehavior::ALWAYS);
@@ -152,30 +181,41 @@ void IntentPickerBubbleView::Init() {
tmp_label->SetMinSize(gfx::Size(kMaxWidth, kRowHeight));
tmp_label->SetMaxSize(gfx::Size(kMaxWidth, kRowHeight));
tmp_label->set_tag(i);
- const gfx::ImageSkia* icon_ = app_info_[i].second.ToImageSkia();
- tmp_label->SetImage(views::ImageButton::STATE_NORMAL, *icon_);
- header_layout->StartRow(0, 0);
- header_layout->AddView(tmp_label);
+ if (!app_info_[i].second.IsEmpty()) {
+ tmp_label->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);
}
- // Adding the Upper part of the Intent Picker |open_with| label and all the
- // app options to |this|.
- header_layout->StartRow(0, 0);
- header_layout->AddPaddingRow(0, 12);
- AddChildView(header);
+ views::ScrollView* scroll_view = new views::ScrollView();
+ scroll_view->SetContents(scrollable_view);
+ scroll_view->SetVerticalScrollBar(new views::OverlayScrollBar(false));
sky 2016/07/21 22:57:06 Is it really necessary to create an overlayscrollb
djacobo_ 2016/07/22 01:22:07 Yes, I want the ScrollBar for this bubble to be sh
+ // This part gives the scroll a fixed width and height. The height depends on
+ // how many app candidates we got and how many we actually want to show.
+ // The added 0.5 on the else block allow us to let the user know there are
sky 2016/07/21 22:57:06 I don't understand the *.5. Why not just add 1 as
djacobo_ 2016/07/22 01:22:07 What I was trying to say is, we are required to sh
sky 2016/07/22 16:32:54 My mistake, got it.
+ // more than |kMaxAppResults| apps accessible by scrolling the list.
+ if (app_info_.size() <= kMaxAppResults) {
+ scroll_view->ClipHeightTo(kMaxWidth, app_info_.size() * kRowHeight);
sky 2016/07/21 22:57:06 The first argument is the min height, not a width.
djacobo_ 2016/07/22 01:22:07 Totally misread the interface, moving to 1 Row at
+ } else {
+ scroll_view->ClipHeightTo(kMaxWidth, (kMaxAppResults + 0.5) * kRowHeight);
+ }
+ AddChildViewAt(scroll_view, static_cast<int>(ViewsId::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, 12, 12, kButtonSeparation);
+ 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);
+ AddChildViewAt(footer, static_cast<int>(ViewsId::FOOTER));
}
IntentPickerBubbleView::IntentPickerBubbleView(
@@ -196,6 +236,15 @@ IntentPickerBubbleView::~IntentPickerBubbleView() {
SetLayoutManager(nullptr);
}
+size_t IntentPickerBubbleView::GetAppInfoSizeForTesting() const {
+ return app_info_.size();
+}
+
+views::LabelButton* IntentPickerBubbleView::GetLabelButtonAtForTesting(
+ size_t index) {
+ return GetLabelButtonAt(index);
+}
+
// 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) {
@@ -215,24 +264,30 @@ 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;
+ if (!was_callback_run_) {
sky 2016/07/21 22:57:06 Why do you need was_callback_run_?
djacobo_ 2016/07/22 01:22:07 I don't want the callback being run twice since th
sky 2016/07/22 16:32:54 How can that happen? AFAICT the widget is closed o
djacobo_ 2016/07/22 22:37:24 We can get rid of the if (!was_callback_run_) chec
sky 2016/07/22 23:45:56 That I understand. My question is solely about the
djacobo_ 2016/07/23 00:20:31 Oh ok, we actually don't need it anymore. The thin
+ 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;
+ if (!was_callback_run_) {
+ throttle_cb_.Run(
+ selected_app_tag_,
+ arc::ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED);
+ was_callback_run_ = true;
+ }
GetWidget()->Close();
break;
default:
- // TODO(djacobo): Paint the background of the selected button on a
- // different color, so the user has a clear remainder of his selection.
// The user cannot click on the |always_button_| or |just_once_button_|
// unless an explicit app has been selected.
+ if (selected_app_tag_ != kAppTagNoneSelected)
+ PaintLabelButton(selected_app_tag_, SK_ColorWHITE);
selected_app_tag_ = sender->tag();
+ PaintLabelButton(selected_app_tag_, SkColorSetRGB(0xeb, 0xeb, 0xeb));
always_button_->SetState(views::Button::STATE_NORMAL);
just_once_button_->SetState(views::Button::STATE_NORMAL);
}
@@ -241,8 +296,15 @@ void IntentPickerBubbleView::ButtonPressed(views::Button* sender,
gfx::Size IntentPickerBubbleView::GetPreferredSize() const {
gfx::Size ps;
ps.set_width(kMaxWidth);
- ps.set_height((std::min(app_info_.size(), kMaxAppResults)) * kRowHeight +
- kHeaderHeight + kFooterHeight);
+ int apps_height = app_info_.size();
+ // We are showing |kMaxAppResults| + 0.5 rows at max, the extra 0.5 is used so
+ // the user can notice that more options are available.
+ if (app_info_.size() > kMaxAppResults) {
+ apps_height = (kMaxAppResults + 0.5) * kRowHeight;
+ } else {
+ apps_height *= kRowHeight;
+ }
+ ps.set_height(apps_height + kHeaderHeight + kFooterHeight);
return ps;
}
@@ -256,3 +318,17 @@ void IntentPickerBubbleView::WebContentsDestroyed() {
}
GetWidget()->Close();
}
+
+views::LabelButton* IntentPickerBubbleView::GetLabelButtonAt(size_t index) {
+ int scroll_index = static_cast<int>(ViewsId::SCROLL_VIEW);
Luis Héctor Chávez 2016/07/21 21:55:34 Having the |selected_app_tag_| be a huge value is
djacobo_ 2016/07/22 01:22:07 Done.
+ views::ScrollView* temp_scroll =
+ static_cast<views::ScrollView*>(child_at(scroll_index));
+ views::View* temp_contents = temp_scroll->contents();
+ return static_cast<views::LabelButton*>(temp_contents->child_at(index));
+}
+
+void IntentPickerBubbleView::PaintLabelButton(size_t index, SkColor color) {
+ views::LabelButton* temp_lb = GetLabelButtonAt(index);
+ temp_lb->set_background(views::Background::CreateSolidBackground(color));
+ temp_lb->SchedulePaint();
+}

Powered by Google App Engine
This is Rietveld 408576698