Add helper method to determine the current application horizontal size classes.
[chromium-blink-merge.git] / ash / magnifier / magnification_controller.cc
blobd07d5d20d3c97e81b1e62cb082a3a710f2752f27
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/magnifier/magnification_controller.h"
7 #include "ash/accelerators/accelerator_controller.h"
8 #include "ash/accessibility_delegate.h"
9 #include "ash/ash_switches.h"
10 #include "ash/display/root_window_transformers.h"
11 #include "ash/host/ash_window_tree_host.h"
12 #include "ash/host/root_window_transformer.h"
13 #include "ash/root_window_controller.h"
14 #include "ash/screen_util.h"
15 #include "ash/shell.h"
16 #include "ash/system/tray/system_tray_delegate.h"
17 #include "base/command_line.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/timer/timer.h"
20 #include "ui/aura/client/cursor_client.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_tree_host.h"
23 #include "ui/base/ime/input_method.h"
24 #include "ui/base/ime/input_method_observer.h"
25 #include "ui/base/ime/text_input_client.h"
26 #include "ui/compositor/dip_util.h"
27 #include "ui/compositor/layer.h"
28 #include "ui/compositor/layer_animation_observer.h"
29 #include "ui/compositor/scoped_layer_animation_settings.h"
30 #include "ui/events/event.h"
31 #include "ui/events/event_handler.h"
32 #include "ui/gfx/geometry/point3_f.h"
33 #include "ui/gfx/geometry/point_conversions.h"
34 #include "ui/gfx/geometry/point_f.h"
35 #include "ui/gfx/geometry/rect_conversions.h"
36 #include "ui/gfx/screen.h"
37 #include "ui/wm/core/compound_event_filter.h"
38 #include "ui/wm/core/coordinate_conversion.h"
40 namespace {
42 const float kMaxMagnifiedScale = 4.0f;
43 const float kMaxMagnifiedScaleThreshold = 4.0f;
44 const float kMinMagnifiedScaleThreshold = 1.1f;
45 const float kNonMagnifiedScale = 1.0f;
47 const float kInitialMagnifiedScale = 2.0f;
48 const float kScrollScaleChangeFactor = 0.0125f;
50 // Default animation parameters for redrawing the magnification window.
51 const gfx::Tween::Type kDefaultAnimationTweenType = gfx::Tween::EASE_OUT;
52 const int kDefaultAnimationDurationInMs = 100;
54 // Use linear transformation to make the magnifier window move smoothly
55 // to center the focus when user types in a text input field.
56 const gfx::Tween::Type kCenterCaretAnimationTweenType = gfx::Tween::LINEAR;
58 // The delay of the timer for moving magnifier window for centering the text
59 // input focus.
60 const int kMoveMagnifierDelayInMs = 10;
62 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of
63 // |kCursorPanningMargin| from the edge, the view-port moves.
64 const int kCursorPanningMargin = 100;
66 // Threadshold of panning. If the caret moves to within pixels (in DIP) of
67 // |kCaretPanningMargin| from the edge, the view-port moves.
68 const int kCaretPanningMargin = 50;
70 void MoveCursorTo(aura::WindowTreeHost* host, const gfx::Point& root_location) {
71 gfx::Point3F host_location_3f(root_location);
72 host->GetRootTransform().TransformPoint(&host_location_3f);
73 host->MoveCursorToHostLocation(
74 gfx::ToCeiledPoint(host_location_3f.AsPointF()));
77 ui::InputMethod* GetInputMethod(aura::Window* root_window) {
78 if (root_window->GetHost())
79 return root_window->GetHost()->GetInputMethod();
80 return nullptr;
83 } // namespace
85 namespace ash {
87 ////////////////////////////////////////////////////////////////////////////////
88 // MagnificationControllerImpl:
90 class MagnificationControllerImpl : public MagnificationController,
91 public ui::EventHandler,
92 public ui::ImplicitAnimationObserver,
93 public aura::WindowObserver,
94 public ui::InputMethodObserver {
95 public:
96 MagnificationControllerImpl();
97 ~MagnificationControllerImpl() override;
99 // MagnificationController overrides:
100 void SetEnabled(bool enabled) override;
101 bool IsEnabled() const override;
102 void SetKeepFocusCentered(bool keep_focus_centered) override;
103 bool KeepFocusCentered() const override;
104 void SetScale(float scale, bool animate) override;
105 float GetScale() const override { return scale_; }
106 void MoveWindow(int x, int y, bool animate) override;
107 void MoveWindow(const gfx::Point& point, bool animate) override;
108 gfx::Point GetWindowPosition() const override {
109 return gfx::ToFlooredPoint(origin_);
111 void SetScrollDirection(ScrollDirection direction) override;
112 gfx::Rect GetViewportRect() const override;
113 void HandleFocusedNodeChanged(
114 bool is_editable_node,
115 const gfx::Rect& node_bounds_in_screen) override;
116 void SwitchTargetRootWindow(aura::Window* new_root_window,
117 bool redraw_original_root_window) override;
119 // For test
120 gfx::Point GetPointOfInterestForTesting() override {
121 return point_of_interest_;
124 bool IsOnAnimationForTesting() const override { return is_on_animation_; }
126 void DisableMoveMagnifierDelayForTesting() override {
127 disable_move_magnifier_delay_ = true;
130 private:
131 // ui::ImplicitAnimationObserver overrides:
132 void OnImplicitAnimationsCompleted() override;
134 // aura::WindowObserver overrides:
135 void OnWindowDestroying(aura::Window* root_window) override;
136 void OnWindowBoundsChanged(aura::Window* window,
137 const gfx::Rect& old_bounds,
138 const gfx::Rect& new_bounds) override;
140 // Redraws the magnification window with the given origin position and the
141 // given scale. Returns true if the window is changed; otherwise, false.
142 // These methods should be called internally just after the scale and/or
143 // the position are changed to redraw the window.
144 bool Redraw(const gfx::PointF& position, float scale, bool animate);
146 // Redraws the magnification window with the given origin position in dip and
147 // the given scale. Returns true if the window is changed; otherwise, false.
148 // The last two parameters specify the animation duration and tween type.
149 // If |animation_in_ms| is zero, there will be no animation, and |tween_type|
150 // will be ignored.
151 bool RedrawDIP(const gfx::PointF& position_in_dip,
152 float scale,
153 int animation_in_ms,
154 gfx::Tween::Type tween_type);
156 // 1) If the screen is scrolling (i.e. animating) and should scroll further,
157 // it does nothing.
158 // 2) If the screen is scrolling (i.e. animating) and the direction is NONE,
159 // it stops the scrolling animation.
160 // 3) If the direction is set to value other than NONE, it starts the
161 // scrolling/ animation towards that direction.
162 void StartOrStopScrollIfNecessary();
164 // Redraw with the given zoom scale keeping the mouse cursor location. In
165 // other words, zoom (or unzoom) centering around the cursor.
166 void RedrawKeepingMousePosition(float scale, bool animate);
168 void OnMouseMove(const gfx::Point& location);
170 // Move the mouse cursot to the given point. Actual move will be done when
171 // the animation is completed. This should be called after animation is
172 // started.
173 void AfterAnimationMoveCursorTo(const gfx::Point& location);
175 // Returns if the magnification scale is 1.0 or not (larger then 1.0).
176 bool IsMagnified() const;
178 // Returns the rect of the magnification window.
179 gfx::RectF GetWindowRectDIP(float scale) const;
180 // Returns the size of the root window.
181 gfx::Size GetHostSizeDIP() const;
183 // Correct the given scale value if necessary.
184 void ValidateScale(float* scale);
186 // ui::EventHandler overrides:
187 void OnMouseEvent(ui::MouseEvent* event) override;
188 void OnScrollEvent(ui::ScrollEvent* event) override;
189 void OnTouchEvent(ui::TouchEvent* event) override;
191 // Moves the view port when |point| is located within
192 // |x_panning_margin| and |y_pannin_margin| to the edge of the visible
193 // window region. The view port will be moved so that the |point| will be
194 // moved to the point where it has |x_target_margin| and |y_target_margin|
195 // to the edge of the visible region.
196 void MoveMagnifierWindowFollowPoint(const gfx::Point& point,
197 int x_panning_margin,
198 int y_panning_margin,
199 int x_target_margin,
200 int y_target_margin);
202 // Moves the view port to center |point| in magnifier screen.
203 void MoveMagnifierWindowCenterPoint(const gfx::Point& point);
205 // Moves the viewport so that |rect| is fully visible. If |rect| is larger
206 // than the viewport horizontally or vertically, the viewport will be moved
207 // to center the |rect| in that dimension.
208 void MoveMagnifierWindowFollowRect(const gfx::Rect& rect);
210 // Invoked when |move_magnifier_timer_| fires to move the magnifier window to
211 // follow the caret.
212 void OnMoveMagnifierTimer();
214 // ui::InputMethodObserver:
215 void OnTextInputTypeChanged(const ui::TextInputClient* client) override {}
216 void OnFocus() override {}
217 void OnBlur() override {}
218 void OnTextInputStateChanged(const ui::TextInputClient* client) override {}
219 void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {}
220 void OnShowImeIfNeeded() override {}
221 void OnCaretBoundsChanged(const ui::TextInputClient* client) override;
223 // Target root window. This must not be NULL.
224 aura::Window* root_window_;
226 // True if the magnified window is currently animating a change. Otherwise,
227 // false.
228 bool is_on_animation_;
230 bool is_enabled_;
232 bool keep_focus_centered_;
234 // True if the cursor needs to move the given position after the animation
235 // will be finished. When using this, set |position_after_animation_| as well.
236 bool move_cursor_after_animation_;
237 // Stores the position of cursor to be moved after animation.
238 gfx::Point position_after_animation_;
240 // Stores the last mouse cursor (or last touched) location. This value is
241 // used on zooming to keep this location visible.
242 gfx::Point point_of_interest_;
244 // Current scale, origin (left-top) position of the magnification window.
245 float scale_;
246 gfx::PointF origin_;
248 ScrollDirection scroll_direction_;
250 // Timer for moving magnifier window when it fires.
251 base::OneShotTimer<MagnificationControllerImpl> move_magnifier_timer_;
253 // Most recent caret position in |root_window_| coordinates.
254 gfx::Point caret_point_;
256 // Flag for disabling moving magnifier delay. It can only be true in testing
257 // mode.
258 bool disable_move_magnifier_delay_;
260 DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl);
263 ////////////////////////////////////////////////////////////////////////////////
264 // MagnificationControllerImpl:
266 MagnificationControllerImpl::MagnificationControllerImpl()
267 : root_window_(Shell::GetPrimaryRootWindow()),
268 is_on_animation_(false),
269 is_enabled_(false),
270 keep_focus_centered_(false),
271 move_cursor_after_animation_(false),
272 scale_(kNonMagnifiedScale),
273 scroll_direction_(SCROLL_NONE),
274 disable_move_magnifier_delay_(false) {
275 Shell::GetInstance()->AddPreTargetHandler(this);
276 root_window_->AddObserver(this);
277 point_of_interest_ = root_window_->bounds().CenterPoint();
280 MagnificationControllerImpl::~MagnificationControllerImpl() {
281 ui::InputMethod* input_method = GetInputMethod(root_window_);
282 if (input_method)
283 input_method->RemoveObserver(this);
285 root_window_->RemoveObserver(this);
287 Shell::GetInstance()->RemovePreTargetHandler(this);
290 void MagnificationControllerImpl::RedrawKeepingMousePosition(
291 float scale, bool animate) {
292 gfx::Point mouse_in_root = point_of_interest_;
294 // mouse_in_root is invalid value when the cursor is hidden.
295 if (!root_window_->bounds().Contains(mouse_in_root))
296 mouse_in_root = root_window_->bounds().CenterPoint();
298 const gfx::PointF origin =
299 gfx::PointF(mouse_in_root.x() -
300 (scale_ / scale) * (mouse_in_root.x() - origin_.x()),
301 mouse_in_root.y() -
302 (scale_ / scale) * (mouse_in_root.y() - origin_.y()));
303 bool changed = RedrawDIP(origin, scale,
304 animate ? kDefaultAnimationDurationInMs : 0,
305 kDefaultAnimationTweenType);
306 if (changed)
307 AfterAnimationMoveCursorTo(mouse_in_root);
310 bool MagnificationControllerImpl::Redraw(const gfx::PointF& position,
311 float scale,
312 bool animate) {
313 const gfx::PointF position_in_dip =
314 ui::ConvertPointToDIP(root_window_->layer(), position);
315 return RedrawDIP(position_in_dip, scale,
316 animate ? kDefaultAnimationDurationInMs : 0,
317 kDefaultAnimationTweenType);
320 bool MagnificationControllerImpl::RedrawDIP(const gfx::PointF& position_in_dip,
321 float scale,
322 int duration_in_ms,
323 gfx::Tween::Type tween_type) {
324 DCHECK(root_window_);
326 float x = position_in_dip.x();
327 float y = position_in_dip.y();
329 ValidateScale(&scale);
331 if (x < 0)
332 x = 0;
333 if (y < 0)
334 y = 0;
336 const gfx::Size host_size_in_dip = GetHostSizeDIP();
337 const gfx::SizeF window_size_in_dip = GetWindowRectDIP(scale).size();
338 float max_x = host_size_in_dip.width() - window_size_in_dip.width();
339 float max_y = host_size_in_dip.height() - window_size_in_dip.height();
340 if (x > max_x)
341 x = max_x;
342 if (y > max_y)
343 y = max_y;
345 // Does nothing if both the origin and the scale are not changed.
346 if (origin_.x() == x &&
347 origin_.y() == y &&
348 scale == scale_) {
349 return false;
352 origin_.set_x(x);
353 origin_.set_y(y);
354 scale_ = scale;
356 // Creates transform matrix.
357 gfx::Transform transform;
358 // Flips the signs intentionally to convert them from the position of the
359 // magnification window.
360 transform.Scale(scale_, scale_);
361 transform.Translate(-origin_.x(), -origin_.y());
363 ui::ScopedLayerAnimationSettings settings(
364 root_window_->layer()->GetAnimator());
365 settings.AddObserver(this);
366 settings.SetPreemptionStrategy(
367 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
368 settings.SetTweenType(tween_type);
369 settings.SetTransitionDuration(
370 base::TimeDelta::FromMilliseconds(duration_in_ms));
372 gfx::Display display =
373 Shell::GetScreen()->GetDisplayNearestWindow(root_window_);
374 scoped_ptr<RootWindowTransformer> transformer(
375 CreateRootWindowTransformerForDisplay(root_window_, display));
376 GetRootWindowController(root_window_)->ash_host()->SetRootWindowTransformer(
377 transformer.Pass());
379 if (duration_in_ms > 0)
380 is_on_animation_ = true;
382 return true;
385 void MagnificationControllerImpl::StartOrStopScrollIfNecessary() {
386 // This value controls the scrolling speed.
387 const int kMoveOffset = 40;
388 if (is_on_animation_) {
389 if (scroll_direction_ == SCROLL_NONE)
390 root_window_->layer()->GetAnimator()->StopAnimating();
391 return;
394 gfx::PointF new_origin = origin_;
395 switch (scroll_direction_) {
396 case SCROLL_NONE:
397 // No need to take action.
398 return;
399 case SCROLL_LEFT:
400 new_origin.Offset(-kMoveOffset, 0);
401 break;
402 case SCROLL_RIGHT:
403 new_origin.Offset(kMoveOffset, 0);
404 break;
405 case SCROLL_UP:
406 new_origin.Offset(0, -kMoveOffset);
407 break;
408 case SCROLL_DOWN:
409 new_origin.Offset(0, kMoveOffset);
410 break;
412 RedrawDIP(new_origin, scale_, kDefaultAnimationDurationInMs,
413 kDefaultAnimationTweenType);
416 void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) {
417 DCHECK(root_window_);
419 gfx::Point mouse(location);
420 int margin = kCursorPanningMargin / scale_; // No need to consider DPI.
421 MoveMagnifierWindowFollowPoint(mouse, margin, margin, margin, margin);
424 gfx::Rect MagnificationControllerImpl::GetViewportRect() const {
425 return gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
428 void MagnificationControllerImpl::HandleFocusedNodeChanged(
429 bool is_editable_node,
430 const gfx::Rect& node_bounds_in_screen) {
431 // The editable node is handled by OnCaretBoundsChanged.
432 if (is_editable_node)
433 return;
435 gfx::Rect node_bounds_in_root =
436 ScreenUtil::ConvertRectFromScreen(root_window_, node_bounds_in_screen);
437 if (GetViewportRect().Contains(node_bounds_in_root))
438 return;
440 MoveMagnifierWindowFollowRect(node_bounds_in_root);
443 void MagnificationControllerImpl::SwitchTargetRootWindow(
444 aura::Window* new_root_window,
445 bool redraw_original_root_window) {
446 DCHECK(new_root_window);
448 if (new_root_window == root_window_)
449 return;
451 // Stores the previous scale.
452 float scale = GetScale();
454 // Unmagnify the previous root window.
455 root_window_->RemoveObserver(this);
456 if (redraw_original_root_window)
457 RedrawKeepingMousePosition(1.0f, true);
458 root_window_ = new_root_window;
459 RedrawKeepingMousePosition(scale, true);
460 root_window_->AddObserver(this);
463 void MagnificationControllerImpl::AfterAnimationMoveCursorTo(
464 const gfx::Point& location) {
465 DCHECK(root_window_);
467 aura::client::CursorClient* cursor_client =
468 aura::client::GetCursorClient(root_window_);
469 if (cursor_client) {
470 // When cursor is invisible, do not move or show the cursor after the
471 // animation.
472 if (!cursor_client->IsCursorVisible())
473 return;
474 cursor_client->DisableMouseEvents();
476 move_cursor_after_animation_ = true;
477 position_after_animation_ = location;
480 gfx::Size MagnificationControllerImpl::GetHostSizeDIP() const {
481 return root_window_->bounds().size();
484 gfx::RectF MagnificationControllerImpl::GetWindowRectDIP(float scale) const {
485 const gfx::Size size_in_dip = root_window_->bounds().size();
486 const float width = size_in_dip.width() / scale;
487 const float height = size_in_dip.height() / scale;
489 return gfx::RectF(origin_.x(), origin_.y(), width, height);
492 bool MagnificationControllerImpl::IsMagnified() const {
493 return scale_ >= kMinMagnifiedScaleThreshold;
496 void MagnificationControllerImpl::ValidateScale(float* scale) {
497 // Adjust the scale to just |kNonMagnifiedScale| if scale is smaller than
498 // |kMinMagnifiedScaleThreshold|;
499 if (*scale < kMinMagnifiedScaleThreshold)
500 *scale = kNonMagnifiedScale;
502 // Adjust the scale to just |kMinMagnifiedScale| if scale is bigger than
503 // |kMinMagnifiedScaleThreshold|;
504 if (*scale > kMaxMagnifiedScaleThreshold)
505 *scale = kMaxMagnifiedScale;
507 DCHECK(kNonMagnifiedScale <= *scale && *scale <= kMaxMagnifiedScale);
510 void MagnificationControllerImpl::OnImplicitAnimationsCompleted() {
511 if (!is_on_animation_)
512 return;
514 if (move_cursor_after_animation_) {
515 MoveCursorTo(root_window_->GetHost(), position_after_animation_);
516 move_cursor_after_animation_ = false;
518 aura::client::CursorClient* cursor_client =
519 aura::client::GetCursorClient(root_window_);
520 if (cursor_client)
521 cursor_client->EnableMouseEvents();
524 is_on_animation_ = false;
526 StartOrStopScrollIfNecessary();
529 void MagnificationControllerImpl::OnWindowDestroying(
530 aura::Window* root_window) {
531 if (root_window == root_window_) {
532 // There must be at least one root window because this controller is
533 // destroyed before the root windows get destroyed.
534 DCHECK(root_window);
536 aura::Window* target_root_window = Shell::GetTargetRootWindow();
537 CHECK(target_root_window);
539 // The destroyed root window must not be target.
540 CHECK_NE(target_root_window, root_window);
541 // Don't redraw the old root window as it's being destroyed.
542 SwitchTargetRootWindow(target_root_window, false);
543 point_of_interest_ = target_root_window->bounds().CenterPoint();
547 void MagnificationControllerImpl::OnWindowBoundsChanged(
548 aura::Window* window,
549 const gfx::Rect& old_bounds,
550 const gfx::Rect& new_bounds) {
551 // TODO(yoshiki): implement here. crbug.com/230979
554 ////////////////////////////////////////////////////////////////////////////////
555 // MagnificationControllerImpl: MagnificationController implementation
557 void MagnificationControllerImpl::SetScale(float scale, bool animate) {
558 if (!is_enabled_)
559 return;
561 ValidateScale(&scale);
562 Shell::GetInstance()->accessibility_delegate()->
563 SaveScreenMagnifierScale(scale);
564 RedrawKeepingMousePosition(scale, animate);
567 void MagnificationControllerImpl::MoveWindow(int x, int y, bool animate) {
568 if (!is_enabled_)
569 return;
571 Redraw(gfx::Point(x, y), scale_, animate);
574 void MagnificationControllerImpl::MoveWindow(const gfx::Point& point,
575 bool animate) {
576 if (!is_enabled_)
577 return;
579 Redraw(point, scale_, animate);
582 void MagnificationControllerImpl::SetScrollDirection(
583 ScrollDirection direction) {
584 scroll_direction_ = direction;
585 StartOrStopScrollIfNecessary();
588 void MagnificationControllerImpl::SetEnabled(bool enabled) {
589 Shell* shell = Shell::GetInstance();
590 ui::InputMethod* input_method = GetInputMethod(root_window_);
591 if (enabled) {
592 if (!is_enabled_ && input_method)
593 input_method->AddObserver(this);
595 float scale =
596 Shell::GetInstance()->accessibility_delegate()->
597 GetSavedScreenMagnifierScale();
598 if (scale <= 0.0f)
599 scale = kInitialMagnifiedScale;
600 ValidateScale(&scale);
602 // Do nothing, if already enabled with same scale.
603 if (is_enabled_ && scale == scale_)
604 return;
606 is_enabled_ = enabled;
607 RedrawKeepingMousePosition(scale, true);
608 shell->accessibility_delegate()->SaveScreenMagnifierScale(scale);
609 } else {
610 // Do nothing, if already disabled.
611 if (!is_enabled_)
612 return;
614 if (input_method)
615 input_method->RemoveObserver(this);
617 RedrawKeepingMousePosition(kNonMagnifiedScale, true);
618 is_enabled_ = enabled;
622 bool MagnificationControllerImpl::IsEnabled() const {
623 return is_enabled_;
626 void MagnificationControllerImpl::SetKeepFocusCentered(
627 bool keep_focus_centered) {
628 keep_focus_centered_ = keep_focus_centered;
631 bool MagnificationControllerImpl::KeepFocusCentered() const {
632 return keep_focus_centered_;
635 ////////////////////////////////////////////////////////////////////////////////
636 // MagnificationControllerImpl: aura::EventFilter implementation
638 void MagnificationControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
639 aura::Window* target = static_cast<aura::Window*>(event->target());
640 aura::Window* current_root = target->GetRootWindow();
641 gfx::Rect root_bounds = current_root->bounds();
643 if (root_bounds.Contains(event->root_location())) {
644 // This must be before |SwitchTargetRootWindow()|.
645 if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
646 point_of_interest_ = event->root_location();
648 if (current_root != root_window_) {
649 DCHECK(current_root);
650 SwitchTargetRootWindow(current_root, true);
653 if (IsMagnified() && event->type() == ui::ET_MOUSE_MOVED)
654 OnMouseMove(event->root_location());
658 void MagnificationControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
659 if (event->IsAltDown() && event->IsControlDown()) {
660 if (event->type() == ui::ET_SCROLL_FLING_START ||
661 event->type() == ui::ET_SCROLL_FLING_CANCEL) {
662 event->StopPropagation();
663 return;
666 if (event->type() == ui::ET_SCROLL) {
667 ui::ScrollEvent* scroll_event = static_cast<ui::ScrollEvent*>(event);
668 float scale = GetScale();
669 scale += scroll_event->y_offset() * kScrollScaleChangeFactor;
670 SetScale(scale, true);
671 event->StopPropagation();
672 return;
677 void MagnificationControllerImpl::OnTouchEvent(ui::TouchEvent* event) {
678 aura::Window* target = static_cast<aura::Window*>(event->target());
679 aura::Window* current_root = target->GetRootWindow();
680 if (current_root == root_window_) {
681 gfx::Rect root_bounds = current_root->bounds();
682 if (root_bounds.Contains(event->root_location()))
683 point_of_interest_ = event->root_location();
687 void MagnificationControllerImpl::MoveMagnifierWindowFollowPoint(
688 const gfx::Point& point,
689 int x_panning_margin,
690 int y_panning_margin,
691 int x_target_margin,
692 int y_target_margin) {
693 DCHECK(root_window_);
694 bool start_zoom = false;
696 const gfx::Rect window_rect = GetViewportRect();
697 const int left = window_rect.x();
698 const int right = window_rect.right();
700 int x_diff = 0;
701 if (point.x() < left + x_panning_margin) {
702 // Panning left.
703 x_diff = point.x() - (left + x_target_margin);
704 start_zoom = true;
705 } else if (right - x_panning_margin < point.x()) {
706 // Panning right.
707 x_diff = point.x() - (right - x_target_margin);
708 start_zoom = true;
710 int x = left + x_diff;
712 const int top = window_rect.y();
713 const int bottom = window_rect.bottom();
715 int y_diff = 0;
716 if (point.y() < top + y_panning_margin) {
717 // Panning up.
718 y_diff = point.y() - (top + y_target_margin);
719 start_zoom = true;
720 } else if (bottom - y_panning_margin < point.y()) {
721 // Panning down.
722 y_diff = point.y() - (bottom - y_target_margin);
723 start_zoom = true;
725 int y = top + y_diff;
726 if (start_zoom && !is_on_animation_) {
727 bool ret = RedrawDIP(gfx::Point(x, y), scale_,
728 0, // No animation on panning.
729 kDefaultAnimationTweenType);
731 if (ret) {
732 // If the magnified region is moved, hides the mouse cursor and moves it.
733 if (x_diff != 0 || y_diff != 0)
734 MoveCursorTo(root_window_->GetHost(), point);
739 void MagnificationControllerImpl::MoveMagnifierWindowCenterPoint(
740 const gfx::Point& point) {
741 DCHECK(root_window_);
743 const gfx::Rect window_rect = GetViewportRect();
744 if (point == window_rect.CenterPoint())
745 return;
747 if (!is_on_animation_) {
748 // With animation on panning.
749 RedrawDIP(window_rect.origin() + (point - window_rect.CenterPoint()),
750 scale_, kDefaultAnimationDurationInMs,
751 kCenterCaretAnimationTweenType);
755 void MagnificationControllerImpl::MoveMagnifierWindowFollowRect(
756 const gfx::Rect& rect) {
757 DCHECK(root_window_);
758 bool should_pan = false;
760 const gfx::Rect viewport_rect = GetViewportRect();
761 const int left = viewport_rect.x();
762 const int right = viewport_rect.right();
763 const gfx::Point rect_center = rect.CenterPoint();
764 const gfx::Point window_center = viewport_rect.CenterPoint();
766 int x = left;
767 if (rect.x() < left || right < rect.right()) {
768 // Panning horizontally.
769 x = rect_center.x() - viewport_rect.width() / 2;
770 should_pan = true;
773 const int top = viewport_rect.y();
774 const int bottom = viewport_rect.bottom();
776 int y = top;
777 if (rect.y() < top || bottom < rect.bottom()) {
778 // Panning vertically.
779 y = rect_center.y() - viewport_rect.height() / 2;
780 should_pan = true;
783 if (should_pan) {
784 if (is_on_animation_) {
785 root_window_->layer()->GetAnimator()->StopAnimating();
786 is_on_animation_ = false;
788 RedrawDIP(gfx::Point(x, y), scale_,
789 0, // No animation on panning.
790 kDefaultAnimationTweenType);
794 void MagnificationControllerImpl::OnMoveMagnifierTimer() {
795 MoveMagnifierWindowCenterPoint(caret_point_);
798 void MagnificationControllerImpl::OnCaretBoundsChanged(
799 const ui::TextInputClient* client) {
800 // caret bounds in screen coordinates.
801 const gfx::Rect caret_bounds = client->GetCaretBounds();
802 // Note: OnCaretBoundsChanged could be fired OnTextInputTypeChanged during
803 // which the caret position is not set a meaning position, and we do not
804 // need to adjust the view port position based on the bogus caret position.
805 // This is only a transition period, the caret position will be fixed upon
806 // focusing right after.
807 if (caret_bounds.width() == 0 && caret_bounds.height() == 0)
808 return;
810 caret_point_ = caret_bounds.CenterPoint();
811 // |caret_point_| in |root_window_| coordinates.
812 wm::ConvertPointFromScreen(root_window_, &caret_point_);
814 // If the feature for centering the text input focus is disabled, the
815 // magnifier window will be moved to follow the focus with a panning margin.
816 if (!KeepFocusCentered()) {
817 // Visible window_rect in |root_window_| coordinates.
818 const gfx::Rect visible_window_rect = GetViewportRect();
819 const int panning_margin = kCaretPanningMargin / scale_;
820 MoveMagnifierWindowFollowPoint(caret_point_,
821 panning_margin,
822 panning_margin,
823 visible_window_rect.width() / 2,
824 visible_window_rect.height() / 2);
825 return;
828 // Move the magnifier window to center the focus with a little delay.
829 // In Gmail compose window, when user types a blank space, it will insert
830 // a non-breaking space(NBSP). NBSP will be replaced with a blank space
831 // character when user types a non-blank space character later, which causes
832 // OnCaretBoundsChanged be called twice. The first call moves the caret back
833 // to the character position just before NBSP, replaces the NBSP with blank
834 // space plus the new character, then the second call will move caret to the
835 // position after the new character. In order to avoid the magnifier window
836 // being moved back and forth with these two OnCaretBoundsChanged events, we
837 // defer moving magnifier window until the |move_magnifier_timer_| fires,
838 // when the caret settles eventually.
839 move_magnifier_timer_.Stop();
840 move_magnifier_timer_.Start(
841 FROM_HERE,
842 base::TimeDelta::FromMilliseconds(
843 disable_move_magnifier_delay_ ? 0 : kMoveMagnifierDelayInMs),
844 this, &MagnificationControllerImpl::OnMoveMagnifierTimer);
847 ////////////////////////////////////////////////////////////////////////////////
848 // MagnificationController:
850 // static
851 MagnificationController* MagnificationController::CreateInstance() {
852 return new MagnificationControllerImpl();
855 } // namespace ash