#include "ash/system/accessibility/floating_accessibility_view.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/ime/ime_controller_impl.h"
#include "ash/keyboard/ui/keyboard_ui_controller.h"
#include "ash/public/cpp/keyboard/keyboard_controller.h"
#include "ash/public/cpp/system_tray.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/accessibility/dictation_button_tray.h"
#include "ash/system/accessibility/floating_menu_button.h"
#include "ash/system/accessibility/select_to_speak/select_to_speak_tray.h"
#include "ash/system/ime_menu/ime_menu_tray.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
#include "base/functional/bind.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/color/color_id.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/border.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/view.h"
namespace ash {
namespace {
constexpr int kPanelPositionButtonPadding = 14;
constexpr int kPanelPositionButtonSize = 36;
constexpr int kSeparatorHeight = 16;
class DynamicRowView : public views::View {
public:
DynamicRowView() = default;
protected:
void ChildVisibilityChanged(views::View* child) override {
bool any_visible = false;
for (views::View* view : children()) {
any_visible |= view->GetVisible();
}
SetVisible(any_visible);
}
};
std::unique_ptr<views::Separator> CreateSeparator() {
auto separator = std::make_unique<views::Separator>();
separator->SetColorId(ui::kColorAshSystemUIMenuSeparator);
separator->SetPreferredLength(kSeparatorHeight);
int total_height = kUnifiedTopShortcutSpacing * 2 + kTrayItemSize;
int separator_spacing = (total_height - kSeparatorHeight) / 2;
separator->SetBorder(views::CreateEmptyBorder(
gfx::Insets::TLBR(separator_spacing - kUnifiedTopShortcutSpacing, 0,
separator_spacing, 0)));
return separator;
}
std::unique_ptr<views::View> CreateButtonRowContainer(int padding) {
auto button_container = std::make_unique<DynamicRowView>();
button_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets::TLBR(0, padding, padding, padding), padding));
return button_container;
}
std::string GetDescriptionForMovedToPosition(FloatingMenuPosition position) {
switch (position) {
case FloatingMenuPosition::kBottomRight:
return l10n_util::GetStringUTF8(
IDS_ASH_FLOATING_ACCESSIBILITY_MAIN_MENU_MOVED_BOTTOM_RIGHT);
case FloatingMenuPosition::kBottomLeft:
return l10n_util::GetStringUTF8(
IDS_ASH_FLOATING_ACCESSIBILITY_MAIN_MENU_MOVED_BOTTOM_LEFT);
case FloatingMenuPosition::kTopLeft:
return l10n_util::GetStringUTF8(
IDS_ASH_FLOATING_ACCESSIBILITY_MAIN_MENU_MOVED_TOP_LEFT);
case FloatingMenuPosition::kTopRight:
return l10n_util::GetStringUTF8(
IDS_ASH_FLOATING_ACCESSIBILITY_MAIN_MENU_MOVED_TOP_RIGHT);
case FloatingMenuPosition::kSystemDefault:
NOTREACHED();
}
}
bool IsKioskImeButtonEnabled() {
return Shell::Get()->session_controller()->IsRunningInAppMode() &&
Shell::Get()->ime_controller()->GetVisibleImes().size() > 1;
}
}
FloatingAccessibilityBubbleView::FloatingAccessibilityBubbleView(
const TrayBubbleView::InitParams& init_params)
: TrayBubbleView(init_params) {
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
GetViewAccessibility().SetRole(ax::mojom::Role::kWindow);
}
FloatingAccessibilityBubbleView::~FloatingAccessibilityBubbleView() = default;
bool FloatingAccessibilityBubbleView::IsAnchoredToStatusArea() const {
return false;
}
bool FloatingAccessibilityBubbleView::AcceleratorPressed(
const ui::Accelerator& accelerator) {
DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE);
GetWidget()->Deactivate();
return true;
}
void FloatingAccessibilityBubbleView::AdjustAccessibleName(
std::u16string& new_name,
ax::mojom::NameFrom& name_from) {
if (!delegate() || !CanActivate()) {
new_name = std::u16string();
name_from = ax::mojom::NameFrom::kAttributeExplicitlyEmpty;
}
}
BEGIN_METADATA(FloatingAccessibilityBubbleView)
END_METADATA
FloatingAccessibilityView::FloatingAccessibilityView(Delegate* delegate)
: delegate_(delegate) {
Shelf* shelf = RootWindowController::ForTargetRootWindow()->shelf();
std::unique_ptr<views::View> feature_buttons_container =
CreateButtonRowContainer(kPanelPositionButtonPadding);
dictation_button_observation_.Observe(feature_buttons_container->AddChildView(
std::make_unique<DictationButtonTray>(
shelf, TrayBackgroundViewCatalogName::kDictationAccesibilityWindow)));
select_to_speak_button_observation_.Observe(
feature_buttons_container->AddChildView(std::make_unique<
SelectToSpeakTray>(
shelf,
TrayBackgroundViewCatalogName::kSelectToSpeakAccessibilityWindow)));
virtual_keyboard_button_observation_.Observe(
feature_buttons_container->AddChildView(std::make_unique<
VirtualKeyboardTray>(
shelf,
TrayBackgroundViewCatalogName::kVirtualKeyboardAccessibilityWindow)));
feature_buttons_container->SetVisible(false);
std::unique_ptr<views::View> tray_button_container =
CreateButtonRowContainer(kUnifiedTopShortcutSpacing);
a11y_tray_button_ =
tray_button_container->AddChildView(std::make_unique<FloatingMenuButton>(
base::BindRepeating(
&FloatingAccessibilityView::OnA11yTrayButtonPressed,
base::Unretained(this)),
kUnifiedMenuAccessibilityIcon,
IDS_ASH_FLOATING_ACCESSIBILITY_DETAILED_MENU_OPEN,
true));
std::unique_ptr<views::View> position_button_container =
CreateButtonRowContainer(kPanelPositionButtonPadding);
position_button_ = position_button_container->AddChildView(
std::make_unique<FloatingMenuButton>(
base::BindRepeating(
&FloatingAccessibilityView::OnPositionButtonPressed,
base::Unretained(this)),
kAutoclickPositionBottomLeftIcon,
IDS_ASH_AUTOCLICK_OPTION_CHANGE_POSITION, false,
kPanelPositionButtonSize, false, false));
if (IsKioskImeButtonEnabled()) {
Shell::Get()->ime_controller()->SetExtraInputOptionsEnabledState(
false, false,
false, false);
std::unique_ptr<views::View> ime_button_container =
CreateButtonRowContainer(kPanelPositionButtonPadding);
ime_button_observation_.Observe(ime_button_container->AddChildView(
std::make_unique<ImeMenuTray>(shelf)));
ime_button_container->SetVisible(true);
ime_button()->SetVisiblePreferred(true);
AddChildView(std::move(ime_button_container));
AddChildView(CreateSeparator());
}
AddChildView(std::move(feature_buttons_container));
AddChildView(std::move(tray_button_container));
AddChildView(CreateSeparator());
AddChildView(std::move(position_button_container));
position_button_->SetID(static_cast<int>(ButtonId::kPosition));
a11y_tray_button_->SetID(static_cast<int>(ButtonId::kSettingsList));
dictation_button()->SetID(static_cast<int>(ButtonId::kDictation));
select_to_speak_button()->SetID(static_cast<int>(ButtonId::kSelectToSpeak));
virtual_keyboard_button()->SetID(
static_cast<int>(ButtonId::kVirtualKeyboard));
if (IsKioskImeButtonEnabled()) {
ime_button()->SetID(static_cast<int>(ButtonId::kIme));
}
}
FloatingAccessibilityView::~FloatingAccessibilityView() {
KeyboardController::Get()->RemoveObserver(this);
Shell::Get()->system_tray_notifier()->RemoveSystemTrayObserver(this);
}
void FloatingAccessibilityView::Initialize() {
Shell::Get()->system_tray_notifier()->AddSystemTrayObserver(this);
KeyboardController::Get()->AddObserver(this);
for (TrayBackgroundView* feature_view : {
dictation_button(),
select_to_speak_button(),
virtual_keyboard_button(),
}) {
feature_view->Initialize();
feature_view->CalculateTargetBounds();
feature_view->UpdateLayout();
}
if (IsKioskImeButtonEnabled()) {
ime_button()->Initialize();
ime_button()->CalculateTargetBounds();
ime_button()->UpdateLayout();
ime_button()->SetVisible(true);
}
}
void FloatingAccessibilityView::SetMenuPosition(FloatingMenuPosition position) {
switch (position) {
case FloatingMenuPosition::kBottomRight:
position_button_->SetVectorIcon(kAutoclickPositionBottomRightIcon);
return;
case FloatingMenuPosition::kBottomLeft:
position_button_->SetVectorIcon(kAutoclickPositionBottomLeftIcon);
return;
case FloatingMenuPosition::kTopLeft:
position_button_->SetVectorIcon(kAutoclickPositionTopLeftIcon);
return;
case FloatingMenuPosition::kTopRight:
position_button_->SetVectorIcon(kAutoclickPositionTopRightIcon);
return;
case FloatingMenuPosition::kSystemDefault:
position_button_->SetVectorIcon(base::i18n::IsRTL()
? kAutoclickPositionBottomLeftIcon
: kAutoclickPositionBottomRightIcon);
return;
}
}
void FloatingAccessibilityView::SetDetailedViewShown(bool shown) {
a11y_tray_button_->SetToggled(shown);
}
void FloatingAccessibilityView::FocusOnDetailedViewButton() {
a11y_tray_button_->RequestFocus();
}
void FloatingAccessibilityView::OnA11yTrayButtonPressed() {
delegate_->OnDetailedMenuEnabled(!a11y_tray_button_->GetToggled());
}
void FloatingAccessibilityView::OnPositionButtonPressed() {
FloatingMenuPosition new_position;
switch (Shell::Get()->accessibility_controller()->GetFloatingMenuPosition()) {
case FloatingMenuPosition::kBottomRight:
new_position = FloatingMenuPosition::kBottomLeft;
break;
case FloatingMenuPosition::kBottomLeft:
new_position = FloatingMenuPosition::kTopLeft;
break;
case FloatingMenuPosition::kTopLeft:
new_position = FloatingMenuPosition::kTopRight;
break;
case FloatingMenuPosition::kTopRight:
new_position = FloatingMenuPosition::kBottomRight;
break;
case FloatingMenuPosition::kSystemDefault:
new_position = base::i18n::IsRTL() ? FloatingMenuPosition::kTopLeft
: FloatingMenuPosition::kBottomLeft;
break;
}
Shell::Get()->accessibility_controller()->SetFloatingMenuPosition(
new_position);
Shell::Get()
->accessibility_controller()
->TriggerAccessibilityAlertWithMessage(
GetDescriptionForMovedToPosition(new_position));
}
void FloatingAccessibilityView::OnViewVisibilityChanged(
views::View* observed_view,
views::View* starting_view,
bool visible) {
if (observed_view != starting_view) {
return;
}
delegate_->OnLayoutChanged();
}
void FloatingAccessibilityView::OnViewFocused(views::View* view) {
delegate_->OnFocused();
}
void FloatingAccessibilityView::OnViewBlurred(views::View* view) {
delegate_->OnBlurred();
}
void FloatingAccessibilityView::OnFocusLeavingSystemTray(bool reverse) {}
void FloatingAccessibilityView::OnImeMenuTrayBubbleShown() {
delegate_->OnDetailedMenuEnabled(false);
}
void FloatingAccessibilityView::OnKeyboardVisibilityChanged(bool visible) {
if (visible)
delegate_->OnDetailedMenuEnabled(false);
}
BEGIN_METADATA(FloatingAccessibilityView)
END_METADATA
}