| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/accessibility/accessibility_highlight_controller.h" |
| |
| #include <vector> |
| |
| #include "ash/accessibility/accessibility_focus_ring_controller_impl.h" |
| #include "ash/display/window_tree_host_manager.h" |
| #include "ash/public/cpp/accessibility_focus_ring_info.h" |
| #include "ash/shell.h" |
| #include "ui/base/ime/input_method.h" |
| #include "ui/base/ime/text_input_client.h" |
| #include "ui/events/event.h" |
| #include "ui/wm/core/coordinate_conversion.h" |
| #include "ui/wm/core/cursor_manager.h" |
| #include "ui/wm/public/activation_client.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kHighlightCallerId[] = "HighlightController"; |
| // The color for the keyboard focus ring. (The same orange color as ChromeVox.) |
| const SkColor kFocusColor = SkColorSetRGB(247, 152, 58); |
| |
| // Returns the input method shared between ash and the browser for in-process |
| // ash. Returns null for out-of-process ash. |
| ui::InputMethod* GetSharedInputMethod() { |
| return Shell::Get()->window_tree_host_manager()->input_method(); |
| } |
| |
| void SetFocusRing(AccessibilityFocusRingController* controller, |
| std::vector<gfx::Rect> rects) { |
| auto focus_ring = std::make_unique<AccessibilityFocusRingInfo>(); |
| focus_ring->rects_in_screen = rects; |
| focus_ring->behavior = FocusRingBehavior::FADE_OUT; |
| focus_ring->type = FocusRingType::GLOW; |
| focus_ring->color = kFocusColor; |
| |
| controller->SetFocusRing(kHighlightCallerId, std::move(focus_ring)); |
| } |
| |
| } // namespace |
| |
| AccessibilityHighlightController::AccessibilityHighlightController() { |
| Shell::Get()->AddPreTargetHandler(this); |
| Shell::Get()->cursor_manager()->AddObserver(this); |
| |
| GetSharedInputMethod()->AddObserver(this); |
| } |
| |
| AccessibilityHighlightController::~AccessibilityHighlightController() { |
| AccessibilityFocusRingControllerImpl* controller = |
| Shell::Get()->accessibility_focus_ring_controller(); |
| SetFocusRing(controller, std::vector<gfx::Rect>()); |
| controller->HideCaretRing(); |
| controller->HideCursorRing(); |
| |
| GetSharedInputMethod()->RemoveObserver(this); |
| Shell::Get()->cursor_manager()->RemoveObserver(this); |
| Shell::Get()->RemovePreTargetHandler(this); |
| } |
| |
| void AccessibilityHighlightController::HighlightFocus(bool focus) { |
| focus_ = focus; |
| UpdateFocusAndCaretHighlights(); |
| } |
| |
| void AccessibilityHighlightController::HighlightCursor(bool cursor) { |
| cursor_ = cursor; |
| UpdateCursorHighlight(); |
| } |
| |
| void AccessibilityHighlightController::HighlightCaret(bool caret) { |
| caret_ = caret; |
| UpdateFocusAndCaretHighlights(); |
| } |
| |
| void AccessibilityHighlightController::SetFocusHighlightRect( |
| const gfx::Rect& bounds_in_screen) { |
| focus_rect_ = bounds_in_screen; |
| UpdateFocusAndCaretHighlights(); |
| } |
| |
| void AccessibilityHighlightController::SetCaretBounds( |
| const gfx::Rect& caret_bounds_in_screen) { |
| gfx::Point new_caret_point = caret_bounds_in_screen.CenterPoint(); |
| ::wm::ConvertPointFromScreen(Shell::GetPrimaryRootWindow(), &new_caret_point); |
| bool new_caret_visible = IsCaretVisible(caret_bounds_in_screen); |
| if (new_caret_point == caret_point_ && new_caret_visible == caret_visible_) |
| return; |
| caret_point_ = new_caret_point; |
| caret_visible_ = IsCaretVisible(caret_bounds_in_screen); |
| UpdateFocusAndCaretHighlights(); |
| } |
| |
| void AccessibilityHighlightController::OnMouseEvent(ui::MouseEvent* event) { |
| if (event->type() == ui::ET_MOUSE_MOVED || |
| event->type() == ui::ET_MOUSE_DRAGGED) { |
| cursor_point_ = event->location(); |
| if (event->target()) { |
| ::wm::ConvertPointToScreen(static_cast<aura::Window*>(event->target()), |
| &cursor_point_); |
| } |
| UpdateCursorHighlight(); |
| } |
| } |
| |
| void AccessibilityHighlightController::OnKeyEvent(ui::KeyEvent* event) { |
| if (event->type() == ui::ET_KEY_PRESSED) |
| UpdateFocusAndCaretHighlights(); |
| } |
| |
| void AccessibilityHighlightController::OnTextInputStateChanged( |
| const ui::TextInputClient* client) { |
| if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) { |
| caret_visible_ = false; |
| UpdateFocusAndCaretHighlights(); |
| } |
| } |
| |
| void AccessibilityHighlightController::OnCaretBoundsChanged( |
| const ui::TextInputClient* client) { |
| if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) { |
| caret_visible_ = false; |
| return; |
| } |
| SetCaretBounds(client->GetCaretBounds()); |
| } |
| |
| void AccessibilityHighlightController::OnCursorVisibilityChanged( |
| bool is_visible) { |
| UpdateCursorHighlight(); |
| } |
| |
| bool AccessibilityHighlightController::IsCursorVisible() { |
| return Shell::Get()->cursor_manager()->IsCursorVisible(); |
| } |
| |
| bool AccessibilityHighlightController::IsCaretVisible( |
| const gfx::Rect& caret_bounds_in_screen) { |
| // Empty bounds are not visible. Don't use IsEmpty() because web contents |
| // carets can have positive height but zero width. |
| if (caret_bounds_in_screen.width() == 0 && |
| caret_bounds_in_screen.height() == 0) { |
| return false; |
| } |
| |
| aura::Window* root_window = Shell::GetPrimaryRootWindow(); |
| aura::Window* active_window = |
| ::wm::GetActivationClient(root_window)->GetActiveWindow(); |
| if (!active_window) |
| active_window = root_window; |
| return active_window->GetBoundsInScreen().Contains(caret_point_); |
| } |
| |
| void AccessibilityHighlightController::UpdateFocusAndCaretHighlights() { |
| AccessibilityFocusRingControllerImpl* controller = |
| Shell::Get()->accessibility_focus_ring_controller(); |
| |
| // The caret highlight takes precedence over the focus highlight if |
| // both are visible. |
| if (caret_ && caret_visible_) { |
| controller->SetCaretRing(caret_point_); |
| SetFocusRing(controller, std::vector<gfx::Rect>()); |
| } else if (focus_) { |
| controller->HideCaretRing(); |
| std::vector<gfx::Rect> rects; |
| if (!focus_rect_.IsEmpty()) |
| rects.push_back(focus_rect_); |
| SetFocusRing(controller, rects); |
| } else { |
| controller->HideCaretRing(); |
| SetFocusRing(controller, std::vector<gfx::Rect>()); |
| } |
| } |
| |
| void AccessibilityHighlightController::UpdateCursorHighlight() { |
| AccessibilityFocusRingControllerImpl* controller = |
| Shell::Get()->accessibility_focus_ring_controller(); |
| if (cursor_ && IsCursorVisible()) |
| controller->SetCursorRing(cursor_point_); |
| else |
| controller->HideCursorRing(); |
| } |
| |
| } // namespace ash |