[go: nahoru, domu]

blob: b05dc45c7648d73dfd8a06554e347e476a5ef86e [file] [log] [blame]
Avi Drissman3a215d1e2022-09-07 19:43:091// Copyright 2012 The Chromium Authors
yoshiki@chromium.orgc1c670172012-04-26 04:20:262// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Josiah K1b783d22021-06-17 00:34:525#include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
yoshiki@chromium.orgc1c670172012-04-26 04:20:266
Kevin Schoedel8c1f0772019-02-13 19:48:477#include <algorithm>
dchenga94547472016-04-08 08:41:118#include <memory>
dchengcbf0d9d2015-12-27 22:49:239#include <utility>
Qiang Xu2657abf2018-03-21 18:01:4510#include <vector>
dchengcbf0d9d2015-12-27 22:49:2311
Katie Dektar582ae102023-12-13 03:15:5412#include "ash/accessibility/accessibility_controller.h"
James Cook37b7d102017-10-06 04:35:1913#include "ash/accessibility/accessibility_delegate.h"
David Tseng00443a32021-03-24 22:24:1314#include "ash/accessibility/magnifier/magnifier_utils.h"
oshima@chromium.orgd2d18a12013-05-29 21:51:5715#include "ash/display/root_window_transformers.h"
oshima@chromium.orgf5c9dbc2014-04-11 08:13:4516#include "ash/host/ash_window_tree_host.h"
17#include "ash/host/root_window_transformer.h"
Darren Shencb2508442019-07-03 21:48:2318#include "ash/keyboard/ui/keyboard_ui_controller.h"
Sarah Chan27ba1d82018-05-24 23:23:4919#include "ash/public/cpp/shell_window_ids.h"
oshima@chromium.orgf5c9dbc2014-04-11 08:13:4520#include "ash/root_window_controller.h"
yoshiki@chromium.orgc1c670172012-04-26 04:20:2621#include "ash/shell.h"
Josiah K6db1587b2021-04-06 05:43:4422#include "ui/accessibility/accessibility_switches.h"
mazda@chromium.org60c26202012-10-12 18:55:2323#include "ui/aura/client/cursor_client.h"
yoshiki@chromium.orgc1c670172012-04-26 04:20:2624#include "ui/aura/window.h"
ben@chromium.org7a60cd32014-03-20 20:54:5725#include "ui/aura/window_tree_host.h"
tfarina@chromium.org116302f2012-05-05 21:45:4126#include "ui/compositor/layer.h"
27#include "ui/compositor/scoped_layer_animation_settings.h"
oshima888290a2016-05-13 20:36:0628#include "ui/display/display.h"
oshimaf84b0da722016-04-27 19:47:1929#include "ui/display/screen.h"
sky@chromium.org86ccbd42013-09-18 18:11:5430#include "ui/events/event.h"
Ahmed Fakhrycbc68f02021-07-27 00:20:1431#include "ui/events/event_handler.h"
Yuki Awanoc6f28222018-02-01 10:49:0832#include "ui/events/gestures/gesture_provider_aura.h"
Sammie Quon666db05c2020-08-05 23:47:4033#include "ui/events/types/event_type.h"
danakjfae0c842020-09-23 15:50:0434#include "ui/gfx/geometry/dip_util.h"
Avi Drissmanfefc2f82014-12-22 19:25:2935#include "ui/gfx/geometry/point3_f.h"
36#include "ui/gfx/geometry/point_conversions.h"
Avi Drissmanfefc2f82014-12-22 19:25:2937#include "ui/gfx/geometry/rect_conversions.h"
jennyz91b6ed0c2014-10-23 21:52:4138#include "ui/wm/core/coordinate_conversion.h"
yoshiki@chromium.orgc1c670172012-04-26 04:20:2639
Qiang Xu2657abf2018-03-21 18:01:4540namespace ash {
41
yoshiki@chromium.orgc1c670172012-04-26 04:20:2642namespace {
43
Qiang Xu2657abf2018-03-21 18:01:4544constexpr float kMaxMagnifiedScale = 20.0f;
45constexpr float kMinMagnifiedScaleThreshold = 1.1f;
46constexpr float kNonMagnifiedScale = 1.0f;
yoshiki@chromium.org5d107022012-06-24 19:58:2547
Qiang Xu2657abf2018-03-21 18:01:4548constexpr float kInitialMagnifiedScale = 2.0f;
49constexpr float kScrollScaleChangeFactor = 0.00125f;
yoshiki@chromium.orgc1c670172012-04-26 04:20:2650
jennyzb894b6d2015-06-01 20:12:1851// Default animation parameters for redrawing the magnification window.
Qiang Xu2657abf2018-03-21 18:01:4552constexpr gfx::Tween::Type kDefaultAnimationTweenType = gfx::Tween::EASE_OUT;
53constexpr int kDefaultAnimationDurationInMs = 100;
yoshiki@chromium.orgecdc6d22013-01-11 09:53:0454
jennyzb894b6d2015-06-01 20:12:1855// Use linear transformation to make the magnifier window move smoothly
56// to center the focus when user types in a text input field.
Qiang Xu2657abf2018-03-21 18:01:4557constexpr gfx::Tween::Type kCenterCaretAnimationTweenType = gfx::Tween::LINEAR;
jennyzb894b6d2015-06-01 20:12:1858
Sarah Chan27ba1d82018-05-24 23:23:4959// Threshold of panning. If the cursor moves to within pixels (in DIP) of
jennyzb894b6d2015-06-01 20:12:1860// |kCursorPanningMargin| from the edge, the view-port moves.
Qiang Xu2657abf2018-03-21 18:01:4561constexpr int kCursorPanningMargin = 100;
jennyzb894b6d2015-06-01 20:12:1862
Sarah Chan27ba1d82018-05-24 23:23:4963// 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.
67constexpr int kKeyboardBottomPanningMargin = 10;
68
yoshiki@chromium.orgc1c670172012-04-26 04:20:2669} // namespace
70
Josiah K1b783d22021-06-17 00:34:5271class FullscreenMagnifierController::GestureProviderClient
Qiang Xu2657abf2018-03-21 18:01:4572 : public ui::GestureProviderAuraClient {
yoshiki@chromium.org9613eacf2012-06-22 02:18:0573 public:
Qiang Xu2657abf2018-03-21 18:01:4574 GestureProviderClient() = default;
Josiah Ke66f8642021-07-02 23:24:3275 GestureProviderClient(const GestureProviderClient&) = delete;
76 GestureProviderClient& operator=(const GestureProviderClient&) = delete;
Qiang Xu2657abf2018-03-21 18:01:4577 ~GestureProviderClient() override = default;
yoshiki@chromium.org9613eacf2012-06-22 02:18:0578
Qiang Xu2657abf2018-03-21 18:01:4579 // 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 K1b783d22021-06-17 00:34:5283 // FullscreenMagnifierController is interested only in pinch and scroll
Qiang Xu2657abf2018-03-21 18:01:4584 // 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());
jennyzb894b6d2015-06-01 20:12:1891 }
yoshiki@chromium.org9613eacf2012-06-22 02:18:0592};
93
Josiah K1b783d22021-06-17 00:34:5294FullscreenMagnifierController::FullscreenMagnifierController()
oshima@chromium.org3dff2ef2014-02-09 22:50:3995 : root_window_(Shell::GetPrimaryRootWindow()),
oshima@chromium.org3dff2ef2014-02-09 22:50:3996 scale_(kNonMagnifiedScale),
Qiang Xu2657abf2018-03-21 18:01:4597 original_scale_(kNonMagnifiedScale) {
Katie Dektar2f938bd2022-10-21 17:47:1298 Shell::Get()->AddAccessibilityEventHandler(
99 this,
100 AccessibilityEventHandlerManager::HandlerType::kFullscreenMagnifier);
oshima@chromium.org779fd9812013-02-28 01:59:22101 root_window_->AddObserver(this);
Yuki Awanod6b10e242018-02-07 01:16:00102 root_window_->GetHost()->GetEventSource()->AddEventRewriter(this);
Yuki Awanoc6f28222018-02-01 10:49:08103
Yuki Awanof598b732018-02-28 05:58:48104 point_of_interest_in_root_ = root_window_->bounds().CenterPoint();
Yuki Awanoc6f28222018-02-01 10:49:08105
106 gesture_provider_client_ = std::make_unique<GestureProviderClient>();
107 gesture_provider_ = std::make_unique<ui::GestureProviderAura>(
108 this, gesture_provider_client_.get());
Josiah K6db1587b2021-04-06 05:43:44109
110 magnifier_debug_draw_rect_ = ::switches::IsMagnifierDebugDrawRectEnabled();
yoshiki@chromium.orgc1c670172012-04-26 04:20:26111}
112
Josiah K1b783d22021-06-17 00:34:52113FullscreenMagnifierController::~FullscreenMagnifierController() {
Yuki Awanod6b10e242018-02-07 01:16:00114 root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
oshima@chromium.org779fd9812013-02-28 01:59:22115 root_window_->RemoveObserver(this);
116
Katie Dektar2f938bd2022-10-21 17:47:12117 Shell::Get()->RemoveAccessibilityEventHandler(this);
yoshiki@chromium.org9613eacf2012-06-22 02:18:05118}
119
Josiah K1b783d22021-06-17 00:34:52120void FullscreenMagnifierController::SetEnabled(bool enabled) {
yoshiki@chromium.org5d107022012-06-24 19:58:25121 if (enabled) {
skycb4be5b2017-04-06 17:52:45122 Shell* shell = Shell::Get();
msw3ce3e0e2016-06-29 01:54:47123 float scale =
sky07a24d42017-03-09 23:57:30124 shell->accessibility_delegate()->GetSavedScreenMagnifierScale();
yoshiki@chromium.orgfd199882012-10-16 10:49:46125 if (scale <= 0.0f)
yoshiki@chromium.orgb7933a142012-12-06 22:35:12126 scale = kInitialMagnifiedScale;
yoshiki@chromium.orgde2d4ae2012-10-05 10:10:55127 ValidateScale(&scale);
yoshiki@chromium.orge2ba8c92012-12-06 08:42:02128
129 // Do nothing, if already enabled with same scale.
130 if (is_enabled_ && scale == scale_)
131 return;
132
yoshiki@chromium.org0ec58342013-04-24 07:39:36133 is_enabled_ = enabled;
weidongg2e1f4b72017-05-18 04:30:56134 RedrawKeepingMousePosition(scale, true, false);
sky07a24d42017-03-09 23:57:30135 shell->accessibility_delegate()->SaveScreenMagnifierScale(scale);
yoshiki@chromium.org5d107022012-06-24 19:58:25136 } else {
yoshiki@chromium.orge2ba8c92012-12-06 08:42:02137 // Do nothing, if already disabled.
138 if (!is_enabled_)
139 return;
140
weidongg2e1f4b72017-05-18 04:30:56141 RedrawKeepingMousePosition(kNonMagnifiedScale, true, false);
yoshiki@chromium.org5d107022012-06-24 19:58:25142 is_enabled_ = enabled;
143 }
Sarah Chan27ba1d82018-05-24 23:23:49144
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 Shencb2508442019-07-03 21:48:23148 auto config = keyboard::KeyboardUIController::Get()->keyboard_config();
Steven Bennetts42a4d4a2018-10-05 19:05:10149 config.overscroll_behavior =
Darren Shen8604a212019-06-07 00:41:38150 is_enabled_ ? keyboard::KeyboardOverscrollBehavior::kDisabled
151 : keyboard::KeyboardOverscrollBehavior::kDefault;
Darren Shencb2508442019-07-03 21:48:23152 keyboard::KeyboardUIController::Get()->UpdateKeyboardConfig(config);
yoshiki@chromium.org5d107022012-06-24 19:58:25153}
154
Josiah K1b783d22021-06-17 00:34:52155bool FullscreenMagnifierController::IsEnabled() const {
zork@chromium.org77f7c132012-11-15 06:52:54156 return is_enabled_;
157}
158
Josiah K1b783d22021-06-17 00:34:52159void FullscreenMagnifierController::SetKeepFocusCentered(
David Tseng6b85bae2021-06-14 22:59:32160 bool keep_focus_centered) {
jennyzb894b6d2015-06-01 20:12:18161 keep_focus_centered_ = keep_focus_centered;
162}
163
Josiah K1b783d22021-06-17 00:34:52164bool FullscreenMagnifierController::KeepFocusCentered() const {
jennyzb894b6d2015-06-01 20:12:18165 return keep_focus_centered_;
166}
167
Josiah K1b783d22021-06-17 00:34:52168void FullscreenMagnifierController::SetScale(float scale, bool animate) {
Qiang Xu2657abf2018-03-21 18:01:45169 if (!is_enabled_)
170 return;
yoshiki@chromium.org9613eacf2012-06-22 02:18:05171
Qiang Xu2657abf2018-03-21 18:01:45172 ValidateScale(&scale);
173 Shell::Get()->accessibility_delegate()->SaveScreenMagnifierScale(scale);
174 RedrawKeepingMousePosition(scale, animate, false);
175}
176
Josiah K1b783d22021-06-17 00:34:52177void FullscreenMagnifierController::StepToNextScaleValue(int delta_index) {
Mike Wasserman15f20f42019-01-08 01:27:04178 SetScale(magnifier_utils::GetNextMagnifierScaleValue(
Qiang Xu2657abf2018-03-21 18:01:45179 delta_index, GetScale(), kNonMagnifiedScale, kMaxMagnifiedScale),
180 true /* animate */);
181}
182
Josiah K1b783d22021-06-17 00:34:52183void FullscreenMagnifierController::MoveWindow(int x, int y, bool animate) {
Qiang Xu2657abf2018-03-21 18:01:45184 if (!is_enabled_)
185 return;
186
187 Redraw(gfx::PointF(x, y), scale_, animate);
188}
189
Josiah K1b783d22021-06-17 00:34:52190void FullscreenMagnifierController::MoveWindow(const gfx::Point& point,
David Tseng6b85bae2021-06-14 22:59:32191 bool animate) {
Qiang Xu2657abf2018-03-21 18:01:45192 if (!is_enabled_)
193 return;
194
195 Redraw(gfx::PointF(point), scale_, animate);
196}
197
Josiah K1b783d22021-06-17 00:34:52198gfx::Point FullscreenMagnifierController::GetWindowPosition() const {
Qiang Xu2657abf2018-03-21 18:01:45199 return gfx::ToFlooredPoint(origin_);
200}
201
Josiah K1b783d22021-06-17 00:34:52202void FullscreenMagnifierController::SetScrollDirection(
David Tseng6b85bae2021-06-14 22:59:32203 ScrollDirection direction) {
Qiang Xu2657abf2018-03-21 18:01:45204 scroll_direction_ = direction;
205 StartOrStopScrollIfNecessary();
206}
207
Josiah K1b783d22021-06-17 00:34:52208gfx::Rect FullscreenMagnifierController::GetViewportRect() const {
Qiang Xu2657abf2018-03-21 18:01:45209 return gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
210}
211
Josiah K1b783d22021-06-17 00:34:52212void FullscreenMagnifierController::CenterOnPoint(
David Tseng6b85bae2021-06-14 22:59:32213 const gfx::Point& point_in_screen) {
Ahmed Fakhryaabb73a2018-04-21 00:39:43214 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 K1b783d22021-06-17 00:34:52220void FullscreenMagnifierController::HandleMoveMagnifierToRect(
Josiah K8912e352020-09-30 18:40:37221 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 Dektar39046c22024-01-04 21:26:53227 // Hide the cursor since this can cause jumps.
228 Shell::Get()->cursor_manager()->HideCursor();
Josiah K8912e352020-09-30 18:40:37229 MoveMagnifierWindowFollowRect(node_bounds_in_root);
230}
231
Josiah K1b783d22021-06-17 00:34:52232void FullscreenMagnifierController::SwitchTargetRootWindow(
Qiang Xu2657abf2018-03-21 18:01:45233 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 K1b783d22021-06-17 00:34:52259gfx::Transform FullscreenMagnifierController::GetMagnifierTransform() const {
Sarah Chan27ba1d82018-05-24 23:23:49260 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 K1b783d22021-06-17 00:34:52270void FullscreenMagnifierController::OnImplicitAnimationsCompleted() {
Qiang Xu2657abf2018-03-21 18:01:45271 if (move_cursor_after_animation_) {
Katie Dektar4c69402c2022-12-22 18:37:48272 MoveCursorTo(position_after_animation_);
Qiang Xu2657abf2018-03-21 18:01:45273 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 Xu2657abf2018-03-21 18:01:45279 }
280
281 is_on_animation_ = false;
282
283 StartOrStopScrollIfNecessary();
284}
285
Josiah K1b783d22021-06-17 00:34:52286void FullscreenMagnifierController::OnWindowDestroying(
David Tseng6b85bae2021-06-14 22:59:32287 aura::Window* root_window) {
Qiang Xu2657abf2018-03-21 18:01:45288 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 K1b783d22021-06-17 00:34:52304void FullscreenMagnifierController::OnWindowBoundsChanged(
Qiang Xu2657abf2018-03-21 18:01:45305 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 K1b783d22021-06-17 00:34:52312void FullscreenMagnifierController::OnMouseEvent(ui::MouseEvent* event) {
yoshiki@chromium.org022fd0a2013-02-06 17:54:08313 aura::Window* target = static_cast<aura::Window*>(event->target());
ben@chromium.orgbf9cdb362013-10-25 19:22:45314 aura::Window* current_root = target->GetRootWindow();
minch9e84ea0a2020-08-20 00:17:09315 gfx::Point root_location = event->root_location();
yoshiki@chromium.orgf49b7a02012-08-06 23:04:24316
minch9e84ea0a2020-08-20 00:17:09317 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.org779fd9812013-02-28 01:59:22332 // This must be before |SwitchTargetRootWindow()|.
hshi@chromium.org868a9912014-02-12 23:19:05333 if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
minch9e84ea0a2020-08-20 00:17:09334 point_of_interest_in_root_ = root_location;
yoshiki@chromium.org022fd0a2013-02-06 17:54:08335
oshima@chromium.org779fd9812013-02-28 01:59:22336 if (current_root != root_window_) {
337 DCHECK(current_root);
338 SwitchTargetRootWindow(current_root, true);
339 }
340
Sammie Quon666db05c2020-08-05 23:47:40341 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 Tapuska1621efb2020-04-29 15:39:54344 event->pointer_details().pointer_type != ui::EventPointerType::kPen) {
minch9e84ea0a2020-08-20 00:17:09345 OnMouseMove(root_location);
jdufault4bc14102016-10-14 18:35:53346 }
yoshiki@chromium.orgf49b7a02012-08-06 23:04:24347 }
yoshiki@chromium.org9613eacf2012-06-22 02:18:05348}
349
Josiah K1b783d22021-06-17 00:34:52350void FullscreenMagnifierController::OnScrollEvent(ui::ScrollEvent* event) {
sadrul@chromium.org9cc041762012-11-15 18:10:23351 if (event->IsAltDown() && event->IsControlDown()) {
Katie Dektar5b0879c2018-01-12 22:20:12352 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.org6f34b4832012-12-14 16:18:08364 event->StopPropagation();
365 return;
sadrul@chromium.org9cc041762012-11-15 18:10:23366 }
367
368 if (event->type() == ui::ET_SCROLL) {
Mike Wasserman15f20f42019-01-08 01:27:04369 SetScale(magnifier_utils::GetScaleFromScroll(
Ahmed Fakhryba41da592018-03-01 18:10:40370 event->y_offset() * kScrollScaleChangeFactor, GetScale(),
371 kMaxMagnifiedScale, kNonMagnifiedScale),
372 false /* animate */);
sadrul@chromium.org6f34b4832012-12-14 16:18:08373 event->StopPropagation();
374 return;
sadrul@chromium.org9cc041762012-11-15 18:10:23375 }
376 }
sadrul@chromium.orgf9f017c2012-11-12 19:05:17377}
378
Josiah K1b783d22021-06-17 00:34:52379void FullscreenMagnifierController::OnTouchEvent(ui::TouchEvent* event) {
yoshiki@chromium.org0f05dfd62013-01-21 16:52:07380 aura::Window* target = static_cast<aura::Window*>(event->target());
ben@chromium.orgbf9cdb362013-10-25 19:22:45381 aura::Window* current_root = target->GetRootWindow();
Yuki Awanof598b732018-02-28 05:58:48382
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.org0f05dfd62013-01-21 16:52:07391}
392
Josiah K1b783d22021-06-17 00:34:52393ui::EventDispatchDetails FullscreenMagnifierController::RewriteEvent(
Yuki Awanoc6f28222018-02-01 10:49:08394 const ui::Event& event,
Kevin Schoedel8c1f0772019-02-13 19:48:47395 const Continuation continuation) {
Yuki Awanoc6f28222018-02-01 10:49:08396 if (!IsEnabled())
Kevin Schoedel8c1f0772019-02-13 19:48:47397 return SendEvent(continuation, &event);
Yuki Awanoc6f28222018-02-01 10:49:08398
399 if (!event.IsTouchEvent())
Kevin Schoedel8c1f0772019-02-13 19:48:47400 return SendEvent(continuation, &event);
Yuki Awanoc6f28222018-02-01 10:49:08401
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 Czubak27e3d3f2022-11-15 16:24:15408 } else if (touch_event->type() == ui::ET_TOUCH_RELEASED ||
409 touch_event->type() == ui::ET_TOUCH_CANCELLED) {
Yuki Awanoc6f28222018-02-01 10:49:08410 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 Kim0bdf0ca2020-11-05 23:35:26418 false /* is_source_touch_event_set_blocking */);
Yuki Awanoc6f28222018-02-01 10:49:08419 } else {
Kevin Schoedel8c1f0772019-02-13 19:48:47420 return DiscardEvent(continuation);
Yuki Awanoc6f28222018-02-01 10:49:08421 }
422
423 // User can change zoom level with two fingers pinch and pan around with two
Josiah K1b783d22021-06-17 00:34:52424 // fingers scroll. Once FullscreenMagnifierController detects one of those two
Yuki Awanoc6f28222018-02-01 10:49:08425 // 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 K1b783d22021-06-17 00:34:52433 // FullscreenMagnifierController starts consuming all touch events after it
Yuki Awanoc6f28222018-02-01 10:49:08434 // cancells existing touches.
435 consume_touch_event_ = true;
436
Yuki Awanode0bea22018-07-11 01:56:50437 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 Padlipsky4687d1e12024-01-04 00:18:45443 touch_cancel_event.SetFlags(it.second->flags());
Yuki Awanoc6f28222018-02-01 10:49:08444
Yuki Awanode0bea22018-07-11 01:56:50445 // TouchExplorationController is watching event stream and managing its
Josiah K1b783d22021-06-17 00:34:52446 // internal state. If an event rewriter (FullscreenMagnifierController)
David Tseng6b85bae2021-06-14 22:59:32447 // 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 Schoedel8c1f0772019-02-13 19:48:47451 ui::EventDispatchDetails details =
452 SendEvent(continuation, &touch_cancel_event);
453 if (details.dispatcher_destroyed || details.target_destroyed)
454 return details;
Yuki Awanode0bea22018-07-11 01:56:50455 }
456 press_event_map_.clear();
Yuki Awanoc6f28222018-02-01 10:49:08457 }
Yuki Awanoc6f28222018-02-01 10:49:08458 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 Awanoc6f28222018-02-01 10:49:08463
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 Awano985417b82018-04-23 03:00:36467 } 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 Awanoc6f28222018-02-01 10:49:08471 }
472 }
473
474 if (discard)
Kevin Schoedel8c1f0772019-02-13 19:48:47475 return DiscardEvent(continuation);
Yuki Awanoc6f28222018-02-01 10:49:08476
Kevin Schoedel8c1f0772019-02-13 19:48:47477 return SendEvent(continuation, &event);
Yuki Awanoc6f28222018-02-01 10:49:08478}
479
Mitsuru Oshima0ccf0502022-02-08 19:22:21480const std::string& FullscreenMagnifierController::GetName() const {
481 static const std::string name("FullscreenMagnifierController");
482 return name;
483}
484
Josiah K1b783d22021-06-17 00:34:52485bool FullscreenMagnifierController::Redraw(
danakjfae0c842020-09-23 15:50:04486 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 Xu2657abf2018-03-21 18:01:45493 kDefaultAnimationTweenType);
494}
yoshiki@chromium.org9613eacf2012-06-22 02:18:05495
Josiah K1b783d22021-06-17 00:34:52496bool FullscreenMagnifierController::RedrawDIP(
David Tseng6b85bae2021-06-14 22:59:32497 const gfx::PointF& position_in_dip,
498 float scale,
499 int duration_in_ms,
500 gfx::Tween::Type tween_type) {
Qiang Xu2657abf2018-03-21 18:01:45501 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 Dektar4c69402c2022-12-22 18:37:48523 // 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 Xu2657abf2018-03-21 18:01:45526 return false;
527 }
528
529 origin_.set_x(x);
530 origin_.set_y(y);
531 scale_ = scale;
532
Sarah Chan27ba1d82018-05-24 23:23:49533 const ui::LayerAnimator::PreemptionStrategy strategy =
534 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET;
Peter Kastinge5a38ed2021-10-02 03:06:35535 const base::TimeDelta duration = base::Milliseconds(duration_in_ms);
Qiang Xu2657abf2018-03-21 18:01:45536
Sarah Chan27ba1d82018-05-24 23:23:49537 ui::ScopedLayerAnimationSettings root_layer_settings(
Qiang Xu2657abf2018-03-21 18:01:45538 root_window_->layer()->GetAnimator());
Sarah Chan27ba1d82018-05-24 23:23:49539 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 Xu2657abf2018-03-21 18:01:45543
544 display::Display display =
545 display::Screen::GetScreen()->GetDisplayNearestWindow(root_window_);
546 std::unique_ptr<RootWindowTransformer> transformer(
Michael Spang2a4b44cc2019-11-05 21:28:34547 CreateRootWindowTransformerForDisplay(display));
Sarah Chan27ba1d82018-05-24 23:23:49548
Su Hong Kood22ceb12020-04-06 16:44:07549 // 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 Chan27ba1d82018-05-24 23:23:49555 // TODO(spqchan): Find a way to sync the layer animations together.
Su Hong Kood22ceb12020-04-06 16:44:07556 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 Chan27ba1d82018-05-24 23:23:49560
Su Hong Kood22ceb12020-04-06 16:44:07561 aura::Window* display_identification_highlight =
Sammie Quond694bae42020-07-30 20:21:26562 root_window_->GetChildById(kShellWindowId_ScreenAnimationContainer)
Su Hong Kood22ceb12020-04-06 16:44:07563 ->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 Chan27ba1d82018-05-24 23:23:49576 }
577
Josiah K6db1587b2021-04-06 05:43:44578 if (!magnifier_debug_draw_rect_) {
579 RootWindowController::ForWindow(root_window_)
580 ->ash_host()
581 ->SetRootWindowTransformer(std::move(transformer));
582 }
Qiang Xu2657abf2018-03-21 18:01:45583
584 if (duration_in_ms > 0)
585 is_on_animation_ = true;
586
Josiah Kb3474bd32020-10-23 21:44:58587 Shell::Get()->accessibility_controller()->MagnifierBoundsChanged(
588 GetViewportRect());
589
Qiang Xu2657abf2018-03-21 18:01:45590 return true;
591}
592
Josiah K1b783d22021-06-17 00:34:52593void FullscreenMagnifierController::StartOrStopScrollIfNecessary() {
Qiang Xu2657abf2018-03-21 18:01:45594 // 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 K1b783d22021-06-17 00:34:52624void FullscreenMagnifierController::RedrawKeepingMousePosition(
Qiang Xu2657abf2018-03-21 18:01:45625 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 Kb77e9f62021-10-05 21:20:39643void FullscreenMagnifierController::OnMouseMove(
644 const gfx::Point& location_in_dip) {
Qiang Xu2657abf2018-03-21 18:01:45645 DCHECK(root_window_);
646
Josiah Kb77e9f62021-10-05 21:20:39647 gfx::Point center_point_in_dip(location_in_dip);
Qiang Xu2657abf2018-03-21 18:01:45648 int margin = kCursorPanningMargin / scale_; // No need to consider DPI.
Sarah Chan27ba1d82018-05-24 23:23:49649
Josiah K600a96c2021-02-13 01:21:04650 // Edge mouse following mode.
Josiah K600a96c2021-02-13 01:21:04651 int x_margin = margin;
652 int y_margin = margin;
653
Josiah Kb77e9f62021-10-05 21:20:39654 if (mouse_following_mode_ == MagnifierMouseFollowingMode::kCentered ||
655 mouse_following_mode_ == MagnifierMouseFollowingMode::kContinuous) {
Josiah K600a96c2021-02-13 01:21:04656 const gfx::Rect window_rect = GetViewportRect();
657 x_margin = window_rect.width() / 2;
658 y_margin = window_rect.height() / 2;
659 }
660
Josiah Kb77e9f62021-10-05 21:20:39661 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 Dektarbc734922022-12-13 16:35:08673
674 // Screen size.
Josiah Kb77e9f62021-10-05 21:20:39675 const gfx::Size host_size_in_dip = GetHostSizeDIP();
Katie Dektarbc734922022-12-13 16:35:08676
677 // Mouse position.
Josiah Kb77e9f62021-10-05 21:20:39678 const float x = location_in_dip.x();
679 const float y = location_in_dip.y();
680
Katie Dektarbc734922022-12-13 16:35:08681 // 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 Kb77e9f62021-10-05 21:20:39709 center_point_in_dip = {center_point_in_dip_x, center_point_in_dip_y};
710 }
711
Sarah Chan27ba1d82018-05-24 23:23:49712 // Reduce the bottom margin if the keyboard is visible.
Steven Bennetts137a18e2018-10-10 19:13:45713 bool reduce_bottom_margin =
Darren Shencb2508442019-07-03 21:48:23714 keyboard::KeyboardUIController::Get()->IsKeyboardVisible();
Sarah Chan27ba1d82018-05-24 23:23:49715
Josiah Kb77e9f62021-10-05 21:20:39716 MoveMagnifierWindowFollowPoint(center_point_in_dip, x_margin, y_margin,
Katie Dektarbc734922022-12-13 16:35:08717 reduce_bottom_margin);
Qiang Xu2657abf2018-03-21 18:01:45718}
719
Josiah K1b783d22021-06-17 00:34:52720void FullscreenMagnifierController::AfterAnimationMoveCursorTo(
Qiang Xu2657abf2018-03-21 18:01:45721 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 Xu2657abf2018-03-21 18:01:45732 }
733 move_cursor_after_animation_ = true;
734 position_after_animation_ = location;
735}
736
Josiah K1b783d22021-06-17 00:34:52737bool FullscreenMagnifierController::IsMagnified() const {
Qiang Xu2657abf2018-03-21 18:01:45738 return scale_ >= kMinMagnifiedScaleThreshold;
739}
740
Josiah K1b783d22021-06-17 00:34:52741gfx::RectF FullscreenMagnifierController::GetWindowRectDIP(float scale) const {
Katie Dektarbc734922022-12-13 16:35:08742 const gfx::Size size_in_dip = GetHostSizeDIP();
Qiang Xu2657abf2018-03-21 18:01:45743 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 K1b783d22021-06-17 00:34:52749gfx::Size FullscreenMagnifierController::GetHostSizeDIP() const {
Qiang Xu2657abf2018-03-21 18:01:45750 return root_window_->bounds().size();
751}
752
Josiah K1b783d22021-06-17 00:34:52753void FullscreenMagnifierController::ValidateScale(float* scale) {
Ho Cheung27c39932023-04-13 16:30:09754 *scale = std::clamp(*scale, kNonMagnifiedScale, kMaxMagnifiedScale);
Qiang Xu2657abf2018-03-21 18:01:45755 DCHECK(kNonMagnifiedScale <= *scale && *scale <= kMaxMagnifiedScale);
756}
757
Josiah K1b783d22021-06-17 00:34:52758bool FullscreenMagnifierController::ProcessGestures() {
Qiang Xu2657abf2018-03-21 18:01:45759 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 Awano23dcb192018-08-10 04:58:10776 float scale = GetScale() * details.scale();
777 ValidateScale(&scale);
Qiang Xu2657abf2018-03-21 18:01:45778
Yuki Awano23dcb192018-08-10 04:58:10779 // |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 Xu2657abf2018-03-21 18:01:45783
Yuki Awano23dcb192018-08-10 04:58:10784 // 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 Wang7afe9ea2022-10-01 03:14:15787 gesture_center =
788 root_window_->GetHost()->GetInverseRootTransform().MapPoint(
789 gesture_center);
Yuki Awanoedafb742018-05-18 00:47:41790
Yuki Awano23dcb192018-08-10 04:58:10791 // 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 Awanoedafb742018-05-18 00:47:41802
Yuki Awano23dcb192018-08-10 04:58:10803 RedrawDIP(origin, scale, 0, kDefaultAnimationTweenType);
Qiang Xu2657abf2018-03-21 18:01:45804 } 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 Fakhry98db19f32020-04-10 23:02:57811 // 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 Wang00be4cb2022-11-01 16:20:18820 gfx::Transform rotation_inverse_transform =
821 rotation_transform.GetCheckedInverse();
Xianzhu Wang7afe9ea2022-10-01 03:14:15822 gfx::PointF scroll = rotation_inverse_transform.MapPoint(
823 gfx::PointF(details.scroll_x(), details.scroll_y()));
Ahmed Fakhry98db19f32020-04-10 23:02:57824
Yuki Awano23dcb192018-08-10 04:58:10825 // Divide by scale to keep scroll speed same at any scale.
Ahmed Fakhry98db19f32020-04-10 23:02:57826 float new_x = origin_.x() + (-scroll.x() / scale_);
827 float new_y = origin_.y() + (-scroll.y() / scale_);
Qiang Xu2657abf2018-03-21 18:01:45828
Yuki Awano23dcb192018-08-10 04:58:10829 RedrawDIP(gfx::PointF(new_x, new_y), scale_, 0,
830 kDefaultAnimationTweenType);
Qiang Xu2657abf2018-03-21 18:01:45831 }
832 }
833
834 return cancel_pressed_touches;
835}
836
Josiah K1b783d22021-06-17 00:34:52837void FullscreenMagnifierController::MoveMagnifierWindowFollowPoint(
Qiang Xu2657abf2018-03-21 18:01:45838 const gfx::Point& point,
Katie Dektarbc734922022-12-13 16:35:08839 int x_margin,
840 int y_margin,
Sarah Chan27ba1d82018-05-24 23:23:49841 bool reduce_bottom_margin) {
Qiang Xu2657abf2018-03-21 18:01:45842 DCHECK(root_window_);
843 bool start_zoom = false;
844
Katie Dektarbc734922022-12-13 16:35:08845 // Current position.
Qiang Xu2657abf2018-03-21 18:01:45846 const gfx::Rect window_rect = GetViewportRect();
Qiang Xu2657abf2018-03-21 18:01:45847 const int top = window_rect.y();
848 const int bottom = window_rect.bottom();
849
Katie Dektarbc734922022-12-13 16:35:08850 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 Chan27ba1d82018-05-24 23:23:49862 // If |reduce_bottom_margin| is true, use kKeyboardBottomPanningMargin instead
Katie Dektarbc734922022-12-13 16:35:08863 // of |y_margin|. This is to prevent the magnifier from panning when
Sarah Chan27ba1d82018-05-24 23:23:49864 // the user is trying to interact with the bottom of the keyboard.
Katie Dektarbc734922022-12-13 16:35:08865 const int bottom_panning_margin =
866 reduce_bottom_margin ? kKeyboardBottomPanningMargin / scale_ : y_margin;
Sarah Chan27ba1d82018-05-24 23:23:49867
Qiang Xu2657abf2018-03-21 18:01:45868 int y_diff = 0;
Katie Dektarbc734922022-12-13 16:35:08869 if (point.y() < top + y_margin) {
Qiang Xu2657abf2018-03-21 18:01:45870 // Panning up.
Katie Dektarbc734922022-12-13 16:35:08871 y_diff = point.y() - (top + y_margin);
Qiang Xu2657abf2018-03-21 18:01:45872 start_zoom = true;
Sarah Chan27ba1d82018-05-24 23:23:49873 } else if (bottom - bottom_panning_margin < point.y()) {
Qiang Xu2657abf2018-03-21 18:01:45874 // Panning down.
Sarah Chan27ba1d82018-05-24 23:23:49875 const int bottom_target_margin =
Katie Dektarbc734922022-12-13 16:35:08876 reduce_bottom_margin ? std::min(bottom_panning_margin, y_margin)
877 : y_margin;
Sarah Chan27ba1d82018-05-24 23:23:49878 y_diff = point.y() - (bottom - bottom_target_margin);
Qiang Xu2657abf2018-03-21 18:01:45879 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 Kb77e9f62021-10-05 21:20:39888 // 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 Dektar4c69402c2022-12-22 18:37:48893 MoveCursorTo(point);
Josiah Kb77e9f62021-10-05 21:20:39894 }
Qiang Xu2657abf2018-03-21 18:01:45895 }
896 }
897}
898
Josiah K1b783d22021-06-17 00:34:52899void FullscreenMagnifierController::MoveMagnifierWindowCenterPoint(
Qiang Xu2657abf2018-03-21 18:01:45900 const gfx::Point& point) {
901 DCHECK(root_window_);
902
Sarah Chan27ba1d82018-05-24 23:23:49903 gfx::Rect window_rect = GetViewportRect();
904
905 // Reduce the viewport bounds if the keyboard is up.
Darren Shencb2508442019-07-03 21:48:23906 if (keyboard::KeyboardUIController::Get()->IsEnabled()) {
907 gfx::Rect keyboard_rect = keyboard::KeyboardUIController::Get()
Darren Shenb18c59f2018-07-03 01:31:24908 ->GetKeyboardWindow()
Darren Shen1a18c3f2018-06-14 14:05:53909 ->GetBoundsInScreen();
Sarah Chan27ba1d82018-05-24 23:23:49910 window_rect.set_height(window_rect.height() -
911 keyboard_rect.height() / scale_);
912 }
913
Qiang Xu2657abf2018-03-21 18:01:45914 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 K1b783d22021-06-17 00:34:52925void FullscreenMagnifierController::MoveMagnifierWindowFollowRect(
Qiang Xu2657abf2018-03-21 18:01:45926 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 K3647bd302020-11-13 05:22:08952 // 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 Xu2657abf2018-03-21 18:01:45957 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 Dektar4c69402c2022-12-22 18:37:48968void 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.orgc1c670172012-04-26 04:20:26979} // namespace ash