Commit c2635b23 authored by Collin Baker's avatar Collin Baker Committed by Commit Bot

Make tabs focusable and add focus ring

This allows tabs to be reached by the keyboard. Only tabs are made
focusable, not the tab close buttons since tabs can
be easily closed from the context menu.

This also makes the tabstrip as an accessible pane so it can be
reached by F6 and SHIFT+F6.

Follow-up changes:
- Look into having CTRL+W close the focused tab rather than the
  active tab when in keyboard accessibility mode.
- Make keyboard focus advance to the next tab when closing the
  focused tab (rather than disappearing as it does now).

Bug: 702414
Change-Id: I052ac56a6adfbc317a67c00314fd9259ab83fc2e
Reviewed-on: https://chromium-review.googlesource.com/c/1241845
Commit-Queue: Collin Baker <collinbaker@chromium.org>
Reviewed-by: default avatarPeter Boström <pbos@chromium.org>
Reviewed-by: default avatarBret Sepulveda <bsep@chromium.org>
Cr-Commit-Position: refs/heads/master@{#599795}
parent 07617eaa
......@@ -2175,6 +2175,8 @@ void BrowserView::GetAccessiblePanes(std::vector<views::View*>* panes) {
// (Windows) or Ctrl+Back/Forward (Chrome OS). If one of these is
// invisible or has no focusable children, it will be automatically
// skipped.
if (tabstrip_)
panes->push_back(tabstrip_);
panes->push_back(toolbar_button_provider_->GetAsAccessiblePaneView());
if (bookmark_bar_view_.get())
panes->push_back(bookmark_bar_view_.get());
......
......@@ -8,6 +8,7 @@
#include <algorithm>
#include <limits>
#include <memory>
#include <utility>
#include "base/bind.h"
......@@ -67,6 +68,7 @@
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/rect_based_targeting_utils.h"
#include "ui/views/view_properties.h"
#include "ui/views/view_targeter.h"
#include "ui/views/widget/tooltip_manager.h"
#include "ui/views/widget/widget.h"
......@@ -168,6 +170,10 @@ Tab::Tab(TabController* controller, gfx::AnimationContainer* container)
title_animation_.SetContainer(animation_container_.get());
hover_controller_.SetAnimationContainer(animation_container_.get());
// Enable keyboard focus.
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
focus_ring_ = views::FocusRing::Install(this);
}
Tab::~Tab() {
......@@ -379,12 +385,21 @@ void Tab::Layout() {
}
}
title_->SetVisible(show_title);
if (focus_ring_)
focus_ring_->Layout();
}
const char* Tab::GetClassName() const {
return kViewClassName;
}
void Tab::OnBoundsChanged(const gfx::Rect& previous_bounds) {
// Update focus ring path.
const SkPath path = tab_style_->GetPath(TabStyle::PathType::kHighlight, 1.0);
SetProperty(views::kHighlightPathKey, new SkPath(path));
}
namespace {
bool IsSelectionModifierDown(const ui::MouseEvent& event) {
#if defined(OS_MACOSX)
......@@ -552,10 +567,17 @@ bool Tab::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* origin) const {
void Tab::GetAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->role = ax::mojom::Role::kTab;
node_data->SetName(controller_->GetAccessibleTabName(this));
node_data->AddState(ax::mojom::State::kMultiselectable);
node_data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
IsSelected());
base::string16 name = controller_->GetAccessibleTabName(this);
if (!name.empty()) {
node_data->SetName(name);
} else {
// Under some conditions, |GetAccessibleTabName| returns an empty string.
node_data->SetNameExplicitlyEmpty();
}
}
gfx::Size Tab::CalculatePreferredSize() const {
......@@ -596,6 +618,14 @@ void Tab::OnThemeChanged() {
void Tab::SetClosing(bool closing) {
closing_ = closing;
ActiveStateChanged();
if (closing) {
// When closing, sometimes DCHECK fails because
// cc::Layer::IsPropertyChangeAllowed() returns false. Deleting
// the focus ring fixes this. TODO(collinbaker): investigate why
// this happens.
focus_ring_.reset();
}
}
SkColor Tab::GetAlertIndicatorColor(TabAlertState state) const {
......
......@@ -21,6 +21,7 @@
#include "ui/gfx/paint_throbber.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/masked_targeter_delegate.h"
#include "ui/views/view.h"
......@@ -82,6 +83,7 @@ class Tab : public gfx::AnimationDelegate,
// views::View:
void Layout() override;
const char* GetClassName() const override;
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
bool OnMousePressed(const ui::MouseEvent& event) override;
bool OnMouseDragged(const ui::MouseEvent& event) override;
void OnMouseReleased(const ui::MouseEvent& event) override;
......@@ -297,6 +299,9 @@ class Tab : public gfx::AnimationDelegate,
// the view bounds.
bool mouse_hovered_ = false;
// Focus ring for accessibility.
std::unique_ptr<views::FocusRing> focus_ring_;
DISALLOW_COPY_AND_ASSIGN(Tab);
};
......
......@@ -39,12 +39,13 @@ TabCloseButton::TabCloseButton(views::ButtonListener* listener,
mouse_event_callback_(std::move(mouse_event_callback)) {
SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
// Disable animation so that the red danger sign shows up immediately
// to help avoid mis-clicks.
SetAnimationDuration(0);
if (focus_ring())
SetFocusPainter(nullptr);
SetInstallFocusRingOnFocus(true);
SetFocusPainter(nullptr);
}
TabCloseButton::~TabCloseButton() {}
......
......@@ -27,6 +27,7 @@
#include "ui/gfx/color_palette.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/accessible_pane_view.h"
#include "ui/views/animation/bounds_animator.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/mouse_watcher.h"
......@@ -62,7 +63,7 @@ class ImageView;
// - It takes part in Tab Drag & Drop with Tab, TabDragHelper and
// DraggedTab, focusing on tasks that require reshuffling other tabs
// in response to dragged tabs.
class TabStrip : public views::View,
class TabStrip : public views::AccessiblePaneView,
public views::ButtonListener,
public views::MouseWatcherListener,
public views::ViewTargeterDelegate,
......
......@@ -23,6 +23,7 @@
#include "ui/base/theme_provider.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/widget/widget.h"
namespace {
......@@ -277,6 +278,16 @@ gfx::Path GM2TabStyle::GetPath(PathType path_type,
if (path_type == PathType::kInteriorClip) {
// Clip path is a simple rectangle.
path.addRect(tab_left, tab_top, tab_right, tab_bottom);
} else if (path_type == PathType::kHighlight) {
// The path is a round rect inset by the focus ring thickness. The
// radius is also adjusted by the inset.
const float inset = views::PlatformStyle::kFocusHaloThickness +
views::PlatformStyle::kFocusHaloInset;
SkRRect rrect = SkRRect::MakeRectXY(
SkRect::MakeLTRB(tab_left + inset, tab_top + inset, tab_right - inset,
tab_bottom - inset),
radius - inset, radius - inset);
path.addRRect(rrect);
} else {
// We will go clockwise from the lower left. We start in the overlap region,
// preventing a gap between toolbar and tabstrip.
......
......@@ -36,7 +36,9 @@ class TabStyle {
// child views. Does not have to be the same shape as the border.
kInteriorClip,
// The outline of the tab, used for occlusion in certain special situations.
kExteriorClip
kExteriorClip,
// The path used for focus rings.
kHighlight,
};
// How we want the resulting path scaled.
......
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