blob: c726a2b28798ae195b4cf1e0f8de8b80deaf28ff [file] [log] [blame]
// 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 <map>
#include <memory>
#include "ash/ash_export.h"
#include "base/macros.h"
#include "base/timer/timer.h"
#include "ui/aura/window_observer.h"
#include "ui/base/ime/input_method_observer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/events/event_handler.h"
#include "ui/events/event_rewriter.h"
#include "ui/events/gestures/gesture_types.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
namespace aura {
class Window;
} // namespace aura
namespace gfx {
class Transform;
namespace ui {
class GestureProviderAura;
} // namespace ui
namespace ash {
// MagnificationController controls the Fullscreen Magnifier feature.
// MagnificationController implements GestureConsumer as it has its own
// GestureProvider to recognize gestures with screen coordinates of touches.
// Logical coordinates of touches cannot be used as they are changed with
// viewport change: scroll, zoom.
// MagnificationController implements EventRewriter to see and rewrite touch
// events. Once the controller detects two fingers pinch or scroll, it starts
// consuming all touch events not to confuse an app or a browser on the screen.
// It needs to rewrite events to dispatch touch cancel events.
class ASH_EXPORT MagnificationController : public ui::EventHandler,
public ui::ImplicitAnimationObserver,
public aura::WindowObserver,
public ui::InputMethodObserver,
public ui::GestureConsumer,
public ui::EventRewriter {
enum ScrollDirection {
~MagnificationController() override;
// Enables (or disables if |enabled| is false) screen magnifier feature.
void SetEnabled(bool enabled);
// Returns if the screen magnifier is enabled or not.
bool IsEnabled() const;
// Enables or disables the feature for keeping the text input focus centered.
void SetKeepFocusCentered(bool keep_focus_centered);
// Returns true if magnifier will keep the focus centered in screen for text
// input.
bool KeepFocusCentered() const;
// Sets the magnification ratio. 1.0f means no magnification.
void SetScale(float scale, bool animate);
// Returns the current magnification ratio.
float GetScale() const { return scale_; }
// Maps the current scale value to an index in the range between the minimum
// and maximum scale values, and steps up or down the scale depending on the
// value of |delta_index|.
void StepToNextScaleValue(int delta_index);
// Set the top-left point of the magnification window.
void MoveWindow(int x, int y, bool animate);
void MoveWindow(const gfx::Point& point, bool animate);
// Returns the current top-left point of the magnification window.
gfx::Point GetWindowPosition() const;
void SetScrollDirection(ScrollDirection direction);
// Returns the view port(i.e. the current visible window)'s Rect in root
// window coordinates.
gfx::Rect GetViewportRect() const;
// Centers the viewport around the given point in screen coordinates.
void CenterOnPoint(const gfx::Point& point_in_screen);
// Follows the focus on web page for non-editable controls. Called from Chrome
// when Fullscreen magnifier feature is enabled.
void HandleFocusedNodeChanged(bool is_editable_node,
const gfx::Rect& node_bounds_in_screen);
// Switch the magnified root window to |new_root_window|. This does following:
// - Unzoom the current root_window.
// - Zoom the given new root_window |new_root_window|.
// - Switch the target window from current window to |new_root_window|.
void SwitchTargetRootWindow(aura::Window* new_root_window,
bool redraw_original_root);
// Returns the magnification transformation for the root window. If
// magnification is disabled, return an empty Transform.
gfx::Transform GetMagnifierTransform() const;
// ui::InputMethodObserver:
void OnFocus() override {}
void OnBlur() override {}
void OnCaretBoundsChanged(const ui::TextInputClient* client) override;
void OnTextInputStateChanged(const ui::TextInputClient* client) override {}
void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {}
void OnShowVirtualKeyboardIfEnabled() override {}
// Returns the last mouse cursor (or last touched) location.
gfx::Point GetPointOfInterestForTesting() {
return point_of_interest_in_root_;
// Returns true if magnifier is still on animation for moving viewport.
bool IsOnAnimationForTesting() const { return is_on_animation_; }
// Disables the delay for moving magnifier window.
void DisableMoveMagnifierDelayForTesting() {
disable_move_magnifier_delay_ = true;
class GestureProviderClient;
enum LockedGestureType { NO_GESTURE, ZOOM, SCROLL };
// ui::ImplicitAnimationObserver overrides:
void OnImplicitAnimationsCompleted() override;
// aura::WindowObserver overrides:
void OnWindowDestroying(aura::Window* root_window) override;
void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) override;
// ui::EventHandler overrides:
void OnMouseEvent(ui::MouseEvent* event) override;
void OnScrollEvent(ui::ScrollEvent* event) override;
void OnTouchEvent(ui::TouchEvent* event) override;
// ui::EventRewriter overrides:
ui::EventRewriteStatus RewriteEvent(
const ui::Event& event,
std::unique_ptr<ui::Event>* new_event) override;
ui::EventRewriteStatus NextDispatchEvent(
const ui::Event& last_event,
std::unique_ptr<ui::Event>* new_event) 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::PointF& position, float scale, bool animate);
// Redraws the magnification window with the given origin position in dip and
// the given scale. Returns true if the window is changed; otherwise, false.
// The last two parameters specify the animation duration and tween type.
// If |animation_in_ms| is zero, there will be no animation, and |tween_type|
// will be ignored.
bool RedrawDIP(const gfx::PointF& position_in_dip,
float scale,
int animation_in_ms,
gfx::Tween::Type tween_type);
// 1) If the screen is scrolling (i.e. animating) and should scroll further,
// it does nothing.
// 2) If the screen is scrolling (i.e. animating) and the direction is NONE,
// it stops the scrolling animation.
// 3) If the direction is set to value other than NONE, it starts the
// scrolling/ animation towards that direction.
void StartOrStopScrollIfNecessary();
// Redraw with the given zoom scale keeping the mouse cursor location. In
// other words, zoom (or unzoom) centering around the cursor.
// Ignore mouse position change after redrawing if |ignore_mouse_change| is
// true.
void RedrawKeepingMousePosition(float scale,
bool animate,
bool ignore_mouse_change);
void OnMouseMove(const gfx::Point& location);
// Move the mouse cursot to the given point. Actual move will be done when
// the animation is completed. This should be called after animation is
// started.
void AfterAnimationMoveCursorTo(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::RectF GetWindowRectDIP(float scale) const;
// Returns the size of the root window.
gfx::Size GetHostSizeDIP() const;
// Correct the given scale value if necessary.
void ValidateScale(float* scale);
// Process pending gestures in |gesture_provider_|. This method returns true
// if the controller needs to cancel existing touches.
bool ProcessGestures();
// Moves the view port when |point| is located within
// |x_panning_margin| and |y_panning_margin| to the edge of the visible
// window region. The view port will be moved so that the |point| will be
// moved to the point where it has |x_target_margin| and |y_target_margin|
// to the edge of the visible region. If |reduce_bottom_margin| is true,
// then a reduced value will be used as the |y_panning_margin| and
// |y_target_margin| for the bottom edge.
void MoveMagnifierWindowFollowPoint(const gfx::Point& point,
int x_panning_margin,
int y_panning_margin,
int x_target_margin,
int y_target_margin,
bool reduce_bottom_margin);
// Moves the view port to center |point| in magnifier screen.
void MoveMagnifierWindowCenterPoint(const gfx::Point& point);
// Moves the viewport so that |rect| is fully visible. If |rect| is larger
// than the viewport horizontally or vertically, the viewport will be moved
// to center the |rect| in that dimension.
void MoveMagnifierWindowFollowRect(const gfx::Rect& rect);
// Invoked when |move_magnifier_timer_| fires to move the magnifier window to
// follow the caret.
void OnMoveMagnifierTimer();
// Target root window. This must not be NULL.
aura::Window* root_window_;
// True if the magnified window is currently animating a change. Otherwise,
// false.
bool is_on_animation_ = false;
bool is_enabled_ = false;
bool keep_focus_centered_ = false;
// True if the cursor needs to move the given position after the animation
// will be finished. When using this, set |position_after_animation_| as well.
bool move_cursor_after_animation_ = false;
// Stores the position of cursor to be moved after animation.
gfx::Point position_after_animation_;
// Stores the last mouse cursor (or last touched) location. This value is
// used on zooming to keep this location visible.
gfx::Point point_of_interest_in_root_;
// Current scale, origin (left-top) position of the magnification window.
float scale_;
gfx::PointF origin_;
float original_scale_;
gfx::PointF original_origin_;
ScrollDirection scroll_direction_ = SCROLL_NONE;
// MagnificationController locks gesture once user performs either scroll or
// pinch gesture above those thresholds.
LockedGestureType locked_gesture_ = NO_GESTURE;
// If true, MagnificationController consumes all touch events.
bool consume_touch_event_ = false;
// Number of touch points on the screen.
int32_t touch_points_ = 0;
// Map for holding ET_TOUCH_PRESS events. Those events are used to dispatch
// ET_TOUCH_CANCELLED events. Events will be removed from this map when press
// events are cancelled, i.e. size of this map can be different from number of
// touches on the screen. Key is pointer id.
std::map<int32_t, std::unique_ptr<ui::TouchEvent>> press_event_map_;
std::unique_ptr<GestureProviderClient> gesture_provider_client_;
// MagnificationCotroller owns its GestureProvider to detect gestures with
// screen coordinates of touch events. As MagnificationController changes zoom
// level and moves viewport, logical coordinates of touches cannot be used for
// gesture detection as they are changed if the controller reacts to gestures.
std::unique_ptr<ui::GestureProviderAura> gesture_provider_;
// Timer for moving magnifier window when it fires.
base::OneShotTimer move_magnifier_timer_;
// Most recent caret position in |root_window_| coordinates.
gfx::Point caret_point_;
// Flag for disabling moving magnifier delay. It can only be true in testing
// mode.
bool disable_move_magnifier_delay_ = false;
} // namespace ash