Avi Drissman | 3a215d1e | 2022-09-07 19:43:09 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 5 | #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h" |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 6 | |
Kevin Schoedel | 8c1f077 | 2019-02-13 19:48:47 | [diff] [blame] | 7 | #include <algorithm> |
dcheng | a9454747 | 2016-04-08 08:41:11 | [diff] [blame] | 8 | #include <memory> |
dcheng | cbf0d9d | 2015-12-27 22:49:23 | [diff] [blame] | 9 | #include <utility> |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 10 | #include <vector> |
dcheng | cbf0d9d | 2015-12-27 22:49:23 | [diff] [blame] | 11 | |
Katie Dektar | 582ae10 | 2023-12-13 03:15:54 | [diff] [blame] | 12 | #include "ash/accessibility/accessibility_controller.h" |
James Cook | 37b7d10 | 2017-10-06 04:35:19 | [diff] [blame] | 13 | #include "ash/accessibility/accessibility_delegate.h" |
David Tseng | 00443a3 | 2021-03-24 22:24:13 | [diff] [blame] | 14 | #include "ash/accessibility/magnifier/magnifier_utils.h" |
oshima@chromium.org | d2d18a1 | 2013-05-29 21:51:57 | [diff] [blame] | 15 | #include "ash/display/root_window_transformers.h" |
oshima@chromium.org | f5c9dbc | 2014-04-11 08:13:45 | [diff] [blame] | 16 | #include "ash/host/ash_window_tree_host.h" |
| 17 | #include "ash/host/root_window_transformer.h" |
Darren Shen | cb250844 | 2019-07-03 21:48:23 | [diff] [blame] | 18 | #include "ash/keyboard/ui/keyboard_ui_controller.h" |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 19 | #include "ash/public/cpp/shell_window_ids.h" |
oshima@chromium.org | f5c9dbc | 2014-04-11 08:13:45 | [diff] [blame] | 20 | #include "ash/root_window_controller.h" |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 21 | #include "ash/shell.h" |
Josiah K | 6db1587b | 2021-04-06 05:43:44 | [diff] [blame] | 22 | #include "ui/accessibility/accessibility_switches.h" |
mazda@chromium.org | 60c2620 | 2012-10-12 18:55:23 | [diff] [blame] | 23 | #include "ui/aura/client/cursor_client.h" |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 24 | #include "ui/aura/window.h" |
ben@chromium.org | 7a60cd3 | 2014-03-20 20:54:57 | [diff] [blame] | 25 | #include "ui/aura/window_tree_host.h" |
tfarina@chromium.org | 116302f | 2012-05-05 21:45:41 | [diff] [blame] | 26 | #include "ui/compositor/layer.h" |
| 27 | #include "ui/compositor/scoped_layer_animation_settings.h" |
oshima | 888290a | 2016-05-13 20:36:06 | [diff] [blame] | 28 | #include "ui/display/display.h" |
oshima | f84b0da72 | 2016-04-27 19:47:19 | [diff] [blame] | 29 | #include "ui/display/screen.h" |
sky@chromium.org | 86ccbd4 | 2013-09-18 18:11:54 | [diff] [blame] | 30 | #include "ui/events/event.h" |
Ahmed Fakhry | cbc68f0 | 2021-07-27 00:20:14 | [diff] [blame] | 31 | #include "ui/events/event_handler.h" |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 32 | #include "ui/events/gestures/gesture_provider_aura.h" |
Sammie Quon | 666db05c | 2020-08-05 23:47:40 | [diff] [blame] | 33 | #include "ui/events/types/event_type.h" |
danakj | fae0c84 | 2020-09-23 15:50:04 | [diff] [blame] | 34 | #include "ui/gfx/geometry/dip_util.h" |
Avi Drissman | fefc2f8 | 2014-12-22 19:25:29 | [diff] [blame] | 35 | #include "ui/gfx/geometry/point3_f.h" |
| 36 | #include "ui/gfx/geometry/point_conversions.h" |
Avi Drissman | fefc2f8 | 2014-12-22 19:25:29 | [diff] [blame] | 37 | #include "ui/gfx/geometry/rect_conversions.h" |
jennyz | 91b6ed0c | 2014-10-23 21:52:41 | [diff] [blame] | 38 | #include "ui/wm/core/coordinate_conversion.h" |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 39 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 40 | namespace ash { |
| 41 | |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 42 | namespace { |
| 43 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 44 | constexpr float kMaxMagnifiedScale = 20.0f; |
| 45 | constexpr float kMinMagnifiedScaleThreshold = 1.1f; |
| 46 | constexpr float kNonMagnifiedScale = 1.0f; |
yoshiki@chromium.org | 5d10702 | 2012-06-24 19:58:25 | [diff] [blame] | 47 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 48 | constexpr float kInitialMagnifiedScale = 2.0f; |
| 49 | constexpr float kScrollScaleChangeFactor = 0.00125f; |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 50 | |
jennyz | b894b6d | 2015-06-01 20:12:18 | [diff] [blame] | 51 | // Default animation parameters for redrawing the magnification window. |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 52 | constexpr gfx::Tween::Type kDefaultAnimationTweenType = gfx::Tween::EASE_OUT; |
| 53 | constexpr int kDefaultAnimationDurationInMs = 100; |
yoshiki@chromium.org | ecdc6d2 | 2013-01-11 09:53:04 | [diff] [blame] | 54 | |
jennyz | b894b6d | 2015-06-01 20:12:18 | [diff] [blame] | 55 | // Use linear transformation to make the magnifier window move smoothly |
| 56 | // to center the focus when user types in a text input field. |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 57 | constexpr gfx::Tween::Type kCenterCaretAnimationTweenType = gfx::Tween::LINEAR; |
jennyz | b894b6d | 2015-06-01 20:12:18 | [diff] [blame] | 58 | |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 59 | // Threshold of panning. If the cursor moves to within pixels (in DIP) of |
jennyz | b894b6d | 2015-06-01 20:12:18 | [diff] [blame] | 60 | // |kCursorPanningMargin| from the edge, the view-port moves. |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 61 | constexpr int kCursorPanningMargin = 100; |
jennyz | b894b6d | 2015-06-01 20:12:18 | [diff] [blame] | 62 | |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 63 | // Threshold of panning at the bottom when the virtual keyboard is up. If the |
| 64 | // cursor moves to within pixels (in DIP) of |kKeyboardBottomPanningMargin| from |
| 65 | // the bottom edge, the view-port moves. This is only used by |
| 66 | // MoveMagnifierWindowFollowPoint() when |reduce_bottom_margin| is true. |
| 67 | constexpr int kKeyboardBottomPanningMargin = 10; |
| 68 | |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 69 | } // namespace |
| 70 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 71 | class FullscreenMagnifierController::GestureProviderClient |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 72 | : public ui::GestureProviderAuraClient { |
yoshiki@chromium.org | 9613eacf | 2012-06-22 02:18:05 | [diff] [blame] | 73 | public: |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 74 | GestureProviderClient() = default; |
Josiah K | e66f864 | 2021-07-02 23:24:32 | [diff] [blame] | 75 | GestureProviderClient(const GestureProviderClient&) = delete; |
| 76 | GestureProviderClient& operator=(const GestureProviderClient&) = delete; |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 77 | ~GestureProviderClient() override = default; |
yoshiki@chromium.org | 9613eacf | 2012-06-22 02:18:05 | [diff] [blame] | 78 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 79 | // ui::GestureProviderAuraClient overrides: |
| 80 | void OnGestureEvent(GestureConsumer* consumer, |
| 81 | ui::GestureEvent* event) override { |
| 82 | // Do nothing. OnGestureEvent is for timer based gesture events, e.g. tap. |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 83 | // FullscreenMagnifierController is interested only in pinch and scroll |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 84 | // gestures. |
| 85 | DCHECK_NE(ui::ET_GESTURE_SCROLL_BEGIN, event->type()); |
| 86 | DCHECK_NE(ui::ET_GESTURE_SCROLL_END, event->type()); |
| 87 | DCHECK_NE(ui::ET_GESTURE_SCROLL_UPDATE, event->type()); |
| 88 | DCHECK_NE(ui::ET_GESTURE_PINCH_BEGIN, event->type()); |
| 89 | DCHECK_NE(ui::ET_GESTURE_PINCH_END, event->type()); |
| 90 | DCHECK_NE(ui::ET_GESTURE_PINCH_UPDATE, event->type()); |
jennyz | b894b6d | 2015-06-01 20:12:18 | [diff] [blame] | 91 | } |
yoshiki@chromium.org | 9613eacf | 2012-06-22 02:18:05 | [diff] [blame] | 92 | }; |
| 93 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 94 | FullscreenMagnifierController::FullscreenMagnifierController() |
oshima@chromium.org | 3dff2ef | 2014-02-09 22:50:39 | [diff] [blame] | 95 | : root_window_(Shell::GetPrimaryRootWindow()), |
oshima@chromium.org | 3dff2ef | 2014-02-09 22:50:39 | [diff] [blame] | 96 | scale_(kNonMagnifiedScale), |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 97 | original_scale_(kNonMagnifiedScale) { |
Katie Dektar | 2f938bd | 2022-10-21 17:47:12 | [diff] [blame] | 98 | Shell::Get()->AddAccessibilityEventHandler( |
| 99 | this, |
| 100 | AccessibilityEventHandlerManager::HandlerType::kFullscreenMagnifier); |
oshima@chromium.org | 779fd981 | 2013-02-28 01:59:22 | [diff] [blame] | 101 | root_window_->AddObserver(this); |
Yuki Awano | d6b10e24 | 2018-02-07 01:16:00 | [diff] [blame] | 102 | root_window_->GetHost()->GetEventSource()->AddEventRewriter(this); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 103 | |
Yuki Awano | f598b73 | 2018-02-28 05:58:48 | [diff] [blame] | 104 | point_of_interest_in_root_ = root_window_->bounds().CenterPoint(); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 105 | |
| 106 | gesture_provider_client_ = std::make_unique<GestureProviderClient>(); |
| 107 | gesture_provider_ = std::make_unique<ui::GestureProviderAura>( |
| 108 | this, gesture_provider_client_.get()); |
Josiah K | 6db1587b | 2021-04-06 05:43:44 | [diff] [blame] | 109 | |
| 110 | magnifier_debug_draw_rect_ = ::switches::IsMagnifierDebugDrawRectEnabled(); |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 111 | } |
| 112 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 113 | FullscreenMagnifierController::~FullscreenMagnifierController() { |
Yuki Awano | d6b10e24 | 2018-02-07 01:16:00 | [diff] [blame] | 114 | root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this); |
oshima@chromium.org | 779fd981 | 2013-02-28 01:59:22 | [diff] [blame] | 115 | root_window_->RemoveObserver(this); |
| 116 | |
Katie Dektar | 2f938bd | 2022-10-21 17:47:12 | [diff] [blame] | 117 | Shell::Get()->RemoveAccessibilityEventHandler(this); |
yoshiki@chromium.org | 9613eacf | 2012-06-22 02:18:05 | [diff] [blame] | 118 | } |
| 119 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 120 | void FullscreenMagnifierController::SetEnabled(bool enabled) { |
yoshiki@chromium.org | 5d10702 | 2012-06-24 19:58:25 | [diff] [blame] | 121 | if (enabled) { |
sky | cb4be5b | 2017-04-06 17:52:45 | [diff] [blame] | 122 | Shell* shell = Shell::Get(); |
msw | 3ce3e0e | 2016-06-29 01:54:47 | [diff] [blame] | 123 | float scale = |
sky | 07a24d4 | 2017-03-09 23:57:30 | [diff] [blame] | 124 | shell->accessibility_delegate()->GetSavedScreenMagnifierScale(); |
yoshiki@chromium.org | fd19988 | 2012-10-16 10:49:46 | [diff] [blame] | 125 | if (scale <= 0.0f) |
yoshiki@chromium.org | b7933a14 | 2012-12-06 22:35:12 | [diff] [blame] | 126 | scale = kInitialMagnifiedScale; |
yoshiki@chromium.org | de2d4ae | 2012-10-05 10:10:55 | [diff] [blame] | 127 | ValidateScale(&scale); |
yoshiki@chromium.org | e2ba8c9 | 2012-12-06 08:42:02 | [diff] [blame] | 128 | |
| 129 | // Do nothing, if already enabled with same scale. |
| 130 | if (is_enabled_ && scale == scale_) |
| 131 | return; |
| 132 | |
yoshiki@chromium.org | 0ec5834 | 2013-04-24 07:39:36 | [diff] [blame] | 133 | is_enabled_ = enabled; |
weidongg | 2e1f4b7 | 2017-05-18 04:30:56 | [diff] [blame] | 134 | RedrawKeepingMousePosition(scale, true, false); |
sky | 07a24d4 | 2017-03-09 23:57:30 | [diff] [blame] | 135 | shell->accessibility_delegate()->SaveScreenMagnifierScale(scale); |
yoshiki@chromium.org | 5d10702 | 2012-06-24 19:58:25 | [diff] [blame] | 136 | } else { |
yoshiki@chromium.org | e2ba8c9 | 2012-12-06 08:42:02 | [diff] [blame] | 137 | // Do nothing, if already disabled. |
| 138 | if (!is_enabled_) |
| 139 | return; |
| 140 | |
weidongg | 2e1f4b7 | 2017-05-18 04:30:56 | [diff] [blame] | 141 | RedrawKeepingMousePosition(kNonMagnifiedScale, true, false); |
yoshiki@chromium.org | 5d10702 | 2012-06-24 19:58:25 | [diff] [blame] | 142 | is_enabled_ = enabled; |
| 143 | } |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 144 | |
| 145 | // Keyboard overscroll creates layout issues with fullscreen magnification |
| 146 | // so it needs to be disabled when magnification is enabled. |
| 147 | // TODO(spqchan): Fix the keyboard overscroll issues. |
Darren Shen | cb250844 | 2019-07-03 21:48:23 | [diff] [blame] | 148 | auto config = keyboard::KeyboardUIController::Get()->keyboard_config(); |
Steven Bennetts | 42a4d4a | 2018-10-05 19:05:10 | [diff] [blame] | 149 | config.overscroll_behavior = |
Darren Shen | 8604a21 | 2019-06-07 00:41:38 | [diff] [blame] | 150 | is_enabled_ ? keyboard::KeyboardOverscrollBehavior::kDisabled |
| 151 | : keyboard::KeyboardOverscrollBehavior::kDefault; |
Darren Shen | cb250844 | 2019-07-03 21:48:23 | [diff] [blame] | 152 | keyboard::KeyboardUIController::Get()->UpdateKeyboardConfig(config); |
yoshiki@chromium.org | 5d10702 | 2012-06-24 19:58:25 | [diff] [blame] | 153 | } |
| 154 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 155 | bool FullscreenMagnifierController::IsEnabled() const { |
zork@chromium.org | 77f7c13 | 2012-11-15 06:52:54 | [diff] [blame] | 156 | return is_enabled_; |
| 157 | } |
| 158 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 159 | void FullscreenMagnifierController::SetKeepFocusCentered( |
David Tseng | 6b85bae | 2021-06-14 22:59:32 | [diff] [blame] | 160 | bool keep_focus_centered) { |
jennyz | b894b6d | 2015-06-01 20:12:18 | [diff] [blame] | 161 | keep_focus_centered_ = keep_focus_centered; |
| 162 | } |
| 163 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 164 | bool FullscreenMagnifierController::KeepFocusCentered() const { |
jennyz | b894b6d | 2015-06-01 20:12:18 | [diff] [blame] | 165 | return keep_focus_centered_; |
| 166 | } |
| 167 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 168 | void FullscreenMagnifierController::SetScale(float scale, bool animate) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 169 | if (!is_enabled_) |
| 170 | return; |
yoshiki@chromium.org | 9613eacf | 2012-06-22 02:18:05 | [diff] [blame] | 171 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 172 | ValidateScale(&scale); |
| 173 | Shell::Get()->accessibility_delegate()->SaveScreenMagnifierScale(scale); |
| 174 | RedrawKeepingMousePosition(scale, animate, false); |
| 175 | } |
| 176 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 177 | void FullscreenMagnifierController::StepToNextScaleValue(int delta_index) { |
Mike Wasserman | 15f20f4 | 2019-01-08 01:27:04 | [diff] [blame] | 178 | SetScale(magnifier_utils::GetNextMagnifierScaleValue( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 179 | delta_index, GetScale(), kNonMagnifiedScale, kMaxMagnifiedScale), |
| 180 | true /* animate */); |
| 181 | } |
| 182 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 183 | void FullscreenMagnifierController::MoveWindow(int x, int y, bool animate) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 184 | if (!is_enabled_) |
| 185 | return; |
| 186 | |
| 187 | Redraw(gfx::PointF(x, y), scale_, animate); |
| 188 | } |
| 189 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 190 | void FullscreenMagnifierController::MoveWindow(const gfx::Point& point, |
David Tseng | 6b85bae | 2021-06-14 22:59:32 | [diff] [blame] | 191 | bool animate) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 192 | if (!is_enabled_) |
| 193 | return; |
| 194 | |
| 195 | Redraw(gfx::PointF(point), scale_, animate); |
| 196 | } |
| 197 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 198 | gfx::Point FullscreenMagnifierController::GetWindowPosition() const { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 199 | return gfx::ToFlooredPoint(origin_); |
| 200 | } |
| 201 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 202 | void FullscreenMagnifierController::SetScrollDirection( |
David Tseng | 6b85bae | 2021-06-14 22:59:32 | [diff] [blame] | 203 | ScrollDirection direction) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 204 | scroll_direction_ = direction; |
| 205 | StartOrStopScrollIfNecessary(); |
| 206 | } |
| 207 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 208 | gfx::Rect FullscreenMagnifierController::GetViewportRect() const { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 209 | return gfx::ToEnclosingRect(GetWindowRectDIP(scale_)); |
| 210 | } |
| 211 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 212 | void FullscreenMagnifierController::CenterOnPoint( |
David Tseng | 6b85bae | 2021-06-14 22:59:32 | [diff] [blame] | 213 | const gfx::Point& point_in_screen) { |
Ahmed Fakhry | aabb73a | 2018-04-21 00:39:43 | [diff] [blame] | 214 | gfx::Point point_in_root = point_in_screen; |
| 215 | ::wm::ConvertPointFromScreen(root_window_, &point_in_root); |
| 216 | |
| 217 | MoveMagnifierWindowCenterPoint(point_in_root); |
| 218 | } |
| 219 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 220 | void FullscreenMagnifierController::HandleMoveMagnifierToRect( |
Josiah K | 8912e35 | 2020-09-30 18:40:37 | [diff] [blame] | 221 | const gfx::Rect& rect_in_screen) { |
| 222 | gfx::Rect node_bounds_in_root = rect_in_screen; |
| 223 | ::wm::ConvertRectFromScreen(root_window_, &node_bounds_in_root); |
| 224 | if (GetViewportRect().Contains(node_bounds_in_root)) |
| 225 | return; |
| 226 | |
Katie Dektar | 39046c2 | 2024-01-04 21:26:53 | [diff] [blame] | 227 | // Hide the cursor since this can cause jumps. |
| 228 | Shell::Get()->cursor_manager()->HideCursor(); |
Josiah K | 8912e35 | 2020-09-30 18:40:37 | [diff] [blame] | 229 | MoveMagnifierWindowFollowRect(node_bounds_in_root); |
| 230 | } |
| 231 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 232 | void FullscreenMagnifierController::SwitchTargetRootWindow( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 233 | aura::Window* new_root_window, |
| 234 | bool redraw_original_root_window) { |
| 235 | DCHECK(new_root_window); |
| 236 | |
| 237 | if (new_root_window == root_window_) |
| 238 | return; |
| 239 | |
| 240 | // Stores the previous scale. |
| 241 | float scale = GetScale(); |
| 242 | |
| 243 | // Unmagnify the previous root window. |
| 244 | root_window_->RemoveObserver(this); |
| 245 | // TODO: This may need to remove the IME observer from the old root window |
| 246 | // and add it to the new root window. https://crbug.com/820464 |
| 247 | |
| 248 | // Do not move mouse back to its original position (point at border of the |
| 249 | // root window) after redrawing as doing so will trigger root window switch |
| 250 | // again. |
| 251 | if (redraw_original_root_window) |
| 252 | RedrawKeepingMousePosition(1.0f, true, true); |
| 253 | root_window_ = new_root_window; |
| 254 | RedrawKeepingMousePosition(scale, true, true); |
| 255 | |
| 256 | root_window_->AddObserver(this); |
| 257 | } |
| 258 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 259 | gfx::Transform FullscreenMagnifierController::GetMagnifierTransform() const { |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 260 | gfx::Transform transform; |
| 261 | if (IsEnabled()) { |
| 262 | transform.Scale(scale_, scale_); |
| 263 | gfx::Point offset = GetWindowPosition(); |
| 264 | transform.Translate(-offset.x(), -offset.y()); |
| 265 | } |
| 266 | |
| 267 | return transform; |
| 268 | } |
| 269 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 270 | void FullscreenMagnifierController::OnImplicitAnimationsCompleted() { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 271 | if (move_cursor_after_animation_) { |
Katie Dektar | 4c69402c | 2022-12-22 18:37:48 | [diff] [blame] | 272 | MoveCursorTo(position_after_animation_); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 273 | move_cursor_after_animation_ = false; |
| 274 | |
| 275 | aura::client::CursorClient* cursor_client = |
| 276 | aura::client::GetCursorClient(root_window_); |
| 277 | if (cursor_client) |
| 278 | cursor_client->EnableMouseEvents(); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 279 | } |
| 280 | |
| 281 | is_on_animation_ = false; |
| 282 | |
| 283 | StartOrStopScrollIfNecessary(); |
| 284 | } |
| 285 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 286 | void FullscreenMagnifierController::OnWindowDestroying( |
David Tseng | 6b85bae | 2021-06-14 22:59:32 | [diff] [blame] | 287 | aura::Window* root_window) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 288 | if (root_window == root_window_) { |
| 289 | // There must be at least one root window because this controller is |
| 290 | // destroyed before the root windows get destroyed. |
| 291 | DCHECK(root_window); |
| 292 | |
| 293 | aura::Window* target_root_window = Shell::GetRootWindowForNewWindows(); |
| 294 | CHECK(target_root_window); |
| 295 | |
| 296 | // The destroyed root window must not be target. |
| 297 | CHECK_NE(target_root_window, root_window); |
| 298 | // Don't redraw the old root window as it's being destroyed. |
| 299 | SwitchTargetRootWindow(target_root_window, false); |
| 300 | point_of_interest_in_root_ = target_root_window->bounds().CenterPoint(); |
| 301 | } |
| 302 | } |
| 303 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 304 | void FullscreenMagnifierController::OnWindowBoundsChanged( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 305 | aura::Window* window, |
| 306 | const gfx::Rect& old_bounds, |
| 307 | const gfx::Rect& new_bounds, |
| 308 | ui::PropertyChangeReason reason) { |
| 309 | // TODO(yoshiki): implement here. crbug.com/230979 |
| 310 | } |
| 311 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 312 | void FullscreenMagnifierController::OnMouseEvent(ui::MouseEvent* event) { |
yoshiki@chromium.org | 022fd0a | 2013-02-06 17:54:08 | [diff] [blame] | 313 | aura::Window* target = static_cast<aura::Window*>(event->target()); |
ben@chromium.org | bf9cdb36 | 2013-10-25 19:22:45 | [diff] [blame] | 314 | aura::Window* current_root = target->GetRootWindow(); |
minch | 9e84ea0a | 2020-08-20 00:17:09 | [diff] [blame] | 315 | gfx::Point root_location = event->root_location(); |
yoshiki@chromium.org | f49b7a0 | 2012-08-06 23:04:24 | [diff] [blame] | 316 | |
minch | 9e84ea0a | 2020-08-20 00:17:09 | [diff] [blame] | 317 | if (event->type() == ui::ET_MOUSE_DRAGGED) { |
| 318 | auto* screen = display::Screen::GetScreen(); |
| 319 | const gfx::Point cursor_screen_location = screen->GetCursorScreenPoint(); |
| 320 | |
| 321 | auto* window = screen->GetWindowAtScreenPoint(cursor_screen_location); |
| 322 | // Update the |current_root| to be the one that contains the cursor |
| 323 | // currently. This will make sure the magnifier be activated in the display |
| 324 | // that contains the cursor while drag a window across displays. |
| 325 | current_root = |
| 326 | window ? window->GetRootWindow() : Shell::GetPrimaryRootWindow(); |
| 327 | root_location = cursor_screen_location; |
| 328 | wm::ConvertPointFromScreen(current_root, &root_location); |
| 329 | } |
| 330 | |
| 331 | if (current_root->bounds().Contains(root_location)) { |
oshima@chromium.org | 779fd981 | 2013-02-28 01:59:22 | [diff] [blame] | 332 | // This must be before |SwitchTargetRootWindow()|. |
hshi@chromium.org | 868a991 | 2014-02-12 23:19:05 | [diff] [blame] | 333 | if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) |
minch | 9e84ea0a | 2020-08-20 00:17:09 | [diff] [blame] | 334 | point_of_interest_in_root_ = root_location; |
yoshiki@chromium.org | 022fd0a | 2013-02-06 17:54:08 | [diff] [blame] | 335 | |
oshima@chromium.org | 779fd981 | 2013-02-28 01:59:22 | [diff] [blame] | 336 | if (current_root != root_window_) { |
| 337 | DCHECK(current_root); |
| 338 | SwitchTargetRootWindow(current_root, true); |
| 339 | } |
| 340 | |
Sammie Quon | 666db05c | 2020-08-05 23:47:40 | [diff] [blame] | 341 | const bool dragged_or_moved = event->type() == ui::ET_MOUSE_MOVED || |
| 342 | event->type() == ui::ET_MOUSE_DRAGGED; |
| 343 | if (IsMagnified() && dragged_or_moved && |
Dave Tapuska | 1621efb | 2020-04-29 15:39:54 | [diff] [blame] | 344 | event->pointer_details().pointer_type != ui::EventPointerType::kPen) { |
minch | 9e84ea0a | 2020-08-20 00:17:09 | [diff] [blame] | 345 | OnMouseMove(root_location); |
jdufault | 4bc1410 | 2016-10-14 18:35:53 | [diff] [blame] | 346 | } |
yoshiki@chromium.org | f49b7a0 | 2012-08-06 23:04:24 | [diff] [blame] | 347 | } |
yoshiki@chromium.org | 9613eacf | 2012-06-22 02:18:05 | [diff] [blame] | 348 | } |
| 349 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 350 | void FullscreenMagnifierController::OnScrollEvent(ui::ScrollEvent* event) { |
sadrul@chromium.org | 9cc04176 | 2012-11-15 18:10:23 | [diff] [blame] | 351 | if (event->IsAltDown() && event->IsControlDown()) { |
Katie Dektar | 5b0879c | 2018-01-12 22:20:12 | [diff] [blame] | 352 | if (event->type() == ui::ET_SCROLL_FLING_START) { |
| 353 | event->StopPropagation(); |
| 354 | return; |
| 355 | } else if (event->type() == ui::ET_SCROLL_FLING_CANCEL) { |
| 356 | float scale = GetScale(); |
| 357 | // Jump back to exactly 1.0 if we are just a tiny bit zoomed in. |
| 358 | // TODO(katie): These events are not fired after every scroll, which means |
| 359 | // we don't always jump back to 1.0. Look into why they are missing. |
| 360 | if (scale < kMinMagnifiedScaleThreshold) { |
| 361 | scale = kNonMagnifiedScale; |
| 362 | SetScale(scale, true); |
| 363 | } |
sadrul@chromium.org | 6f34b483 | 2012-12-14 16:18:08 | [diff] [blame] | 364 | event->StopPropagation(); |
| 365 | return; |
sadrul@chromium.org | 9cc04176 | 2012-11-15 18:10:23 | [diff] [blame] | 366 | } |
| 367 | |
| 368 | if (event->type() == ui::ET_SCROLL) { |
Mike Wasserman | 15f20f4 | 2019-01-08 01:27:04 | [diff] [blame] | 369 | SetScale(magnifier_utils::GetScaleFromScroll( |
Ahmed Fakhry | ba41da59 | 2018-03-01 18:10:40 | [diff] [blame] | 370 | event->y_offset() * kScrollScaleChangeFactor, GetScale(), |
| 371 | kMaxMagnifiedScale, kNonMagnifiedScale), |
| 372 | false /* animate */); |
sadrul@chromium.org | 6f34b483 | 2012-12-14 16:18:08 | [diff] [blame] | 373 | event->StopPropagation(); |
| 374 | return; |
sadrul@chromium.org | 9cc04176 | 2012-11-15 18:10:23 | [diff] [blame] | 375 | } |
| 376 | } |
sadrul@chromium.org | f9f017c | 2012-11-12 19:05:17 | [diff] [blame] | 377 | } |
| 378 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 379 | void FullscreenMagnifierController::OnTouchEvent(ui::TouchEvent* event) { |
yoshiki@chromium.org | 0f05dfd6 | 2013-01-21 16:52:07 | [diff] [blame] | 380 | aura::Window* target = static_cast<aura::Window*>(event->target()); |
ben@chromium.org | bf9cdb36 | 2013-10-25 19:22:45 | [diff] [blame] | 381 | aura::Window* current_root = target->GetRootWindow(); |
Yuki Awano | f598b73 | 2018-02-28 05:58:48 | [diff] [blame] | 382 | |
| 383 | gfx::Rect root_bounds = current_root->bounds(); |
| 384 | if (!root_bounds.Contains(event->root_location())) |
| 385 | return; |
| 386 | |
| 387 | point_of_interest_in_root_ = event->root_location(); |
| 388 | |
| 389 | if (current_root != root_window_) |
| 390 | SwitchTargetRootWindow(current_root, true); |
yoshiki@chromium.org | 0f05dfd6 | 2013-01-21 16:52:07 | [diff] [blame] | 391 | } |
| 392 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 393 | ui::EventDispatchDetails FullscreenMagnifierController::RewriteEvent( |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 394 | const ui::Event& event, |
Kevin Schoedel | 8c1f077 | 2019-02-13 19:48:47 | [diff] [blame] | 395 | const Continuation continuation) { |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 396 | if (!IsEnabled()) |
Kevin Schoedel | 8c1f077 | 2019-02-13 19:48:47 | [diff] [blame] | 397 | return SendEvent(continuation, &event); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 398 | |
| 399 | if (!event.IsTouchEvent()) |
Kevin Schoedel | 8c1f077 | 2019-02-13 19:48:47 | [diff] [blame] | 400 | return SendEvent(continuation, &event); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 401 | |
| 402 | const ui::TouchEvent* touch_event = event.AsTouchEvent(); |
| 403 | |
| 404 | if (touch_event->type() == ui::ET_TOUCH_PRESSED) { |
| 405 | touch_points_++; |
| 406 | press_event_map_[touch_event->pointer_details().id] = |
| 407 | std::make_unique<ui::TouchEvent>(*touch_event); |
Angela Czubak | 27e3d3f | 2022-11-15 16:24:15 | [diff] [blame] | 408 | } else if (touch_event->type() == ui::ET_TOUCH_RELEASED || |
| 409 | touch_event->type() == ui::ET_TOUCH_CANCELLED) { |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 410 | touch_points_--; |
| 411 | press_event_map_.erase(touch_event->pointer_details().id); |
| 412 | } |
| 413 | |
| 414 | ui::TouchEvent touch_event_copy = *touch_event; |
| 415 | if (gesture_provider_->OnTouchEvent(&touch_event_copy)) { |
| 416 | gesture_provider_->OnTouchEventAck( |
| 417 | touch_event_copy.unique_event_id(), false /* event_consumed */, |
Jihwan Marc Kim | 0bdf0ca | 2020-11-05 23:35:26 | [diff] [blame] | 418 | false /* is_source_touch_event_set_blocking */); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 419 | } else { |
Kevin Schoedel | 8c1f077 | 2019-02-13 19:48:47 | [diff] [blame] | 420 | return DiscardEvent(continuation); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 421 | } |
| 422 | |
| 423 | // User can change zoom level with two fingers pinch and pan around with two |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 424 | // fingers scroll. Once FullscreenMagnifierController detects one of those two |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 425 | // gestures, it starts consuming all touch events with cancelling existing |
| 426 | // touches. If cancel_pressed_touches is set to true, ET_TOUCH_CANCELLED |
| 427 | // events are dispatched for existing touches after the next for-loop. |
| 428 | bool cancel_pressed_touches = ProcessGestures(); |
| 429 | |
| 430 | if (cancel_pressed_touches) { |
| 431 | DCHECK_EQ(2u, press_event_map_.size()); |
| 432 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 433 | // FullscreenMagnifierController starts consuming all touch events after it |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 434 | // cancells existing touches. |
| 435 | consume_touch_event_ = true; |
| 436 | |
Yuki Awano | de0bea2 | 2018-07-11 01:56:50 | [diff] [blame] | 437 | for (const auto& it : press_event_map_) { |
| 438 | ui::TouchEvent touch_cancel_event(ui::ET_TOUCH_CANCELLED, gfx::Point(), |
| 439 | touch_event->time_stamp(), |
| 440 | it.second->pointer_details()); |
| 441 | touch_cancel_event.set_location_f(it.second->location_f()); |
| 442 | touch_cancel_event.set_root_location_f(it.second->root_location_f()); |
David Padlipsky | 4687d1e1 | 2024-01-04 00:18:45 | [diff] [blame] | 443 | touch_cancel_event.SetFlags(it.second->flags()); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 444 | |
Yuki Awano | de0bea2 | 2018-07-11 01:56:50 | [diff] [blame] | 445 | // TouchExplorationController is watching event stream and managing its |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 446 | // internal state. If an event rewriter (FullscreenMagnifierController) |
David Tseng | 6b85bae | 2021-06-14 22:59:32 | [diff] [blame] | 447 | // rewrites event stream, the next event rewriter won't get the event, |
| 448 | // which makes TouchExplorationController confused. Send cancelled event |
| 449 | // for recorded touch events to the next event rewriter here instead of |
| 450 | // rewriting an event in the stream. |
Kevin Schoedel | 8c1f077 | 2019-02-13 19:48:47 | [diff] [blame] | 451 | ui::EventDispatchDetails details = |
| 452 | SendEvent(continuation, &touch_cancel_event); |
| 453 | if (details.dispatcher_destroyed || details.target_destroyed) |
| 454 | return details; |
Yuki Awano | de0bea2 | 2018-07-11 01:56:50 | [diff] [blame] | 455 | } |
| 456 | press_event_map_.clear(); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 457 | } |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 458 | bool discard = consume_touch_event_; |
| 459 | |
| 460 | // Reset state once no point is touched on the screen. |
| 461 | if (touch_points_ == 0) { |
| 462 | consume_touch_event_ = false; |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 463 | |
| 464 | // Jump back to exactly 1.0 if we are just a tiny bit zoomed in. |
| 465 | if (scale_ < kMinMagnifiedScaleThreshold) { |
| 466 | SetScale(kNonMagnifiedScale, true /* animate */); |
Yuki Awano | 985417b8 | 2018-04-23 03:00:36 | [diff] [blame] | 467 | } else { |
| 468 | // Store current magnifier scale in pref. We don't need to call this if we |
| 469 | // call SetScale (the above case) as SetScale does this. |
| 470 | Shell::Get()->accessibility_delegate()->SaveScreenMagnifierScale(scale_); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 471 | } |
| 472 | } |
| 473 | |
| 474 | if (discard) |
Kevin Schoedel | 8c1f077 | 2019-02-13 19:48:47 | [diff] [blame] | 475 | return DiscardEvent(continuation); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 476 | |
Kevin Schoedel | 8c1f077 | 2019-02-13 19:48:47 | [diff] [blame] | 477 | return SendEvent(continuation, &event); |
Yuki Awano | c6f2822 | 2018-02-01 10:49:08 | [diff] [blame] | 478 | } |
| 479 | |
Mitsuru Oshima | 0ccf050 | 2022-02-08 19:22:21 | [diff] [blame] | 480 | const std::string& FullscreenMagnifierController::GetName() const { |
| 481 | static const std::string name("FullscreenMagnifierController"); |
| 482 | return name; |
| 483 | } |
| 484 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 485 | bool FullscreenMagnifierController::Redraw( |
danakj | fae0c84 | 2020-09-23 15:50:04 | [diff] [blame] | 486 | const gfx::PointF& position_in_physical_pixels, |
| 487 | float scale, |
| 488 | bool animate) { |
| 489 | gfx::PointF position = |
| 490 | gfx::ConvertPointToDips(position_in_physical_pixels, |
| 491 | root_window_->layer()->device_scale_factor()); |
| 492 | return RedrawDIP(position, scale, animate ? kDefaultAnimationDurationInMs : 0, |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 493 | kDefaultAnimationTweenType); |
| 494 | } |
yoshiki@chromium.org | 9613eacf | 2012-06-22 02:18:05 | [diff] [blame] | 495 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 496 | bool FullscreenMagnifierController::RedrawDIP( |
David Tseng | 6b85bae | 2021-06-14 22:59:32 | [diff] [blame] | 497 | const gfx::PointF& position_in_dip, |
| 498 | float scale, |
| 499 | int duration_in_ms, |
| 500 | gfx::Tween::Type tween_type) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 501 | DCHECK(root_window_); |
| 502 | |
| 503 | float x = position_in_dip.x(); |
| 504 | float y = position_in_dip.y(); |
| 505 | |
| 506 | ValidateScale(&scale); |
| 507 | |
| 508 | if (x < 0) |
| 509 | x = 0; |
| 510 | if (y < 0) |
| 511 | y = 0; |
| 512 | |
| 513 | const gfx::Size host_size_in_dip = GetHostSizeDIP(); |
| 514 | const gfx::SizeF window_size_in_dip = GetWindowRectDIP(scale).size(); |
| 515 | float max_x = host_size_in_dip.width() - window_size_in_dip.width(); |
| 516 | float max_y = host_size_in_dip.height() - window_size_in_dip.height(); |
| 517 | if (x > max_x) |
| 518 | x = max_x; |
| 519 | if (y > max_y) |
| 520 | y = max_y; |
| 521 | |
| 522 | // Does nothing if both the origin and the scale are not changed. |
Katie Dektar | 4c69402c | 2022-12-22 18:37:48 | [diff] [blame] | 523 | // Cast origin points back to int, as viewport can only be integer values. |
| 524 | if (static_cast<int>(origin_.x()) == static_cast<int>(x) && |
| 525 | static_cast<int>(origin_.y()) == static_cast<int>(y) && scale == scale_) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 526 | return false; |
| 527 | } |
| 528 | |
| 529 | origin_.set_x(x); |
| 530 | origin_.set_y(y); |
| 531 | scale_ = scale; |
| 532 | |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 533 | const ui::LayerAnimator::PreemptionStrategy strategy = |
| 534 | ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET; |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 535 | const base::TimeDelta duration = base::Milliseconds(duration_in_ms); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 536 | |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 537 | ui::ScopedLayerAnimationSettings root_layer_settings( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 538 | root_window_->layer()->GetAnimator()); |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 539 | root_layer_settings.AddObserver(this); |
| 540 | root_layer_settings.SetPreemptionStrategy(strategy); |
| 541 | root_layer_settings.SetTweenType(tween_type); |
| 542 | root_layer_settings.SetTransitionDuration(duration); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 543 | |
| 544 | display::Display display = |
| 545 | display::Screen::GetScreen()->GetDisplayNearestWindow(root_window_); |
| 546 | std::unique_ptr<RootWindowTransformer> transformer( |
Michael Spang | 2a4b44cc | 2019-11-05 21:28:34 | [diff] [blame] | 547 | CreateRootWindowTransformerForDisplay(display)); |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 548 | |
Su Hong Koo | d22ceb1 | 2020-04-06 16:44:07 | [diff] [blame] | 549 | // Inverse the transformation on the keyboard container and display |
| 550 | // identification highlight so the keyboard will remain zoomed out and the |
| 551 | // highlight will render around the edges of the display. Apply the same |
| 552 | // animation settings to it. Note: if |scale_| is 1.0f, the transform matrix |
| 553 | // will be an identity matrix. Applying the inverse of an identity matrix will |
| 554 | // not change the transformation. |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 555 | // TODO(spqchan): Find a way to sync the layer animations together. |
Su Hong Koo | d22ceb1 | 2020-04-06 16:44:07 | [diff] [blame] | 556 | gfx::Transform inverse_transform; |
| 557 | if (GetMagnifierTransform().GetInverse(&inverse_transform)) { |
| 558 | std::vector<aura::Window*> undo_transform_windows = { |
| 559 | root_window_->GetChildById(kShellWindowId_ImeWindowParentContainer)}; |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 560 | |
Su Hong Koo | d22ceb1 | 2020-04-06 16:44:07 | [diff] [blame] | 561 | aura::Window* display_identification_highlight = |
Sammie Quon | d694bae4 | 2020-07-30 20:21:26 | [diff] [blame] | 562 | root_window_->GetChildById(kShellWindowId_ScreenAnimationContainer) |
Su Hong Koo | d22ceb1 | 2020-04-06 16:44:07 | [diff] [blame] | 563 | ->GetChildById(kShellWindowId_DisplayIdentificationHighlightWindow); |
| 564 | |
| 565 | if (display_identification_highlight) |
| 566 | undo_transform_windows.push_back(display_identification_highlight); |
| 567 | |
| 568 | for (auto* window : undo_transform_windows) { |
| 569 | ui::ScopedLayerAnimationSettings layer_settings( |
| 570 | window->layer()->GetAnimator()); |
| 571 | layer_settings.SetPreemptionStrategy(strategy); |
| 572 | layer_settings.SetTweenType(tween_type); |
| 573 | layer_settings.SetTransitionDuration(duration); |
| 574 | window->SetTransform(inverse_transform); |
| 575 | } |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 576 | } |
| 577 | |
Josiah K | 6db1587b | 2021-04-06 05:43:44 | [diff] [blame] | 578 | if (!magnifier_debug_draw_rect_) { |
| 579 | RootWindowController::ForWindow(root_window_) |
| 580 | ->ash_host() |
| 581 | ->SetRootWindowTransformer(std::move(transformer)); |
| 582 | } |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 583 | |
| 584 | if (duration_in_ms > 0) |
| 585 | is_on_animation_ = true; |
| 586 | |
Josiah K | b3474bd3 | 2020-10-23 21:44:58 | [diff] [blame] | 587 | Shell::Get()->accessibility_controller()->MagnifierBoundsChanged( |
| 588 | GetViewportRect()); |
| 589 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 590 | return true; |
| 591 | } |
| 592 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 593 | void FullscreenMagnifierController::StartOrStopScrollIfNecessary() { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 594 | // This value controls the scrolling speed. |
| 595 | const int kMoveOffset = 40; |
| 596 | if (is_on_animation_) { |
| 597 | if (scroll_direction_ == SCROLL_NONE) |
| 598 | root_window_->layer()->GetAnimator()->StopAnimating(); |
| 599 | return; |
| 600 | } |
| 601 | |
| 602 | gfx::PointF new_origin = origin_; |
| 603 | switch (scroll_direction_) { |
| 604 | case SCROLL_NONE: |
| 605 | // No need to take action. |
| 606 | return; |
| 607 | case SCROLL_LEFT: |
| 608 | new_origin.Offset(-kMoveOffset, 0); |
| 609 | break; |
| 610 | case SCROLL_RIGHT: |
| 611 | new_origin.Offset(kMoveOffset, 0); |
| 612 | break; |
| 613 | case SCROLL_UP: |
| 614 | new_origin.Offset(0, -kMoveOffset); |
| 615 | break; |
| 616 | case SCROLL_DOWN: |
| 617 | new_origin.Offset(0, kMoveOffset); |
| 618 | break; |
| 619 | } |
| 620 | RedrawDIP(new_origin, scale_, kDefaultAnimationDurationInMs, |
| 621 | kDefaultAnimationTweenType); |
| 622 | } |
| 623 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 624 | void FullscreenMagnifierController::RedrawKeepingMousePosition( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 625 | float scale, |
| 626 | bool animate, |
| 627 | bool ignore_mouse_change) { |
| 628 | gfx::Point mouse_in_root = point_of_interest_in_root_; |
| 629 | // mouse_in_root is invalid value when the cursor is hidden. |
| 630 | if (!root_window_->bounds().Contains(mouse_in_root)) |
| 631 | mouse_in_root = root_window_->bounds().CenterPoint(); |
| 632 | |
| 633 | const gfx::PointF origin = gfx::PointF( |
| 634 | mouse_in_root.x() - (scale_ / scale) * (mouse_in_root.x() - origin_.x()), |
| 635 | mouse_in_root.y() - (scale_ / scale) * (mouse_in_root.y() - origin_.y())); |
| 636 | bool changed = |
| 637 | RedrawDIP(origin, scale, animate ? kDefaultAnimationDurationInMs : 0, |
| 638 | kDefaultAnimationTweenType); |
| 639 | if (!ignore_mouse_change && changed) |
| 640 | AfterAnimationMoveCursorTo(mouse_in_root); |
| 641 | } |
| 642 | |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 643 | void FullscreenMagnifierController::OnMouseMove( |
| 644 | const gfx::Point& location_in_dip) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 645 | DCHECK(root_window_); |
| 646 | |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 647 | gfx::Point center_point_in_dip(location_in_dip); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 648 | int margin = kCursorPanningMargin / scale_; // No need to consider DPI. |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 649 | |
Josiah K | 600a96c | 2021-02-13 01:21:04 | [diff] [blame] | 650 | // Edge mouse following mode. |
Josiah K | 600a96c | 2021-02-13 01:21:04 | [diff] [blame] | 651 | int x_margin = margin; |
| 652 | int y_margin = margin; |
| 653 | |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 654 | if (mouse_following_mode_ == MagnifierMouseFollowingMode::kCentered || |
| 655 | mouse_following_mode_ == MagnifierMouseFollowingMode::kContinuous) { |
Josiah K | 600a96c | 2021-02-13 01:21:04 | [diff] [blame] | 656 | const gfx::Rect window_rect = GetViewportRect(); |
| 657 | x_margin = window_rect.width() / 2; |
| 658 | y_margin = window_rect.height() / 2; |
| 659 | } |
| 660 | |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 661 | if (mouse_following_mode_ == MagnifierMouseFollowingMode::kContinuous) { |
| 662 | // Continuous mouse panning mode is similar to centered mouse panning mode, |
| 663 | // in that the screen moves behind the cursor when the user moves the mouse. |
| 664 | // Unlike centered mouse panning mode however, the cursor is not centered in |
| 665 | // the middle of the screen, but is able to freely move around, with the |
| 666 | // screen moving in the opposite direction; for example, when the cursor |
| 667 | // approaches the top left corner, the screen also scrolls behind it, so |
| 668 | // that more of the top left portion of the screen is visible, until the |
| 669 | // cursor reaches and meets up with the corner of the screen. This logic |
| 670 | // calculates where the center point of the magnified region should be, |
| 671 | // such that where the cursor is located in the magnified region corresponds |
| 672 | // in proportion to where the cursor is located on the screen overall. |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 673 | |
| 674 | // Screen size. |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 675 | const gfx::Size host_size_in_dip = GetHostSizeDIP(); |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 676 | |
| 677 | // Mouse position. |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 678 | const float x = location_in_dip.x(); |
| 679 | const float y = location_in_dip.y(); |
| 680 | |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 681 | // Viewport dimensions for calculation, increased by variable padding: |
| 682 | // The cursor can never reach the bottom or right of the screen, it's always |
| 683 | // at least one DIP away so that you can see it. (Note the cursor can reach |
| 684 | // the top left at (0, 0)). Calculate the viewport size, adding some scaled |
| 685 | // viewport padding as we move down and right so that the padding 0 in the |
| 686 | // top/left and greater in the bottom right to account for the cursor not |
| 687 | // being able to access the bottom corner. |
| 688 | const float height = |
| 689 | host_size_in_dip.height() / scale_ + 4 * y / host_size_in_dip.height(); |
| 690 | const float width = |
| 691 | host_size_in_dip.width() / scale_ + 4 * x / host_size_in_dip.width(); |
| 692 | |
| 693 | // The viewport center point is the mouse center point, minus the scaled |
| 694 | // mouse center point to get to the viewport left/top edge, plus half |
| 695 | // the viewport size. |
| 696 | // In the example below, the host size is 12 units in width, the |
| 697 | // mouse point x is at 7, and the viewport width is 3 (scale is 4.0). |
| 698 | // The center_point_in_dip_x should be 6, with some integer rounding. |
| 699 | // 6 = int(7 - (7 / 4.0) + (3 / 2.0)) |
| 700 | // ____________ |
| 701 | // | | host |
| 702 | // | ___ | |
| 703 | // | | *| | <-- mouse x = 7, viewport width = 3 |
| 704 | // | |___| | |
| 705 | // |____________| |
| 706 | // 012345678901 <-- Indexes |
| 707 | const int center_point_in_dip_x = x - x / scale_ + width / 2.0; |
| 708 | const int center_point_in_dip_y = y - y / scale_ + height / 2.0; |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 709 | center_point_in_dip = {center_point_in_dip_x, center_point_in_dip_y}; |
| 710 | } |
| 711 | |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 712 | // Reduce the bottom margin if the keyboard is visible. |
Steven Bennetts | 137a18e | 2018-10-10 19:13:45 | [diff] [blame] | 713 | bool reduce_bottom_margin = |
Darren Shen | cb250844 | 2019-07-03 21:48:23 | [diff] [blame] | 714 | keyboard::KeyboardUIController::Get()->IsKeyboardVisible(); |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 715 | |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 716 | MoveMagnifierWindowFollowPoint(center_point_in_dip, x_margin, y_margin, |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 717 | reduce_bottom_margin); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 718 | } |
| 719 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 720 | void FullscreenMagnifierController::AfterAnimationMoveCursorTo( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 721 | const gfx::Point& location) { |
| 722 | DCHECK(root_window_); |
| 723 | |
| 724 | aura::client::CursorClient* cursor_client = |
| 725 | aura::client::GetCursorClient(root_window_); |
| 726 | if (cursor_client) { |
| 727 | // When cursor is invisible, do not move or show the cursor after the |
| 728 | // animation. |
| 729 | if (!cursor_client->IsCursorVisible()) |
| 730 | return; |
| 731 | cursor_client->DisableMouseEvents(); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 732 | } |
| 733 | move_cursor_after_animation_ = true; |
| 734 | position_after_animation_ = location; |
| 735 | } |
| 736 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 737 | bool FullscreenMagnifierController::IsMagnified() const { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 738 | return scale_ >= kMinMagnifiedScaleThreshold; |
| 739 | } |
| 740 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 741 | gfx::RectF FullscreenMagnifierController::GetWindowRectDIP(float scale) const { |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 742 | const gfx::Size size_in_dip = GetHostSizeDIP(); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 743 | const float width = size_in_dip.width() / scale; |
| 744 | const float height = size_in_dip.height() / scale; |
| 745 | |
| 746 | return gfx::RectF(origin_.x(), origin_.y(), width, height); |
| 747 | } |
| 748 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 749 | gfx::Size FullscreenMagnifierController::GetHostSizeDIP() const { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 750 | return root_window_->bounds().size(); |
| 751 | } |
| 752 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 753 | void FullscreenMagnifierController::ValidateScale(float* scale) { |
Ho Cheung | 27c3993 | 2023-04-13 16:30:09 | [diff] [blame] | 754 | *scale = std::clamp(*scale, kNonMagnifiedScale, kMaxMagnifiedScale); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 755 | DCHECK(kNonMagnifiedScale <= *scale && *scale <= kMaxMagnifiedScale); |
| 756 | } |
| 757 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 758 | bool FullscreenMagnifierController::ProcessGestures() { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 759 | bool cancel_pressed_touches = false; |
| 760 | |
| 761 | std::vector<std::unique_ptr<ui::GestureEvent>> gestures = |
| 762 | gesture_provider_->GetAndResetPendingGestures(); |
| 763 | for (const auto& gesture : gestures) { |
| 764 | const ui::GestureEventDetails& details = gesture->details(); |
| 765 | |
| 766 | if (details.touch_points() != 2) |
| 767 | continue; |
| 768 | |
| 769 | if (gesture->type() == ui::ET_GESTURE_PINCH_BEGIN) { |
| 770 | original_scale_ = scale_; |
| 771 | |
| 772 | // Start consuming touch events with cancelling existing touches. |
| 773 | if (!consume_touch_event_) |
| 774 | cancel_pressed_touches = true; |
| 775 | } else if (gesture->type() == ui::ET_GESTURE_PINCH_UPDATE) { |
Yuki Awano | 23dcb19 | 2018-08-10 04:58:10 | [diff] [blame] | 776 | float scale = GetScale() * details.scale(); |
| 777 | ValidateScale(&scale); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 778 | |
Yuki Awano | 23dcb19 | 2018-08-10 04:58:10 | [diff] [blame] | 779 | // |details.bounding_box().CenterPoint()| return center of touch points |
| 780 | // of gesture in non-dip screen coordinate. |
| 781 | gfx::PointF gesture_center = |
| 782 | gfx::PointF(details.bounding_box().CenterPoint()); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 783 | |
Yuki Awano | 23dcb19 | 2018-08-10 04:58:10 | [diff] [blame] | 784 | // Root transform does dip scaling, screen magnification scaling and |
| 785 | // translation. Apply inverse transform to convert non-dip screen |
| 786 | // coordinate to dip logical coordinate. |
Xianzhu Wang | 7afe9ea | 2022-10-01 03:14:15 | [diff] [blame] | 787 | gesture_center = |
| 788 | root_window_->GetHost()->GetInverseRootTransform().MapPoint( |
| 789 | gesture_center); |
Yuki Awano | edafb74 | 2018-05-18 00:47:41 | [diff] [blame] | 790 | |
Yuki Awano | 23dcb19 | 2018-08-10 04:58:10 | [diff] [blame] | 791 | // Calcualte new origin to keep the distance between |gesture_center| |
| 792 | // and |origin| same in screen coordinate. This means the following |
| 793 | // equation. |
| 794 | // (gesture_center.x - origin_.x) * scale_ = |
| 795 | // (gesture_center.x - new_origin.x) * scale |
| 796 | // If you solve it for |new_origin|, you will get the following formula. |
| 797 | const gfx::PointF origin = gfx::PointF( |
| 798 | gesture_center.x() - |
| 799 | (scale_ / scale) * (gesture_center.x() - origin_.x()), |
| 800 | gesture_center.y() - |
| 801 | (scale_ / scale) * (gesture_center.y() - origin_.y())); |
Yuki Awano | edafb74 | 2018-05-18 00:47:41 | [diff] [blame] | 802 | |
Yuki Awano | 23dcb19 | 2018-08-10 04:58:10 | [diff] [blame] | 803 | RedrawDIP(origin, scale, 0, kDefaultAnimationTweenType); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 804 | } else if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN) { |
| 805 | original_origin_ = origin_; |
| 806 | |
| 807 | // Start consuming all touch events with cancelling existing touches. |
| 808 | if (!consume_touch_event_) |
| 809 | cancel_pressed_touches = true; |
| 810 | } else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { |
Ahmed Fakhry | 98db19f3 | 2020-04-10 23:02:57 | [diff] [blame] | 811 | // The scroll offsets are apparently in pixels and does not take into |
| 812 | // account the display rotation. Convert back to dip by applying the |
| 813 | // inverse transform of the rotation (these are offsets, so we don't care |
| 814 | // about scale or translation. We'll take care of the scale below). |
| 815 | // https://crbug.com/867537. |
| 816 | const auto display = |
| 817 | display::Screen::GetScreen()->GetDisplayNearestWindow(root_window_); |
| 818 | gfx::Transform rotation_transform; |
| 819 | rotation_transform.Rotate(display.PanelRotationAsDegree()); |
Xianzhu Wang | 00be4cb | 2022-11-01 16:20:18 | [diff] [blame] | 820 | gfx::Transform rotation_inverse_transform = |
| 821 | rotation_transform.GetCheckedInverse(); |
Xianzhu Wang | 7afe9ea | 2022-10-01 03:14:15 | [diff] [blame] | 822 | gfx::PointF scroll = rotation_inverse_transform.MapPoint( |
| 823 | gfx::PointF(details.scroll_x(), details.scroll_y())); |
Ahmed Fakhry | 98db19f3 | 2020-04-10 23:02:57 | [diff] [blame] | 824 | |
Yuki Awano | 23dcb19 | 2018-08-10 04:58:10 | [diff] [blame] | 825 | // Divide by scale to keep scroll speed same at any scale. |
Ahmed Fakhry | 98db19f3 | 2020-04-10 23:02:57 | [diff] [blame] | 826 | float new_x = origin_.x() + (-scroll.x() / scale_); |
| 827 | float new_y = origin_.y() + (-scroll.y() / scale_); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 828 | |
Yuki Awano | 23dcb19 | 2018-08-10 04:58:10 | [diff] [blame] | 829 | RedrawDIP(gfx::PointF(new_x, new_y), scale_, 0, |
| 830 | kDefaultAnimationTweenType); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 831 | } |
| 832 | } |
| 833 | |
| 834 | return cancel_pressed_touches; |
| 835 | } |
| 836 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 837 | void FullscreenMagnifierController::MoveMagnifierWindowFollowPoint( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 838 | const gfx::Point& point, |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 839 | int x_margin, |
| 840 | int y_margin, |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 841 | bool reduce_bottom_margin) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 842 | DCHECK(root_window_); |
| 843 | bool start_zoom = false; |
| 844 | |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 845 | // Current position. |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 846 | const gfx::Rect window_rect = GetViewportRect(); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 847 | const int top = window_rect.y(); |
| 848 | const int bottom = window_rect.bottom(); |
| 849 | |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 850 | int x_diff = 0; |
| 851 | if (point.x() < window_rect.x() + x_margin) { |
| 852 | // Panning left. |
| 853 | x_diff = point.x() - (window_rect.x() + x_margin); |
| 854 | start_zoom = true; |
| 855 | } else if (point.x() > window_rect.right() - x_margin) { |
| 856 | // Panning right. |
| 857 | x_diff = point.x() - (window_rect.right() - x_margin); |
| 858 | start_zoom = true; |
| 859 | } |
| 860 | int x = window_rect.x() + x_diff; |
| 861 | |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 862 | // If |reduce_bottom_margin| is true, use kKeyboardBottomPanningMargin instead |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 863 | // of |y_margin|. This is to prevent the magnifier from panning when |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 864 | // the user is trying to interact with the bottom of the keyboard. |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 865 | const int bottom_panning_margin = |
| 866 | reduce_bottom_margin ? kKeyboardBottomPanningMargin / scale_ : y_margin; |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 867 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 868 | int y_diff = 0; |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 869 | if (point.y() < top + y_margin) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 870 | // Panning up. |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 871 | y_diff = point.y() - (top + y_margin); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 872 | start_zoom = true; |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 873 | } else if (bottom - bottom_panning_margin < point.y()) { |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 874 | // Panning down. |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 875 | const int bottom_target_margin = |
Katie Dektar | bc73492 | 2022-12-13 16:35:08 | [diff] [blame] | 876 | reduce_bottom_margin ? std::min(bottom_panning_margin, y_margin) |
| 877 | : y_margin; |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 878 | y_diff = point.y() - (bottom - bottom_target_margin); |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 879 | start_zoom = true; |
| 880 | } |
| 881 | int y = top + y_diff; |
| 882 | if (start_zoom && !is_on_animation_) { |
| 883 | bool ret = RedrawDIP(gfx::PointF(x, y), scale_, |
| 884 | 0, // No animation on panning. |
| 885 | kDefaultAnimationTweenType); |
| 886 | |
| 887 | if (ret) { |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 888 | // If the magnified region is moved, hides the mouse cursor and moves it, |
| 889 | // unless we're in continuous mode (in which case mouse position is |
| 890 | // good already). |
| 891 | if ((x_diff != 0 || y_diff != 0) && |
| 892 | mouse_following_mode_ != MagnifierMouseFollowingMode::kContinuous) { |
Katie Dektar | 4c69402c | 2022-12-22 18:37:48 | [diff] [blame] | 893 | MoveCursorTo(point); |
Josiah K | b77e9f6 | 2021-10-05 21:20:39 | [diff] [blame] | 894 | } |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 895 | } |
| 896 | } |
| 897 | } |
| 898 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 899 | void FullscreenMagnifierController::MoveMagnifierWindowCenterPoint( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 900 | const gfx::Point& point) { |
| 901 | DCHECK(root_window_); |
| 902 | |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 903 | gfx::Rect window_rect = GetViewportRect(); |
| 904 | |
| 905 | // Reduce the viewport bounds if the keyboard is up. |
Darren Shen | cb250844 | 2019-07-03 21:48:23 | [diff] [blame] | 906 | if (keyboard::KeyboardUIController::Get()->IsEnabled()) { |
| 907 | gfx::Rect keyboard_rect = keyboard::KeyboardUIController::Get() |
Darren Shen | b18c59f | 2018-07-03 01:31:24 | [diff] [blame] | 908 | ->GetKeyboardWindow() |
Darren Shen | 1a18c3f | 2018-06-14 14:05:53 | [diff] [blame] | 909 | ->GetBoundsInScreen(); |
Sarah Chan | 27ba1d8 | 2018-05-24 23:23:49 | [diff] [blame] | 910 | window_rect.set_height(window_rect.height() - |
| 911 | keyboard_rect.height() / scale_); |
| 912 | } |
| 913 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 914 | if (point == window_rect.CenterPoint()) |
| 915 | return; |
| 916 | |
| 917 | if (!is_on_animation_) { |
| 918 | // With animation on panning. |
| 919 | RedrawDIP( |
| 920 | gfx::PointF(window_rect.origin() + (point - window_rect.CenterPoint())), |
| 921 | scale_, kDefaultAnimationDurationInMs, kCenterCaretAnimationTweenType); |
| 922 | } |
| 923 | } |
| 924 | |
Josiah K | 1b783d2 | 2021-06-17 00:34:52 | [diff] [blame] | 925 | void FullscreenMagnifierController::MoveMagnifierWindowFollowRect( |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 926 | const gfx::Rect& rect) { |
| 927 | DCHECK(root_window_); |
| 928 | bool should_pan = false; |
| 929 | |
| 930 | const gfx::Rect viewport_rect = GetViewportRect(); |
| 931 | const int left = viewport_rect.x(); |
| 932 | const int right = viewport_rect.right(); |
| 933 | const gfx::Point rect_center = rect.CenterPoint(); |
| 934 | |
| 935 | int x = left; |
| 936 | if (rect.x() < left || right < rect.right()) { |
| 937 | // Panning horizontally. |
| 938 | x = rect_center.x() - viewport_rect.width() / 2; |
| 939 | should_pan = true; |
| 940 | } |
| 941 | |
| 942 | const int top = viewport_rect.y(); |
| 943 | const int bottom = viewport_rect.bottom(); |
| 944 | |
| 945 | int y = top; |
| 946 | if (rect.y() < top || bottom < rect.bottom()) { |
| 947 | // Panning vertically. |
| 948 | y = rect_center.y() - viewport_rect.height() / 2; |
| 949 | should_pan = true; |
| 950 | } |
| 951 | |
Josiah K | 3647bd30 | 2020-11-13 05:22:08 | [diff] [blame] | 952 | // If rect is too wide to fit in viewport, include as much as we can, starting |
| 953 | // with the left edge. |
| 954 | if (rect.width() > viewport_rect.width()) |
| 955 | x = rect.x() - magnifier_utils::kLeftEdgeContextPadding; |
| 956 | |
Qiang Xu | 2657abf | 2018-03-21 18:01:45 | [diff] [blame] | 957 | if (should_pan) { |
| 958 | if (is_on_animation_) { |
| 959 | root_window_->layer()->GetAnimator()->StopAnimating(); |
| 960 | is_on_animation_ = false; |
| 961 | } |
| 962 | RedrawDIP(gfx::PointF(x, y), scale_, |
| 963 | 0, // No animation on panning. |
| 964 | kDefaultAnimationTweenType); |
| 965 | } |
| 966 | } |
| 967 | |
Katie Dektar | 4c69402c | 2022-12-22 18:37:48 | [diff] [blame] | 968 | void FullscreenMagnifierController::MoveCursorTo( |
| 969 | const gfx::Point& root_location) { |
| 970 | aura::WindowTreeHost* host = root_window_->GetHost(); |
| 971 | host->MoveCursorToLocationInPixels(gfx::ToCeiledPoint( |
| 972 | host->GetRootTransform().MapPoint(gfx::PointF(root_location)))); |
| 973 | |
| 974 | if (cursor_moved_callback_for_testing_) { |
| 975 | cursor_moved_callback_for_testing_.Run(root_location); |
| 976 | } |
| 977 | } |
| 978 | |
yoshiki@chromium.org | c1c67017 | 2012-04-26 04:20:26 | [diff] [blame] | 979 | } // namespace ash |