blob: 194ecc128eae0732ab4180672f9c5dfbf5a7ccb6 [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.text.method;
import android.view.MotionEvent;
import android.text.*;
import android.widget.TextView;
import android.view.View;
/**
* A movement method that interprets movement keys by scrolling the text buffer.
*/
public class ScrollingMovementMethod extends BaseMovementMethod implements MovementMethod {
private int getTopLine(TextView widget) {
return widget.getLayout().getLineForVertical(widget.getScrollY());
}
private int getBottomLine(TextView widget) {
return widget.getLayout().getLineForVertical(widget.getScrollY() + getInnerHeight(widget));
}
private int getInnerWidth(TextView widget) {
return widget.getWidth() - widget.getTotalPaddingLeft() - widget.getTotalPaddingRight();
}
private int getInnerHeight(TextView widget) {
return widget.getHeight() - widget.getTotalPaddingTop() - widget.getTotalPaddingBottom();
}
private int getCharacterWidth(TextView widget) {
return (int) Math.ceil(widget.getPaint().getFontSpacing());
}
private int getScrollBoundsLeft(TextView widget) {
final Layout layout = widget.getLayout();
final int topLine = getTopLine(widget);
final int bottomLine = getBottomLine(widget);
if (topLine > bottomLine) {
return 0;
}
int left = Integer.MAX_VALUE;
for (int line = topLine; line <= bottomLine; line++) {
final int lineLeft = (int) Math.floor(layout.getLineLeft(line));
if (lineLeft < left) {
left = lineLeft;
}
}
return left;
}
private int getScrollBoundsRight(TextView widget) {
final Layout layout = widget.getLayout();
final int topLine = getTopLine(widget);
final int bottomLine = getBottomLine(widget);
if (topLine > bottomLine) {
return 0;
}
int right = Integer.MIN_VALUE;
for (int line = topLine; line <= bottomLine; line++) {
final int lineRight = (int) Math.ceil(layout.getLineRight(line));
if (lineRight > right) {
right = lineRight;
}
}
return right;
}
@Override
protected boolean left(TextView widget, Spannable buffer) {
final int minScrollX = getScrollBoundsLeft(widget);
int scrollX = widget.getScrollX();
if (scrollX > minScrollX) {
scrollX = Math.max(scrollX - getCharacterWidth(widget), minScrollX);
widget.scrollTo(scrollX, widget.getScrollY());
return true;
}
return false;
}
@Override
protected boolean right(TextView widget, Spannable buffer) {
final int maxScrollX = getScrollBoundsRight(widget) - getInnerWidth(widget);
int scrollX = widget.getScrollX();
if (scrollX < maxScrollX) {
scrollX = Math.min(scrollX + getCharacterWidth(widget), maxScrollX);
widget.scrollTo(scrollX, widget.getScrollY());
return true;
}
return false;
}
@Override
protected boolean up(TextView widget, Spannable buffer) {
final Layout layout = widget.getLayout();
final int top = widget.getScrollY();
int topLine = layout.getLineForVertical(top);
if (layout.getLineTop(topLine) == top) {
// If the top line is partially visible, bring it all the way
// into view; otherwise, bring the previous line into view.
topLine -= 1;
}
if (topLine >= 0) {
Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(topLine));
return true;
}
return false;
}
@Override
protected boolean down(TextView widget, Spannable buffer) {
final Layout layout = widget.getLayout();
final int innerHeight = getInnerHeight(widget);
final int bottom = widget.getScrollY() + innerHeight;
int bottomLine = layout.getLineForVertical(bottom);
if (layout.getLineTop(bottomLine + 1) < bottom + 1) {
// Less than a pixel of this line is out of view,
// so we must have tried to make it entirely in view
// and now want the next line to be in view instead.
bottomLine += 1;
}
if (bottomLine <= layout.getLineCount() - 1) {
Touch.scrollTo(widget, layout, widget.getScrollX(),
layout.getLineTop(bottomLine + 1) - innerHeight);
return true;
}
return false;
}
@Override
protected boolean pageUp(TextView widget, Spannable buffer) {
final Layout layout = widget.getLayout();
final int top = widget.getScrollY() - getInnerHeight(widget);
int topLine = layout.getLineForVertical(top);
if (topLine >= 0) {
Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(topLine));
return true;
}
return false;
}
@Override
protected boolean pageDown(TextView widget, Spannable buffer) {
final Layout layout = widget.getLayout();
final int innerHeight = getInnerHeight(widget);
final int bottom = widget.getScrollY() + innerHeight + innerHeight;
int bottomLine = layout.getLineForVertical(bottom);
if (bottomLine <= layout.getLineCount() - 1) {
Touch.scrollTo(widget, layout, widget.getScrollX(),
layout.getLineTop(bottomLine + 1) - innerHeight);
return true;
}
return false;
}
@Override
protected boolean top(TextView widget, Spannable buffer) {
final Layout layout = widget.getLayout();
if (getTopLine(widget) >= 0) {
Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(0));
return true;
}
return false;
}
@Override
protected boolean bottom(TextView widget, Spannable buffer) {
final Layout layout = widget.getLayout();
final int lineCount = layout.getLineCount();
if (getBottomLine(widget) <= lineCount - 1) {
Touch.scrollTo(widget, layout, widget.getScrollX(),
layout.getLineTop(lineCount) - getInnerHeight(widget));
return true;
}
return false;
}
@Override
protected boolean lineStart(TextView widget, Spannable buffer) {
final int minScrollX = getScrollBoundsLeft(widget);
int scrollX = widget.getScrollX();
if (scrollX > minScrollX) {
widget.scrollTo(minScrollX, widget.getScrollY());
return true;
}
return false;
}
@Override
protected boolean lineEnd(TextView widget, Spannable buffer) {
final int maxScrollX = getScrollBoundsRight(widget) - getInnerWidth(widget);
int scrollX = widget.getScrollX();
if (scrollX < maxScrollX) {
widget.scrollTo(maxScrollX, widget.getScrollY());
return true;
}
return false;
}
@Override
protected boolean home(TextView widget, Spannable buffer) {
return top(widget, buffer);
}
@Override
protected boolean end(TextView widget, Spannable buffer) {
return bottom(widget, buffer);
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
return Touch.onTouchEvent(widget, buffer, event);
}
@Override
public void onTakeFocus(TextView widget, Spannable text, int dir) {
Layout layout = widget.getLayout();
if (layout != null && (dir & View.FOCUS_FORWARD) != 0) {
widget.scrollTo(widget.getScrollX(),
layout.getLineTop(0));
}
if (layout != null && (dir & View.FOCUS_BACKWARD) != 0) {
int padding = widget.getTotalPaddingTop() +
widget.getTotalPaddingBottom();
int line = layout.getLineCount() - 1;
widget.scrollTo(widget.getScrollX(),
layout.getLineTop(line+1) -
(widget.getHeight() - padding));
}
}
public static MovementMethod getInstance() {
if (sInstance == null)
sInstance = new ScrollingMovementMethod();
return sInstance;
}
private static ScrollingMovementMethod sInstance;
}