Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ash / magnifier / magnification_controller.cc
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.
4
5 #include "ash/magnifier/magnification_controller.h"
6
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/shell.h"
12 #include "ash/system/tray/system_tray_delegate.h"
13 #include "base/command_line.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "ui/aura/client/cursor_client.h"
16 #include "ui/aura/root_window.h"
17 #include "ui/aura/root_window_transformer.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_property.h"
20 #include "ui/compositor/dip_util.h"
21 #include "ui/compositor/layer.h"
22 #include "ui/compositor/layer_animation_observer.h"
23 #include "ui/compositor/scoped_layer_animation_settings.h"
24 #include "ui/events/event.h"
25 #include "ui/events/event_handler.h"
26 #include "ui/gfx/point3_f.h"
27 #include "ui/gfx/point_conversions.h"
28 #include "ui/gfx/point_f.h"
29 #include "ui/gfx/rect_conversions.h"
30 #include "ui/gfx/screen.h"
31 #include "ui/views/corewm/compound_event_filter.h"
32
33 namespace {
34
35 const float kMaxMagnifiedScale = 4.0f;
36 const float kMaxMagnifiedScaleThreshold = 4.0f;
37 const float kMinMagnifiedScaleThreshold = 1.1f;
38 const float kNonMagnifiedScale = 1.0f;
39
40 const float kInitialMagnifiedScale = 2.0f;
41 const float kScrollScaleChangeFactor = 0.05f;
42
43 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of
44 // |kPanningMergin| from the edge, the view-port moves.
45 const int kPanningMergin = 100;
46
47 void MoveCursorTo(aura::RootWindow* root_window,
48                   const gfx::Point& root_location) {
49   gfx::Point3F host_location_3f(root_location);
50   root_window->host()->GetRootTransform().TransformPoint(&host_location_3f);
51   root_window->host()->MoveCursorToHostLocation(
52       gfx::ToCeiledPoint(host_location_3f.AsPointF()));
53 }
54
55 }  // namespace
56
57 namespace ash {
58
59 ////////////////////////////////////////////////////////////////////////////////
60 // MagnificationControllerImpl:
61
62 class MagnificationControllerImpl : virtual public MagnificationController,
63                                     public ui::EventHandler,
64                                     public ui::ImplicitAnimationObserver,
65                                     public aura::WindowObserver {
66  public:
67   MagnificationControllerImpl();
68   virtual ~MagnificationControllerImpl();
69
70   // MagnificationController overrides:
71   virtual void SetEnabled(bool enabled) OVERRIDE;
72   virtual bool IsEnabled() const OVERRIDE;
73   virtual void SetScale(float scale, bool animate) OVERRIDE;
74   virtual float GetScale() const OVERRIDE { return scale_; }
75   virtual void MoveWindow(int x, int y, bool animate) OVERRIDE;
76   virtual void MoveWindow(const gfx::Point& point, bool animate) OVERRIDE;
77   virtual gfx::Point GetWindowPosition() const OVERRIDE {
78     return gfx::ToFlooredPoint(origin_);
79   }
80   virtual void SetScrollDirection(ScrollDirection direction) OVERRIDE;
81
82   // For test
83   virtual gfx::Point GetPointOfInterestForTesting() OVERRIDE {
84     return point_of_interest_;
85   }
86
87  private:
88   // ui::ImplicitAnimationObserver overrides:
89   virtual void OnImplicitAnimationsCompleted() OVERRIDE;
90
91   // aura::WindowObserver overrides:
92   virtual void OnWindowDestroying(aura::Window* root_window) OVERRIDE;
93   virtual void OnWindowBoundsChanged(aura::Window* window,
94                                      const gfx::Rect& old_bounds,
95                                      const gfx::Rect& new_bounds) OVERRIDE;
96
97   // Redraws the magnification window with the given origin position and the
98   // given scale. Returns true if the window is changed; otherwise, false.
99   // These methods should be called internally just after the scale and/or
100   // the position are changed to redraw the window.
101   bool Redraw(const gfx::PointF& position, float scale, bool animate);
102   bool RedrawDIP(const gfx::PointF& position, float scale, bool animate);
103
104   // 1) If the screen is scrolling (i.e. animating) and should scroll further,
105   // it does nothing.
106   // 2) If the screen is scrolling (i.e. animating) and the direction is NONE,
107   // it stops the scrolling animation.
108   // 3) If the direction is set to value other than NONE, it starts the
109   // scrolling/ animation towards that direction.
110   void StartOrStopScrollIfNecessary();
111
112   // Redraw with the given zoom scale keeping the mouse cursor location. In
113   // other words, zoom (or unzoom) centering around the cursor.
114   void RedrawKeepingMousePosition(float scale, bool animate);
115
116   void OnMouseMove(const gfx::Point& location);
117
118   // Move the mouse cursot to the given point. Actual move will be done when
119   // the animation is completed. This should be called after animation is
120   // started.
121   void AfterAnimationMoveCursorTo(const gfx::Point& location);
122
123   // Switch Magnified RootWindow to |new_root_window|. This does following:
124   //  - Unzoom the current root_window.
125   //  - Zoom the given new root_window |new_root_window|.
126   //  - Switch the target window from current window to |new_root_window|.
127   void SwitchTargetRootWindow(aura::Window* new_root_window,
128                               bool redraw_original_root_window);
129
130   // Returns if the magnification scale is 1.0 or not (larger then 1.0).
131   bool IsMagnified() const;
132
133   // Returns the rect of the magnification window.
134   gfx::RectF GetWindowRectDIP(float scale) const;
135   // Returns the size of the root window.
136   gfx::Size GetHostSizeDIP() const;
137
138   // Correct the givin scale value if nessesary.
139   void ValidateScale(float* scale);
140
141   // ui::EventHandler overrides:
142   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
143   virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
144   virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
145
146   // Target root window. This must not be NULL.
147   aura::Window* root_window_;
148
149   // True if the magnified window is currently animating a change. Otherwise,
150   // false.
151   bool is_on_animation_;
152
153   bool is_enabled_;
154
155   // True if the cursor needs to move the given position after the animation
156   // will be finished. When using this, set |position_after_animation_| as well.
157   bool move_cursor_after_animation_;
158   // Stores the position of cursor to be moved after animation.
159   gfx::Point position_after_animation_;
160
161   // Stores the last mouse cursor (or last touched) location. This value is
162   // used on zooming to keep this location visible.
163   gfx::Point point_of_interest_;
164
165   // Current scale, origin (left-top) position of the magnification window.
166   float scale_;
167   gfx::PointF origin_;
168
169   ScrollDirection scroll_direction_;
170
171   DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl);
172 };
173
174 ////////////////////////////////////////////////////////////////////////////////
175 // MagnificationControllerImpl:
176
177 MagnificationControllerImpl::MagnificationControllerImpl()
178     : root_window_(Shell::GetPrimaryRootWindow()),
179       is_on_animation_(false),
180       is_enabled_(false),
181       move_cursor_after_animation_(false),
182       scale_(kNonMagnifiedScale),
183       scroll_direction_(SCROLL_NONE) {
184   Shell::GetInstance()->AddPreTargetHandler(this);
185   root_window_->AddObserver(this);
186   point_of_interest_ = root_window_->bounds().CenterPoint();
187 }
188
189 MagnificationControllerImpl::~MagnificationControllerImpl() {
190   root_window_->RemoveObserver(this);
191
192   Shell::GetInstance()->RemovePreTargetHandler(this);
193 }
194
195 void MagnificationControllerImpl::RedrawKeepingMousePosition(
196     float scale, bool animate) {
197   gfx::Point mouse_in_root = point_of_interest_;
198
199   // mouse_in_root is invalid value when the cursor is hidden.
200   if (!root_window_->bounds().Contains(mouse_in_root))
201     mouse_in_root = root_window_->bounds().CenterPoint();
202
203   const gfx::PointF origin =
204       gfx::PointF(mouse_in_root.x() -
205                       (scale_ / scale) * (mouse_in_root.x() - origin_.x()),
206                   mouse_in_root.y() -
207                       (scale_ / scale) * (mouse_in_root.y() - origin_.y()));
208   bool changed = RedrawDIP(origin, scale, animate);
209   if (changed)
210     AfterAnimationMoveCursorTo(mouse_in_root);
211 }
212
213 bool MagnificationControllerImpl::Redraw(const gfx::PointF& position,
214                                          float scale,
215                                          bool animate) {
216   const gfx::PointF position_in_dip =
217       ui::ConvertPointToDIP(root_window_->layer(), position);
218   return RedrawDIP(position_in_dip, scale, animate);
219 }
220
221 bool MagnificationControllerImpl::RedrawDIP(const gfx::PointF& position_in_dip,
222                                             float scale,
223                                             bool animate) {
224   DCHECK(root_window_);
225
226   float x = position_in_dip.x();
227   float y = position_in_dip.y();
228
229   ValidateScale(&scale);
230
231   if (x < 0)
232     x = 0;
233   if (y < 0)
234     y = 0;
235
236   const gfx::Size host_size_in_dip = GetHostSizeDIP();
237   const gfx::SizeF window_size_in_dip = GetWindowRectDIP(scale).size();
238   float max_x = host_size_in_dip.width() - window_size_in_dip.width();
239   float max_y = host_size_in_dip.height() - window_size_in_dip.height();
240   if (x > max_x)
241     x = max_x;
242   if (y > max_y)
243     y = max_y;
244
245   // Does nothing if both the origin and the scale are not changed.
246   if (origin_.x() == x  &&
247       origin_.y() == y &&
248       scale == scale_) {
249     return false;
250   }
251
252   origin_.set_x(x);
253   origin_.set_y(y);
254   scale_ = scale;
255
256   // Creates transform matrix.
257   gfx::Transform transform;
258   // Flips the signs intentionally to convert them from the position of the
259   // magnification window.
260   transform.Scale(scale_, scale_);
261   transform.Translate(-origin_.x(), -origin_.y());
262
263   ui::ScopedLayerAnimationSettings settings(
264       root_window_->layer()->GetAnimator());
265   settings.AddObserver(this);
266   settings.SetPreemptionStrategy(
267       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
268   settings.SetTweenType(gfx::Tween::EASE_OUT);
269   settings.SetTransitionDuration(
270       base::TimeDelta::FromMilliseconds(animate ? 100 : 0));
271
272   gfx::Display display =
273       Shell::GetScreen()->GetDisplayNearestWindow(root_window_);
274   scoped_ptr<aura::RootWindowTransformer> transformer(
275       internal::CreateRootWindowTransformerForDisplay(root_window_, display));
276   root_window_->GetDispatcher()->host()->SetRootWindowTransformer(
277       transformer.Pass());
278
279   if (animate)
280     is_on_animation_ = true;
281
282   return true;
283 }
284
285 void MagnificationControllerImpl::StartOrStopScrollIfNecessary() {
286   // This value controls the scrolling speed.
287   const int kMoveOffset = 40;
288   if (is_on_animation_) {
289     if (scroll_direction_ == SCROLL_NONE)
290       root_window_->layer()->GetAnimator()->StopAnimating();
291     return;
292   }
293
294   gfx::PointF new_origin = origin_;
295   switch (scroll_direction_) {
296     case SCROLL_NONE:
297       // No need to take action.
298       return;
299     case SCROLL_LEFT:
300       new_origin.Offset(-kMoveOffset, 0);
301       break;
302     case SCROLL_RIGHT:
303       new_origin.Offset(kMoveOffset, 0);
304       break;
305     case SCROLL_UP:
306       new_origin.Offset(0, -kMoveOffset);
307       break;
308     case SCROLL_DOWN:
309       new_origin.Offset(0, kMoveOffset);
310       break;
311   }
312   RedrawDIP(new_origin, scale_, true);
313 }
314
315 void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) {
316   DCHECK(root_window_);
317
318   gfx::Point mouse(location);
319
320   int x = origin_.x();
321   int y = origin_.y();
322   bool start_zoom = false;
323
324   const gfx::Rect window_rect = gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
325   const int left = window_rect.x();
326   const int right = window_rect.right();
327   int margin = kPanningMergin / scale_;  // No need to consider DPI.
328
329   int x_diff = 0;
330
331   if (mouse.x() < left + margin) {
332     // Panning left.
333     x_diff = mouse.x() - (left + margin);
334     start_zoom = true;
335   } else if (right - margin < mouse.x()) {
336     // Panning right.
337     x_diff = mouse.x() - (right - margin);
338     start_zoom = true;
339   }
340   x = left + x_diff;
341
342   const int top = window_rect.y();
343   const int bottom = window_rect.bottom();
344
345   int y_diff = 0;
346   if (mouse.y() < top + margin) {
347     // Panning up.
348     y_diff = mouse.y() - (top + margin);
349     start_zoom = true;
350   } else if (bottom - margin < mouse.y()) {
351     // Panning down.
352     y_diff = mouse.y() - (bottom - margin);
353     start_zoom = true;
354   }
355   y = top + y_diff;
356
357   if (start_zoom && !is_on_animation_) {
358     // No animation on panning.
359     bool animate = false;
360     bool ret = RedrawDIP(gfx::Point(x, y), scale_, animate);
361
362     if (ret) {
363       // If the magnified region is moved, hides the mouse cursor and moves it.
364       if (x_diff != 0 || y_diff != 0)
365         MoveCursorTo(root_window_->GetDispatcher(), mouse);
366     }
367   }
368 }
369
370 void MagnificationControllerImpl::AfterAnimationMoveCursorTo(
371     const gfx::Point& location) {
372   DCHECK(root_window_);
373
374   aura::client::CursorClient* cursor_client =
375       aura::client::GetCursorClient(root_window_);
376   if (cursor_client) {
377     // When cursor is invisible, do not move or show the cursor after the
378     // animation.
379     if (!cursor_client->IsCursorVisible())
380       return;
381     cursor_client->DisableMouseEvents();
382   }
383   move_cursor_after_animation_ = true;
384   position_after_animation_ = location;
385 }
386
387 gfx::Size MagnificationControllerImpl::GetHostSizeDIP() const {
388   return root_window_->bounds().size();
389 }
390
391 gfx::RectF MagnificationControllerImpl::GetWindowRectDIP(float scale) const {
392   const gfx::Size size_in_dip = root_window_->bounds().size();
393   const float width = size_in_dip.width() / scale;
394   const float height = size_in_dip.height() / scale;
395
396   return gfx::RectF(origin_.x(), origin_.y(), width, height);
397 }
398
399 bool MagnificationControllerImpl::IsMagnified() const {
400   return scale_ >= kMinMagnifiedScaleThreshold;
401 }
402
403 void MagnificationControllerImpl::ValidateScale(float* scale) {
404   // Adjust the scale to just |kNonMagnifiedScale| if scale is smaller than
405   // |kMinMagnifiedScaleThreshold|;
406   if (*scale < kMinMagnifiedScaleThreshold)
407     *scale = kNonMagnifiedScale;
408
409   // Adjust the scale to just |kMinMagnifiedScale| if scale is bigger than
410   // |kMinMagnifiedScaleThreshold|;
411   if (*scale > kMaxMagnifiedScaleThreshold)
412     *scale = kMaxMagnifiedScale;
413
414   DCHECK(kNonMagnifiedScale <= *scale && *scale <= kMaxMagnifiedScale);
415 }
416
417 void MagnificationControllerImpl::OnImplicitAnimationsCompleted() {
418   if (!is_on_animation_)
419     return;
420
421   if (move_cursor_after_animation_) {
422     MoveCursorTo(root_window_->GetDispatcher(), position_after_animation_);
423     move_cursor_after_animation_ = false;
424
425     aura::client::CursorClient* cursor_client =
426         aura::client::GetCursorClient(root_window_);
427     if (cursor_client)
428       cursor_client->EnableMouseEvents();
429   }
430
431   is_on_animation_ = false;
432
433   StartOrStopScrollIfNecessary();
434 }
435
436 void MagnificationControllerImpl::OnWindowDestroying(
437     aura::Window* root_window) {
438   if (root_window == root_window_) {
439     // There must be at least one root window because this controller is
440     // destroyed before the root windows get destroyed.
441     DCHECK(root_window);
442
443     aura::Window* target_root_window = Shell::GetTargetRootWindow();
444     CHECK(target_root_window);
445
446     // The destroyed root window must not be target.
447     CHECK_NE(target_root_window, root_window);
448     // Don't redraw the old root window as it's being destroyed.
449     SwitchTargetRootWindow(target_root_window, false);
450     point_of_interest_ = target_root_window->bounds().CenterPoint();
451   }
452 }
453
454 void MagnificationControllerImpl::OnWindowBoundsChanged(
455     aura::Window* window,
456     const gfx::Rect& old_bounds,
457     const gfx::Rect& new_bounds) {
458   // TODO(yoshiki): implement here. crbug.com/230979
459 }
460
461 void MagnificationControllerImpl::SwitchTargetRootWindow(
462     aura::Window* new_root_window,
463     bool redraw_original_root_window) {
464   DCHECK(new_root_window);
465
466   if (new_root_window == root_window_)
467     return;
468
469   // Stores the previous scale.
470   float scale = GetScale();
471
472   // Unmagnify the previous root window.
473   root_window_->RemoveObserver(this);
474   if (redraw_original_root_window)
475     RedrawKeepingMousePosition(1.0f, true);
476
477   root_window_ = new_root_window;
478   RedrawKeepingMousePosition(scale, true);
479   root_window_->AddObserver(this);
480 }
481
482 ////////////////////////////////////////////////////////////////////////////////
483 // MagnificationControllerImpl: MagnificationController implementation
484
485 void MagnificationControllerImpl::SetScale(float scale, bool animate) {
486   if (!is_enabled_)
487     return;
488
489   ValidateScale(&scale);
490   Shell::GetInstance()->accessibility_delegate()->
491       SaveScreenMagnifierScale(scale);
492   RedrawKeepingMousePosition(scale, animate);
493 }
494
495 void MagnificationControllerImpl::MoveWindow(int x, int y, bool animate) {
496   if (!is_enabled_)
497     return;
498
499   Redraw(gfx::Point(x, y), scale_, animate);
500 }
501
502 void MagnificationControllerImpl::MoveWindow(const gfx::Point& point,
503                                              bool animate) {
504   if (!is_enabled_)
505     return;
506
507   Redraw(point, scale_, animate);
508 }
509
510 void MagnificationControllerImpl::SetScrollDirection(
511     ScrollDirection direction) {
512   scroll_direction_ = direction;
513   StartOrStopScrollIfNecessary();
514 }
515
516 void MagnificationControllerImpl::SetEnabled(bool enabled) {
517   Shell* shell = Shell::GetInstance();
518   if (enabled) {
519     float scale =
520         Shell::GetInstance()->accessibility_delegate()->
521         GetSavedScreenMagnifierScale();
522     if (scale <= 0.0f)
523       scale = kInitialMagnifiedScale;
524     ValidateScale(&scale);
525
526     // Do nothing, if already enabled with same scale.
527     if (is_enabled_ && scale == scale_)
528       return;
529
530     is_enabled_ = enabled;
531     RedrawKeepingMousePosition(scale, true);
532     shell->accessibility_delegate()->SaveScreenMagnifierScale(scale);
533   } else {
534     // Do nothing, if already disabled.
535     if (!is_enabled_)
536       return;
537
538     RedrawKeepingMousePosition(kNonMagnifiedScale, true);
539     is_enabled_ = enabled;
540   }
541 }
542
543 bool MagnificationControllerImpl::IsEnabled() const {
544   return is_enabled_;
545 }
546
547 ////////////////////////////////////////////////////////////////////////////////
548 // MagnificationControllerImpl: aura::EventFilter implementation
549
550 void MagnificationControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
551   aura::Window* target = static_cast<aura::Window*>(event->target());
552   aura::Window* current_root = target->GetRootWindow();
553   gfx::Rect root_bounds = current_root->bounds();
554
555   if (root_bounds.Contains(event->root_location())) {
556     // This must be before |SwitchTargetRootWindow()|.
557     if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
558       point_of_interest_ = event->root_location();
559
560     if (current_root != root_window_) {
561       DCHECK(current_root);
562       SwitchTargetRootWindow(current_root, true);
563     }
564
565     if (IsMagnified() && event->type() == ui::ET_MOUSE_MOVED)
566       OnMouseMove(event->root_location());
567   }
568 }
569
570 void MagnificationControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
571   if (event->IsAltDown() && event->IsControlDown()) {
572     if (event->type() == ui::ET_SCROLL_FLING_START ||
573         event->type() == ui::ET_SCROLL_FLING_CANCEL) {
574       event->StopPropagation();
575       return;
576     }
577
578     if (event->type() == ui::ET_SCROLL) {
579       ui::ScrollEvent* scroll_event = static_cast<ui::ScrollEvent*>(event);
580       float scale = GetScale();
581       scale += scroll_event->y_offset() * kScrollScaleChangeFactor;
582       SetScale(scale, true);
583       event->StopPropagation();
584       return;
585     }
586   }
587 }
588
589 void MagnificationControllerImpl::OnTouchEvent(ui::TouchEvent* event) {
590   aura::Window* target = static_cast<aura::Window*>(event->target());
591   aura::Window* current_root = target->GetRootWindow();
592   if (current_root == root_window_) {
593     gfx::Rect root_bounds = current_root->bounds();
594     if (root_bounds.Contains(event->root_location()))
595       point_of_interest_ = event->root_location();
596   }
597 }
598
599 ////////////////////////////////////////////////////////////////////////////////
600 // MagnificationController:
601
602 // static
603 MagnificationController* MagnificationController::CreateInstance() {
604   return new MagnificationControllerImpl();
605 }
606
607 }  // namespace ash