[OriginChip] Show the default search provider in the Omnibox hint text.
[chromium-blink-merge.git] / ash / magnifier / magnification_controller.cc
blob66bebf2c29685d089d3f6af3002f07205ee5c33f
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/accessibility_delegate.h"
8 #include "ash/display/root_window_transformers.h"
9 #include "ash/shell.h"
10 #include "ash/system/tray/system_tray_delegate.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "ui/aura/client/cursor_client.h"
13 #include "ui/aura/root_window.h"
14 #include "ui/aura/root_window_transformer.h"
15 #include "ui/aura/window.h"
16 #include "ui/aura/window_property.h"
17 #include "ui/compositor/dip_util.h"
18 #include "ui/compositor/layer.h"
19 #include "ui/compositor/layer_animation_observer.h"
20 #include "ui/compositor/scoped_layer_animation_settings.h"
21 #include "ui/events/event.h"
22 #include "ui/events/event_handler.h"
23 #include "ui/gfx/point3_f.h"
24 #include "ui/gfx/point_conversions.h"
25 #include "ui/gfx/point_f.h"
26 #include "ui/gfx/rect_conversions.h"
27 #include "ui/gfx/screen.h"
28 #include "ui/views/corewm/compound_event_filter.h"
30 namespace {
32 const float kMaxMagnifiedScale = 4.0f;
33 const float kMaxMagnifiedScaleThreshold = 4.0f;
34 const float kMinMagnifiedScaleThreshold = 1.1f;
35 const float kNonMagnifiedScale = 1.0f;
37 const float kInitialMagnifiedScale = 2.0f;
38 const float kScrollScaleChangeFactor = 0.05f;
40 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of
41 // |kPanningMergin| from the edge, the view-port moves.
42 const int kPanningMergin = 100;
44 void MoveCursorTo(aura::RootWindow* root_window,
45 const gfx::Point& root_location) {
46 gfx::Point3F host_location_3f(root_location);
47 root_window->host()->GetRootTransform().TransformPoint(&host_location_3f);
48 root_window->MoveCursorToHostLocation(
49 gfx::ToCeiledPoint(host_location_3f.AsPointF()));
52 } // namespace
54 namespace ash {
56 ////////////////////////////////////////////////////////////////////////////////
57 // MagnificationControllerImpl:
59 class MagnificationControllerImpl : virtual public MagnificationController,
60 public ui::EventHandler,
61 public ui::ImplicitAnimationObserver,
62 public aura::WindowObserver {
63 public:
64 MagnificationControllerImpl();
65 virtual ~MagnificationControllerImpl();
67 // MagnificationController overrides:
68 virtual void SetEnabled(bool enabled) OVERRIDE;
69 virtual bool IsEnabled() const OVERRIDE;
70 virtual void SetScale(float scale, bool animate) OVERRIDE;
71 virtual float GetScale() const OVERRIDE { return scale_; }
72 virtual void MoveWindow(int x, int y, bool animate) OVERRIDE;
73 virtual void MoveWindow(const gfx::Point& point, bool animate) OVERRIDE;
74 virtual gfx::Point GetWindowPosition() const OVERRIDE {
75 return gfx::ToFlooredPoint(origin_);
77 virtual void EnsureRectIsVisible(const gfx::Rect& rect,
78 bool animate) OVERRIDE;
79 virtual void EnsurePointIsVisible(const gfx::Point& point,
80 bool animate) OVERRIDE;
81 // For test
82 virtual gfx::Point GetPointOfInterestForTesting() OVERRIDE {
83 return point_of_interest_;
86 private:
87 // ui::ImplicitAnimationObserver overrides:
88 virtual void OnImplicitAnimationsCompleted() OVERRIDE;
90 // aura::WindowObserver overrides:
91 virtual void OnWindowDestroying(aura::Window* root_window) OVERRIDE;
92 virtual void OnWindowBoundsChanged(aura::Window* window,
93 const gfx::Rect& old_bounds,
94 const gfx::Rect& new_bounds) OVERRIDE;
96 // Redraws the magnification window with the given origin position and the
97 // given scale. Returns true if the window is changed; otherwise, false.
98 // These methods should be called internally just after the scale and/or
99 // the position are changed to redraw the window.
100 bool Redraw(const gfx::PointF& position, float scale, bool animate);
101 bool RedrawDIP(const gfx::PointF& position, float scale, bool animate);
103 // Redraw with the given zoom scale keeping the mouse cursor location. In
104 // other words, zoom (or unzoom) centering around the cursor.
105 void RedrawKeepingMousePosition(float scale, bool animate);
107 // Ensures that the given point, rect or last mouse location is inside
108 // magnification window. If not, the controller moves the window to contain
109 // the given point/rect.
110 void EnsureRectIsVisibleWithScale(const gfx::Rect& target_rect,
111 float scale,
112 bool animate);
113 void EnsureRectIsVisibleDIP(const gfx::Rect& target_rect_in_dip,
114 float scale,
115 bool animate);
116 void EnsurePointIsVisibleWithScale(const gfx::Point& point,
117 float scale,
118 bool animate);
119 void OnMouseMove(const gfx::Point& location);
121 // Move the mouse cursot to the given point. Actual move will be done when
122 // the animation is completed. This should be called after animation is
123 // started.
124 void AfterAnimationMoveCursorTo(const gfx::Point& location);
126 // Switch Magnified RootWindow to |new_root_window|. This does following:
127 // - Unzoom the current root_window.
128 // - Zoom the given new root_window |new_root_window|.
129 // - Switch the target window from current window to |new_root_window|.
130 void SwitchTargetRootWindow(aura::Window* new_root_window,
131 bool redraw_original_root_window);
133 // Returns if the magnification scale is 1.0 or not (larger then 1.0).
134 bool IsMagnified() const;
136 // Returns the rect of the magnification window.
137 gfx::RectF GetWindowRectDIP(float scale) const;
138 // Returns the size of the root window.
139 gfx::Size GetHostSizeDIP() const;
141 // Correct the givin scale value if nessesary.
142 void ValidateScale(float* scale);
144 // ui::EventHandler overrides:
145 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
146 virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
147 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
149 // Target root window. This must not be NULL.
150 aura::Window* root_window_;
152 // True if the magnified window is currently animating a change. Otherwise,
153 // false.
154 bool is_on_animation_;
156 bool is_enabled_;
158 // True if the cursor needs to move the given position after the animation
159 // will be finished. When using this, set |position_after_animation_| as well.
160 bool move_cursor_after_animation_;
161 // Stores the position of cursor to be moved after animation.
162 gfx::Point position_after_animation_;
164 // Stores the last mouse cursor (or last touched) location. This value is
165 // used on zooming to keep this location visible.
166 gfx::Point point_of_interest_;
168 // Current scale, origin (left-top) position of the magnification window.
169 float scale_;
170 gfx::PointF origin_;
172 DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl);
175 ////////////////////////////////////////////////////////////////////////////////
176 // MagnificationControllerImpl:
178 MagnificationControllerImpl::MagnificationControllerImpl()
179 : root_window_(ash::Shell::GetPrimaryRootWindow()),
180 is_on_animation_(false),
181 is_enabled_(false),
182 move_cursor_after_animation_(false),
183 scale_(kNonMagnifiedScale) {
184 Shell::GetInstance()->AddPreTargetHandler(this);
185 root_window_->AddObserver(this);
186 point_of_interest_ = root_window_->bounds().CenterPoint();
189 MagnificationControllerImpl::~MagnificationControllerImpl() {
190 root_window_->RemoveObserver(this);
192 Shell::GetInstance()->RemovePreTargetHandler(this);
195 void MagnificationControllerImpl::RedrawKeepingMousePosition(
196 float scale, bool animate) {
197 gfx::Point mouse_in_root = point_of_interest_;
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();
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);
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);
221 bool MagnificationControllerImpl::RedrawDIP(const gfx::PointF& position_in_dip,
222 float scale,
223 bool animate) {
224 DCHECK(root_window_);
226 float x = position_in_dip.x();
227 float y = position_in_dip.y();
229 ValidateScale(&scale);
231 if (x < 0)
232 x = 0;
233 if (y < 0)
234 y = 0;
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;
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;
252 origin_.set_x(x);
253 origin_.set_y(y);
254 scale_ = scale;
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());
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));
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());
279 if (animate)
280 is_on_animation_ = true;
282 return true;
285 void MagnificationControllerImpl::EnsureRectIsVisibleWithScale(
286 const gfx::Rect& target_rect,
287 float scale,
288 bool animate) {
289 const gfx::Rect target_rect_in_dip =
290 ui::ConvertRectToDIP(root_window_->layer(), target_rect);
291 EnsureRectIsVisibleDIP(target_rect_in_dip, scale, animate);
294 void MagnificationControllerImpl::EnsureRectIsVisibleDIP(
295 const gfx::Rect& target_rect,
296 float scale,
297 bool animate) {
298 ValidateScale(&scale);
300 const gfx::Rect window_rect = gfx::ToEnclosingRect(GetWindowRectDIP(scale));
301 if (scale == scale_ && window_rect.Contains(target_rect))
302 return;
304 // TODO(yoshiki): Un-zoom and change the scale if the magnification window
305 // can't contain the whole given rect.
307 gfx::Rect rect = window_rect;
308 if (target_rect.width() > rect.width())
309 rect.set_x(target_rect.CenterPoint().x() - rect.x() / 2);
310 else if (target_rect.right() < rect.x())
311 rect.set_x(target_rect.right());
312 else if (rect.right() < target_rect.x())
313 rect.set_x(target_rect.x() - rect.width());
315 if (rect.height() > window_rect.height())
316 rect.set_y(target_rect.CenterPoint().y() - rect.y() / 2);
317 else if (target_rect.bottom() < rect.y())
318 rect.set_y(target_rect.bottom());
319 else if (rect.bottom() < target_rect.y())
320 rect.set_y(target_rect.y() - rect.height());
322 RedrawDIP(rect.origin(), scale, animate);
325 void MagnificationControllerImpl::EnsurePointIsVisibleWithScale(
326 const gfx::Point& point,
327 float scale,
328 bool animate) {
329 EnsureRectIsVisibleWithScale(gfx::Rect(point, gfx::Size(0, 0)),
330 scale,
331 animate);
334 void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) {
335 DCHECK(root_window_);
337 gfx::Point mouse(location);
339 int x = origin_.x();
340 int y = origin_.y();
341 bool start_zoom = false;
343 const gfx::Rect window_rect = gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
344 const int left = window_rect.x();
345 const int right = window_rect.right();
346 int margin = kPanningMergin / scale_; // No need to consider DPI.
348 int x_diff = 0;
350 if (mouse.x() < left + margin) {
351 // Panning left.
352 x_diff = mouse.x() - (left + margin);
353 start_zoom = true;
354 } else if (right - margin < mouse.x()) {
355 // Panning right.
356 x_diff = mouse.x() - (right - margin);
357 start_zoom = true;
359 x = left + x_diff;
361 const int top = window_rect.y();
362 const int bottom = window_rect.bottom();
364 int y_diff = 0;
365 if (mouse.y() < top + margin) {
366 // Panning up.
367 y_diff = mouse.y() - (top + margin);
368 start_zoom = true;
369 } else if (bottom - margin < mouse.y()) {
370 // Panning down.
371 y_diff = mouse.y() - (bottom - margin);
372 start_zoom = true;
374 y = top + y_diff;
376 if (start_zoom && !is_on_animation_) {
377 // No animation on panning.
378 bool animate = false;
379 bool ret = RedrawDIP(gfx::Point(x, y), scale_, animate);
381 if (ret) {
382 // If the magnified region is moved, hides the mouse cursor and moves it.
383 if (x_diff != 0 || y_diff != 0)
384 MoveCursorTo(root_window_->GetDispatcher(), mouse);
389 void MagnificationControllerImpl::AfterAnimationMoveCursorTo(
390 const gfx::Point& location) {
391 DCHECK(root_window_);
393 aura::client::CursorClient* cursor_client =
394 aura::client::GetCursorClient(root_window_);
395 if (cursor_client) {
396 // When cursor is invisible, do not move or show the cursor after the
397 // animation.
398 if (!cursor_client->IsCursorVisible())
399 return;
400 cursor_client->DisableMouseEvents();
402 move_cursor_after_animation_ = true;
403 position_after_animation_ = location;
406 gfx::Size MagnificationControllerImpl::GetHostSizeDIP() const {
407 return root_window_->bounds().size();
410 gfx::RectF MagnificationControllerImpl::GetWindowRectDIP(float scale) const {
411 const gfx::Size size_in_dip = root_window_->bounds().size();
412 const float width = size_in_dip.width() / scale;
413 const float height = size_in_dip.height() / scale;
415 return gfx::RectF(origin_.x(), origin_.y(), width, height);
418 bool MagnificationControllerImpl::IsMagnified() const {
419 return scale_ >= kMinMagnifiedScaleThreshold;
422 void MagnificationControllerImpl::ValidateScale(float* scale) {
423 // Adjust the scale to just |kNonMagnifiedScale| if scale is smaller than
424 // |kMinMagnifiedScaleThreshold|;
425 if (*scale < kMinMagnifiedScaleThreshold)
426 *scale = kNonMagnifiedScale;
428 // Adjust the scale to just |kMinMagnifiedScale| if scale is bigger than
429 // |kMinMagnifiedScaleThreshold|;
430 if (*scale > kMaxMagnifiedScaleThreshold)
431 *scale = kMaxMagnifiedScale;
433 DCHECK(kNonMagnifiedScale <= *scale && *scale <= kMaxMagnifiedScale);
436 void MagnificationControllerImpl::OnImplicitAnimationsCompleted() {
437 if (!is_on_animation_)
438 return;
440 if (move_cursor_after_animation_) {
441 MoveCursorTo(root_window_->GetDispatcher(), position_after_animation_);
442 move_cursor_after_animation_ = false;
444 aura::client::CursorClient* cursor_client =
445 aura::client::GetCursorClient(root_window_);
446 if (cursor_client)
447 cursor_client->EnableMouseEvents();
450 is_on_animation_ = false;
453 void MagnificationControllerImpl::OnWindowDestroying(
454 aura::Window* root_window) {
455 if (root_window == root_window_) {
456 // There must be at least one root window because this controller is
457 // destroyed before the root windows get destroyed.
458 DCHECK(root_window);
460 aura::Window* target_root_window = Shell::GetTargetRootWindow();
461 CHECK(target_root_window);
463 // The destroyed root window must not be target.
464 CHECK_NE(target_root_window, root_window);
465 // Don't redraw the old root window as it's being destroyed.
466 SwitchTargetRootWindow(target_root_window, false);
467 point_of_interest_ = target_root_window->bounds().CenterPoint();
471 void MagnificationControllerImpl::OnWindowBoundsChanged(
472 aura::Window* window,
473 const gfx::Rect& old_bounds,
474 const gfx::Rect& new_bounds) {
475 // TODO(yoshiki): implement here. crbug.com/230979
478 void MagnificationControllerImpl::SwitchTargetRootWindow(
479 aura::Window* new_root_window,
480 bool redraw_original_root_window) {
481 DCHECK(new_root_window);
483 if (new_root_window == root_window_)
484 return;
486 // Stores the previous scale.
487 float scale = GetScale();
489 // Unmagnify the previous root window.
490 root_window_->RemoveObserver(this);
491 if (redraw_original_root_window)
492 RedrawKeepingMousePosition(1.0f, true);
494 root_window_ = new_root_window;
495 RedrawKeepingMousePosition(scale, true);
496 root_window_->AddObserver(this);
499 ////////////////////////////////////////////////////////////////////////////////
500 // MagnificationControllerImpl: MagnificationController implementation
502 void MagnificationControllerImpl::SetScale(float scale, bool animate) {
503 if (!is_enabled_)
504 return;
506 ValidateScale(&scale);
507 ash::Shell::GetInstance()->accessibility_delegate()->
508 SaveScreenMagnifierScale(scale);
509 RedrawKeepingMousePosition(scale, animate);
512 void MagnificationControllerImpl::MoveWindow(int x, int y, bool animate) {
513 if (!is_enabled_)
514 return;
516 Redraw(gfx::Point(x, y), scale_, animate);
519 void MagnificationControllerImpl::MoveWindow(const gfx::Point& point,
520 bool animate) {
521 if (!is_enabled_)
522 return;
524 Redraw(point, scale_, animate);
527 void MagnificationControllerImpl::EnsureRectIsVisible(
528 const gfx::Rect& target_rect,
529 bool animate) {
530 if (!is_enabled_)
531 return;
533 EnsureRectIsVisibleWithScale(target_rect, scale_, animate);
536 void MagnificationControllerImpl::EnsurePointIsVisible(
537 const gfx::Point& point,
538 bool animate) {
539 if (!is_enabled_)
540 return;
542 EnsurePointIsVisibleWithScale(point, scale_, animate);
545 void MagnificationControllerImpl::SetEnabled(bool enabled) {
546 if (enabled) {
547 float scale =
548 ash::Shell::GetInstance()->accessibility_delegate()->
549 GetSavedScreenMagnifierScale();
550 if (scale <= 0.0f)
551 scale = kInitialMagnifiedScale;
552 ValidateScale(&scale);
554 // Do nothing, if already enabled with same scale.
555 if (is_enabled_ && scale == scale_)
556 return;
558 is_enabled_ = enabled;
559 RedrawKeepingMousePosition(scale, true);
560 ash::Shell::GetInstance()->accessibility_delegate()->
561 SaveScreenMagnifierScale(scale);
562 } else {
563 // Do nothing, if already disabled.
564 if (!is_enabled_)
565 return;
567 RedrawKeepingMousePosition(kNonMagnifiedScale, true);
568 is_enabled_ = enabled;
572 bool MagnificationControllerImpl::IsEnabled() const {
573 return is_enabled_;
576 ////////////////////////////////////////////////////////////////////////////////
577 // MagnificationControllerImpl: aura::EventFilter implementation
579 void MagnificationControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
580 aura::Window* target = static_cast<aura::Window*>(event->target());
581 aura::Window* current_root = target->GetRootWindow();
582 gfx::Rect root_bounds = current_root->bounds();
584 if (root_bounds.Contains(event->root_location())) {
585 // This must be before |SwitchTargetRootWindow()|.
586 point_of_interest_ = event->root_location();
588 if (current_root != root_window_) {
589 DCHECK(current_root);
590 SwitchTargetRootWindow(current_root, true);
593 if (IsMagnified() && event->type() == ui::ET_MOUSE_MOVED)
594 OnMouseMove(event->root_location());
598 void MagnificationControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
599 if (event->IsAltDown() && event->IsControlDown()) {
600 if (event->type() == ui::ET_SCROLL_FLING_START ||
601 event->type() == ui::ET_SCROLL_FLING_CANCEL) {
602 event->StopPropagation();
603 return;
606 if (event->type() == ui::ET_SCROLL) {
607 ui::ScrollEvent* scroll_event = static_cast<ui::ScrollEvent*>(event);
608 float scale = GetScale();
609 scale += scroll_event->y_offset() * kScrollScaleChangeFactor;
610 SetScale(scale, true);
611 event->StopPropagation();
612 return;
617 void MagnificationControllerImpl::OnTouchEvent(ui::TouchEvent* event) {
618 aura::Window* target = static_cast<aura::Window*>(event->target());
619 aura::Window* current_root = target->GetRootWindow();
620 if (current_root == root_window_) {
621 gfx::Rect root_bounds = current_root->bounds();
622 if (root_bounds.Contains(event->root_location()))
623 point_of_interest_ = event->root_location();
627 ////////////////////////////////////////////////////////////////////////////////
628 // MagnificationController:
630 // static
631 MagnificationController* MagnificationController::CreateInstance() {
632 return new MagnificationControllerImpl();
635 } // namespace ash