1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130

ash / quick_insert / views / quick_insert_pseudo_focus.cc [blame]

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/quick_insert/views/quick_insert_pseudo_focus.h"

#include "ash/quick_insert/views/quick_insert_item_view.h"
#include "ash/quick_insert/views/quick_insert_list_item_view.h"
#include "ash/quick_insert/views/quick_insert_search_bar_textfield.h"
#include "base/functional/bind.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/events/types/event_type.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/view.h"
#include "ui/views/view_utils.h"

namespace ash {

void ApplyQuickInsertPseudoFocusToView(views::View* view) {
  if (view == nullptr) {
    return;
  }

  // QuickInsertItemView has special pseudo focus behavior, so handle it
  // separately.
  if (auto* item_view = views::AsViewClass<QuickInsertItemView>(view)) {
    item_view->SetItemState(QuickInsertItemView::ItemState::kPseudoFocused);
    if (auto* list_item_view =
            views::AsViewClass<QuickInsertListItemView>(view)) {
      list_item_view->SetBadgeVisible(true);
    }
    return;
  }

  // QuickInsertSearchBarTextfield has special pseudo focus appearance.
  if (auto* textfield =
          views::AsViewClass<QuickInsertSearchBarTextfield>(view)) {
    textfield->SetShouldShowFocusIndicator(true);
    return;
  }

  // Otherwise, default to drawing a focus ring around the view.
  // TODO: b/328144222 - Add accessibility announcement when a view gains
  // pseudo focus.
  if (views::FocusRing* focus_ring = views::FocusRing::Get(view)) {
    focus_ring->SetHasFocusPredicate(
        base::BindRepeating([](const views::View* view) { return true; }));
    focus_ring->SchedulePaint();
  }
}

void RemoveQuickInsertPseudoFocusFromView(views::View* view) {
  if (view == nullptr) {
    return;
  }

  // QuickInsertItemView has special pseudo focus behavior, so handle it
  // separately.
  if (auto* item_view = views::AsViewClass<QuickInsertItemView>(view)) {
    item_view->SetItemState(QuickInsertItemView::ItemState::kNormal);
    if (auto* list_item_view =
            views::AsViewClass<QuickInsertListItemView>(view)) {
      list_item_view->SetBadgeVisible(false);
    }
    return;
  }

  // QuickInsertSearchBarTextfield has special pseudo focus appearance.
  if (auto* textfield =
          views::AsViewClass<QuickInsertSearchBarTextfield>(view)) {
    textfield->SetShouldShowFocusIndicator(false);
    return;
  }

  // Otherwise, default to removing the focus ring around the view.
  // TODO: b/328144222 - Add accessibility announcement when a view loses
  // pseudo focus.
  if (views::FocusRing* focus_ring = views::FocusRing::Get(view)) {
    focus_ring->SetHasFocusPredicate(
        base::BindRepeating([](const views::View* view) { return false; }));
    focus_ring->SchedulePaint();
  }
}

bool DoQuickInsertPseudoFocusedActionOnView(views::View* view) {
  if (view == nullptr) {
    return false;
  }

  // QuickInsertSearchBarTextfield has no pseudo focus action.
  if (views::IsViewClass<QuickInsertSearchBarTextfield>(view)) {
    return true;
  }

  // QuickInsertItemView has special pseudo focus behavior, so handle it
  // separately.
  if (auto* item_view = views::AsViewClass<QuickInsertItemView>(view)) {
    item_view->SelectItem();
    return true;
  }

  // Otherwise, default to behaving the same way as pressing the enter key.
  // Here we check that `view` does not have actual focus, to ensure we won't
  // trigger an infinite recursive loop of pseudo focused actions when manually
  // calling `OnKeyEvent` (since the focused view may forward key events to be
  // handled by the pseudo focused view).
  CHECK(!view->HasFocus());
  ui::KeyEvent key_event(ui::EventType::kKeyPressed, ui::VKEY_RETURN,
                         ui::DomCode::ENTER, ui::EF_NONE);
  view->OnKeyEvent(&key_event);
  return key_event.handled();
}

views::View* GetNextQuickInsertPseudoFocusableView(
    views::View* view,
    QuickInsertPseudoFocusDirection direction,
    bool should_loop) {
  return view == nullptr || view->GetFocusManager() == nullptr
             ? nullptr
             : view->GetFocusManager()->GetNextFocusableView(
                   view, view->GetWidget(),
                   direction == QuickInsertPseudoFocusDirection::kBackward,
                   !should_loop);
}

}  // namespace ash