Move pending tile priorities to active on tree activation
[chromium-blink-merge.git] / ash / magnifier / magnification_controller.cc
blob33379c52678fcd2926d54399ba55dbba65da001f
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/shell.h"
8 #include "ash/shell_delegate.h"
9 #include "ash/system/tray/system_tray_delegate.h"
10 #include "ui/aura/client/cursor_client.h"
11 #include "ui/aura/root_window.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_property.h"
14 #include "ui/base/events/event.h"
15 #include "ui/base/events/event_handler.h"
16 #include "ui/compositor/dip_util.h"
17 #include "ui/compositor/layer.h"
18 #include "ui/compositor/layer_animation_observer.h"
19 #include "ui/compositor/scoped_layer_animation_settings.h"
20 #include "ui/gfx/point_conversions.h"
21 #include "ui/gfx/point_f.h"
22 #include "ui/gfx/rect_conversions.h"
23 #include "ui/gfx/screen.h"
24 #include "ui/views/corewm/compound_event_filter.h"
26 namespace {
28 const float kMaxMagnifiedScale = 4.0f;
29 const float kMaxMagnifiedScaleThreshold = 4.0f;
30 const float kMinMagnifiedScaleThreshold = 1.1f;
31 const float kNonMagnifiedScale = 1.0f;
33 const float kInitialMagnifiedScale = 2.0f;
34 const float kScrollScaleChangeFactor = 0.05f;
36 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of
37 // |kPanningMergin| from the edge, the view-port moves.
38 const int kPanningMergin = 100;
40 } // namespace
42 namespace ash {
44 ////////////////////////////////////////////////////////////////////////////////
45 // MagnificationControllerImpl:
47 class MagnificationControllerImpl : virtual public MagnificationController,
48 public ui::EventHandler,
49 public ui::ImplicitAnimationObserver {
50 public:
51 MagnificationControllerImpl();
52 virtual ~MagnificationControllerImpl();
54 // MagnificationController overrides:
55 virtual void SetEnabled(bool enabled) OVERRIDE;
56 virtual bool IsEnabled() const OVERRIDE;
57 virtual void SetScale(float scale, bool animate) OVERRIDE;
58 virtual float GetScale() const OVERRIDE { return scale_; }
59 virtual void MoveWindow(int x, int y, bool animate) OVERRIDE;
60 virtual void MoveWindow(const gfx::Point& point, bool animate) OVERRIDE;
61 virtual gfx::Point GetWindowPosition() const OVERRIDE {
62 return gfx::ToFlooredPoint(origin_);
64 virtual void EnsureRectIsVisible(const gfx::Rect& rect,
65 bool animate) OVERRIDE;
66 virtual void EnsurePointIsVisible(const gfx::Point& point,
67 bool animate) OVERRIDE;
69 private:
70 // ui::ImplicitAnimationObserver overrides:
71 virtual void OnImplicitAnimationsCompleted() OVERRIDE;
73 // Redraws the magnification window with the given origin position and the
74 // given scale. Returns true if the window is changed; otherwise, false.
75 // These methods should be called internally just after the scale and/or
76 // the position are changed to redraw the window.
77 bool Redraw(const gfx::PointF& position, float scale, bool animate);
78 bool RedrawDIP(const gfx::PointF& position, float scale, bool animate);
80 // Redraw with the given zoom scale keeping the mouse cursor location. In
81 // other words, zoom (or unzoom) centering around the cursor.
82 void RedrawKeepingMousePosition(float scale, bool animate);
84 // Ensures that the given point, rect or last mouse location is inside
85 // magnification window. If not, the controller moves the window to contain
86 // the given point/rect.
87 void EnsureRectIsVisibleWithScale(const gfx::Rect& target_rect,
88 float scale,
89 bool animate);
90 void EnsureRectIsVisibleDIP(const gfx::Rect& target_rect_in_dip,
91 float scale,
92 bool animate);
93 void EnsurePointIsVisibleWithScale(const gfx::Point& point,
94 float scale,
95 bool animate);
96 void OnMouseMove(const gfx::Point& location);
98 // Move the mouse cursot to the given point. Actual move will be done when
99 // the animation is completed. This should be called after animation is
100 // started.
101 void AfterAnimationMoveCursorTo(const gfx::Point& location);
103 // Switch Magnified RootWindow to |new_root_window|. This does following:
104 // - Unzoom the current root_window.
105 // - Zoom the given new root_window |new_root_window|.
106 // - Switch the target window from current window to |new_root_window|.
107 void SwitchTargetRootWindow(aura::RootWindow* new_root_window);
109 // Returns if the magnification scale is 1.0 or not (larger then 1.0).
110 bool IsMagnified() const;
112 // Returns the rect of the magnification window.
113 gfx::RectF GetWindowRectDIP(float scale) const;
114 // Returns the size of the root window.
115 gfx::Size GetHostSizeDIP() const;
117 // Correct the givin scale value if nessesary.
118 void ValidateScale(float* scale);
120 // ui::EventHandler overrides:
121 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
122 virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
124 aura::RootWindow* root_window_;
126 // True if the magnified window is currently animating a change. Otherwise,
127 // false.
128 bool is_on_animation_;
130 bool is_enabled_;
132 // True if the cursor needs to move the given position after the animation
133 // will be finished. When using this, set |position_after_animation_| as well.
134 bool move_cursor_after_animation_;
135 // Stores the position of cursor to be moved after animation.
136 gfx::Point position_after_animation_;
138 // Current scale, origin (left-top) position of the magnification window.
139 float scale_;
140 gfx::PointF origin_;
142 DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl);
145 ////////////////////////////////////////////////////////////////////////////////
146 // MagnificationControllerImpl:
148 MagnificationControllerImpl::MagnificationControllerImpl()
149 : root_window_(ash::Shell::GetPrimaryRootWindow()),
150 is_on_animation_(false),
151 is_enabled_(false),
152 move_cursor_after_animation_(false),
153 scale_(kNonMagnifiedScale) {
154 Shell::GetInstance()->AddPreTargetHandler(this);
157 MagnificationControllerImpl::~MagnificationControllerImpl() {
158 Shell::GetInstance()->RemovePreTargetHandler(this);
161 void MagnificationControllerImpl::RedrawKeepingMousePosition(
162 float scale, bool animate) {
163 gfx::Point mouse_in_root = root_window_->GetLastMouseLocationInRoot();
165 // mouse_in_root is invalid value when the cursor is hidden.
166 if (!root_window_->bounds().Contains(mouse_in_root))
167 mouse_in_root = root_window_->bounds().CenterPoint();
169 const gfx::PointF origin =
170 gfx::PointF(mouse_in_root.x() -
171 (scale_ / scale) * (mouse_in_root.x() - origin_.x()),
172 mouse_in_root.y() -
173 (scale_ / scale) * (mouse_in_root.y() - origin_.y()));
174 bool changed = Redraw(origin, scale, animate);
175 if (changed)
176 AfterAnimationMoveCursorTo(mouse_in_root);
179 bool MagnificationControllerImpl::Redraw(const gfx::PointF& position,
180 float scale,
181 bool animate) {
182 const gfx::PointF position_in_dip =
183 ui::ConvertPointToDIP(root_window_->layer(), position);
184 return RedrawDIP(position_in_dip, scale, animate);
187 bool MagnificationControllerImpl::RedrawDIP(const gfx::PointF& position_in_dip,
188 float scale,
189 bool animate) {
190 float x = position_in_dip.x();
191 float y = position_in_dip.y();
193 ValidateScale(&scale);
195 if (x < 0)
196 x = 0;
197 if (y < 0)
198 y = 0;
200 const gfx::Size host_size_in_dip = GetHostSizeDIP();
201 const gfx::SizeF window_size_in_dip = GetWindowRectDIP(scale).size();
202 float max_x = host_size_in_dip.width() - window_size_in_dip.width();
203 float max_y = host_size_in_dip.height() - window_size_in_dip.height();
204 if (x > max_x)
205 x = max_x;
206 if (y > max_y)
207 y = max_y;
209 // Does nothing if both the origin and the scale are not changed.
210 if (origin_.x() == x &&
211 origin_.y() == y &&
212 scale == scale_) {
213 return false;
216 origin_.set_x(x);
217 origin_.set_y(y);
218 scale_ = scale;
220 // Creates transform matrix.
221 gfx::Transform transform;
222 // Flips the signs intentionally to convert them from the position of the
223 // magnification window.
224 transform.Scale(scale_, scale_);
225 transform.Translate(-origin_.x(), -origin_.y());
227 ui::ScopedLayerAnimationSettings settings(
228 root_window_->layer()->GetAnimator());
229 settings.AddObserver(this);
230 settings.SetPreemptionStrategy(
231 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
232 settings.SetTweenType(ui::Tween::EASE_OUT);
233 settings.SetTransitionDuration(
234 base::TimeDelta::FromMilliseconds(animate ? 100 : 0));
236 root_window_->layer()->SetTransform(transform);
238 if (animate)
239 is_on_animation_ = true;
241 return true;
244 void MagnificationControllerImpl::EnsureRectIsVisibleWithScale(
245 const gfx::Rect& target_rect,
246 float scale,
247 bool animate) {
248 const gfx::Rect target_rect_in_dip =
249 ui::ConvertRectToDIP(root_window_->layer(), target_rect);
250 EnsureRectIsVisibleDIP(target_rect_in_dip, scale, animate);
253 void MagnificationControllerImpl::EnsureRectIsVisibleDIP(
254 const gfx::Rect& target_rect,
255 float scale,
256 bool animate) {
257 ValidateScale(&scale);
259 const gfx::Rect window_rect = gfx::ToEnclosingRect(GetWindowRectDIP(scale));
260 if (scale == scale_ && window_rect.Contains(target_rect))
261 return;
263 // TODO(yoshiki): Un-zoom and change the scale if the magnification window
264 // can't contain the whole given rect.
266 gfx::Rect rect = window_rect;
267 if (target_rect.width() > rect.width())
268 rect.set_x(target_rect.CenterPoint().x() - rect.x() / 2);
269 else if (target_rect.right() < rect.x())
270 rect.set_x(target_rect.right());
271 else if (rect.right() < target_rect.x())
272 rect.set_x(target_rect.x() - rect.width());
274 if (rect.height() > window_rect.height())
275 rect.set_y(target_rect.CenterPoint().y() - rect.y() / 2);
276 else if (target_rect.bottom() < rect.y())
277 rect.set_y(target_rect.bottom());
278 else if (rect.bottom() < target_rect.y())
279 rect.set_y(target_rect.y() - rect.height());
281 RedrawDIP(rect.origin(), scale, animate);
284 void MagnificationControllerImpl::EnsurePointIsVisibleWithScale(
285 const gfx::Point& point,
286 float scale,
287 bool animate) {
288 EnsureRectIsVisibleWithScale(gfx::Rect(point, gfx::Size(0, 0)),
289 scale,
290 animate);
293 void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) {
294 gfx::Point mouse(location);
296 int x = origin_.x();
297 int y = origin_.y();
298 bool start_zoom = false;
300 const gfx::Rect window_rect = gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
301 const int left = window_rect.x();
302 const int right = window_rect.right();
303 int margin = kPanningMergin / scale_; // No need to consider DPI.
305 int x_diff = 0;
307 if (mouse.x() < left + margin) {
308 // Panning left.
309 x_diff = mouse.x() - (left + margin);
310 start_zoom = true;
311 } else if (right - margin < mouse.x()) {
312 // Panning right.
313 x_diff = mouse.x() - (right - margin);
314 start_zoom = true;
316 x = left + x_diff;
318 const int top = window_rect.y();
319 const int bottom = window_rect.bottom();
321 int y_diff = 0;
322 if (mouse.y() < top + margin) {
323 // Panning up.
324 y_diff = mouse.y() - (top + margin);
325 start_zoom = true;
326 } else if (bottom - margin < mouse.y()) {
327 // Panning down.
328 y_diff = mouse.y() - (bottom - margin);
329 start_zoom = true;
331 y = top + y_diff;
333 if (start_zoom && !is_on_animation_) {
334 // No animation on panning.
335 bool animate = false;
336 bool ret = RedrawDIP(gfx::Point(x, y), scale_, animate);
338 if (ret) {
339 // If the magnified region is moved, hides the mouse cursor and moves it.
340 if (x_diff != 0 || y_diff != 0)
341 root_window_->MoveCursorTo(mouse);
346 void MagnificationControllerImpl::AfterAnimationMoveCursorTo(
347 const gfx::Point& location) {
348 aura::client::CursorClient* cursor_client =
349 aura::client::GetCursorClient(root_window_);
350 if (cursor_client) {
351 // When cursor is invisible, do not move or show the cursor after the
352 // animation.
353 if (!cursor_client->IsCursorVisible())
354 return;
355 cursor_client->DisableMouseEvents();
357 move_cursor_after_animation_ = true;
358 position_after_animation_ = location;
361 gfx::Size MagnificationControllerImpl::GetHostSizeDIP() const {
362 return ui::ConvertSizeToDIP(root_window_->layer(),
363 root_window_->GetHostSize());
366 gfx::RectF MagnificationControllerImpl::GetWindowRectDIP(float scale) const {
367 const gfx::Size size_in_dip =
368 ui::ConvertSizeToDIP(root_window_->layer(),
369 root_window_->GetHostSize());
370 const float width = size_in_dip.width() / scale;
371 const float height = size_in_dip.height() / scale;
373 return gfx::RectF(origin_.x(), origin_.y(), width, height);
376 bool MagnificationControllerImpl::IsMagnified() const {
377 return scale_ >= kMinMagnifiedScaleThreshold;
380 void MagnificationControllerImpl::ValidateScale(float* scale) {
381 // Adjust the scale to just |kNonMagnifiedScale| if scale is smaller than
382 // |kMinMagnifiedScaleThreshold|;
383 if (*scale < kMinMagnifiedScaleThreshold)
384 *scale = kNonMagnifiedScale;
386 // Adjust the scale to just |kMinMagnifiedScale| if scale is bigger than
387 // |kMinMagnifiedScaleThreshold|;
388 if (*scale > kMaxMagnifiedScaleThreshold)
389 *scale = kMaxMagnifiedScale;
391 DCHECK(kNonMagnifiedScale <= *scale && *scale <= kMaxMagnifiedScale);
394 void MagnificationControllerImpl::OnImplicitAnimationsCompleted() {
395 if (!is_on_animation_)
396 return;
398 if (move_cursor_after_animation_) {
399 root_window_->MoveCursorTo(position_after_animation_);
400 move_cursor_after_animation_ = false;
402 aura::client::CursorClient* cursor_client =
403 aura::client::GetCursorClient(root_window_);
404 if (cursor_client)
405 cursor_client->EnableMouseEvents();
408 is_on_animation_ = false;
411 void MagnificationControllerImpl::SwitchTargetRootWindow(
412 aura::RootWindow* new_root_window) {
413 if (new_root_window == root_window_)
414 return;
416 float scale = GetScale();
418 RedrawKeepingMousePosition(1.0f, true);
419 root_window_ = new_root_window;
420 RedrawKeepingMousePosition(scale, true);
423 ////////////////////////////////////////////////////////////////////////////////
424 // MagnificationControllerImpl: MagnificationController implementation
426 void MagnificationControllerImpl::SetScale(float scale, bool animate) {
427 if (!is_enabled_)
428 return;
430 ValidateScale(&scale);
431 ash::Shell::GetInstance()->delegate()->SaveScreenMagnifierScale(scale);
432 RedrawKeepingMousePosition(scale, animate);
435 void MagnificationControllerImpl::MoveWindow(int x, int y, bool animate) {
436 if (!is_enabled_)
437 return;
439 Redraw(gfx::Point(x, y), scale_, animate);
442 void MagnificationControllerImpl::MoveWindow(const gfx::Point& point,
443 bool animate) {
444 if (!is_enabled_)
445 return;
447 Redraw(point, scale_, animate);
450 void MagnificationControllerImpl::EnsureRectIsVisible(
451 const gfx::Rect& target_rect,
452 bool animate) {
453 if (!is_enabled_)
454 return;
456 EnsureRectIsVisibleWithScale(target_rect, scale_, animate);
459 void MagnificationControllerImpl::EnsurePointIsVisible(
460 const gfx::Point& point,
461 bool animate) {
462 if (!is_enabled_)
463 return;
465 EnsurePointIsVisibleWithScale(point, scale_, animate);
468 void MagnificationControllerImpl::SetEnabled(bool enabled) {
469 if (enabled) {
470 float scale =
471 ash::Shell::GetInstance()->delegate()->GetSavedScreenMagnifierScale();
472 if (scale <= 0.0f)
473 scale = kInitialMagnifiedScale;
474 ValidateScale(&scale);
476 // Do nothing, if already enabled with same scale.
477 if (is_enabled_ && scale == scale_)
478 return;
480 RedrawKeepingMousePosition(scale, true);
481 ash::Shell::GetInstance()->delegate()->SaveScreenMagnifierScale(scale);
482 is_enabled_ = enabled;
483 } else {
484 // Do nothing, if already disabled.
485 if (!is_enabled_)
486 return;
488 RedrawKeepingMousePosition(kNonMagnifiedScale, true);
489 is_enabled_ = enabled;
493 bool MagnificationControllerImpl::IsEnabled() const {
494 return is_enabled_;
497 ////////////////////////////////////////////////////////////////////////////////
498 // MagnificationControllerImpl: aura::EventFilter implementation
500 void MagnificationControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
501 if (IsMagnified() && event->type() == ui::ET_MOUSE_MOVED) {
502 aura::Window* target = static_cast<aura::Window*>(event->target());
503 aura::RootWindow* current_root = target->GetRootWindow();
504 gfx::Rect root_bounds = current_root->bounds();
506 if (root_bounds.Contains(event->root_location())) {
507 if (current_root != root_window_)
508 SwitchTargetRootWindow(current_root);
510 OnMouseMove(event->root_location());
515 void MagnificationControllerImpl::OnScrollEvent(
516 ui::ScrollEvent* event) {
517 if (event->IsAltDown() && event->IsControlDown()) {
518 if (event->type() == ui::ET_SCROLL_FLING_START ||
519 event->type() == ui::ET_SCROLL_FLING_CANCEL) {
520 event->StopPropagation();
521 return;
524 if (event->type() == ui::ET_SCROLL) {
525 ui::ScrollEvent* scroll_event = static_cast<ui::ScrollEvent*>(event);
526 float scale = GetScale();
527 scale += scroll_event->y_offset() * kScrollScaleChangeFactor;
528 SetScale(scale, true);
529 event->StopPropagation();
530 return;
535 ////////////////////////////////////////////////////////////////////////////////
536 // MagnificationController:
538 // static
539 MagnificationController* MagnificationController::CreateInstance() {
540 return new MagnificationControllerImpl();
543 } // namespace ash