[Sync] Test local bookmark sync on Android
[chromium-blink-merge.git] / ash / magnifier / magnification_controller.cc
blobfee51d228eb3f3ac3cf1213f891f83e20b05ce58
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"
41 namespace {
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
60 // input focus.
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()));
78 } // namespace
80 namespace ash {
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 {
90 public:
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;
114 // For test
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;
125 private:
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|
145 // will be ignored.
146 bool RedrawDIP(const gfx::PointF& position_in_dip,
147 float scale,
148 int animation_in_ms,
149 gfx::Tween::Type tween_type);
151 // 1) If the screen is scrolling (i.e. animating) and should scroll further,
152 // it does nothing.
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
167 // started.
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,
194 int x_target_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
206 // follow the caret.
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,
222 // false.
223 bool is_on_animation_;
225 bool is_enabled_;
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.
240 float scale_;
241 gfx::PointF origin_;
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
254 // mode.
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),
266 is_enabled_(false),
267 keep_focus_centered_(false),
268 move_cursor_after_animation_(false),
269 scale_(kNonMagnifiedScale),
270 scroll_direction_(SCROLL_NONE),
271 input_method_(NULL),
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() {
279 if (input_method_)
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()),
298 mouse_in_root.y() -
299 (scale_ / scale) * (mouse_in_root.y() - origin_.y()));
300 bool changed = RedrawDIP(origin, scale,
301 animate ? kDefaultAnimationDurationInMs : 0,
302 kDefaultAnimationTweenType);
303 if (changed)
304 AfterAnimationMoveCursorTo(mouse_in_root);
307 bool MagnificationControllerImpl::Redraw(const gfx::PointF& position,
308 float scale,
309 bool animate) {
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,
318 float scale,
319 int duration_in_ms,
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);
328 if (x < 0)
329 x = 0;
330 if (y < 0)
331 y = 0;
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();
337 if (x > max_x)
338 x = max_x;
339 if (y > max_y)
340 y = max_y;
342 // Does nothing if both the origin and the scale are not changed.
343 if (origin_.x() == x &&
344 origin_.y() == y &&
345 scale == scale_) {
346 return false;
349 origin_.set_x(x);
350 origin_.set_y(y);
351 scale_ = scale;
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(
374 transformer.Pass());
376 if (duration_in_ms > 0)
377 is_on_animation_ = true;
379 return 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();
388 return;
391 gfx::PointF new_origin = origin_;
392 switch (scroll_direction_) {
393 case SCROLL_NONE:
394 // No need to take action.
395 return;
396 case SCROLL_LEFT:
397 new_origin.Offset(-kMoveOffset, 0);
398 break;
399 case SCROLL_RIGHT:
400 new_origin.Offset(kMoveOffset, 0);
401 break;
402 case SCROLL_UP:
403 new_origin.Offset(0, -kMoveOffset);
404 break;
405 case SCROLL_DOWN:
406 new_origin.Offset(0, kMoveOffset);
407 break;
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)
430 return;
432 gfx::Rect node_bounds_in_root =
433 ScreenUtil::ConvertRectFromScreen(root_window_, node_bounds_in_screen);
434 if (GetViewportRect().Contains(node_bounds_in_root))
435 return;
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_)
446 return;
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_);
466 if (cursor_client) {
467 // When cursor is invisible, do not move or show the cursor after the
468 // animation.
469 if (!cursor_client->IsCursorVisible())
470 return;
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_)
509 return;
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_);
517 if (cursor_client)
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.
531 DCHECK(root_window);
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) {
555 if (!is_enabled_)
556 return;
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) {
565 if (!is_enabled_)
566 return;
568 Redraw(gfx::Point(x, y), scale_, animate);
571 void MagnificationControllerImpl::MoveWindow(const gfx::Point& point,
572 bool animate) {
573 if (!is_enabled_)
574 return;
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();
587 if (enabled) {
588 if (!input_method_) {
589 input_method_ =
590 root_window_->GetProperty(aura::client::kRootWindowInputMethodKey);
591 if (input_method_)
592 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);
616 input_method_ = NULL;
619 RedrawKeepingMousePosition(kNonMagnifiedScale, true);
620 is_enabled_ = enabled;
624 bool MagnificationControllerImpl::IsEnabled() const {
625 return is_enabled_;
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();
665 return;
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();
674 return;
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,
693 int x_target_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();
702 int x_diff = 0;
703 if (point.x() < left + x_panning_margin) {
704 // Panning left.
705 x_diff = point.x() - (left + x_target_margin);
706 start_zoom = true;
707 } else if (right - x_panning_margin < point.x()) {
708 // Panning right.
709 x_diff = point.x() - (right - x_target_margin);
710 start_zoom = true;
712 int x = left + x_diff;
714 const int top = window_rect.y();
715 const int bottom = window_rect.bottom();
717 int y_diff = 0;
718 if (point.y() < top + y_panning_margin) {
719 // Panning up.
720 y_diff = point.y() - (top + y_target_margin);
721 start_zoom = true;
722 } else if (bottom - y_panning_margin < point.y()) {
723 // Panning down.
724 y_diff = point.y() - (bottom - y_target_margin);
725 start_zoom = true;
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);
733 if (ret) {
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())
747 return;
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();
768 int x = left;
769 if (rect.x() < left || right < rect.right()) {
770 // Panning horizontally.
771 x = rect_center.x() - viewport_rect.width() / 2;
772 should_pan = true;
775 const int top = viewport_rect.y();
776 const int bottom = viewport_rect.bottom();
778 int y = top;
779 if (rect.y() < top || bottom < rect.bottom()) {
780 // Panning vertically.
781 y = rect_center.y() - viewport_rect.height() / 2;
782 should_pan = true;
785 if (should_pan) {
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)
810 return;
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_,
823 panning_margin,
824 panning_margin,
825 visible_window_rect.width() / 2,
826 visible_window_rect.height() / 2);
827 return;
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(
843 FROM_HERE,
844 base::TimeDelta::FromMilliseconds(
845 disable_move_magnifier_delay_ ? 0 : kMoveMagnifierDelayInMs),
846 this, &MagnificationControllerImpl::OnMoveMagnifierTimer);
849 ////////////////////////////////////////////////////////////////////////////////
850 // MagnificationController:
852 // static
853 MagnificationController* MagnificationController::CreateInstance() {
854 return new MagnificationControllerImpl();
857 } // namespace ash