Commit ced8a9b0 authored by Abhijeet Singh's avatar Abhijeet Singh Committed by Commit Bot

Add focus support for QuickAnswersView

For the case when a menu is active, QuickAnswersView currently is unable
to obtain the focus because that would require activating its top-level
widget, which would cause the menu to disappear like it does on
observing any change in the activated window.

This CL makes QuickAnswerView's widget a child of the menu's owning
widget, as a result of which it can now share the owning widget's
FocusManager and successfully request focus.

This is needed to incorporate keyboard accessibility for the view. Key
or accelerator handling will be added in follow up CLs.

Tests are also added.

Bug: b:152057976
Test: Tested on Chrome OS VM and ran ash_unittests.
Change-Id: I2acbbd5d3f918fffe33d31d8f7764ecf28ffcaec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2222119Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Commit-Queue: Abhijeet Singh <siabhijeet@google.com>
Cr-Commit-Position: refs/heads/master@{#774371}
parent 33f85e07
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
#include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/layout/box_layout.h" #include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/fill_layout.h"
#include "ui/views/painter.h" #include "ui/views/painter.h"
...@@ -127,6 +128,10 @@ QuickAnswersView::QuickAnswersView(const gfx::Rect& anchor_view_bounds, ...@@ -127,6 +128,10 @@ QuickAnswersView::QuickAnswersView(const gfx::Rect& anchor_view_bounds,
InitLayout(); InitLayout();
InitWidget(); InitWidget();
// Focus.
SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
SetInstallFocusRingOnFocus(false);
// This is because waiting for mouse-release to fire buttons would be too // This is because waiting for mouse-release to fire buttons would be too
// late, since mouse-press dismisses the menu. // late, since mouse-press dismisses the menu.
SetButtonNotifyActionToOnPress(this); SetButtonNotifyActionToOnPress(this);
...@@ -143,18 +148,22 @@ const char* QuickAnswersView::GetClassName() const { ...@@ -143,18 +148,22 @@ const char* QuickAnswersView::GetClassName() const {
return "QuickAnswersView"; return "QuickAnswersView";
} }
void QuickAnswersView::OnFocus() {
SetBackgroundState(true);
}
void QuickAnswersView::OnBlur() {
SetBackgroundState(false);
}
void QuickAnswersView::StateChanged(views::Button::ButtonState old_state) { void QuickAnswersView::StateChanged(views::Button::ButtonState old_state) {
switch (state()) { switch (state()) {
case Button::ButtonState::STATE_NORMAL: { case Button::ButtonState::STATE_NORMAL: {
main_view_->SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); SetBackgroundState(false);
break; break;
} }
case Button::ButtonState::STATE_HOVERED: { case Button::ButtonState::STATE_HOVERED: {
if (!retry_label_) SetBackgroundState(true);
main_view_->SetBackground(views::CreateBackgroundFromPainter(
views::Painter::CreateSolidRoundRectPainter(
SkColorSetA(SK_ColorBLACK, kHoverStateAlpha * 0xFF),
/*radius=*/0, kMainViewInsets)));
break; break;
} }
default: default:
...@@ -289,9 +298,17 @@ void QuickAnswersView::InitWidget() { ...@@ -289,9 +298,17 @@ void QuickAnswersView::InitWidget() {
params.shadow_elevation = 2; params.shadow_elevation = 2;
params.shadow_type = views::Widget::InitParams::ShadowType::kDrop; params.shadow_type = views::Widget::InitParams::ShadowType::kDrop;
params.type = views::Widget::InitParams::TYPE_POPUP; params.type = views::Widget::InitParams::TYPE_POPUP;
params.context = Shell::Get()->GetRootWindowForNewWindows();
params.z_order = ui::ZOrderLevel::kFloatingUIElement; params.z_order = ui::ZOrderLevel::kFloatingUIElement;
// Parent the widget depending on the context.
auto* active_menu_controller = views::MenuController::GetActiveInstance();
if (active_menu_controller && active_menu_controller->owner()) {
params.parent = active_menu_controller->owner()->GetNativeView();
params.child = true;
} else {
params.context = Shell::Get()->GetRootWindowForNewWindows();
}
views::Widget* widget = new views::Widget(); views::Widget* widget = new views::Widget();
widget->Init(std::move(params)); widget->Init(std::move(params));
widget->SetContentsView(this); widget->SetContentsView(this);
...@@ -361,6 +378,17 @@ void QuickAnswersView::UpdateQuickAnswerResult( ...@@ -361,6 +378,17 @@ void QuickAnswersView::UpdateQuickAnswerResult(
} }
} }
void QuickAnswersView::SetBackgroundState(bool highlight) {
if (highlight && !retry_label_) {
main_view_->SetBackground(views::CreateBackgroundFromPainter(
views::Painter::CreateSolidRoundRectPainter(
SkColorSetA(SK_ColorBLACK, kHoverStateAlpha * 0xFF),
/*radius=*/0, kMainViewInsets)));
} else if (!highlight) {
main_view_->SetBackground(views::CreateSolidBackground(SK_ColorWHITE));
}
}
void QuickAnswersView::ResetContentView() { void QuickAnswersView::ResetContentView() {
content_view_->RemoveAllChildViews(true); content_view_->RemoveAllChildViews(true);
first_answer_label_ = nullptr; first_answer_label_ = nullptr;
......
...@@ -42,6 +42,8 @@ class ASH_EXPORT QuickAnswersView : public views::Button, ...@@ -42,6 +42,8 @@ class ASH_EXPORT QuickAnswersView : public views::Button,
// views::View: // views::View:
const char* GetClassName() const override; const char* GetClassName() const override;
void OnFocus() override;
void OnBlur() override;
// views::Button: // views::Button:
void StateChanged(views::Button::ButtonState old_state) override; void StateChanged(views::Button::ButtonState old_state) override;
...@@ -66,6 +68,7 @@ class ASH_EXPORT QuickAnswersView : public views::Button, ...@@ -66,6 +68,7 @@ class ASH_EXPORT QuickAnswersView : public views::Button,
void AddDogfoodButton(); void AddDogfoodButton();
void AddAssistantIcon(); void AddAssistantIcon();
void ResetContentView(); void ResetContentView();
void SetBackgroundState(bool highlight);
void UpdateBounds(); void UpdateBounds();
void UpdateQuickAnswerResult( void UpdateQuickAnswerResult(
const chromeos::quick_answers::QuickAnswer& quick_answer); const chromeos::quick_answers::QuickAnswer& quick_answer);
......
...@@ -8,6 +8,11 @@ ...@@ -8,6 +8,11 @@
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "chromeos/constants/chromeos_features.h" #include "chromeos/constants/chromeos_features.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/widget/widget.h"
namespace ash { namespace ash {
...@@ -43,6 +48,12 @@ class QuickAnswersViewsTest : public AshTestBase { ...@@ -43,6 +48,12 @@ class QuickAnswersViewsTest : public AshTestBase {
void TearDown() override { void TearDown() override {
quick_answers_view_.reset(); quick_answers_view_.reset();
// Menu.
menu_parent_.reset();
menu_runner_.reset();
menu_model_.reset();
menu_delegate_.reset();
AshTestBase::TearDown(); AshTestBase::TearDown();
} }
...@@ -67,10 +78,28 @@ class QuickAnswersViewsTest : public AshTestBase { ...@@ -67,10 +78,28 @@ class QuickAnswersViewsTest : public AshTestBase {
dummy_anchor_bounds_, title, ui_controller); dummy_anchor_bounds_, title, ui_controller);
} }
void CreateAndShowBasicMenu() {
menu_delegate_ = std::make_unique<views::Label>();
menu_model_ = std::make_unique<ui::SimpleMenuModel>(menu_delegate_.get());
menu_model_->AddItem(0, base::ASCIIToUTF16("Menu item"));
menu_runner_ = std::make_unique<views::MenuRunner>(
menu_model_.get(), views::MenuRunner::CONTEXT_MENU);
menu_parent_ = CreateTestWidget();
menu_runner_->RunMenuAt(menu_parent_.get(), nullptr, gfx::Rect(),
views::MenuAnchorPosition::kTopLeft,
ui::MENU_SOURCE_MOUSE);
}
private: private:
std::unique_ptr<QuickAnswersView> quick_answers_view_; std::unique_ptr<QuickAnswersView> quick_answers_view_;
gfx::Rect dummy_anchor_bounds_; gfx::Rect dummy_anchor_bounds_;
base::test::ScopedFeatureList scoped_feature_list_; base::test::ScopedFeatureList scoped_feature_list_;
// Menu.
std::unique_ptr<views::Label> menu_delegate_;
std::unique_ptr<ui::SimpleMenuModel> menu_model_;
std::unique_ptr<views::MenuRunner> menu_runner_;
std::unique_ptr<views::Widget> menu_parent_;
}; };
TEST_F(QuickAnswersViewsTest, DefaultLayoutAroundAnchor) { TEST_F(QuickAnswersViewsTest, DefaultLayoutAroundAnchor) {
...@@ -98,4 +127,27 @@ TEST_F(QuickAnswersViewsTest, PositionedBelowAnchorIfLessSpaceAbove) { ...@@ -98,4 +127,27 @@ TEST_F(QuickAnswersViewsTest, PositionedBelowAnchorIfLessSpaceAbove) {
EXPECT_EQ(anchor_bounds.bottom() + kMarginDip, view_bounds.y()); EXPECT_EQ(anchor_bounds.bottom() + kMarginDip, view_bounds.y());
} }
TEST_F(QuickAnswersViewsTest, FocusProperties) {
// Not focused by default.
EXPECT_FALSE(view()->HasFocus());
// Does not gain focus upon request if no active menu.
CHECK(views::MenuController::GetActiveInstance() == nullptr);
view()->RequestFocus();
EXPECT_FALSE(view()->HasFocus());
// Set up an owned menu and create a new view.
CreateAndShowBasicMenu();
CHECK(views::MenuController::GetActiveInstance() &&
views::MenuController::GetActiveInstance()->owner());
CreateQuickAnswersView(dummy_anchor_bounds(), "dummy_title");
// Gains focus only upon request, if an owned menu was active when the view
// was created.
CHECK(views::MenuController::GetActiveInstance() != nullptr);
EXPECT_FALSE(view()->HasFocus());
view()->RequestFocus();
EXPECT_TRUE(view()->HasFocus());
}
} // namespace ash } // namespace ash
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