Commit 8155c88c authored by Peter Kasting's avatar Peter Kasting Committed by Commit Bot

Allow device chooser table to handle clicks.

Originally, when the table was visible, the device chooser had three children: a
scroll view containiner the table, and two invisible centered labels.  In
https://chromium-review.googlesource.com/c/chromium/src/+/1837079 I reworked
this to center the labels using anonymous views with layout managers.  This
meant that the device chooser now contained three visible full-size children
(which, in two cases, just contained an invisible centered child).  As a result
the last child (which was highest in Z order) became the event handler at all
times, and while it would bubble events to its parent, there was no way for them
to get over to the sibling scroll view.

The solution is to toggle the visibility of these centering views, rather than
toggling the visibility of their contained labels.  This causes event targeting
to select the scroll view again.

Also does various additional cleanup I noticed while working on this.

Bug: 1011536
Change-Id: I27c496ebf4899b94bd442a1eaeb5e6ddbca387a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1843294
Commit-Queue: Peter Boström <pbos@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarPeter Boström <pbos@chromium.org>
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703497}
parent f90d1e44
...@@ -37,7 +37,24 @@ constexpr int kReScanButtonTag = 2; ...@@ -37,7 +37,24 @@ constexpr int kReScanButtonTag = 2;
} // namespace } // namespace
DeviceChooserContentView::BluetoothStatusContainer::BluetoothStatusContainer( class BluetoothStatusContainer : public views::View {
public:
explicit BluetoothStatusContainer(views::ButtonListener* listener);
void ShowScanningLabelAndThrobber();
void ShowReScanButton(bool enabled);
private:
friend class DeviceChooserContentView;
views::LabelButton* re_scan_button_;
views::Throbber* throbber_;
views::Label* scanning_label_;
DISALLOW_COPY_AND_ASSIGN(BluetoothStatusContainer);
};
BluetoothStatusContainer::BluetoothStatusContainer(
views::ButtonListener* listener) { views::ButtonListener* listener) {
SetLayoutManager(std::make_unique<views::FillLayout>()); SetLayoutManager(std::make_unique<views::FillLayout>());
...@@ -76,16 +93,14 @@ DeviceChooserContentView::BluetoothStatusContainer::BluetoothStatusContainer( ...@@ -76,16 +93,14 @@ DeviceChooserContentView::BluetoothStatusContainer::BluetoothStatusContainer(
scanning_label_ = scan_container->AddChildView(std::move(scanning_label)); scanning_label_ = scan_container->AddChildView(std::move(scanning_label));
} }
void DeviceChooserContentView::BluetoothStatusContainer:: void BluetoothStatusContainer::ShowScanningLabelAndThrobber() {
ShowScanningLabelAndThrobber() {
re_scan_button_->SetVisible(false); re_scan_button_->SetVisible(false);
throbber_->SetVisible(true); throbber_->SetVisible(true);
scanning_label_->SetVisible(true); scanning_label_->SetVisible(true);
throbber_->Start(); throbber_->Start();
} }
void DeviceChooserContentView::BluetoothStatusContainer::ShowReScanButton( void BluetoothStatusContainer::ShowReScanButton(bool enabled) {
bool enabled) {
re_scan_button_->SetVisible(true); re_scan_button_->SetVisible(true);
re_scan_button_->SetEnabled(enabled); re_scan_button_->SetEnabled(enabled);
throbber_->Stop(); throbber_->Stop();
...@@ -115,7 +130,7 @@ DeviceChooserContentView::DeviceChooserContentView( ...@@ -115,7 +130,7 @@ DeviceChooserContentView::DeviceChooserContentView(
table_parent_ = AddChildView( table_parent_ = AddChildView(
views::TableView::CreateScrollViewWithTable(std::move(table_view))); views::TableView::CreateScrollViewWithTable(std::move(table_view)));
const auto add_centered_view = [this](auto view) { const auto add_centering_view = [this](auto view) {
auto* container = AddChildView(std::make_unique<views::View>()); auto* container = AddChildView(std::make_unique<views::View>());
auto* layout = auto* layout =
container->SetLayoutManager(std::make_unique<views::BoxLayout>( container->SetLayoutManager(std::make_unique<views::BoxLayout>(
...@@ -124,24 +139,26 @@ DeviceChooserContentView::DeviceChooserContentView( ...@@ -124,24 +139,26 @@ DeviceChooserContentView::DeviceChooserContentView(
views::BoxLayout::MainAxisAlignment::kCenter); views::BoxLayout::MainAxisAlignment::kCenter);
layout->set_cross_axis_alignment( layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter); views::BoxLayout::CrossAxisAlignment::kCenter);
return container->AddChildView(std::move(view)); layout->set_inside_border_insets(gfx::Insets(0, 6));
container->AddChildView(std::move(view));
return container;
}; };
no_options_help_ = add_centered_view( auto no_options_help =
std::make_unique<views::Label>(chooser_controller_->GetNoOptionsText())); std::make_unique<views::Label>(chooser_controller_->GetNoOptionsText());
no_options_help_->SetMultiLine(true); no_options_help->SetMultiLine(true);
no_options_view_ = add_centering_view(std::move(no_options_help));
base::string16 link_text = l10n_util::GetStringUTF16( base::string16 link_text = l10n_util::GetStringUTF16(
IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ON_BLUETOOTH_LINK_TEXT); IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ON_BLUETOOTH_LINK_TEXT);
size_t offset = 0; size_t offset = 0;
base::string16 text = l10n_util::GetStringFUTF16( base::string16 text = l10n_util::GetStringFUTF16(
IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ADAPTER_OFF, link_text, &offset); IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ADAPTER_OFF, link_text, &offset);
adapter_off_help_ = auto adapter_off_help = std::make_unique<views::StyledLabel>(text, this);
add_centered_view(std::make_unique<views::StyledLabel>(text, this)); adapter_off_help->AddStyleRange(
adapter_off_help_->AddStyleRange(
gfx::Range(0, link_text.size()), gfx::Range(0, link_text.size()),
views::StyledLabel::RangeStyleInfo::CreateForLink()); views::StyledLabel::RangeStyleInfo::CreateForLink());
adapter_off_help_->SetVisible(false); adapter_off_view_ = add_centering_view(std::move(adapter_off_help));
UpdateTableView(); UpdateTableView();
} }
...@@ -157,13 +174,6 @@ gfx::Size DeviceChooserContentView::GetMinimumSize() const { ...@@ -157,13 +174,6 @@ gfx::Size DeviceChooserContentView::GetMinimumSize() const {
return gfx::Size(); return gfx::Size();
} }
void DeviceChooserContentView::OnBoundsChanged(
const gfx::Rect& previous_bounds) {
constexpr int kMessagePadding = 6;
no_options_help_->SizeToFit(width() - 2 * kMessagePadding);
adapter_off_help_->SizeToFit(width() - 2 * kMessagePadding);
}
int DeviceChooserContentView::RowCount() { int DeviceChooserContentView::RowCount() {
return base::checked_cast<int>(chooser_controller_->NumOptions()); return base::checked_cast<int>(chooser_controller_->NumOptions());
} }
...@@ -258,7 +268,6 @@ void DeviceChooserContentView::OnRefreshStateChanged(bool refreshing) { ...@@ -258,7 +268,6 @@ void DeviceChooserContentView::OnRefreshStateChanged(bool refreshing) {
void DeviceChooserContentView::StyledLabelLinkClicked(views::StyledLabel* label, void DeviceChooserContentView::StyledLabelLinkClicked(views::StyledLabel* label,
const gfx::Range& range, const gfx::Range& range,
int event_flags) { int event_flags) {
DCHECK_EQ(adapter_off_help_, label);
chooser_controller_->OpenAdapterOffHelpUrl(); chooser_controller_->OpenAdapterOffHelpUrl();
} }
...@@ -349,10 +358,20 @@ void DeviceChooserContentView::Close() { ...@@ -349,10 +358,20 @@ void DeviceChooserContentView::Close() {
void DeviceChooserContentView::UpdateTableView() { void DeviceChooserContentView::UpdateTableView() {
bool has_options = adapter_enabled_ && RowCount() > 0; bool has_options = adapter_enabled_ && RowCount() > 0;
table_parent_->SetVisible(has_options); table_parent_->SetVisible(has_options);
if (chooser_controller_->TableViewAlwaysDisabled()) table_view_->SetEnabled(has_options &&
table_view_->SetEnabled(false); !chooser_controller_->TableViewAlwaysDisabled());
else no_options_view_->SetVisible(!has_options && adapter_enabled_);
table_view_->SetEnabled(has_options); adapter_off_view_->SetVisible(!adapter_enabled_);
no_options_help_->SetVisible(!has_options && adapter_enabled_); }
adapter_off_help_->SetVisible(!adapter_enabled_);
views::LabelButton* DeviceChooserContentView::ReScanButtonForTesting() {
return bluetooth_status_container_->re_scan_button_;
}
views::Throbber* DeviceChooserContentView::ThrobberForTesting() {
return bluetooth_status_container_->throbber_;
}
views::Label* DeviceChooserContentView::ScanningLabelForTesting() {
return bluetooth_status_container_->scanning_label_;
} }
...@@ -13,13 +13,13 @@ ...@@ -13,13 +13,13 @@
#include "ui/base/models/table_model.h" #include "ui/base/models/table_model.h"
#include "ui/gfx/range/range.h" #include "ui/gfx/range/range.h"
#include "ui/views/controls/button/button.h" #include "ui/views/controls/button/button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/styled_label_listener.h" #include "ui/views/controls/styled_label_listener.h"
#include "ui/views/view.h" #include "ui/views/view.h"
class BluetoothStatusContainer;
namespace views { namespace views {
class Label;
class LabelButton; class LabelButton;
class StyledLabel;
class TableView; class TableView;
class TableViewObserver; class TableViewObserver;
class Throbber; class Throbber;
...@@ -40,7 +40,6 @@ class DeviceChooserContentView : public views::View, ...@@ -40,7 +40,6 @@ class DeviceChooserContentView : public views::View,
// views::View: // views::View:
gfx::Size GetMinimumSize() const override; gfx::Size GetMinimumSize() const override;
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
// ui::TableModel: // ui::TableModel:
int RowCount() override; int RowCount() override;
...@@ -73,39 +72,23 @@ class DeviceChooserContentView : public views::View, ...@@ -73,39 +72,23 @@ class DeviceChooserContentView : public views::View,
void Close(); void Close();
void UpdateTableView(); void UpdateTableView();
private: // Test-only accessors to children.
friend class ChooserDialogViewTest; views::TableView* table_view_for_testing() { return table_view_; }
friend class DeviceChooserContentViewTest; views::LabelButton* ReScanButtonForTesting();
FRIEND_TEST_ALL_PREFIXES(DeviceChooserContentViewTest, ClickRescanLink); views::Throbber* ThrobberForTesting();
FRIEND_TEST_ALL_PREFIXES(DeviceChooserContentViewTest, ClickGetHelpLink); views::Label* ScanningLabelForTesting();
class BluetoothStatusContainer : public views::View {
public:
explicit BluetoothStatusContainer(views::ButtonListener* listener);
void ShowScanningLabelAndThrobber();
void ShowReScanButton(bool enabled);
views::LabelButton* re_scan_button() { return re_scan_button_; }
views::Throbber* throbber() { return throbber_; }
views::Label* scanning_label() { return scanning_label_; }
private: private:
views::LabelButton* re_scan_button_; friend class DeviceChooserContentViewTest;
views::Throbber* throbber_;
views::Label* scanning_label_;
DISALLOW_COPY_AND_ASSIGN(BluetoothStatusContainer);
};
std::unique_ptr<ChooserController> chooser_controller_; std::unique_ptr<ChooserController> chooser_controller_;
bool adapter_enabled_ = true; bool adapter_enabled_ = true;
views::TableView* table_view_ = nullptr;
views::View* table_parent_ = nullptr; views::View* table_parent_ = nullptr;
views::Label* no_options_help_ = nullptr; views::TableView* table_view_ = nullptr;
views::StyledLabel* adapter_off_help_ = nullptr; views::View* no_options_view_ = nullptr;
views::View* adapter_off_view_ = nullptr;
BluetoothStatusContainer* bluetooth_status_container_ = nullptr; BluetoothStatusContainer* bluetooth_status_container_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(DeviceChooserContentView); DISALLOW_COPY_AND_ASSIGN(DeviceChooserContentView);
......
...@@ -48,7 +48,8 @@ class DeviceChooserContentViewTest : public ChromeViewsTestBase { ...@@ -48,7 +48,8 @@ class DeviceChooserContentViewTest : public ChromeViewsTestBase {
extra_views_container_ = content_view().CreateExtraView(); extra_views_container_ = content_view().CreateExtraView();
ASSERT_NE(nullptr, table_view()); ASSERT_NE(nullptr, table_view());
ASSERT_NE(nullptr, adapter_off_help_link()); ASSERT_NE(nullptr, no_options_view());
ASSERT_NE(nullptr, adapter_off_view());
ASSERT_NE(nullptr, re_scan_button()); ASSERT_NE(nullptr, re_scan_button());
ASSERT_NE(nullptr, throbber()); ASSERT_NE(nullptr, throbber());
ASSERT_NE(nullptr, scanning_label()); ASSERT_NE(nullptr, scanning_label());
...@@ -61,21 +62,19 @@ class DeviceChooserContentViewTest : public ChromeViewsTestBase { ...@@ -61,21 +62,19 @@ class DeviceChooserContentViewTest : public ChromeViewsTestBase {
MockTableViewObserver& table_observer() { return *table_observer_; } MockTableViewObserver& table_observer() { return *table_observer_; }
DeviceChooserContentView& content_view() { return *content_view_; } DeviceChooserContentView& content_view() { return *content_view_; }
views::TableView* table_view() { return content_view().table_view_; } views::TableView* table_view() {
return content_view().table_view_for_testing();
}
views::View* table_parent() { return content_view().table_parent_; } views::View* table_parent() { return content_view().table_parent_; }
ui::TableModel* table_model() { return table_view()->model(); } ui::TableModel* table_model() { return table_view()->model(); }
views::Label* no_options_label() { return content_view().no_options_help_; } views::View* no_options_view() { return content_view().no_options_view_; }
views::StyledLabel* adapter_off_help_link() { views::View* adapter_off_view() { return content_view().adapter_off_view_; }
return content_view().adapter_off_help_;
}
views::LabelButton* re_scan_button() { views::LabelButton* re_scan_button() {
return content_view().bluetooth_status_container_->re_scan_button(); return content_view().ReScanButtonForTesting();
}
views::Throbber* throbber() {
return content_view().bluetooth_status_container_->throbber();
} }
views::Throbber* throbber() { return content_view().ThrobberForTesting(); }
views::Label* scanning_label() { views::Label* scanning_label() {
return content_view().bluetooth_status_container_->scanning_label(); return content_view().ScanningLabelForTesting();
} }
void AddUnpairedDevice() { void AddUnpairedDevice() {
...@@ -105,7 +104,7 @@ class DeviceChooserContentViewTest : public ChromeViewsTestBase { ...@@ -105,7 +104,7 @@ class DeviceChooserContentViewTest : public ChromeViewsTestBase {
bool IsDeviceSelected() { return !table_view()->selection_model().empty(); } bool IsDeviceSelected() { return !table_view()->selection_model().empty(); }
void ExpectNoDevices() { void ExpectNoDevices() {
EXPECT_TRUE(no_options_label()->GetVisible()); EXPECT_TRUE(no_options_view()->GetVisible());
EXPECT_EQ(0, table_view()->GetRowCount()); EXPECT_EQ(0, table_view()->GetRowCount());
// The table should be disabled since there are no (real) options. // The table should be disabled since there are no (real) options.
EXPECT_FALSE(table_parent()->GetVisible()); EXPECT_FALSE(table_parent()->GetVisible());
...@@ -128,7 +127,7 @@ TEST_F(DeviceChooserContentViewTest, InitialState) { ...@@ -128,7 +127,7 @@ TEST_F(DeviceChooserContentViewTest, InitialState) {
EXPECT_CALL(table_observer(), OnSelectionChanged()).Times(0); EXPECT_CALL(table_observer(), OnSelectionChanged()).Times(0);
ExpectNoDevices(); ExpectNoDevices();
EXPECT_FALSE(adapter_off_help_link()->GetVisible()); EXPECT_FALSE(adapter_off_view()->GetVisible());
EXPECT_FALSE(throbber()->GetVisible()); EXPECT_FALSE(throbber()->GetVisible());
EXPECT_FALSE(scanning_label()->GetVisible()); EXPECT_FALSE(scanning_label()->GetVisible());
EXPECT_TRUE(re_scan_button()->GetVisible()); EXPECT_TRUE(re_scan_button()->GetVisible());
...@@ -210,8 +209,8 @@ TEST_F(DeviceChooserContentViewTest, TurnBluetoothOffAndOn) { ...@@ -210,8 +209,8 @@ TEST_F(DeviceChooserContentViewTest, TurnBluetoothOffAndOn) {
FakeBluetoothChooserController::BluetoothStatus::UNAVAILABLE); FakeBluetoothChooserController::BluetoothStatus::UNAVAILABLE);
EXPECT_FALSE(table_parent()->GetVisible()); EXPECT_FALSE(table_parent()->GetVisible());
EXPECT_FALSE(no_options_label()->GetVisible()); EXPECT_FALSE(no_options_view()->GetVisible());
EXPECT_TRUE(adapter_off_help_link()->GetVisible()); EXPECT_TRUE(adapter_off_view()->GetVisible());
EXPECT_FALSE(throbber()->GetVisible()); EXPECT_FALSE(throbber()->GetVisible());
EXPECT_FALSE(scanning_label()->GetVisible()); EXPECT_FALSE(scanning_label()->GetVisible());
EXPECT_TRUE(re_scan_button()->GetVisible()); EXPECT_TRUE(re_scan_button()->GetVisible());
...@@ -221,7 +220,7 @@ TEST_F(DeviceChooserContentViewTest, TurnBluetoothOffAndOn) { ...@@ -221,7 +220,7 @@ TEST_F(DeviceChooserContentViewTest, TurnBluetoothOffAndOn) {
controller()->SetBluetoothStatus( controller()->SetBluetoothStatus(
FakeBluetoothChooserController::BluetoothStatus::IDLE); FakeBluetoothChooserController::BluetoothStatus::IDLE);
ExpectNoDevices(); ExpectNoDevices();
EXPECT_FALSE(adapter_off_help_link()->GetVisible()); EXPECT_FALSE(adapter_off_view()->GetVisible());
EXPECT_FALSE(throbber()->GetVisible()); EXPECT_FALSE(throbber()->GetVisible());
EXPECT_FALSE(scanning_label()->GetVisible()); EXPECT_FALSE(scanning_label()->GetVisible());
EXPECT_TRUE(re_scan_button()->GetVisible()); EXPECT_TRUE(re_scan_button()->GetVisible());
...@@ -233,7 +232,7 @@ TEST_F(DeviceChooserContentViewTest, ScanForDevices) { ...@@ -233,7 +232,7 @@ TEST_F(DeviceChooserContentViewTest, ScanForDevices) {
FakeBluetoothChooserController::BluetoothStatus::SCANNING); FakeBluetoothChooserController::BluetoothStatus::SCANNING);
EXPECT_EQ(0, table_view()->GetRowCount()); EXPECT_EQ(0, table_view()->GetRowCount());
EXPECT_FALSE(table_view()->GetEnabled()); EXPECT_FALSE(table_view()->GetEnabled());
EXPECT_FALSE(adapter_off_help_link()->GetVisible()); EXPECT_FALSE(adapter_off_view()->GetVisible());
EXPECT_TRUE(throbber()->GetVisible()); EXPECT_TRUE(throbber()->GetVisible());
EXPECT_TRUE(scanning_label()->GetVisible()); EXPECT_TRUE(scanning_label()->GetVisible());
EXPECT_FALSE(re_scan_button()->GetVisible()); EXPECT_FALSE(re_scan_button()->GetVisible());
...@@ -241,7 +240,7 @@ TEST_F(DeviceChooserContentViewTest, ScanForDevices) { ...@@ -241,7 +240,7 @@ TEST_F(DeviceChooserContentViewTest, ScanForDevices) {
AddUnpairedDevice(); AddUnpairedDevice();
EXPECT_EQ(1, table_view()->GetRowCount()); EXPECT_EQ(1, table_view()->GetRowCount());
EXPECT_TRUE(table_view()->GetEnabled()); EXPECT_TRUE(table_view()->GetEnabled());
EXPECT_FALSE(adapter_off_help_link()->GetVisible()); EXPECT_FALSE(adapter_off_view()->GetVisible());
EXPECT_TRUE(throbber()->GetVisible()); EXPECT_TRUE(throbber()->GetVisible());
EXPECT_TRUE(scanning_label()->GetVisible()); EXPECT_TRUE(scanning_label()->GetVisible());
EXPECT_FALSE(IsDeviceSelected()); EXPECT_FALSE(IsDeviceSelected());
...@@ -250,7 +249,8 @@ TEST_F(DeviceChooserContentViewTest, ScanForDevices) { ...@@ -250,7 +249,8 @@ TEST_F(DeviceChooserContentViewTest, ScanForDevices) {
TEST_F(DeviceChooserContentViewTest, ClickAdapterOffHelpLink) { TEST_F(DeviceChooserContentViewTest, ClickAdapterOffHelpLink) {
EXPECT_CALL(*controller(), OpenAdapterOffHelpUrl()).Times(1); EXPECT_CALL(*controller(), OpenAdapterOffHelpUrl()).Times(1);
adapter_off_help_link()->LinkClicked(nullptr, 0); static_cast<views::StyledLabel*>(adapter_off_view()->children().front())
->LinkClicked(nullptr, 0);
} }
TEST_F(DeviceChooserContentViewTest, ClickRescanButton) { TEST_F(DeviceChooserContentViewTest, ClickRescanButton) {
......
...@@ -63,12 +63,13 @@ class ChooserDialogViewTest : public ChromeViewsTestBase { ...@@ -63,12 +63,13 @@ class ChooserDialogViewTest : public ChromeViewsTestBase {
} }
views::TableView* table_view() { views::TableView* table_view() {
return dialog_->device_chooser_content_view_for_test()->table_view_; return dialog_->device_chooser_content_view_for_test()
->table_view_for_testing();
} }
views::LabelButton* re_scan_button() { views::LabelButton* re_scan_button() {
return dialog_->device_chooser_content_view_for_test() return dialog_->device_chooser_content_view_for_test()
->bluetooth_status_container_->re_scan_button(); ->ReScanButtonForTesting();
} }
void AddDevice() { void AddDevice() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment