blob: d28286dfb035e488aac0436146a550a6275d69be [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.
package org.chromium.content.browser.input;
import android.view.View;
import com.google.common.annotations.VisibleForTesting;
import org.chromium.content.browser.PositionObserver;
/**
* CursorController for selecting a range of text.
*/
public abstract class SelectionHandleController implements CursorController {
// The following constants match the ones in
// third_party/WebKit/public/web/WebTextDirection.h
private static final int TEXT_DIRECTION_DEFAULT = 0;
private static final int TEXT_DIRECTION_LTR = 1;
private static final int TEXT_DIRECTION_RTL = 2;
/** The cursor controller images, lazily created when shown. */
private HandleView mStartHandle, mEndHandle;
/** Whether handles should show automatically when text is selected. */
private boolean mAllowAutomaticShowing = true;
/** Whether selection anchors are active. */
private boolean mIsShowing;
private View mParent;
private int mFixedHandleX;
private int mFixedHandleY;
private PositionObserver mPositionObserver;
public SelectionHandleController(View parent, PositionObserver positionObserver) {
mParent = parent;
mPositionObserver = positionObserver;
}
/** Automatically show selection anchors when text is selected. */
public void allowAutomaticShowing() {
mAllowAutomaticShowing = true;
}
/** Hide selection anchors, and don't automatically show them. */
public void hideAndDisallowAutomaticShowing() {
hide();
mAllowAutomaticShowing = false;
}
@Override
public boolean isShowing() {
return mIsShowing;
}
@Override
public void hide() {
if (mIsShowing) {
if (mStartHandle != null) mStartHandle.hide();
if (mEndHandle != null) mEndHandle.hide();
mIsShowing = false;
}
}
void cancelFadeOutAnimation() {
hide();
}
/**
* Updates the selection for a movement of the given handle (which
* should be the start handle or end handle) to coordinates x,y.
* Note that this will not actually result in the handle moving to (x,y):
* selectBetweenCoordinates(x1,y1,x2,y2) will trigger the selection and set the
* actual coordinates later via set[Start|End]HandlePosition.
*/
@Override
public void updatePosition(HandleView handle, int x, int y) {
selectBetweenCoordinates(mFixedHandleX, mFixedHandleY, x, y);
}
@Override
public void beforeStartUpdatingPosition(HandleView handle) {
HandleView fixedHandle = (handle == mStartHandle) ? mEndHandle : mStartHandle;
mFixedHandleX = fixedHandle.getAdjustedPositionX();
mFixedHandleY = fixedHandle.getLineAdjustedPositionY();
}
/**
* The concrete implementation must trigger a selection between the given
* coordinates and (possibly asynchronously) set the actual handle positions
* after the selection is made via set[Start|End]HandlePosition.
*/
protected abstract void selectBetweenCoordinates(int x1, int y1, int x2, int y2);
/**
* @return true iff this controller is being used to move the selection start.
*/
boolean isSelectionStartDragged() {
return mStartHandle != null && mStartHandle.isDragging();
}
/**
* @return true iff this controller is being used to drag either the selection start or end.
*/
public boolean isDragging() {
return (mStartHandle != null && mStartHandle.isDragging()) ||
(mEndHandle != null && mEndHandle.isDragging());
}
@Override
public void onTouchModeChanged(boolean isInTouchMode) {
if (!isInTouchMode) {
hide();
}
}
@Override
public void onDetached() {}
/**
* Moves the start handle so that it points at the given coordinates.
* @param x The start handle position X in physical pixels.
* @param y The start handle position Y in physical pixels.
*/
public void setStartHandlePosition(float x, float y) {
mStartHandle.positionAt((int) x, (int) y);
}
/**
* Moves the end handle so that it points at the given coordinates.
* @param x The end handle position X in physical pixels.
* @param y The end handle position Y in physical pixels.
*/
public void setEndHandlePosition(float x, float y) {
mEndHandle.positionAt((int) x, (int) y);
}
/**
* If the handles are not visible, sets their visibility to View.VISIBLE and begins fading them
* in.
*/
public void beginHandleFadeIn() {
mStartHandle.beginFadeIn();
mEndHandle.beginFadeIn();
}
/**
* Sets the start and end handles to the given visibility.
*/
public void setHandleVisibility(int visibility) {
mStartHandle.setVisibility(visibility);
mEndHandle.setVisibility(visibility);
}
/**
* Shows the handles if allowed.
*
* @param startDir Direction (left/right) of start handle.
* @param endDir Direction (left/right) of end handle.
*/
public void onSelectionChanged(int startDir, int endDir) {
if (mAllowAutomaticShowing) {
showHandles(startDir, endDir);
}
}
/**
* Sets both start and end position and show the handles.
* Note: this method does not trigger a selection, see
* selectBetweenCoordinates()
*
* @param startDir Direction (left/right) of start handle.
* @param endDir Direction (left/right) of end handle.
*/
public void showHandles(int startDir, int endDir) {
createHandlesIfNeeded(startDir, endDir);
showHandlesIfNeeded();
}
@VisibleForTesting
public HandleView getStartHandleViewForTest() {
return mStartHandle;
}
@VisibleForTesting
public HandleView getEndHandleViewForTest() {
return mEndHandle;
}
private void createHandlesIfNeeded(int startDir, int endDir) {
if (mStartHandle == null) {
mStartHandle = new HandleView(this,
startDir == TEXT_DIRECTION_RTL ? HandleView.RIGHT : HandleView.LEFT, mParent,
mPositionObserver);
}
if (mEndHandle == null) {
mEndHandle = new HandleView(this,
endDir == TEXT_DIRECTION_RTL ? HandleView.LEFT : HandleView.RIGHT, mParent,
mPositionObserver);
}
}
private void showHandlesIfNeeded() {
if (!mIsShowing) {
mIsShowing = true;
mStartHandle.show();
mEndHandle.show();
setHandleVisibility(HandleView.VISIBLE);
}
}
}