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