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/aura_constants.h"
21 #include "ui/aura/client/cursor_client.h"
22 #include "ui/aura/window.h"
23 #include "ui/aura/window_tree_host.h"
24 #include "ui/base/ime/input_method.h"
25 #include "ui/base/ime/input_method_observer.h"
26 #include "ui/base/ime/text_input_client.h"
27 #include "ui/compositor/dip_util.h"
28 #include "ui/compositor/layer.h"
29 #include "ui/compositor/layer_animation_observer.h"
30 #include "ui/compositor/scoped_layer_animation_settings.h"
31 #include "ui/events/event.h"
32 #include "ui/events/event_handler.h"
33 #include "ui/gfx/geometry/point3_f.h"
34 #include "ui/gfx/geometry/point_conversions.h"
35 #include "ui/gfx/geometry/point_f.h"
36 #include "ui/gfx/geometry/rect_conversions.h"
37 #include "ui/gfx/screen.h"
38 #include "ui/wm/core/compound_event_filter.h"
39 #include "ui/wm/core/coordinate_conversion.h"
43 const float kMaxMagnifiedScale
= 4.0f
;
44 const float kMaxMagnifiedScaleThreshold
= 4.0f
;
45 const float kMinMagnifiedScaleThreshold
= 1.1f
;
46 const float kNonMagnifiedScale
= 1.0f
;
48 const float kInitialMagnifiedScale
= 2.0f
;
49 const float kScrollScaleChangeFactor
= 0.0125f
;
51 // Default animation parameters for redrawing the magnification window.
52 const gfx::Tween::Type kDefaultAnimationTweenType
= gfx::Tween::EASE_OUT
;
53 const int kDefaultAnimationDurationInMs
= 100;
55 // Use linear transformation to make the magnifier window move smoothly
56 // to center the focus when user types in a text input field.
57 const gfx::Tween::Type kCenterCaretAnimationTweenType
= gfx::Tween::LINEAR
;
59 // The delay of the timer for moving magnifier window for centering the text
61 const int kMoveMagnifierDelayInMs
= 10;
63 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of
64 // |kCursorPanningMargin| from the edge, the view-port moves.
65 const int kCursorPanningMargin
= 100;
67 // Threadshold of panning. If the caret moves to within pixels (in DIP) of
68 // |kCaretPanningMargin| from the edge, the view-port moves.
69 const int kCaretPanningMargin
= 50;
71 void MoveCursorTo(aura::WindowTreeHost
* host
, const gfx::Point
& root_location
) {
72 gfx::Point3F
host_location_3f(root_location
);
73 host
->GetRootTransform().TransformPoint(&host_location_3f
);
74 host
->MoveCursorToHostLocation(
75 gfx::ToCeiledPoint(host_location_3f
.AsPointF()));
82 ////////////////////////////////////////////////////////////////////////////////
83 // MagnificationControllerImpl:
85 class MagnificationControllerImpl
: virtual public MagnificationController
,
86 public ui::EventHandler
,
87 public ui::ImplicitAnimationObserver
,
88 public aura::WindowObserver
,
89 public ui::InputMethodObserver
{
91 MagnificationControllerImpl();
92 ~MagnificationControllerImpl() override
;
94 // MagnificationController overrides:
95 void SetEnabled(bool enabled
) override
;
96 bool IsEnabled() const override
;
97 void SetKeepFocusCentered(bool keep_focus_centered
) override
;
98 bool KeepFocusCentered() const override
;
99 void SetScale(float scale
, bool animate
) override
;
100 float GetScale() const override
{ return scale_
; }
101 void MoveWindow(int x
, int y
, bool animate
) override
;
102 void MoveWindow(const gfx::Point
& point
, bool animate
) override
;
103 gfx::Point
GetWindowPosition() const override
{
104 return gfx::ToFlooredPoint(origin_
);
106 void SetScrollDirection(ScrollDirection direction
) override
;
107 gfx::Rect
GetViewportRect() const override
;
108 void HandleFocusedNodeChanged(
109 bool is_editable_node
,
110 const gfx::Rect
& node_bounds_in_screen
) override
;
111 void SwitchTargetRootWindow(aura::Window
* new_root_window
,
112 bool redraw_original_root_window
) override
;
115 gfx::Point
GetPointOfInterestForTesting() override
{
116 return point_of_interest_
;
119 bool IsOnAnimationForTesting() const override
{ return is_on_animation_
; }
121 void DisableMoveMagnifierDelayForTesting() override
{
122 disable_move_magnifier_delay_
= true;
126 // ui::ImplicitAnimationObserver overrides:
127 void OnImplicitAnimationsCompleted() override
;
129 // aura::WindowObserver overrides:
130 void OnWindowDestroying(aura::Window
* root_window
) override
;
131 void OnWindowBoundsChanged(aura::Window
* window
,
132 const gfx::Rect
& old_bounds
,
133 const gfx::Rect
& new_bounds
) override
;
135 // Redraws the magnification window with the given origin position and the
136 // given scale. Returns true if the window is changed; otherwise, false.
137 // These methods should be called internally just after the scale and/or
138 // the position are changed to redraw the window.
139 bool Redraw(const gfx::PointF
& position
, float scale
, bool animate
);
141 // Redraws the magnification window with the given origin position in dip and
142 // the given scale. Returns true if the window is changed; otherwise, false.
143 // The last two parameters specify the animation duration and tween type.
144 // If |animation_in_ms| is zero, there will be no animation, and |tween_type|
146 bool RedrawDIP(const gfx::PointF
& position_in_dip
,
149 gfx::Tween::Type tween_type
);
151 // 1) If the screen is scrolling (i.e. animating) and should scroll further,
153 // 2) If the screen is scrolling (i.e. animating) and the direction is NONE,
154 // it stops the scrolling animation.
155 // 3) If the direction is set to value other than NONE, it starts the
156 // scrolling/ animation towards that direction.
157 void StartOrStopScrollIfNecessary();
159 // Redraw with the given zoom scale keeping the mouse cursor location. In
160 // other words, zoom (or unzoom) centering around the cursor.
161 void RedrawKeepingMousePosition(float scale
, bool animate
);
163 void OnMouseMove(const gfx::Point
& location
);
165 // Move the mouse cursot to the given point. Actual move will be done when
166 // the animation is completed. This should be called after animation is
168 void AfterAnimationMoveCursorTo(const gfx::Point
& location
);
170 // Returns if the magnification scale is 1.0 or not (larger then 1.0).
171 bool IsMagnified() const;
173 // Returns the rect of the magnification window.
174 gfx::RectF
GetWindowRectDIP(float scale
) const;
175 // Returns the size of the root window.
176 gfx::Size
GetHostSizeDIP() const;
178 // Correct the given scale value if necessary.
179 void ValidateScale(float* scale
);
181 // ui::EventHandler overrides:
182 void OnMouseEvent(ui::MouseEvent
* event
) override
;
183 void OnScrollEvent(ui::ScrollEvent
* event
) override
;
184 void OnTouchEvent(ui::TouchEvent
* event
) override
;
186 // Moves the view port when |point| is located within
187 // |x_panning_margin| and |y_pannin_margin| to the edge of the visible
188 // window region. The view port will be moved so that the |point| will be
189 // moved to the point where it has |x_target_margin| and |y_target_margin|
190 // to the edge of the visible region.
191 void MoveMagnifierWindowFollowPoint(const gfx::Point
& point
,
192 int x_panning_margin
,
193 int y_panning_margin
,
195 int y_target_margin
);
197 // Moves the view port to center |point| in magnifier screen.
198 void MoveMagnifierWindowCenterPoint(const gfx::Point
& point
);
200 // Moves the viewport so that |rect| is fully visible. If |rect| is larger
201 // than the viewport horizontally or vertically, the viewport will be moved
202 // to center the |rect| in that dimension.
203 void MoveMagnifierWindowFollowRect(const gfx::Rect
& rect
);
205 // Invoked when |move_magnifier_timer_| fires to move the magnifier window to
207 void OnMoveMagnifierTimer();
209 // ui::InputMethodObserver:
210 void OnTextInputTypeChanged(const ui::TextInputClient
* client
) override
{}
211 void OnFocus() override
{}
212 void OnBlur() override
{}
213 void OnTextInputStateChanged(const ui::TextInputClient
* client
) override
{}
214 void OnInputMethodDestroyed(const ui::InputMethod
* input_method
) override
{}
215 void OnShowImeIfNeeded() override
{}
216 void OnCaretBoundsChanged(const ui::TextInputClient
* client
) override
;
218 // Target root window. This must not be NULL.
219 aura::Window
* root_window_
;
221 // True if the magnified window is currently animating a change. Otherwise,
223 bool is_on_animation_
;
227 bool keep_focus_centered_
;
229 // True if the cursor needs to move the given position after the animation
230 // will be finished. When using this, set |position_after_animation_| as well.
231 bool move_cursor_after_animation_
;
232 // Stores the position of cursor to be moved after animation.
233 gfx::Point position_after_animation_
;
235 // Stores the last mouse cursor (or last touched) location. This value is
236 // used on zooming to keep this location visible.
237 gfx::Point point_of_interest_
;
239 // Current scale, origin (left-top) position of the magnification window.
243 ScrollDirection scroll_direction_
;
245 ui::InputMethod
* input_method_
; // Not owned.
247 // Timer for moving magnifier window when it fires.
248 base::OneShotTimer
<MagnificationControllerImpl
> move_magnifier_timer_
;
250 // Most recent caret position in |root_window_| coordinates.
251 gfx::Point caret_point_
;
253 // Flag for disabling moving magnifier delay. It can only be true in testing
255 bool disable_move_magnifier_delay_
;
257 DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl
);
260 ////////////////////////////////////////////////////////////////////////////////
261 // MagnificationControllerImpl:
263 MagnificationControllerImpl::MagnificationControllerImpl()
264 : root_window_(Shell::GetPrimaryRootWindow()),
265 is_on_animation_(false),
267 keep_focus_centered_(false),
268 move_cursor_after_animation_(false),
269 scale_(kNonMagnifiedScale
),
270 scroll_direction_(SCROLL_NONE
),
272 disable_move_magnifier_delay_(false) {
273 Shell::GetInstance()->AddPreTargetHandler(this);
274 root_window_
->AddObserver(this);
275 point_of_interest_
= root_window_
->bounds().CenterPoint();
278 MagnificationControllerImpl::~MagnificationControllerImpl() {
280 input_method_
->RemoveObserver(this);
282 root_window_
->RemoveObserver(this);
284 Shell::GetInstance()->RemovePreTargetHandler(this);
287 void MagnificationControllerImpl::RedrawKeepingMousePosition(
288 float scale
, bool animate
) {
289 gfx::Point mouse_in_root
= point_of_interest_
;
291 // mouse_in_root is invalid value when the cursor is hidden.
292 if (!root_window_
->bounds().Contains(mouse_in_root
))
293 mouse_in_root
= root_window_
->bounds().CenterPoint();
295 const gfx::PointF origin
=
296 gfx::PointF(mouse_in_root
.x() -
297 (scale_
/ scale
) * (mouse_in_root
.x() - origin_
.x()),
299 (scale_
/ scale
) * (mouse_in_root
.y() - origin_
.y()));
300 bool changed
= RedrawDIP(origin
, scale
,
301 animate
? kDefaultAnimationDurationInMs
: 0,
302 kDefaultAnimationTweenType
);
304 AfterAnimationMoveCursorTo(mouse_in_root
);
307 bool MagnificationControllerImpl::Redraw(const gfx::PointF
& position
,
310 const gfx::PointF position_in_dip
=
311 ui::ConvertPointToDIP(root_window_
->layer(), position
);
312 return RedrawDIP(position_in_dip
, scale
,
313 animate
? kDefaultAnimationDurationInMs
: 0,
314 kDefaultAnimationTweenType
);
317 bool MagnificationControllerImpl::RedrawDIP(const gfx::PointF
& position_in_dip
,
320 gfx::Tween::Type tween_type
) {
321 DCHECK(root_window_
);
323 float x
= position_in_dip
.x();
324 float y
= position_in_dip
.y();
326 ValidateScale(&scale
);
333 const gfx::Size host_size_in_dip
= GetHostSizeDIP();
334 const gfx::SizeF window_size_in_dip
= GetWindowRectDIP(scale
).size();
335 float max_x
= host_size_in_dip
.width() - window_size_in_dip
.width();
336 float max_y
= host_size_in_dip
.height() - window_size_in_dip
.height();
342 // Does nothing if both the origin and the scale are not changed.
343 if (origin_
.x() == x
&&
353 // Creates transform matrix.
354 gfx::Transform transform
;
355 // Flips the signs intentionally to convert them from the position of the
356 // magnification window.
357 transform
.Scale(scale_
, scale_
);
358 transform
.Translate(-origin_
.x(), -origin_
.y());
360 ui::ScopedLayerAnimationSettings
settings(
361 root_window_
->layer()->GetAnimator());
362 settings
.AddObserver(this);
363 settings
.SetPreemptionStrategy(
364 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET
);
365 settings
.SetTweenType(tween_type
);
366 settings
.SetTransitionDuration(
367 base::TimeDelta::FromMilliseconds(duration_in_ms
));
369 gfx::Display display
=
370 Shell::GetScreen()->GetDisplayNearestWindow(root_window_
);
371 scoped_ptr
<RootWindowTransformer
> transformer(
372 CreateRootWindowTransformerForDisplay(root_window_
, display
));
373 GetRootWindowController(root_window_
)->ash_host()->SetRootWindowTransformer(
376 if (duration_in_ms
> 0)
377 is_on_animation_
= true;
382 void MagnificationControllerImpl::StartOrStopScrollIfNecessary() {
383 // This value controls the scrolling speed.
384 const int kMoveOffset
= 40;
385 if (is_on_animation_
) {
386 if (scroll_direction_
== SCROLL_NONE
)
387 root_window_
->layer()->GetAnimator()->StopAnimating();
391 gfx::PointF new_origin
= origin_
;
392 switch (scroll_direction_
) {
394 // No need to take action.
397 new_origin
.Offset(-kMoveOffset
, 0);
400 new_origin
.Offset(kMoveOffset
, 0);
403 new_origin
.Offset(0, -kMoveOffset
);
406 new_origin
.Offset(0, kMoveOffset
);
409 RedrawDIP(new_origin
, scale_
, kDefaultAnimationDurationInMs
,
410 kDefaultAnimationTweenType
);
413 void MagnificationControllerImpl::OnMouseMove(const gfx::Point
& location
) {
414 DCHECK(root_window_
);
416 gfx::Point
mouse(location
);
417 int margin
= kCursorPanningMargin
/ scale_
; // No need to consider DPI.
418 MoveMagnifierWindowFollowPoint(mouse
, margin
, margin
, margin
, margin
);
421 gfx::Rect
MagnificationControllerImpl::GetViewportRect() const {
422 return gfx::ToEnclosingRect(GetWindowRectDIP(scale_
));
425 void MagnificationControllerImpl::HandleFocusedNodeChanged(
426 bool is_editable_node
,
427 const gfx::Rect
& node_bounds_in_screen
) {
428 // The editable node is handled by OnCaretBoundsChanged.
429 if (is_editable_node
)
432 gfx::Rect node_bounds_in_root
=
433 ScreenUtil::ConvertRectFromScreen(root_window_
, node_bounds_in_screen
);
434 if (GetViewportRect().Contains(node_bounds_in_root
))
437 MoveMagnifierWindowFollowRect(node_bounds_in_root
);
440 void MagnificationControllerImpl::SwitchTargetRootWindow(
441 aura::Window
* new_root_window
,
442 bool redraw_original_root_window
) {
443 DCHECK(new_root_window
);
445 if (new_root_window
== root_window_
)
448 // Stores the previous scale.
449 float scale
= GetScale();
451 // Unmagnify the previous root window.
452 root_window_
->RemoveObserver(this);
453 if (redraw_original_root_window
)
454 RedrawKeepingMousePosition(1.0f
, true);
455 root_window_
= new_root_window
;
456 RedrawKeepingMousePosition(scale
, true);
457 root_window_
->AddObserver(this);
460 void MagnificationControllerImpl::AfterAnimationMoveCursorTo(
461 const gfx::Point
& location
) {
462 DCHECK(root_window_
);
464 aura::client::CursorClient
* cursor_client
=
465 aura::client::GetCursorClient(root_window_
);
467 // When cursor is invisible, do not move or show the cursor after the
469 if (!cursor_client
->IsCursorVisible())
471 cursor_client
->DisableMouseEvents();
473 move_cursor_after_animation_
= true;
474 position_after_animation_
= location
;
477 gfx::Size
MagnificationControllerImpl::GetHostSizeDIP() const {
478 return root_window_
->bounds().size();
481 gfx::RectF
MagnificationControllerImpl::GetWindowRectDIP(float scale
) const {
482 const gfx::Size size_in_dip
= root_window_
->bounds().size();
483 const float width
= size_in_dip
.width() / scale
;
484 const float height
= size_in_dip
.height() / scale
;
486 return gfx::RectF(origin_
.x(), origin_
.y(), width
, height
);
489 bool MagnificationControllerImpl::IsMagnified() const {
490 return scale_
>= kMinMagnifiedScaleThreshold
;
493 void MagnificationControllerImpl::ValidateScale(float* scale
) {
494 // Adjust the scale to just |kNonMagnifiedScale| if scale is smaller than
495 // |kMinMagnifiedScaleThreshold|;
496 if (*scale
< kMinMagnifiedScaleThreshold
)
497 *scale
= kNonMagnifiedScale
;
499 // Adjust the scale to just |kMinMagnifiedScale| if scale is bigger than
500 // |kMinMagnifiedScaleThreshold|;
501 if (*scale
> kMaxMagnifiedScaleThreshold
)
502 *scale
= kMaxMagnifiedScale
;
504 DCHECK(kNonMagnifiedScale
<= *scale
&& *scale
<= kMaxMagnifiedScale
);
507 void MagnificationControllerImpl::OnImplicitAnimationsCompleted() {
508 if (!is_on_animation_
)
511 if (move_cursor_after_animation_
) {
512 MoveCursorTo(root_window_
->GetHost(), position_after_animation_
);
513 move_cursor_after_animation_
= false;
515 aura::client::CursorClient
* cursor_client
=
516 aura::client::GetCursorClient(root_window_
);
518 cursor_client
->EnableMouseEvents();
521 is_on_animation_
= false;
523 StartOrStopScrollIfNecessary();
526 void MagnificationControllerImpl::OnWindowDestroying(
527 aura::Window
* root_window
) {
528 if (root_window
== root_window_
) {
529 // There must be at least one root window because this controller is
530 // destroyed before the root windows get destroyed.
533 aura::Window
* target_root_window
= Shell::GetTargetRootWindow();
534 CHECK(target_root_window
);
536 // The destroyed root window must not be target.
537 CHECK_NE(target_root_window
, root_window
);
538 // Don't redraw the old root window as it's being destroyed.
539 SwitchTargetRootWindow(target_root_window
, false);
540 point_of_interest_
= target_root_window
->bounds().CenterPoint();
544 void MagnificationControllerImpl::OnWindowBoundsChanged(
545 aura::Window
* window
,
546 const gfx::Rect
& old_bounds
,
547 const gfx::Rect
& new_bounds
) {
548 // TODO(yoshiki): implement here. crbug.com/230979
551 ////////////////////////////////////////////////////////////////////////////////
552 // MagnificationControllerImpl: MagnificationController implementation
554 void MagnificationControllerImpl::SetScale(float scale
, bool animate
) {
558 ValidateScale(&scale
);
559 Shell::GetInstance()->accessibility_delegate()->
560 SaveScreenMagnifierScale(scale
);
561 RedrawKeepingMousePosition(scale
, animate
);
564 void MagnificationControllerImpl::MoveWindow(int x
, int y
, bool animate
) {
568 Redraw(gfx::Point(x
, y
), scale_
, animate
);
571 void MagnificationControllerImpl::MoveWindow(const gfx::Point
& point
,
576 Redraw(point
, scale_
, animate
);
579 void MagnificationControllerImpl::SetScrollDirection(
580 ScrollDirection direction
) {
581 scroll_direction_
= direction
;
582 StartOrStopScrollIfNecessary();
585 void MagnificationControllerImpl::SetEnabled(bool enabled
) {
586 Shell
* shell
= Shell::GetInstance();
588 if (!input_method_
) {
590 root_window_
->GetProperty(aura::client::kRootWindowInputMethodKey
);
592 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);
616 input_method_
= NULL
;
619 RedrawKeepingMousePosition(kNonMagnifiedScale
, true);
620 is_enabled_
= enabled
;
624 bool MagnificationControllerImpl::IsEnabled() const {
628 void MagnificationControllerImpl::SetKeepFocusCentered(
629 bool keep_focus_centered
) {
630 keep_focus_centered_
= keep_focus_centered
;
633 bool MagnificationControllerImpl::KeepFocusCentered() const {
634 return keep_focus_centered_
;
637 ////////////////////////////////////////////////////////////////////////////////
638 // MagnificationControllerImpl: aura::EventFilter implementation
640 void MagnificationControllerImpl::OnMouseEvent(ui::MouseEvent
* event
) {
641 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
642 aura::Window
* current_root
= target
->GetRootWindow();
643 gfx::Rect root_bounds
= current_root
->bounds();
645 if (root_bounds
.Contains(event
->root_location())) {
646 // This must be before |SwitchTargetRootWindow()|.
647 if (event
->type() != ui::ET_MOUSE_CAPTURE_CHANGED
)
648 point_of_interest_
= event
->root_location();
650 if (current_root
!= root_window_
) {
651 DCHECK(current_root
);
652 SwitchTargetRootWindow(current_root
, true);
655 if (IsMagnified() && event
->type() == ui::ET_MOUSE_MOVED
)
656 OnMouseMove(event
->root_location());
660 void MagnificationControllerImpl::OnScrollEvent(ui::ScrollEvent
* event
) {
661 if (event
->IsAltDown() && event
->IsControlDown()) {
662 if (event
->type() == ui::ET_SCROLL_FLING_START
||
663 event
->type() == ui::ET_SCROLL_FLING_CANCEL
) {
664 event
->StopPropagation();
668 if (event
->type() == ui::ET_SCROLL
) {
669 ui::ScrollEvent
* scroll_event
= static_cast<ui::ScrollEvent
*>(event
);
670 float scale
= GetScale();
671 scale
+= scroll_event
->y_offset() * kScrollScaleChangeFactor
;
672 SetScale(scale
, true);
673 event
->StopPropagation();
679 void MagnificationControllerImpl::OnTouchEvent(ui::TouchEvent
* event
) {
680 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
681 aura::Window
* current_root
= target
->GetRootWindow();
682 if (current_root
== root_window_
) {
683 gfx::Rect root_bounds
= current_root
->bounds();
684 if (root_bounds
.Contains(event
->root_location()))
685 point_of_interest_
= event
->root_location();
689 void MagnificationControllerImpl::MoveMagnifierWindowFollowPoint(
690 const gfx::Point
& point
,
691 int x_panning_margin
,
692 int y_panning_margin
,
694 int y_target_margin
) {
695 DCHECK(root_window_
);
696 bool start_zoom
= false;
698 const gfx::Rect window_rect
= GetViewportRect();
699 const int left
= window_rect
.x();
700 const int right
= window_rect
.right();
703 if (point
.x() < left
+ x_panning_margin
) {
705 x_diff
= point
.x() - (left
+ x_target_margin
);
707 } else if (right
- x_panning_margin
< point
.x()) {
709 x_diff
= point
.x() - (right
- x_target_margin
);
712 int x
= left
+ x_diff
;
714 const int top
= window_rect
.y();
715 const int bottom
= window_rect
.bottom();
718 if (point
.y() < top
+ y_panning_margin
) {
720 y_diff
= point
.y() - (top
+ y_target_margin
);
722 } else if (bottom
- y_panning_margin
< point
.y()) {
724 y_diff
= point
.y() - (bottom
- y_target_margin
);
727 int y
= top
+ y_diff
;
728 if (start_zoom
&& !is_on_animation_
) {
729 bool ret
= RedrawDIP(gfx::Point(x
, y
), scale_
,
730 0, // No animation on panning.
731 kDefaultAnimationTweenType
);
734 // If the magnified region is moved, hides the mouse cursor and moves it.
735 if (x_diff
!= 0 || y_diff
!= 0)
736 MoveCursorTo(root_window_
->GetHost(), point
);
741 void MagnificationControllerImpl::MoveMagnifierWindowCenterPoint(
742 const gfx::Point
& point
) {
743 DCHECK(root_window_
);
745 const gfx::Rect window_rect
= GetViewportRect();
746 if (point
== window_rect
.CenterPoint())
749 if (!is_on_animation_
) {
750 // With animation on panning.
751 RedrawDIP(window_rect
.origin() + (point
- window_rect
.CenterPoint()),
752 scale_
, kDefaultAnimationDurationInMs
,
753 kCenterCaretAnimationTweenType
);
757 void MagnificationControllerImpl::MoveMagnifierWindowFollowRect(
758 const gfx::Rect
& rect
) {
759 DCHECK(root_window_
);
760 bool should_pan
= false;
762 const gfx::Rect viewport_rect
= GetViewportRect();
763 const int left
= viewport_rect
.x();
764 const int right
= viewport_rect
.right();
765 const gfx::Point rect_center
= rect
.CenterPoint();
766 const gfx::Point window_center
= viewport_rect
.CenterPoint();
769 if (rect
.x() < left
|| right
< rect
.right()) {
770 // Panning horizontally.
771 x
= rect_center
.x() - viewport_rect
.width() / 2;
775 const int top
= viewport_rect
.y();
776 const int bottom
= viewport_rect
.bottom();
779 if (rect
.y() < top
|| bottom
< rect
.bottom()) {
780 // Panning vertically.
781 y
= rect_center
.y() - viewport_rect
.height() / 2;
786 if (is_on_animation_
) {
787 root_window_
->layer()->GetAnimator()->StopAnimating();
788 is_on_animation_
= false;
790 RedrawDIP(gfx::Point(x
, y
), scale_
,
791 0, // No animation on panning.
792 kDefaultAnimationTweenType
);
796 void MagnificationControllerImpl::OnMoveMagnifierTimer() {
797 MoveMagnifierWindowCenterPoint(caret_point_
);
800 void MagnificationControllerImpl::OnCaretBoundsChanged(
801 const ui::TextInputClient
* client
) {
802 // caret bounds in screen coordinates.
803 const gfx::Rect caret_bounds
= client
->GetCaretBounds();
804 // Note: OnCaretBoundsChanged could be fired OnTextInputTypeChanged during
805 // which the caret position is not set a meaning position, and we do not
806 // need to adjust the view port position based on the bogus caret position.
807 // This is only a transition period, the caret position will be fixed upon
808 // focusing right after.
809 if (caret_bounds
.width() == 0 && caret_bounds
.height() == 0)
812 caret_point_
= caret_bounds
.CenterPoint();
813 // |caret_point_| in |root_window_| coordinates.
814 wm::ConvertPointFromScreen(root_window_
, &caret_point_
);
816 // If the feature for centering the text input focus is disabled, the
817 // magnifier window will be moved to follow the focus with a panning margin.
818 if (!KeepFocusCentered()) {
819 // Visible window_rect in |root_window_| coordinates.
820 const gfx::Rect visible_window_rect
= GetViewportRect();
821 const int panning_margin
= kCaretPanningMargin
/ scale_
;
822 MoveMagnifierWindowFollowPoint(caret_point_
,
825 visible_window_rect
.width() / 2,
826 visible_window_rect
.height() / 2);
830 // Move the magnifier window to center the focus with a little delay.
831 // In Gmail compose window, when user types a blank space, it will insert
832 // a non-breaking space(NBSP). NBSP will be replaced with a blank space
833 // character when user types a non-blank space character later, which causes
834 // OnCaretBoundsChanged be called twice. The first call moves the caret back
835 // to the character position just before NBSP, replaces the NBSP with blank
836 // space plus the new character, then the second call will move caret to the
837 // position after the new character. In order to avoid the magnifier window
838 // being moved back and forth with these two OnCaretBoundsChanged events, we
839 // defer moving magnifier window until the |move_magnifier_timer_| fires,
840 // when the caret settles eventually.
841 move_magnifier_timer_
.Stop();
842 move_magnifier_timer_
.Start(
844 base::TimeDelta::FromMilliseconds(
845 disable_move_magnifier_delay_
? 0 : kMoveMagnifierDelayInMs
),
846 this, &MagnificationControllerImpl::OnMoveMagnifierTimer
);
849 ////////////////////////////////////////////////////////////////////////////////
850 // MagnificationController:
853 MagnificationController
* MagnificationController::CreateInstance() {
854 return new MagnificationControllerImpl();