| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/magnifier/magnification_controller.h" |
| |
| #include "ash/shell.h" |
| #include "ui/aura/event.h" |
| #include "ui/aura/event_filter.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/aura/shared/compound_event_filter.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_property.h" |
| #include "ui/gfx/point3.h" |
| #include "ui/compositor/dip_util.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_animation_observer.h" |
| #include "ui/compositor/scoped_layer_animation_settings.h" |
| |
| namespace { |
| |
| const float kMaxMagnifiedScale = 4.0f; |
| const float kMaxMagnifiedScaleThreshold = 4.0f; |
| const float kMinMagnifiedScaleThreshold = 1.1f; |
| const float kNonMagnifiedScale = 1.0f; |
| |
| const float kInitialMagnifiedScale = 2.0f; |
| |
| } // namespace |
| |
| namespace ash { |
| namespace internal { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // MagnificationControllerImpl: |
| |
| class MagnificationControllerImpl : virtual public MagnificationController, |
| public aura::EventFilter, |
| public ui::ImplicitAnimationObserver { |
| public: |
| MagnificationControllerImpl(); |
| virtual ~MagnificationControllerImpl(); |
| |
| // MagnificationController overrides: |
| virtual void SetEnabled(bool enabled) OVERRIDE; |
| virtual void SetScale(float scale, bool animate) OVERRIDE; |
| virtual float GetScale() const OVERRIDE { return scale_; } |
| virtual void MoveWindow(int x, int y, bool animate) OVERRIDE; |
| virtual void MoveWindow(const gfx::Point& point, bool animate) OVERRIDE; |
| virtual gfx::Point GetWindowPosition() const OVERRIDE { return origin_; } |
| virtual void EnsureRectIsVisible(const gfx::Rect& rect, |
| bool animate) OVERRIDE; |
| virtual void EnsurePointIsVisible(const gfx::Point& point, |
| bool animate) OVERRIDE; |
| |
| private: |
| // ui::ImplicitAnimationObserver overrides: |
| virtual void OnImplicitAnimationsCompleted() OVERRIDE; |
| |
| // Redraws the magnification window with the given origin position and the |
| // given scale. Returns true if the window is changed; otherwise, false. |
| // These methods should be called internally just after the scale and/or |
| // the position are changed to redraw the window. |
| bool Redraw(const gfx::Point& position, float scale, bool animate); |
| bool RedrawDIP(const gfx::Point& position, float scale, bool animate); |
| |
| // Ensures that the given point, rect or last mouse location is inside |
| // magnification window. If not, the controller moves the window to contain |
| // the given point/rect. |
| void EnsureRectIsVisibleWithScale(const gfx::Rect& target_rect, |
| float scale, |
| bool animate); |
| void EnsureRectIsVisibleDIP(const gfx::Rect& target_rect_in_dip, |
| float scale, |
| bool animate); |
| void EnsurePointIsVisibleWithScale(const gfx::Point& point, |
| float scale, |
| bool animate); |
| void OnMouseMove(const gfx::Point& location); |
| |
| // Returns if the magnification scale is 1.0 or not (larger then 1.0). |
| bool IsMagnified() const; |
| |
| // Returns the rect of the magnification window. |
| gfx::Rect GetWindowRectDIP(float scale) const; |
| // Returns the size of the root window. |
| gfx::Size GetHostSizeDIP() const; |
| |
| // Correct the givin scale value if nessesary. |
| void ValidateScale(float* scale); |
| |
| // aura::EventFilter overrides: |
| virtual bool PreHandleKeyEvent(aura::Window* target, |
| aura::KeyEvent* event) OVERRIDE; |
| virtual bool PreHandleMouseEvent(aura::Window* target, |
| aura::MouseEvent* event) OVERRIDE; |
| virtual ui::TouchStatus PreHandleTouchEvent(aura::Window* target, |
| aura::TouchEvent* event) OVERRIDE; |
| virtual ui::GestureStatus PreHandleGestureEvent( |
| aura::Window* target, |
| aura::GestureEvent* event) OVERRIDE; |
| |
| aura::RootWindow* root_window_; |
| |
| // True if the magnified window is in motion of zooming or un-zooming effect. |
| // Otherwise, false. |
| bool is_on_zooming_; |
| |
| bool is_enabled_; |
| |
| // Current scale, origin (left-top) position of the magnification window. |
| float scale_; |
| gfx::Point origin_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl); |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // MagnificationControllerImpl: |
| |
| MagnificationControllerImpl::MagnificationControllerImpl() |
| : root_window_(ash::Shell::GetPrimaryRootWindow()), |
| is_on_zooming_(false), |
| is_enabled_(false), |
| scale_(kNonMagnifiedScale) { |
| Shell::GetInstance()->AddEnvEventFilter(this); |
| } |
| |
| MagnificationControllerImpl::~MagnificationControllerImpl() { |
| Shell::GetInstance()->RemoveEnvEventFilter(this); |
| } |
| |
| bool MagnificationControllerImpl::Redraw(const gfx::Point& position, |
| float scale, |
| bool animate) { |
| const gfx::Point position_in_dip = |
| ui::ConvertPointToDIP(root_window_->layer(), position); |
| return RedrawDIP(position_in_dip, scale, animate); |
| } |
| |
| bool MagnificationControllerImpl::RedrawDIP(const gfx::Point& position_in_dip, |
| float scale, |
| bool animate) { |
| int x = position_in_dip.x(); |
| int y = position_in_dip.y(); |
| |
| ValidateScale(&scale); |
| |
| if (x < 0) |
| x = 0; |
| if (y < 0) |
| y = 0; |
| |
| const gfx::Size host_size_in_dip = GetHostSizeDIP(); |
| const gfx::Size window_size_in_dip = GetWindowRectDIP(scale).size(); |
| int max_x = host_size_in_dip.width() - window_size_in_dip.width(); |
| int max_y = host_size_in_dip.height() - window_size_in_dip.height(); |
| if (x > max_x) |
| x = max_x; |
| if (y > max_y) |
| y = max_y; |
| |
| // Ignores 1 px diffirence because it may be error on calculation. |
| if (std::abs(origin_.x() - x) <= 1 && |
| std::abs(origin_.y() - y) <= 1 && |
| scale == scale_) |
| return false; |
| |
| origin_.set_x(x); |
| origin_.set_y(y); |
| scale_ = scale; |
| |
| // Creates transform matrix. |
| ui::Transform transform; |
| // Flips the signs intentionally to convert them from the position of the |
| // magnification window. |
| transform.ConcatTranslate(-origin_.x(), -origin_.y()); |
| transform.ConcatScale(scale_, scale_); |
| |
| ui::ScopedLayerAnimationSettings settings( |
| root_window_->layer()->GetAnimator()); |
| settings.AddObserver(this); |
| settings.SetPreemptionStrategy( |
| ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| settings.SetTweenType(ui::Tween::EASE_OUT); |
| settings.SetTransitionDuration( |
| base::TimeDelta::FromMilliseconds(animate ? 100 : 0)); |
| |
| root_window_->layer()->SetTransform(transform); |
| |
| return true; |
| } |
| |
| void MagnificationControllerImpl::EnsureRectIsVisibleWithScale( |
| const gfx::Rect& target_rect, |
| float scale, |
| bool animate) { |
| const gfx::Rect target_rect_in_dip = |
| ui::ConvertRectToDIP(root_window_->layer(), target_rect); |
| EnsureRectIsVisibleDIP(target_rect_in_dip, scale, animate); |
| } |
| |
| void MagnificationControllerImpl::EnsureRectIsVisibleDIP( |
| const gfx::Rect& target_rect, |
| float scale, |
| bool animate) { |
| ValidateScale(&scale); |
| |
| const gfx::Rect window_rect = GetWindowRectDIP(scale); |
| if (scale == scale_ && window_rect.Contains(target_rect)) |
| return; |
| |
| // TODO(yoshiki): Un-zoom and change the scale if the magnification window |
| // can't contain the whole given rect. |
| |
| gfx::Rect rect = window_rect; |
| if (target_rect.width() > rect.width()) |
| rect.set_x(target_rect.CenterPoint().x() - rect.x() / 2); |
| else if (target_rect.right() < rect.x()) |
| rect.set_x(target_rect.right()); |
| else if (rect.right() < target_rect.x()) |
| rect.set_x(target_rect.x() - rect.width()); |
| |
| if (rect.height() > window_rect.height()) |
| rect.set_y(target_rect.CenterPoint().y() - rect.y() / 2); |
| else if (target_rect.bottom() < rect.y()) |
| rect.set_y(target_rect.bottom()); |
| else if (rect.bottom() < target_rect.y()) |
| rect.set_y(target_rect.y() - rect.height()); |
| |
| RedrawDIP(rect.origin(), scale, animate); |
| } |
| |
| void MagnificationControllerImpl::EnsurePointIsVisibleWithScale( |
| const gfx::Point& point, |
| float scale, |
| bool animate) { |
| EnsureRectIsVisibleWithScale(gfx::Rect(point, gfx::Size(0, 0)), |
| scale, |
| animate); |
| } |
| |
| void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) { |
| gfx::Point mouse(location); |
| |
| int x = origin_.x(); |
| int y = origin_.y(); |
| bool start_zoom = false; |
| |
| const gfx::Rect window_rect = GetWindowRectDIP(scale_); |
| const int left = window_rect.x(); |
| const int right = window_rect.right(); |
| const int width_margin = static_cast<int>(0.1f * window_rect.width()); |
| const int width_offset = static_cast<int>(0.5f * window_rect.width()); |
| |
| if (mouse.x() < left + width_margin) { |
| x -= width_offset; |
| start_zoom = true; |
| } else if (right - width_margin < mouse.x()) { |
| x += width_offset; |
| start_zoom = true; |
| } |
| |
| const int top = window_rect.y(); |
| const int bottom = window_rect.bottom(); |
| // Uses same margin with x-axis's one. |
| const int height_margin = width_margin; |
| const int height_offset = static_cast<int>(0.5f * window_rect.height()); |
| |
| if (mouse.y() < top + height_margin) { |
| y -= height_offset; |
| start_zoom = true; |
| } else if (bottom - height_margin < mouse.y()) { |
| y += height_offset; |
| start_zoom = true; |
| } |
| |
| if (start_zoom && !is_on_zooming_) { |
| bool ret = Redraw(gfx::Point(x, y), scale_, true); |
| |
| if (ret) { |
| is_on_zooming_ = true; |
| |
| int x_diff = origin_.x() - window_rect.x(); |
| int y_diff = origin_.y() - window_rect.y(); |
| // If the magnified region is moved, hides the mouse cursor and moves it. |
| if (x_diff != 0 || y_diff != 0) { |
| ash::Shell::GetInstance()-> |
| env_filter()->set_update_cursor_visibility(false); |
| root_window_->ShowCursor(false); |
| mouse.set_x(mouse.x() - (origin_.x() - window_rect.x())); |
| mouse.set_y(mouse.y() - (origin_.y() - window_rect.y())); |
| root_window_->MoveCursorTo(mouse); |
| } |
| } |
| } |
| } |
| |
| gfx::Size MagnificationControllerImpl::GetHostSizeDIP() const { |
| return ui::ConvertSizeToDIP(root_window_->layer(), |
| root_window_->GetHostSize()); |
| } |
| |
| gfx::Rect MagnificationControllerImpl::GetWindowRectDIP(float scale) const { |
| const gfx::Size size_in_dip = |
| ui::ConvertSizeToDIP(root_window_->layer(), |
| root_window_->GetHostSize()); |
| const int width = size_in_dip.width() / scale; |
| const int height = size_in_dip.height() / scale; |
| |
| return gfx::Rect(origin_.x(), origin_.y(), width, height); |
| } |
| |
| bool MagnificationControllerImpl::IsMagnified() const { |
| return scale_ >= kMinMagnifiedScaleThreshold; |
| } |
| |
| void MagnificationControllerImpl::ValidateScale(float* scale) { |
| // Adjust the scale to just |kNonMagnifiedScale| if scale is smaller than |
| // |kMinMagnifiedScaleThreshold|; |
| if (*scale < kMinMagnifiedScaleThreshold) |
| *scale = kNonMagnifiedScale; |
| |
| // Adjust the scale to just |kMinMagnifiedScale| if scale is bigger than |
| // |kMinMagnifiedScaleThreshold|; |
| if (*scale > kMaxMagnifiedScaleThreshold) |
| *scale = kMaxMagnifiedScale; |
| } |
| |
| void MagnificationControllerImpl::OnImplicitAnimationsCompleted() { |
| root_window_->ShowCursor(true); |
| is_on_zooming_ = false; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // MagnificationControllerImpl: MagnificationController implementation |
| |
| void MagnificationControllerImpl::SetScale(float scale, bool animate) { |
| if (!is_enabled_) |
| return; |
| |
| ValidateScale(&scale); |
| |
| // Try not to change the point which the mouse cursor indicates to. |
| const gfx::Rect window_rect = GetWindowRectDIP(scale); |
| const gfx::Point mouse = root_window_->last_mouse_location(); |
| const gfx::Point origin = gfx::Point(mouse.x() * (1.0f - 1.0f / scale), |
| mouse.y() * (1.0f - 1.0f / scale)); |
| Redraw(origin, scale, animate); |
| } |
| |
| void MagnificationControllerImpl::MoveWindow(int x, int y, bool animate) { |
| if (!is_enabled_) |
| return; |
| |
| Redraw(gfx::Point(x, y), scale_, animate); |
| } |
| |
| void MagnificationControllerImpl::MoveWindow(const gfx::Point& point, |
| bool animate) { |
| if (!is_enabled_) |
| return; |
| |
| Redraw(point, scale_, animate); |
| } |
| |
| void MagnificationControllerImpl::EnsureRectIsVisible( |
| const gfx::Rect& target_rect, |
| bool animate) { |
| if (!is_enabled_) |
| return; |
| |
| EnsureRectIsVisibleWithScale(target_rect, scale_, animate); |
| } |
| |
| void MagnificationControllerImpl::EnsurePointIsVisible( |
| const gfx::Point& point, |
| bool animate) { |
| if (!is_enabled_) |
| return; |
| |
| EnsurePointIsVisibleWithScale(point, scale_, animate); |
| } |
| |
| void MagnificationControllerImpl::SetEnabled(bool enabled) { |
| if (enabled) { |
| is_enabled_ = enabled; |
| SetScale(kInitialMagnifiedScale, true); |
| } else { |
| SetScale(kNonMagnifiedScale, true); |
| is_enabled_ = enabled; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // MagnificationControllerImpl: aura::EventFilter implementation |
| |
| bool MagnificationControllerImpl::PreHandleKeyEvent(aura::Window* target, |
| aura::KeyEvent* event) { |
| return false; |
| } |
| |
| bool MagnificationControllerImpl::PreHandleMouseEvent(aura::Window* target, |
| aura::MouseEvent* event) { |
| if (IsMagnified() && event->type() == ui::ET_MOUSE_MOVED) |
| OnMouseMove(event->root_location()); |
| return false; |
| } |
| |
| ui::TouchStatus MagnificationControllerImpl::PreHandleTouchEvent( |
| aura::Window* target, |
| aura::TouchEvent* event) { |
| return ui::TOUCH_STATUS_UNKNOWN; |
| } |
| |
| ui::GestureStatus MagnificationControllerImpl::PreHandleGestureEvent( |
| aura::Window* target, |
| aura::GestureEvent* event) { |
| return ui::GESTURE_STATUS_UNKNOWN; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // MagnificationController: |
| |
| // static |
| MagnificationController* MagnificationController::CreateInstance() { |
| return new MagnificationControllerImpl(); |
| } |
| |
| } // namespace internal |
| } // namespace ash |