[go: nahoru, domu]

blob: 4ba63e69ee4cc943e547d34a98ec74c817a83f18 [file] [log] [blame]
// Copyright 2020 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/system/accessibility/floating_accessibility_controller.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/accessibility/floating_menu_utils.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "ash/wm/work_area_insets.h"
#include "base/check.h"
#include "base/notreached.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
namespace ash {
namespace {
constexpr int kFloatingMenuHeight = 64;
constexpr base::TimeDelta kAnimationDuration =
base::TimeDelta::FromMilliseconds(150);
} // namespace
FloatingAccessibilityController::FloatingAccessibilityController(
AccessibilityControllerImpl* accessibility_controller)
: accessibility_controller_(accessibility_controller) {
Shell::Get()->locale_update_controller()->AddObserver(this);
accessibility_controller_->AddObserver(this);
}
FloatingAccessibilityController::~FloatingAccessibilityController() {
Shell::Get()->locale_update_controller()->RemoveObserver(this);
accessibility_controller_->RemoveObserver(this);
if (bubble_widget_ && !bubble_widget_->IsClosed())
bubble_widget_->CloseNow();
}
void FloatingAccessibilityController::Show(FloatingMenuPosition position) {
// Kiosk check.
if (!Shell::Get()->session_controller()->IsRunningInAppMode()) {
NOTREACHED()
<< "Floating accessibility menu can only be run in a kiosk session.";
return;
}
DCHECK(!bubble_view_);
TrayBubbleView::InitParams init_params;
init_params.delegate = this;
// Our view uses SettingsBubbleContainer since it is activatable and is
// included in the collision detection logic.
init_params.parent_window = Shell::GetContainer(
Shell::GetPrimaryRootWindow(), kShellWindowId_SettingBubbleContainer);
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
// The widget's shadow is drawn below and on the sides of the view, with a
// width of kCollisionWindowWorkAreaInsetsDp. Set the top inset to 0 to ensure
// the detailed view is drawn at kCollisionWindowWorkAreaInsetsDp above the
// bubble menu when the position is at the bottom of the screen. The space
// between the bubbles belongs to the detailed view bubble's shadow.
init_params.insets = gfx::Insets(0, kCollisionWindowWorkAreaInsetsDp,
kCollisionWindowWorkAreaInsetsDp,
kCollisionWindowWorkAreaInsetsDp);
init_params.corner_radius = kUnifiedTrayCornerRadius;
init_params.has_shadow = false;
init_params.max_height = kFloatingMenuHeight;
init_params.translucent = true;
init_params.close_on_deactivate = false;
bubble_view_ = new FloatingAccessibilityBubbleView(init_params);
menu_view_ = new FloatingAccessibilityView(this);
menu_view_->SetBorder(
views::CreateEmptyBorder(kUnifiedTopShortcutSpacing, 0, 0, 0));
bubble_view_->AddChildView(menu_view_);
bubble_view_->SetFocusBehavior(
ActionableView::FocusBehavior::ACCESSIBLE_ONLY);
menu_view_->SetPaintToLayer();
menu_view_->layer()->SetFillsBoundsOpaquely(false);
bubble_widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
bubble_view_->SetCanActivate(true);
TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_);
bubble_view_->InitializeAndShowBubble();
menu_view_->Initialize();
SetMenuPosition(position);
}
void FloatingAccessibilityController::SetMenuPosition(
FloatingMenuPosition new_position) {
if (!menu_view_ || !bubble_view_ || !bubble_widget_)
return;
// Update the menu view's UX if the position has changed, or if it's not the
// default position (because that can change with language direction).
if (position_ != new_position ||
new_position == FloatingMenuPosition::kSystemDefault) {
menu_view_->SetMenuPosition(new_position);
}
position_ = new_position;
// If this is the default system position, pick the position based on the
// language direction.
if (new_position == FloatingMenuPosition::kSystemDefault)
new_position = DefaultSystemFloatingMenuPosition();
gfx::Rect new_bounds = GetOnScreenBoundsForFloatingMenuPosition(
menu_view_->GetPreferredSize(), new_position);
gfx::Rect resting_bounds =
CollisionDetectionUtils::AdjustToFitMovementAreaByGravity(
display::Screen::GetScreen()->GetDisplayNearestWindow(
bubble_widget_->GetNativeWindow()),
new_bounds);
// Un-inset the bounds to get the widget's bounds, which includes the drop
// shadow.
resting_bounds.Inset(-kCollisionWindowWorkAreaInsetsDp, 0,
-kCollisionWindowWorkAreaInsetsDp,
-kCollisionWindowWorkAreaInsetsDp);
if (bubble_widget_->GetWindowBoundsInScreen() == resting_bounds)
return;
ui::ScopedLayerAnimationSettings settings(
bubble_widget_->GetLayer()->GetAnimator());
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTransitionDuration(kAnimationDuration);
settings.SetTweenType(gfx::Tween::EASE_OUT);
bubble_widget_->SetBounds(resting_bounds);
if (detailed_menu_controller_) {
detailed_menu_controller_->UpdateAnchorRect(
resting_bounds, GetAnchorAlignmentForFloatingMenuPosition(position_));
}
}
void FloatingAccessibilityController::FocusOnMenu() {
bubble_view_->GetFocusManager()->ClearFocus();
bubble_view_->GetFocusManager()->AdvanceFocus(false /* reverse */);
}
void FloatingAccessibilityController::OnDetailedMenuEnabled(bool enabled) {
if (enabled) {
detailed_menu_controller_ =
std::make_unique<FloatingAccessibilityDetailedController>(this);
gfx::Rect anchor_rect = bubble_view_->GetBoundsInScreen();
anchor_rect.Inset(-kCollisionWindowWorkAreaInsetsDp, 0,
-kCollisionWindowWorkAreaInsetsDp,
-kCollisionWindowWorkAreaInsetsDp);
detailed_menu_controller_->Show(
anchor_rect, GetAnchorAlignmentForFloatingMenuPosition(position_));
menu_view_->SetDetailedViewShown(true);
} else {
detailed_menu_controller_.reset();
// We may need to update the autoclick bounds.
Shell::Get()
->accessibility_controller()
->UpdateAutoclickMenuBoundsIfNeeded();
}
}
void FloatingAccessibilityController::OnLayoutChanged() {
if (on_layout_change_)
on_layout_change_.Run();
SetMenuPosition(position_);
}
void FloatingAccessibilityController::OnDetailedMenuClosed() {
detailed_menu_controller_.reset();
if (!menu_view_)
return;
menu_view_->SetDetailedViewShown(false);
if (bubble_widget_->IsActive())
menu_view_->FocusOnDetailedViewButton();
}
views::Widget* FloatingAccessibilityController::GetBubbleWidget() {
return bubble_widget_;
}
void FloatingAccessibilityController::BubbleViewDestroyed() {
bubble_view_ = nullptr;
bubble_widget_ = nullptr;
menu_view_ = nullptr;
}
std::u16string FloatingAccessibilityController::GetAccessibleNameForBubble() {
return l10n_util::GetStringUTF16(IDS_ASH_FLOATING_ACCESSIBILITY_MAIN_MENU);
}
void FloatingAccessibilityController::OnLocaleChanged() {
// Layout update is needed when language changes between LTR and RTL, if the
// position is the system default.
if (position_ == FloatingMenuPosition::kSystemDefault)
SetMenuPosition(position_);
}
void FloatingAccessibilityController::OnAccessibilityStatusChanged() {
// Some features may change the available screen area(docked magnifier), we
// will update the location of the menu in such cases.
SetMenuPosition(position_);
if (detailed_menu_controller_)
detailed_menu_controller_->OnAccessibilityStatusChanged();
}
} // namespace ash