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"
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
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();
87 ////////////////////////////////////////////////////////////////////////////////
88 // MagnificationControllerImpl:
90 class MagnificationControllerImpl
: virtual public MagnificationController
,
91 public ui::EventHandler
,
92 public ui::ImplicitAnimationObserver
,
93 public aura::WindowObserver
,
94 public ui::InputMethodObserver
{
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
;
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;
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|
151 bool RedrawDIP(const gfx::PointF
& position_in_dip
,
154 gfx::Tween::Type tween_type
);
156 // 1) If the screen is scrolling (i.e. animating) and should scroll further,
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
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
,
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
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,
228 bool is_on_animation_
;
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.
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
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),
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_
);
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()),
302 (scale_
/ scale
) * (mouse_in_root
.y() - origin_
.y()));
303 bool changed
= RedrawDIP(origin
, scale
,
304 animate
? kDefaultAnimationDurationInMs
: 0,
305 kDefaultAnimationTweenType
);
307 AfterAnimationMoveCursorTo(mouse_in_root
);
310 bool MagnificationControllerImpl::Redraw(const gfx::PointF
& position
,
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
,
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
);
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();
345 // Does nothing if both the origin and the scale are not changed.
346 if (origin_
.x() == x
&&
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(
379 if (duration_in_ms
> 0)
380 is_on_animation_
= 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();
394 gfx::PointF new_origin
= origin_
;
395 switch (scroll_direction_
) {
397 // No need to take action.
400 new_origin
.Offset(-kMoveOffset
, 0);
403 new_origin
.Offset(kMoveOffset
, 0);
406 new_origin
.Offset(0, -kMoveOffset
);
409 new_origin
.Offset(0, kMoveOffset
);
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
)
435 gfx::Rect node_bounds_in_root
=
436 ScreenUtil::ConvertRectFromScreen(root_window_
, node_bounds_in_screen
);
437 if (GetViewportRect().Contains(node_bounds_in_root
))
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_
)
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_
);
470 // When cursor is invisible, do not move or show the cursor after the
472 if (!cursor_client
->IsCursorVisible())
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_
)
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_
);
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.
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
) {
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
) {
571 Redraw(gfx::Point(x
, y
), scale_
, animate
);
574 void MagnificationControllerImpl::MoveWindow(const gfx::Point
& point
,
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_
);
592 if (!is_enabled_
&& input_method
)
593 input_method
->AddObserver(this);
596 Shell::GetInstance()->accessibility_delegate()->
597 GetSavedScreenMagnifierScale();
599 scale
= kInitialMagnifiedScale
;
600 ValidateScale(&scale
);
602 // Do nothing, if already enabled with same scale.
603 if (is_enabled_
&& scale
== scale_
)
606 is_enabled_
= enabled
;
607 RedrawKeepingMousePosition(scale
, true);
608 shell
->accessibility_delegate()->SaveScreenMagnifierScale(scale
);
610 // Do nothing, if already disabled.
615 input_method
->RemoveObserver(this);
617 RedrawKeepingMousePosition(kNonMagnifiedScale
, true);
618 is_enabled_
= enabled
;
622 bool MagnificationControllerImpl::IsEnabled() const {
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();
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();
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
,
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();
701 if (point
.x() < left
+ x_panning_margin
) {
703 x_diff
= point
.x() - (left
+ x_target_margin
);
705 } else if (right
- x_panning_margin
< point
.x()) {
707 x_diff
= point
.x() - (right
- x_target_margin
);
710 int x
= left
+ x_diff
;
712 const int top
= window_rect
.y();
713 const int bottom
= window_rect
.bottom();
716 if (point
.y() < top
+ y_panning_margin
) {
718 y_diff
= point
.y() - (top
+ y_target_margin
);
720 } else if (bottom
- y_panning_margin
< point
.y()) {
722 y_diff
= point
.y() - (bottom
- y_target_margin
);
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
);
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())
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();
767 if (rect
.x() < left
|| right
< rect
.right()) {
768 // Panning horizontally.
769 x
= rect_center
.x() - viewport_rect
.width() / 2;
773 const int top
= viewport_rect
.y();
774 const int bottom
= viewport_rect
.bottom();
777 if (rect
.y() < top
|| bottom
< rect
.bottom()) {
778 // Panning vertically.
779 y
= rect_center
.y() - viewport_rect
.height() / 2;
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)
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_
,
823 visible_window_rect
.width() / 2,
824 visible_window_rect
.height() / 2);
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(
842 base::TimeDelta::FromMilliseconds(
843 disable_move_magnifier_delay_
? 0 : kMoveMagnifierDelayInMs
),
844 this, &MagnificationControllerImpl::OnMoveMagnifierTimer
);
847 ////////////////////////////////////////////////////////////////////////////////
848 // MagnificationController:
851 MagnificationController
* MagnificationController::CreateInstance() {
852 return new MagnificationControllerImpl();