// Copyright 2014 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. #ifndef UI_TOUCH_SELECTION_TOUCH_SELECTION_CONTROLLER_H_ #define UI_TOUCH_SELECTION_TOUCH_SELECTION_CONTROLLER_H_ #include "base/macros.h" #include "base/time/time.h" #include "ui/base/touch/selection_bound.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/vector2d_f.h" #include "ui/touch_selection/longpress_drag_selector.h" #include "ui/touch_selection/selection_event_type.h" #include "ui/touch_selection/touch_handle.h" #include "ui/touch_selection/touch_handle_orientation.h" #include "ui/touch_selection/ui_touch_selection_export.h" namespace ui { class MotionEvent; // Interface through which |TouchSelectionController| issues selection-related // commands, notifications and requests. class UI_TOUCH_SELECTION_EXPORT TouchSelectionControllerClient { public: virtual ~TouchSelectionControllerClient() {} virtual bool SupportsAnimation() const = 0; virtual void SetNeedsAnimate() = 0; virtual void MoveCaret(const gfx::PointF& position) = 0; virtual void MoveRangeSelectionExtent(const gfx::PointF& extent) = 0; virtual void SelectBetweenCoordinates(const gfx::PointF& base, const gfx::PointF& extent) = 0; virtual void OnSelectionEvent(SelectionEventType event) = 0; virtual scoped_ptr CreateDrawable() = 0; }; // Controller for manipulating text selection via touch input. class UI_TOUCH_SELECTION_EXPORT TouchSelectionController : public TouchHandleClient, public LongPressDragSelectorClient { public: enum ActiveStatus { INACTIVE, INSERTION_ACTIVE, SELECTION_ACTIVE, }; struct UI_TOUCH_SELECTION_EXPORT Config { Config(); ~Config(); // Maximum allowed time for handle tap detection. Defaults to 300 ms. base::TimeDelta max_tap_duration; // Defaults to 8 DIPs. float tap_slop; // Controls whether adaptive orientation for selection handles is enabled. // Defaults to false. bool enable_adaptive_handle_orientation; // Controls whether drag selection after a longpress is enabled. // Defaults to false. bool enable_longpress_drag_selection; // Controls whether an insertion handle is shown on a tap for an empty // editable text. Defauls to false. // TODO(mohsen): This flag used to be set to |true| on Aura. That's not the // case anymore and it is always |false|. Consider removing it. bool show_on_tap_for_empty_editable; }; TouchSelectionController(TouchSelectionControllerClient* client, const Config& config); ~TouchSelectionController() override; // To be called when the selection bounds have changed. // Note that such updates will trigger handle updates only if preceded // by an appropriate call to allow automatic showing. void OnSelectionBoundsChanged(const SelectionBound& start, const SelectionBound& end); // To be called when the viewport rect has been changed. This is used for // setting the state of the handles. void OnViewportChanged(const gfx::RectF viewport_rect); // Allows touch-dragging of the handle. // Returns true iff the event was consumed, in which case the caller should // cease further handling of the event. bool WillHandleTouchEvent(const MotionEvent& event); // To be called before forwarding a tap event. This allows automatically // showing the insertion handle from subsequent bounds changes. // |tap_count| is tap index in a repeated sequence, i.e., 1 for the first // tap, 2 for the second tap, etc... bool WillHandleTapEvent(const gfx::PointF& location, int tap_count); // To be called before forwarding a longpress event. This allows automatically // showing the selection or insertion handles from subsequent bounds changes. bool WillHandleLongPressEvent(base::TimeTicks event_time, const gfx::PointF& location); // To be called before forwarding a gesture scroll begin event to prevent // long-press drag. void OnScrollBeginEvent(); // Allow showing the selection handles from the most recent selection bounds // update (if valid), or a future valid bounds update. void AllowShowingFromCurrentSelection(); // Hide the handles and suppress bounds updates until the next explicit // showing allowance. void HideAndDisallowShowingAutomatically(); // Override the handle visibility according to |hidden|. void SetTemporarilyHidden(bool hidden); // To be called when the editability of the focused region changes. void OnSelectionEditable(bool editable); // To be called when the contents of the focused region changes. void OnSelectionEmpty(bool empty); // Ticks an active animation, as requested to the client by |SetNeedsAnimate|. // Returns true if an animation is active and requires further ticking. bool Animate(base::TimeTicks animate_time); // Returns the rect between the two active selection bounds. If just one of // the bounds is visible, the rect is simply the (one-dimensional) rect of // that bound. If no selection is active, an empty rect will be returned. gfx::RectF GetRectBetweenBounds() const; // Returns the visible rect of specified touch handle. For an active insertion // these values will be identical. gfx::RectF GetStartHandleRect() const; gfx::RectF GetEndHandleRect() const; // Returns the focal point of the start and end bounds, as defined by // their bottom coordinate. const gfx::PointF& GetStartPosition() const; const gfx::PointF& GetEndPosition() const; const SelectionBound& start() const { return start_; } const SelectionBound& end() const { return end_; } ActiveStatus active_status() const { return active_status_; } private: friend class TouchSelectionControllerTestApi; enum InputEventType { TAP, REPEATED_TAP, LONG_PRESS, INPUT_EVENT_TYPE_NONE }; // TouchHandleClient implementation. void OnDragBegin(const TouchSelectionDraggable& draggable, const gfx::PointF& drag_position) override; void OnDragUpdate(const TouchSelectionDraggable& draggable, const gfx::PointF& drag_position) override; void OnDragEnd(const TouchSelectionDraggable& draggable) override; bool IsWithinTapSlop(const gfx::Vector2dF& delta) const override; void OnHandleTapped(const TouchHandle& handle) override; void SetNeedsAnimate() override; scoped_ptr CreateDrawable() override; base::TimeDelta GetMaxTapDuration() const override; bool IsAdaptiveHandleOrientationEnabled() const override; // LongPressDragSelectorClient implementation. void OnLongPressDragActiveStateChanged() override; gfx::PointF GetSelectionStart() const override; gfx::PointF GetSelectionEnd() const override; void ShowInsertionHandleAutomatically(); void ShowSelectionHandlesAutomatically(); bool WillHandleTapOrLongPress(const gfx::PointF& location); void OnInsertionChanged(); void OnSelectionChanged(); // Returns true if insertion mode was newly (re)activated. bool ActivateInsertionIfNecessary(); void DeactivateInsertion(); // Returns true if selection mode was newly (re)activated. bool ActivateSelectionIfNecessary(); void DeactivateSelection(); void ForceNextUpdateIfInactive(); void UpdateHandleLayoutIfNecessary(); bool WillHandleTouchEventForLongPressDrag(const MotionEvent& event); void SetTemporarilyHiddenForLongPressDrag(bool hidden); void RefreshHandleVisibility(); gfx::Vector2dF GetStartLineOffset() const; gfx::Vector2dF GetEndLineOffset() const; bool GetStartVisible() const; bool GetEndVisible() const; TouchHandle::AnimationStyle GetAnimationStyle(bool was_active) const; void LogSelectionEnd(); TouchSelectionControllerClient* const client_; const Config config_; // Whether to force an update on the next selection event even if the // cached selection matches the new selection. bool force_next_update_; InputEventType response_pending_input_event_; SelectionBound start_; SelectionBound end_; TouchHandleOrientation start_orientation_; TouchHandleOrientation end_orientation_; ActiveStatus active_status_; scoped_ptr insertion_handle_; bool activate_insertion_automatically_; scoped_ptr start_selection_handle_; scoped_ptr end_selection_handle_; bool activate_selection_automatically_; bool selection_empty_; bool selection_editable_; bool temporarily_hidden_; // Whether to use the start bound (if false, the end bound) for computing the // appropriate text line offset when performing a selection drag. This helps // ensure that the initial selection induced by the drag doesn't "jump" // between lines. bool anchor_drag_to_selection_start_; // Longpress drag allows direct manipulation of longpress-initiated selection. LongPressDragSelector longpress_drag_selector_; gfx::RectF viewport_rect_; base::TimeTicks selection_start_time_; // Whether a selection handle was dragged during the current 'selection // session' - i.e. since the current selection has been activated. bool selection_handle_dragged_; DISALLOW_COPY_AND_ASSIGN(TouchSelectionController); }; } // namespace ui #endif // UI_TOUCH_SELECTION_TOUCH_SELECTION_CONTROLLER_H_