Home | History | Annotate | Download | only in input
      1 // Copyright 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.
      4 
      5 package org.chromium.content.browser.input;
      6 
      7 import android.view.View;
      8 
      9 import com.google.common.annotations.VisibleForTesting;
     10 
     11 import org.chromium.content.browser.PositionObserver;
     12 
     13 /**
     14  * CursorController for selecting a range of text.
     15  */
     16 public abstract class SelectionHandleController implements CursorController {
     17 
     18     // The following constants match the ones in
     19     // third_party/WebKit/public/web/WebTextDirection.h
     20     private static final int TEXT_DIRECTION_DEFAULT = 0;
     21     private static final int TEXT_DIRECTION_LTR = 1;
     22     private static final int TEXT_DIRECTION_RTL = 2;
     23 
     24     /** The cursor controller images, lazily created when shown. */
     25     private HandleView mStartHandle, mEndHandle;
     26 
     27     /** Whether handles should show automatically when text is selected. */
     28     private boolean mAllowAutomaticShowing = true;
     29 
     30     /** Whether selection anchors are active. */
     31     private boolean mIsShowing;
     32 
     33     private View mParent;
     34 
     35     private int mFixedHandleX;
     36     private int mFixedHandleY;
     37 
     38     private PositionObserver mPositionObserver;
     39 
     40     public SelectionHandleController(View parent, PositionObserver positionObserver) {
     41         mParent = parent;
     42         mPositionObserver = positionObserver;
     43     }
     44 
     45     /** Automatically show selection anchors when text is selected. */
     46     public void allowAutomaticShowing() {
     47         mAllowAutomaticShowing = true;
     48     }
     49 
     50     /** Hide selection anchors, and don't automatically show them. */
     51     public void hideAndDisallowAutomaticShowing() {
     52         hide();
     53         mAllowAutomaticShowing = false;
     54     }
     55 
     56     @Override
     57     public boolean isShowing() {
     58         return mIsShowing;
     59     }
     60 
     61     @Override
     62     public void hide() {
     63         if (mIsShowing) {
     64             if (mStartHandle != null) mStartHandle.hide();
     65             if (mEndHandle != null) mEndHandle.hide();
     66             mIsShowing = false;
     67         }
     68     }
     69 
     70     void cancelFadeOutAnimation() {
     71         hide();
     72     }
     73 
     74     /**
     75      * Updates the selection for a movement of the given handle (which
     76      * should be the start handle or end handle) to coordinates x,y.
     77      * Note that this will not actually result in the handle moving to (x,y):
     78      * selectBetweenCoordinates(x1,y1,x2,y2) will trigger the selection and set the
     79      * actual coordinates later via set[Start|End]HandlePosition.
     80      */
     81     @Override
     82     public void updatePosition(HandleView handle, int x, int y) {
     83         selectBetweenCoordinates(mFixedHandleX, mFixedHandleY, x, y);
     84     }
     85 
     86     @Override
     87     public void beforeStartUpdatingPosition(HandleView handle) {
     88         HandleView fixedHandle = (handle == mStartHandle) ? mEndHandle : mStartHandle;
     89         mFixedHandleX = fixedHandle.getAdjustedPositionX();
     90         mFixedHandleY = fixedHandle.getLineAdjustedPositionY();
     91     }
     92 
     93     /**
     94      * The concrete implementation must trigger a selection between the given
     95      * coordinates and (possibly asynchronously) set the actual handle positions
     96      * after the selection is made via set[Start|End]HandlePosition.
     97      */
     98     protected abstract void selectBetweenCoordinates(int x1, int y1, int x2, int y2);
     99 
    100     /**
    101      * @return true iff this controller is being used to move the selection start.
    102      */
    103     boolean isSelectionStartDragged() {
    104         return mStartHandle != null && mStartHandle.isDragging();
    105     }
    106 
    107     /**
    108      * @return true iff this controller is being used to drag either the selection start or end.
    109      */
    110     public boolean isDragging() {
    111         return (mStartHandle != null && mStartHandle.isDragging()) ||
    112                (mEndHandle != null && mEndHandle.isDragging());
    113     }
    114 
    115     @Override
    116     public void onTouchModeChanged(boolean isInTouchMode) {
    117         if (!isInTouchMode) {
    118             hide();
    119         }
    120     }
    121 
    122     @Override
    123     public void onDetached() {}
    124 
    125     /**
    126      * Moves the start handle so that it points at the given coordinates.
    127      * @param x The start handle position X in physical pixels.
    128      * @param y The start handle position Y in physical pixels.
    129      */
    130     public void setStartHandlePosition(float x, float y) {
    131         mStartHandle.positionAt((int) x, (int) y);
    132     }
    133 
    134     /**
    135      * Moves the end handle so that it points at the given coordinates.
    136      * @param x The end handle position X in physical pixels.
    137      * @param y The end handle position Y in physical pixels.
    138      */
    139     public void setEndHandlePosition(float x, float y) {
    140         mEndHandle.positionAt((int) x, (int) y);
    141     }
    142 
    143     /**
    144      * If the handles are not visible, sets their visibility to View.VISIBLE and begins fading them
    145      * in.
    146      */
    147     public void beginHandleFadeIn() {
    148         mStartHandle.beginFadeIn();
    149         mEndHandle.beginFadeIn();
    150     }
    151 
    152     /**
    153      * Sets the start and end handles to the given visibility.
    154      */
    155     public void setHandleVisibility(int visibility) {
    156         mStartHandle.setVisibility(visibility);
    157         mEndHandle.setVisibility(visibility);
    158     }
    159 
    160     /**
    161      * Shows the handles if allowed.
    162      *
    163      * @param startDir Direction (left/right) of start handle.
    164      * @param endDir Direction (left/right) of end handle.
    165      */
    166     public void onSelectionChanged(int startDir, int endDir) {
    167         if (mAllowAutomaticShowing) {
    168             showHandles(startDir, endDir);
    169         }
    170     }
    171 
    172     /**
    173      * Sets both start and end position and show the handles.
    174      * Note: this method does not trigger a selection, see
    175      * selectBetweenCoordinates()
    176      *
    177      * @param startDir Direction (left/right) of start handle.
    178      * @param endDir Direction (left/right) of end handle.
    179      */
    180     public void showHandles(int startDir, int endDir) {
    181         createHandlesIfNeeded(startDir, endDir);
    182         showHandlesIfNeeded();
    183     }
    184 
    185     @VisibleForTesting
    186     public HandleView getStartHandleViewForTest() {
    187         return mStartHandle;
    188     }
    189 
    190     @VisibleForTesting
    191     public HandleView getEndHandleViewForTest() {
    192         return mEndHandle;
    193     }
    194 
    195     private void createHandlesIfNeeded(int startDir, int endDir) {
    196         if (mStartHandle == null) {
    197             mStartHandle = new HandleView(this,
    198                     startDir == TEXT_DIRECTION_RTL ? HandleView.RIGHT : HandleView.LEFT, mParent,
    199                     mPositionObserver);
    200         }
    201         if (mEndHandle == null) {
    202             mEndHandle = new HandleView(this,
    203                     endDir == TEXT_DIRECTION_RTL ? HandleView.LEFT : HandleView.RIGHT, mParent,
    204                     mPositionObserver);
    205         }
    206     }
    207 
    208     private void showHandlesIfNeeded() {
    209         if (!mIsShowing) {
    210             mIsShowing = true;
    211             mStartHandle.show();
    212             mEndHandle.show();
    213             setHandleVisibility(HandleView.VISIBLE);
    214         }
    215     }
    216 }
    217