#include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
#include <string>
#include "ash/bubble/bubble_constants.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/accessibility/autoclick_scroll_bubble_controller.h"
#include "ash/system/accessibility/floating_menu_utils.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_bubble_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 "ash/wm/workspace/workspace_layout_manager.h"
#include "ash/wm/workspace_controller.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/strings/string_number_conversions.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/events/event_utils.h"
#include "ui/views/border.h"
#include "ui/views/bubble/bubble_border.h"
namespace ash {
namespace {
const int kAutoclickMenuWidth = 369;
const int kAutoclickMenuHeight = 64;
}
AutoclickMenuBubbleController::AutoclickMenuBubbleController() {
Shell::Get()->locale_update_controller()->AddObserver(this);
}
AutoclickMenuBubbleController::~AutoclickMenuBubbleController() {
if (bubble_widget_ && !bubble_widget_->IsClosed())
bubble_widget_->CloseNow();
Shell::Get()->locale_update_controller()->RemoveObserver(this);
scroll_bubble_controller_.reset();
}
void AutoclickMenuBubbleController::SetEventType(AutoclickEventType type) {
if (menu_view_)
menu_view_->UpdateEventType(type);
if (type == AutoclickEventType::kScroll) {
if (!scroll_bubble_controller_) {
scroll_bubble_controller_ =
std::make_unique<AutoclickScrollBubbleController>();
}
gfx::Rect anchor_rect = bubble_view_->GetBoundsInScreen();
anchor_rect.Inset(-kCollisionWindowWorkAreaInsetsDp);
scroll_bubble_controller_->ShowBubble(
anchor_rect, GetAnchorAlignmentForFloatingMenuPosition(position_));
} else if (scroll_bubble_controller_) {
scroll_bubble_controller_ = nullptr;
SetPosition(position_);
}
}
void AutoclickMenuBubbleController::SetPosition(
FloatingMenuPosition new_position) {
if (!menu_view_ || !bubble_view_ || !bubble_widget_)
return;
if (position_ != new_position ||
new_position == FloatingMenuPosition::kSystemDefault) {
menu_view_->UpdatePosition(new_position);
}
position_ = new_position;
if (new_position == FloatingMenuPosition::kSystemDefault)
new_position = DefaultSystemFloatingMenuPosition();
gfx::Rect new_bounds = GetOnScreenBoundsForFloatingMenuPosition(
gfx::Size(kAutoclickMenuWidth, kAutoclickMenuHeight), new_position);
const display::Display target_display =
display::Screen::Get()->GetDisplayNearestWindow(
bubble_widget_->GetNativeWindow());
if (!target_display.is_valid() ||
!Shell::GetRootWindowControllerWithDisplayId(target_display.id())) {
SCOPED_CRASH_KEY_NUMBER("AMBC", "target_display", target_display.id());
if (display::DisplayManager* display_manager =
Shell::Get()->display_manager()) {
std::string key;
for (size_t i = 0; i < display_manager->GetNumDisplays(); i++) {
const display::Display& current_display =
display_manager->GetDisplayAt(i);
key += (i ? "_" : "") + base::NumberToString(i);
key += "_" + base::NumberToString(current_display.id());
key += "_" + base::NumberToString(current_display.is_valid());
key += "_" + base::NumberToString(
!!Shell::GetRootWindowControllerWithDisplayId(
current_display.id()));
}
key += "_" + base::NumberToString(display_manager->IsInUnifiedMode());
SCOPED_CRASH_KEY_STRING256("AMBC", "display_info", key);
}
base::debug::DumpWithoutCrashing();
return;
}
gfx::Rect resting_bounds = CollisionDetectionUtils::GetRestingPosition(
target_display, new_bounds,
CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu);
resting_bounds.Inset(gfx::Insets::TLBR(0, -kCollisionWindowWorkAreaInsetsDp,
-kCollisionWindowWorkAreaInsetsDp,
-kCollisionWindowWorkAreaInsetsDp));
if (bubble_widget_->GetWindowBoundsInScreen() == resting_bounds)
return;
if (animate_) {
ui::ScopedLayerAnimationSettings settings(
bubble_widget_->GetLayer()->GetAnimator());
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTransitionDuration(base::Milliseconds(kAnimationDurationMs));
settings.SetTweenType(gfx::Tween::EASE_OUT);
}
bubble_widget_->SetBounds(resting_bounds);
if (!scroll_bubble_controller_)
return;
scroll_bubble_controller_->UpdateAnchorRect(
resting_bounds, GetAnchorAlignmentForFloatingMenuPosition(new_position));
}
void AutoclickMenuBubbleController::SetScrollPosition(
gfx::Rect scroll_bounds_in_dips,
const gfx::Point& scroll_point_in_dips) {
if (!scroll_bubble_controller_)
return;
scroll_bubble_controller_->SetScrollPosition(scroll_bounds_in_dips,
scroll_point_in_dips);
}
void AutoclickMenuBubbleController::ShowBubble(AutoclickEventType type,
FloatingMenuPosition position) {
if (bubble_widget_)
return;
DCHECK(!bubble_view_);
TrayBubbleView::InitParams init_params;
init_params.delegate = GetWeakPtr();
init_params.parent_window =
Shell::GetContainer(Shell::GetPrimaryRootWindow(),
kShellWindowId_AccessibilityBubbleContainer);
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
init_params.is_anchored_to_status_area = false;
init_params.close_on_deactivate = false;
init_params.insets = gfx::Insets::TLBR(0, kCollisionWindowWorkAreaInsetsDp,
kCollisionWindowWorkAreaInsetsDp,
kCollisionWindowWorkAreaInsetsDp);
init_params.preferred_width = kAutoclickMenuWidth;
init_params.translucent = true;
init_params.type = TrayBubbleView::TrayBubbleType::kAccessibilityBubble;
bubble_view_ = new TrayBubbleView(init_params);
menu_view_ = new AutoclickMenuView(type, position);
menu_view_->SetBorder(views::CreateEmptyBorder(
gfx::Insets::TLBR(kUnifiedTopShortcutSpacing, 0, 0, 0)));
bubble_view_->AddChildViewRaw(menu_view_.get());
bubble_widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_);
CollisionDetectionUtils::MarkWindowPriorityForCollisionDetection(
bubble_widget_->GetNativeWindow(),
CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu);
bubble_view_->InitializeAndShowBubble();
SetPosition(position);
}
void AutoclickMenuBubbleController::CloseBubble() {
if (!bubble_widget_ || bubble_widget_->IsClosed())
return;
bubble_widget_->Close();
}
void AutoclickMenuBubbleController::SetBubbleVisibility(bool is_visible) {
if (!bubble_widget_)
return;
if (is_visible)
bubble_widget_->Show();
else
bubble_widget_->Hide();
if (!scroll_bubble_controller_)
return;
scroll_bubble_controller_->SetBubbleVisibility(is_visible);
}
void AutoclickMenuBubbleController::ClickOnBubble(gfx::Point location_in_dips,
int mouse_event_flags) {
if (!bubble_widget_)
return;
location_in_dips -= bubble_view_->GetBoundsInScreen().OffsetFromOrigin();
const ui::MouseEvent press_event(
ui::EventType::kMousePressed, location_in_dips, location_in_dips,
ui::EventTimeForNow(), mouse_event_flags | ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
const ui::MouseEvent release_event(
ui::EventType::kMouseReleased, location_in_dips, location_in_dips,
ui::EventTimeForNow(), mouse_event_flags | ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
bubble_widget_->GetRootView()->OnMousePressed(press_event);
bubble_widget_->GetRootView()->OnMouseReleased(release_event);
}
void AutoclickMenuBubbleController::ClickOnScrollBubble(
gfx::Point location_in_dips,
int mouse_event_flags) {
if (!scroll_bubble_controller_)
return;
scroll_bubble_controller_->ClickOnBubble(location_in_dips, mouse_event_flags);
}
bool AutoclickMenuBubbleController::ContainsPointInScreen(
const gfx::Point& point) {
return bubble_view_ && bubble_view_->GetBoundsInScreen().Contains(point);
}
bool AutoclickMenuBubbleController::ScrollBubbleContainsPointInScreen(
const gfx::Point& point) {
return scroll_bubble_controller_ &&
scroll_bubble_controller_->ContainsPointInScreen(point);
}
void AutoclickMenuBubbleController::BubbleViewDestroyed() {
bubble_view_ = nullptr;
bubble_widget_ = nullptr;
menu_view_ = nullptr;
}
std::u16string AutoclickMenuBubbleController::GetAccessibleNameForBubble() {
return l10n_util::GetStringUTF16(IDS_ASH_AUTOCLICK_MENU);
}
void AutoclickMenuBubbleController::HideBubble(
const TrayBubbleView* bubble_view) {
}
void AutoclickMenuBubbleController::OnLocaleChanged() {
if (position_ == FloatingMenuPosition::kSystemDefault)
SetPosition(position_);
}
}