Rewrite AndroidSyncSettings to be significantly simpler.
[chromium-blink-merge.git] / ash / magnifier / magnification_controller.cc
blob68386a9ed934a970f303c5901d855c3b48cc91f0
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 "ui/aura/client/aura_constants.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.05f;
50 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of
51 // |kPanningMergin| from the edge, the view-port moves.
52 const int kPanningMergin = 100;
54 // Gives a little panning margin for following caret, so that we will move the
55 // view-port before the caret is completely out of sight.
56 const int kCaretPanningMargin = 10;
58 void MoveCursorTo(aura::WindowTreeHost* host, const gfx::Point& root_location) {
59 gfx::Point3F host_location_3f(root_location);
60 host->GetRootTransform().TransformPoint(&host_location_3f);
61 host->MoveCursorToHostLocation(
62 gfx::ToCeiledPoint(host_location_3f.AsPointF()));
65 } // namespace
67 namespace ash {
69 ////////////////////////////////////////////////////////////////////////////////
70 // MagnificationControllerImpl:
72 class MagnificationControllerImpl : virtual public MagnificationController,
73 public ui::EventHandler,
74 public ui::ImplicitAnimationObserver,
75 public aura::WindowObserver,
76 public ui::InputMethodObserver {
77 public:
78 MagnificationControllerImpl();
79 ~MagnificationControllerImpl() override;
81 // MagnificationController overrides:
82 void SetEnabled(bool enabled) override;
83 bool IsEnabled() const override;
84 void SetScale(float scale, bool animate) override;
85 float GetScale() const override { return scale_; }
86 void MoveWindow(int x, int y, bool animate) override;
87 void MoveWindow(const gfx::Point& point, bool animate) override;
88 gfx::Point GetWindowPosition() const override {
89 return gfx::ToFlooredPoint(origin_);
91 void SetScrollDirection(ScrollDirection direction) override;
92 gfx::Rect GetViewportRect() const override;
93 void HandleFocusedNodeChanged(
94 bool is_editable_node,
95 const gfx::Rect& node_bounds_in_screen) override;
97 // For test
98 gfx::Point GetPointOfInterestForTesting() override {
99 return point_of_interest_;
102 bool IsOnAnimationForTesting() const override { return is_on_animation_; }
104 private:
105 // ui::ImplicitAnimationObserver overrides:
106 void OnImplicitAnimationsCompleted() override;
108 // aura::WindowObserver overrides:
109 void OnWindowDestroying(aura::Window* root_window) override;
110 void OnWindowBoundsChanged(aura::Window* window,
111 const gfx::Rect& old_bounds,
112 const gfx::Rect& new_bounds) override;
114 // Redraws the magnification window with the given origin position and the
115 // given scale. Returns true if the window is changed; otherwise, false.
116 // These methods should be called internally just after the scale and/or
117 // the position are changed to redraw the window.
118 bool Redraw(const gfx::PointF& position, float scale, bool animate);
119 bool RedrawDIP(const gfx::PointF& position, float scale, bool animate);
121 // 1) If the screen is scrolling (i.e. animating) and should scroll further,
122 // it does nothing.
123 // 2) If the screen is scrolling (i.e. animating) and the direction is NONE,
124 // it stops the scrolling animation.
125 // 3) If the direction is set to value other than NONE, it starts the
126 // scrolling/ animation towards that direction.
127 void StartOrStopScrollIfNecessary();
129 // Redraw with the given zoom scale keeping the mouse cursor location. In
130 // other words, zoom (or unzoom) centering around the cursor.
131 void RedrawKeepingMousePosition(float scale, bool animate);
133 void OnMouseMove(const gfx::Point& location);
135 // Move the mouse cursot to the given point. Actual move will be done when
136 // the animation is completed. This should be called after animation is
137 // started.
138 void AfterAnimationMoveCursorTo(const gfx::Point& location);
140 // Switch Magnified RootWindow to |new_root_window|. This does following:
141 // - Unzoom the current root_window.
142 // - Zoom the given new root_window |new_root_window|.
143 // - Switch the target window from current window to |new_root_window|.
144 void SwitchTargetRootWindow(aura::Window* new_root_window,
145 bool redraw_original_root_window);
147 // Returns if the magnification scale is 1.0 or not (larger then 1.0).
148 bool IsMagnified() const;
150 // Returns the rect of the magnification window.
151 gfx::RectF GetWindowRectDIP(float scale) const;
152 // Returns the size of the root window.
153 gfx::Size GetHostSizeDIP() const;
155 // Correct the given scale value if necessary.
156 void ValidateScale(float* scale);
158 // ui::EventHandler overrides:
159 void OnMouseEvent(ui::MouseEvent* event) override;
160 void OnScrollEvent(ui::ScrollEvent* event) override;
161 void OnTouchEvent(ui::TouchEvent* event) override;
163 // Moves the view port when |point| is located within
164 // |x_panning_margin| and |y_pannin_margin| to the edge of the visible
165 // window region. The view port will be moved so that the |point| will be
166 // moved to the point where it has |x_target_margin| and |y_target_margin|
167 // to the edge of the visible region.
168 void MoveMagnifierWindowFollowPoint(const gfx::Point& point,
169 int x_panning_margin,
170 int y_panning_margin,
171 int x_target_margin,
172 int y_target_margin);
174 // Moves the viewport so that |rect| is fully visible. If |rect| is larger
175 // than the viewport horizontally or vertically, the viewport will be moved
176 // to center the |rect| in that dimension.
177 void MoveMagnifierWindowFollowRect(const gfx::Rect& rect);
179 // ui::InputMethodObserver:
180 void OnTextInputTypeChanged(const ui::TextInputClient* client) override {}
181 void OnFocus() override {}
182 void OnBlur() override {}
183 void OnTextInputStateChanged(const ui::TextInputClient* client) override {}
184 void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {}
185 void OnShowImeIfNeeded() override {}
186 void OnCaretBoundsChanged(const ui::TextInputClient* client) override;
188 // Target root window. This must not be NULL.
189 aura::Window* root_window_;
191 // True if the magnified window is currently animating a change. Otherwise,
192 // false.
193 bool is_on_animation_;
195 bool is_enabled_;
197 // True if the cursor needs to move the given position after the animation
198 // will be finished. When using this, set |position_after_animation_| as well.
199 bool move_cursor_after_animation_;
200 // Stores the position of cursor to be moved after animation.
201 gfx::Point position_after_animation_;
203 // Stores the last mouse cursor (or last touched) location. This value is
204 // used on zooming to keep this location visible.
205 gfx::Point point_of_interest_;
207 // Current scale, origin (left-top) position of the magnification window.
208 float scale_;
209 gfx::PointF origin_;
211 ScrollDirection scroll_direction_;
213 ui::InputMethod* input_method_; // Not owned.
215 DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl);
218 ////////////////////////////////////////////////////////////////////////////////
219 // MagnificationControllerImpl:
221 MagnificationControllerImpl::MagnificationControllerImpl()
222 : root_window_(Shell::GetPrimaryRootWindow()),
223 is_on_animation_(false),
224 is_enabled_(false),
225 move_cursor_after_animation_(false),
226 scale_(kNonMagnifiedScale),
227 scroll_direction_(SCROLL_NONE),
228 input_method_(NULL) {
229 Shell::GetInstance()->AddPreTargetHandler(this);
230 root_window_->AddObserver(this);
231 point_of_interest_ = root_window_->bounds().CenterPoint();
234 MagnificationControllerImpl::~MagnificationControllerImpl() {
235 if (input_method_)
236 input_method_->RemoveObserver(this);
238 root_window_->RemoveObserver(this);
240 Shell::GetInstance()->RemovePreTargetHandler(this);
243 void MagnificationControllerImpl::RedrawKeepingMousePosition(
244 float scale, bool animate) {
245 gfx::Point mouse_in_root = point_of_interest_;
247 // mouse_in_root is invalid value when the cursor is hidden.
248 if (!root_window_->bounds().Contains(mouse_in_root))
249 mouse_in_root = root_window_->bounds().CenterPoint();
251 const gfx::PointF origin =
252 gfx::PointF(mouse_in_root.x() -
253 (scale_ / scale) * (mouse_in_root.x() - origin_.x()),
254 mouse_in_root.y() -
255 (scale_ / scale) * (mouse_in_root.y() - origin_.y()));
256 bool changed = RedrawDIP(origin, scale, animate);
257 if (changed)
258 AfterAnimationMoveCursorTo(mouse_in_root);
261 bool MagnificationControllerImpl::Redraw(const gfx::PointF& position,
262 float scale,
263 bool animate) {
264 const gfx::PointF position_in_dip =
265 ui::ConvertPointToDIP(root_window_->layer(), position);
266 return RedrawDIP(position_in_dip, scale, animate);
269 bool MagnificationControllerImpl::RedrawDIP(const gfx::PointF& position_in_dip,
270 float scale,
271 bool animate) {
272 DCHECK(root_window_);
274 float x = position_in_dip.x();
275 float y = position_in_dip.y();
277 ValidateScale(&scale);
279 if (x < 0)
280 x = 0;
281 if (y < 0)
282 y = 0;
284 const gfx::Size host_size_in_dip = GetHostSizeDIP();
285 const gfx::SizeF window_size_in_dip = GetWindowRectDIP(scale).size();
286 float max_x = host_size_in_dip.width() - window_size_in_dip.width();
287 float max_y = host_size_in_dip.height() - window_size_in_dip.height();
288 if (x > max_x)
289 x = max_x;
290 if (y > max_y)
291 y = max_y;
293 // Does nothing if both the origin and the scale are not changed.
294 if (origin_.x() == x &&
295 origin_.y() == y &&
296 scale == scale_) {
297 return false;
300 origin_.set_x(x);
301 origin_.set_y(y);
302 scale_ = scale;
304 // Creates transform matrix.
305 gfx::Transform transform;
306 // Flips the signs intentionally to convert them from the position of the
307 // magnification window.
308 transform.Scale(scale_, scale_);
309 transform.Translate(-origin_.x(), -origin_.y());
311 ui::ScopedLayerAnimationSettings settings(
312 root_window_->layer()->GetAnimator());
313 settings.AddObserver(this);
314 settings.SetPreemptionStrategy(
315 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
316 settings.SetTweenType(gfx::Tween::EASE_OUT);
317 settings.SetTransitionDuration(
318 base::TimeDelta::FromMilliseconds(animate ? 100 : 0));
320 gfx::Display display =
321 Shell::GetScreen()->GetDisplayNearestWindow(root_window_);
322 scoped_ptr<RootWindowTransformer> transformer(
323 CreateRootWindowTransformerForDisplay(root_window_, display));
324 GetRootWindowController(root_window_)->ash_host()->SetRootWindowTransformer(
325 transformer.Pass());
327 if (animate)
328 is_on_animation_ = true;
330 return true;
333 void MagnificationControllerImpl::StartOrStopScrollIfNecessary() {
334 // This value controls the scrolling speed.
335 const int kMoveOffset = 40;
336 if (is_on_animation_) {
337 if (scroll_direction_ == SCROLL_NONE)
338 root_window_->layer()->GetAnimator()->StopAnimating();
339 return;
342 gfx::PointF new_origin = origin_;
343 switch (scroll_direction_) {
344 case SCROLL_NONE:
345 // No need to take action.
346 return;
347 case SCROLL_LEFT:
348 new_origin.Offset(-kMoveOffset, 0);
349 break;
350 case SCROLL_RIGHT:
351 new_origin.Offset(kMoveOffset, 0);
352 break;
353 case SCROLL_UP:
354 new_origin.Offset(0, -kMoveOffset);
355 break;
356 case SCROLL_DOWN:
357 new_origin.Offset(0, kMoveOffset);
358 break;
360 RedrawDIP(new_origin, scale_, true);
363 void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) {
364 DCHECK(root_window_);
366 gfx::Point mouse(location);
367 int margin = kPanningMergin / scale_; // No need to consider DPI.
368 MoveMagnifierWindowFollowPoint(mouse, margin, margin, margin, margin);
371 gfx::Rect MagnificationControllerImpl::GetViewportRect() const {
372 return gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
375 void MagnificationControllerImpl::HandleFocusedNodeChanged(
376 bool is_editable_node,
377 const gfx::Rect& node_bounds_in_screen) {
378 // The editable node is handled by OnCaretBoundsChanged.
379 if (is_editable_node)
380 return;
382 gfx::Rect node_bounds_in_root =
383 ScreenUtil::ConvertRectFromScreen(root_window_, node_bounds_in_screen);
384 if (GetViewportRect().Contains(node_bounds_in_root))
385 return;
387 MoveMagnifierWindowFollowRect(node_bounds_in_root);
390 void MagnificationControllerImpl::AfterAnimationMoveCursorTo(
391 const gfx::Point& location) {
392 DCHECK(root_window_);
394 aura::client::CursorClient* cursor_client =
395 aura::client::GetCursorClient(root_window_);
396 if (cursor_client) {
397 // When cursor is invisible, do not move or show the cursor after the
398 // animation.
399 if (!cursor_client->IsCursorVisible())
400 return;
401 cursor_client->DisableMouseEvents();
403 move_cursor_after_animation_ = true;
404 position_after_animation_ = location;
407 gfx::Size MagnificationControllerImpl::GetHostSizeDIP() const {
408 return root_window_->bounds().size();
411 gfx::RectF MagnificationControllerImpl::GetWindowRectDIP(float scale) const {
412 const gfx::Size size_in_dip = root_window_->bounds().size();
413 const float width = size_in_dip.width() / scale;
414 const float height = size_in_dip.height() / scale;
416 return gfx::RectF(origin_.x(), origin_.y(), width, height);
419 bool MagnificationControllerImpl::IsMagnified() const {
420 return scale_ >= kMinMagnifiedScaleThreshold;
423 void MagnificationControllerImpl::ValidateScale(float* scale) {
424 // Adjust the scale to just |kNonMagnifiedScale| if scale is smaller than
425 // |kMinMagnifiedScaleThreshold|;
426 if (*scale < kMinMagnifiedScaleThreshold)
427 *scale = kNonMagnifiedScale;
429 // Adjust the scale to just |kMinMagnifiedScale| if scale is bigger than
430 // |kMinMagnifiedScaleThreshold|;
431 if (*scale > kMaxMagnifiedScaleThreshold)
432 *scale = kMaxMagnifiedScale;
434 DCHECK(kNonMagnifiedScale <= *scale && *scale <= kMaxMagnifiedScale);
437 void MagnificationControllerImpl::OnImplicitAnimationsCompleted() {
438 if (!is_on_animation_)
439 return;
441 if (move_cursor_after_animation_) {
442 MoveCursorTo(root_window_->GetHost(), position_after_animation_);
443 move_cursor_after_animation_ = false;
445 aura::client::CursorClient* cursor_client =
446 aura::client::GetCursorClient(root_window_);
447 if (cursor_client)
448 cursor_client->EnableMouseEvents();
451 is_on_animation_ = false;
453 StartOrStopScrollIfNecessary();
456 void MagnificationControllerImpl::OnWindowDestroying(
457 aura::Window* root_window) {
458 if (root_window == root_window_) {
459 // There must be at least one root window because this controller is
460 // destroyed before the root windows get destroyed.
461 DCHECK(root_window);
463 aura::Window* target_root_window = Shell::GetTargetRootWindow();
464 CHECK(target_root_window);
466 // The destroyed root window must not be target.
467 CHECK_NE(target_root_window, root_window);
468 // Don't redraw the old root window as it's being destroyed.
469 SwitchTargetRootWindow(target_root_window, false);
470 point_of_interest_ = target_root_window->bounds().CenterPoint();
474 void MagnificationControllerImpl::OnWindowBoundsChanged(
475 aura::Window* window,
476 const gfx::Rect& old_bounds,
477 const gfx::Rect& new_bounds) {
478 // TODO(yoshiki): implement here. crbug.com/230979
481 void MagnificationControllerImpl::SwitchTargetRootWindow(
482 aura::Window* new_root_window,
483 bool redraw_original_root_window) {
484 DCHECK(new_root_window);
486 if (new_root_window == root_window_)
487 return;
489 // Stores the previous scale.
490 float scale = GetScale();
492 // Unmagnify the previous root window.
493 root_window_->RemoveObserver(this);
494 if (redraw_original_root_window)
495 RedrawKeepingMousePosition(1.0f, true);
497 root_window_ = new_root_window;
498 RedrawKeepingMousePosition(scale, true);
499 root_window_->AddObserver(this);
502 ////////////////////////////////////////////////////////////////////////////////
503 // MagnificationControllerImpl: MagnificationController implementation
505 void MagnificationControllerImpl::SetScale(float scale, bool animate) {
506 if (!is_enabled_)
507 return;
509 ValidateScale(&scale);
510 Shell::GetInstance()->accessibility_delegate()->
511 SaveScreenMagnifierScale(scale);
512 RedrawKeepingMousePosition(scale, animate);
515 void MagnificationControllerImpl::MoveWindow(int x, int y, bool animate) {
516 if (!is_enabled_)
517 return;
519 Redraw(gfx::Point(x, y), scale_, animate);
522 void MagnificationControllerImpl::MoveWindow(const gfx::Point& point,
523 bool animate) {
524 if (!is_enabled_)
525 return;
527 Redraw(point, scale_, animate);
530 void MagnificationControllerImpl::SetScrollDirection(
531 ScrollDirection direction) {
532 scroll_direction_ = direction;
533 StartOrStopScrollIfNecessary();
536 void MagnificationControllerImpl::SetEnabled(bool enabled) {
537 Shell* shell = Shell::GetInstance();
538 if (enabled) {
539 if (!input_method_) {
540 input_method_ =
541 root_window_->GetProperty(aura::client::kRootWindowInputMethodKey);
542 if (input_method_)
543 input_method_->AddObserver(this);
546 float scale =
547 Shell::GetInstance()->accessibility_delegate()->
548 GetSavedScreenMagnifierScale();
549 if (scale <= 0.0f)
550 scale = kInitialMagnifiedScale;
551 ValidateScale(&scale);
553 // Do nothing, if already enabled with same scale.
554 if (is_enabled_ && scale == scale_)
555 return;
557 is_enabled_ = enabled;
558 RedrawKeepingMousePosition(scale, true);
559 shell->accessibility_delegate()->SaveScreenMagnifierScale(scale);
560 } else {
561 // Do nothing, if already disabled.
562 if (!is_enabled_)
563 return;
565 if (input_method_) {
566 input_method_->RemoveObserver(this);
567 input_method_ = NULL;
570 RedrawKeepingMousePosition(kNonMagnifiedScale, true);
571 is_enabled_ = enabled;
575 bool MagnificationControllerImpl::IsEnabled() const {
576 return is_enabled_;
579 ////////////////////////////////////////////////////////////////////////////////
580 // MagnificationControllerImpl: aura::EventFilter implementation
582 void MagnificationControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
583 aura::Window* target = static_cast<aura::Window*>(event->target());
584 aura::Window* current_root = target->GetRootWindow();
585 gfx::Rect root_bounds = current_root->bounds();
587 if (root_bounds.Contains(event->root_location())) {
588 // This must be before |SwitchTargetRootWindow()|.
589 if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
590 point_of_interest_ = event->root_location();
592 if (current_root != root_window_) {
593 DCHECK(current_root);
594 SwitchTargetRootWindow(current_root, true);
597 if (IsMagnified() && event->type() == ui::ET_MOUSE_MOVED)
598 OnMouseMove(event->root_location());
602 void MagnificationControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
603 if (event->IsAltDown() && event->IsControlDown()) {
604 if (event->type() == ui::ET_SCROLL_FLING_START ||
605 event->type() == ui::ET_SCROLL_FLING_CANCEL) {
606 event->StopPropagation();
607 return;
610 if (event->type() == ui::ET_SCROLL) {
611 ui::ScrollEvent* scroll_event = static_cast<ui::ScrollEvent*>(event);
612 float scale = GetScale();
613 scale += scroll_event->y_offset() * kScrollScaleChangeFactor;
614 SetScale(scale, true);
615 event->StopPropagation();
616 return;
621 void MagnificationControllerImpl::OnTouchEvent(ui::TouchEvent* event) {
622 aura::Window* target = static_cast<aura::Window*>(event->target());
623 aura::Window* current_root = target->GetRootWindow();
624 if (current_root == root_window_) {
625 gfx::Rect root_bounds = current_root->bounds();
626 if (root_bounds.Contains(event->root_location()))
627 point_of_interest_ = event->root_location();
631 void MagnificationControllerImpl::MoveMagnifierWindowFollowPoint(
632 const gfx::Point& point,
633 int x_panning_margin,
634 int y_panning_margin,
635 int x_target_margin,
636 int y_target_margin) {
637 DCHECK(root_window_);
638 bool start_zoom = false;
640 const gfx::Rect window_rect = GetViewportRect();
641 const int left = window_rect.x();
642 const int right = window_rect.right();
644 int x_diff = 0;
645 if (point.x() < left + x_panning_margin) {
646 // Panning left.
647 x_diff = point.x() - (left + x_target_margin);
648 start_zoom = true;
649 } else if (right - x_panning_margin < point.x()) {
650 // Panning right.
651 x_diff = point.x() - (right - x_target_margin);
652 start_zoom = true;
654 int x = left + x_diff;
656 const int top = window_rect.y();
657 const int bottom = window_rect.bottom();
659 int y_diff = 0;
660 if (point.y() < top + y_panning_margin) {
661 // Panning up.
662 y_diff = point.y() - (top + y_target_margin);
663 start_zoom = true;
664 } else if (bottom - y_panning_margin < point.y()) {
665 // Panning down.
666 y_diff = point.y() - (bottom - y_target_margin);
667 start_zoom = true;
669 int y = top + y_diff;
670 if (start_zoom && !is_on_animation_) {
671 // No animation on panning.
672 bool animate = false;
673 bool ret = RedrawDIP(gfx::Point(x, y), scale_, animate);
675 if (ret) {
676 // If the magnified region is moved, hides the mouse cursor and moves it.
677 if (x_diff != 0 || y_diff != 0)
678 MoveCursorTo(root_window_->GetHost(), point);
683 void MagnificationControllerImpl::MoveMagnifierWindowFollowRect(
684 const gfx::Rect& rect) {
685 DCHECK(root_window_);
686 bool should_pan = false;
688 const gfx::Rect viewport_rect = GetViewportRect();
689 const int left = viewport_rect.x();
690 const int right = viewport_rect.right();
691 const gfx::Point rect_center = rect.CenterPoint();
692 const gfx::Point window_center = viewport_rect.CenterPoint();
694 int x = left;
695 if (rect.x() < left || right < rect.right()) {
696 // Panning horizontally.
697 x = rect_center.x() - viewport_rect.width() / 2;
698 should_pan = true;
701 const int top = viewport_rect.y();
702 const int bottom = viewport_rect.bottom();
704 int y = top;
705 if (rect.y() < top || bottom < rect.bottom()) {
706 // Panning vertically.
707 y = rect_center.y() - viewport_rect.height() / 2;
708 should_pan = true;
711 if (should_pan) {
712 if (is_on_animation_) {
713 root_window_->layer()->GetAnimator()->StopAnimating();
714 is_on_animation_ = false;
716 RedrawDIP(gfx::Point(x, y), scale_, false); // No animation on panning.
720 void MagnificationControllerImpl::OnCaretBoundsChanged(
721 const ui::TextInputClient* client) {
722 // caret bounds in screen coordinates.
723 const gfx::Rect caret_bounds = client->GetCaretBounds();
724 // Note: OnCaretBoundsChanged could be fired OnTextInputTypeChanged during
725 // which the caret position is not set a meaning position, and we do not
726 // need to adjust the view port position based on the bogus caret position.
727 // This is only a transition period, the caret position will be fixed upon
728 // focusing right after.
729 if (caret_bounds.width() == 0 && caret_bounds.height() == 0)
730 return;
732 gfx::Point caret_origin = caret_bounds.origin();
733 // caret_origin in |root_window_| coordinates.
734 wm::ConvertPointFromScreen(root_window_, &caret_origin);
736 // Visible window_rect in |root_window_| coordinates.
737 const gfx::Rect visible_window_rect = GetViewportRect();
739 const int panning_margin = kCaretPanningMargin / scale_;
740 MoveMagnifierWindowFollowPoint(caret_origin, panning_margin, panning_margin,
741 visible_window_rect.width() / 2,
742 visible_window_rect.height() / 2);
745 ////////////////////////////////////////////////////////////////////////////////
746 // MagnificationController:
748 // static
749 MagnificationController* MagnificationController::CreateInstance() {
750 return new MagnificationControllerImpl();
753 } // namespace ash