1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/system/accessibility/switch_access_menu_bubble_controller.h"
6
7 #include "ash/public/cpp/shell_window_ids.h"
8 #include "ash/shell.h"
9 #include "ash/system/accessibility/switch_access_back_button_bubble_controller.h"
10 #include "ash/system/accessibility/switch_access_menu_view.h"
11 #include "ash/system/tray/tray_background_view.h"
12 #include "ash/system/tray/tray_constants.h"
13 #include "ash/system/unified/unified_system_tray_view.h"
14 #include "ash/wm/collision_detection/collision_detection_utils.h"
15 #include "ui/accessibility/ax_enums.mojom.h"
16
17 namespace ash {
18
SwitchAccessMenuBubbleController()19 SwitchAccessMenuBubbleController::SwitchAccessMenuBubbleController()
20 : back_button_controller_(
21 std::make_unique<SwitchAccessBackButtonBubbleController>()) {}
22
~SwitchAccessMenuBubbleController()23 SwitchAccessMenuBubbleController::~SwitchAccessMenuBubbleController() {
24 if (widget_ && !widget_->IsClosed())
25 widget_->CloseNow();
26 }
27
ShowBackButton(const gfx::Rect & anchor)28 void SwitchAccessMenuBubbleController::ShowBackButton(const gfx::Rect& anchor) {
29 back_button_controller_->ShowBackButton(anchor, /*show_focus_ring=*/true,
30 menu_open_);
31 }
32
ShowMenu(const gfx::Rect & anchor,const std::vector<std::string> & actions_to_show)33 void SwitchAccessMenuBubbleController::ShowMenu(
34 const gfx::Rect& anchor,
35 const std::vector<std::string>& actions_to_show) {
36 menu_open_ = true;
37 if (!widget_) {
38 TrayBubbleView::InitParams init_params;
39 init_params.delegate = this;
40 // Anchor within the overlay container.
41 init_params.parent_window =
42 Shell::GetContainer(Shell::GetPrimaryRootWindow(),
43 kShellWindowId_AccessibilityBubbleContainer);
44 init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
45 init_params.is_anchored_to_status_area = false;
46 init_params.insets = gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding);
47 init_params.corner_radius = kUnifiedTrayCornerRadius;
48 init_params.has_shadow = false;
49 init_params.translucent = true;
50 bubble_view_ = new TrayBubbleView(init_params);
51 bubble_view_->SetArrow(views::BubbleBorder::Arrow::TOP_LEFT);
52
53 menu_view_ = new SwitchAccessMenuView();
54 menu_view_->SetBorder(
55 views::CreateEmptyBorder(gfx::Insets(kUnifiedMenuPadding)));
56 bubble_view_->AddChildView(menu_view_);
57
58 menu_view_->SetPaintToLayer();
59 menu_view_->layer()->SetFillsBoundsOpaquely(false);
60
61 widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
62 TrayBackgroundView::InitializeBubbleAnimations(widget_);
63 bubble_view_->InitializeAndShowBubble();
64
65 CollisionDetectionUtils::MarkWindowPriorityForCollisionDetection(
66 widget_->GetNativeWindow(),
67 CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu);
68 }
69
70 DCHECK(bubble_view_);
71
72 menu_view_->SetActions(actions_to_show);
73 bubble_view_->SetPreferredWidth(menu_view_->GetBubbleWidthDip());
74 bubble_view_->ChangeAnchorRect(anchor);
75
76 gfx::Rect new_bounds = widget_->GetWindowBoundsInScreen();
77
78 // Adjust the bounds to fit entirely within the screen.
79 gfx::Rect display_bounds =
80 display::Screen::GetScreen()->GetDisplayMatching(new_bounds).bounds();
81 new_bounds.AdjustToFit(display_bounds);
82
83 // Update the preferred bounds based on other system windows.
84 gfx::Rect resting_bounds = CollisionDetectionUtils::AvoidObstacles(
85 display::Screen::GetScreen()->GetDisplayNearestWindow(
86 widget_->GetNativeWindow()),
87 new_bounds, CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu);
88
89 widget_->SetBounds(resting_bounds);
90 widget_->Show();
91 bubble_view_->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
92 true);
93
94 // The resting bounds includes padding on each side of the menu.
95 // Remove that before passing to the back button controller so the back button
96 // appears in the correct position.
97 resting_bounds.Inset(kUnifiedMenuPadding, kUnifiedMenuPadding);
98 back_button_controller_->ShowBackButton(resting_bounds,
99 /*show_focus_ring=*/false,
100 /*for_menu=*/true);
101 }
102
HideBackButton()103 void SwitchAccessMenuBubbleController::HideBackButton() {
104 if (widget_ && widget_->IsVisible())
105 back_button_controller_->HideFocusRing();
106 else
107 back_button_controller_->Hide();
108 }
109
HideMenuBubble()110 void SwitchAccessMenuBubbleController::HideMenuBubble() {
111 menu_open_ = false;
112 back_button_controller_->Hide();
113 if (widget_)
114 widget_->Hide();
115 if (bubble_view_)
116 bubble_view_->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
117 true);
118 }
119
BubbleViewDestroyed()120 void SwitchAccessMenuBubbleController::BubbleViewDestroyed() {
121 bubble_view_ = nullptr;
122 menu_view_ = nullptr;
123 widget_ = nullptr;
124 }
125
126 } // namespace ash
127