mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
257 lines
8.2 KiB
Java
257 lines
8.2 KiB
Java
package forge.toolbox;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import com.badlogic.gdx.math.Vector2;
|
|
import com.badlogic.gdx.utils.TimeUtils;
|
|
|
|
import forge.Forge.Animation;
|
|
import forge.Forge.Graphics;
|
|
import forge.utils.PhysicsObject;
|
|
|
|
public abstract class FScrollPane extends FContainer {
|
|
private static float FLING_DECEL = 750f;
|
|
private static long FLING_STOP_DELAY = 500000000l; //half a second
|
|
|
|
private float scrollLeft, scrollTop;
|
|
private ScrollBounds scrollBounds;
|
|
|
|
public FScrollPane() {
|
|
scrollBounds = new ScrollBounds();
|
|
}
|
|
|
|
public float getScrollLeft() {
|
|
return scrollLeft;
|
|
}
|
|
|
|
public float getScrollTop() {
|
|
return scrollTop;
|
|
}
|
|
|
|
public float getScrollWidth() {
|
|
return scrollBounds.width;
|
|
}
|
|
|
|
public float getScrollHeight() {
|
|
return scrollBounds.height;
|
|
}
|
|
|
|
public float getMaxScrollLeft() {
|
|
return getScrollWidth() - getWidth();
|
|
}
|
|
|
|
public float getMaxScrollTop() {
|
|
return getScrollHeight() - getHeight();
|
|
}
|
|
|
|
public void scrollToLeft() {
|
|
setScrollPositions(0, scrollTop);
|
|
}
|
|
|
|
public void scrollToRight() {
|
|
setScrollPositions(getMaxScrollLeft(), scrollTop);
|
|
}
|
|
|
|
public void scrollToTop() {
|
|
setScrollPositions(scrollLeft, 0);
|
|
}
|
|
|
|
public void scrollToBottom() {
|
|
setScrollPositions(scrollLeft, getMaxScrollTop());
|
|
}
|
|
|
|
public void scrollIntoView(FDisplayObject child) {
|
|
Vector2 childPos = getChildRelativePosition(child);
|
|
if (childPos == null) { return; } //do nothing if not a valid child
|
|
|
|
float childLeft = childPos.x;
|
|
float childRight = childLeft + child.getWidth();
|
|
float childTop = childPos.y;
|
|
float childBottom = childTop + child.getHeight();
|
|
|
|
float dx = 0;
|
|
if (childLeft < 0) {
|
|
dx = childLeft;
|
|
}
|
|
else if (childRight > getWidth()) {
|
|
dx = childRight - getWidth();
|
|
}
|
|
float dy = 0;
|
|
if (childTop < 0) {
|
|
dy = childTop;
|
|
}
|
|
else if (childBottom > getHeight()) {
|
|
dy = childBottom - getHeight();
|
|
}
|
|
|
|
if (dx == 0 && dy == 0) { return; }
|
|
|
|
setScrollPositions(scrollLeft + dx, scrollTop + dy);
|
|
}
|
|
|
|
private boolean setScrollPositions(float scrollLeft0, float scrollTop0) {
|
|
if (scrollLeft0 < 0) {
|
|
scrollLeft0 = 0;
|
|
}
|
|
else {
|
|
float maxScrollLeft = getMaxScrollLeft();
|
|
if (scrollLeft0 > maxScrollLeft) {
|
|
scrollLeft0 = maxScrollLeft;
|
|
}
|
|
}
|
|
if (scrollTop0 < 0) {
|
|
scrollTop0 = 0;
|
|
}
|
|
else {
|
|
float maxScrollTop = getMaxScrollTop();
|
|
if (scrollTop0 > maxScrollTop) {
|
|
scrollTop0 = maxScrollTop;
|
|
}
|
|
}
|
|
float dx = scrollLeft - scrollLeft0;
|
|
float dy = scrollTop - scrollTop0;
|
|
if (dx == 0 && dy == 0) { return false; } //do nothing if scroll didn't change
|
|
|
|
scrollLeft = scrollLeft0;
|
|
scrollTop = scrollTop0;
|
|
|
|
//shift position of all children based on change in scroll positions
|
|
for (FDisplayObject obj : getChildren()) {
|
|
obj.setPosition(obj.getLeft() + dx, obj.getTop() + dy);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected final void doLayout(float width, float height) {
|
|
scrollBounds = layoutAndGetScrollBounds(width, height);
|
|
scrollBounds.increaseWidthTo(width); //ensure scroll bounds extends at least to visible bounds
|
|
scrollBounds.increaseHeightTo(height);
|
|
|
|
//attempt to restore current scroll positions after reseting them in order to update positions of newly laid out stuff
|
|
float oldScrollLeft = scrollLeft;
|
|
float oldScrollTop = scrollTop;
|
|
scrollLeft = 0;
|
|
scrollTop = 0;
|
|
setScrollPositionsAfterLayout(oldScrollLeft, oldScrollTop);
|
|
}
|
|
|
|
//allow overriding to adjust what scroll positions are restored after layout
|
|
protected void setScrollPositionsAfterLayout(float scrollLeft0, float scrollTop0) {
|
|
setScrollPositions(scrollLeft0, scrollTop0);
|
|
}
|
|
|
|
protected abstract ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight);
|
|
|
|
public static class ScrollBounds {
|
|
private float width, height;
|
|
|
|
protected ScrollBounds() {
|
|
this(0, 0);
|
|
}
|
|
public ScrollBounds(float width0, float height0) {
|
|
width = width0;
|
|
height = height0;
|
|
}
|
|
|
|
public float getWidth() {
|
|
return width;
|
|
}
|
|
|
|
public float getHeight() {
|
|
return height;
|
|
}
|
|
|
|
//increase width the given value if it's higher
|
|
public void increaseWidthTo(float width0) {
|
|
if (width0 > width) {
|
|
width = width0;
|
|
}
|
|
}
|
|
|
|
//increase height to the given value if it's higher
|
|
public void increaseHeightTo(float height0) {
|
|
if (height0 > height) {
|
|
height = height0;
|
|
}
|
|
}
|
|
}
|
|
|
|
private FlingAnimation activeFlingAnimation;
|
|
private long lastFlingStopTime;
|
|
|
|
private class FlingAnimation extends Animation {
|
|
private final PhysicsObject physicsObj;
|
|
|
|
private FlingAnimation(float velocityX, float velocityY) {
|
|
physicsObj = new PhysicsObject(new Vector2(scrollLeft, scrollTop), new Vector2(velocityX, velocityY));
|
|
physicsObj.setDecel(FLING_DECEL, FLING_DECEL);
|
|
}
|
|
|
|
@Override
|
|
protected boolean advance(float dt) {
|
|
if (physicsObj.isMoving()) { //avoid storing last fling stop time if scroll manually stopped
|
|
physicsObj.advance(dt);
|
|
Vector2 pos = physicsObj.getPosition();
|
|
if (setScrollPositions(pos.x, pos.y) && physicsObj.isMoving()) {
|
|
return true;
|
|
}
|
|
|
|
//end fling animation if can't scroll anymore or physics object is no longer moving
|
|
lastFlingStopTime = TimeUtils.nanoTime();
|
|
}
|
|
activeFlingAnimation = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean fling(float velocityX, float velocityY) {
|
|
velocityX = -velocityX; //reverse velocities to account for scroll moving in opposite direction
|
|
velocityY = -velocityY;
|
|
|
|
if (activeFlingAnimation == null) {
|
|
activeFlingAnimation = new FlingAnimation(velocityX, velocityY);
|
|
activeFlingAnimation.start();
|
|
}
|
|
else { //update existing animation with new velocity if needed
|
|
activeFlingAnimation.physicsObj.getVelocity().set(velocityX, velocityY);
|
|
activeFlingAnimation.physicsObj.setDecel(FLING_DECEL, FLING_DECEL);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void buildTouchListeners(float screenX, float screenY, ArrayList<FDisplayObject> listeners) {
|
|
//if fling animation active, stop it and prevent child controls handling touch events before next touch down
|
|
if (activeFlingAnimation != null) {
|
|
activeFlingAnimation.physicsObj.stop();
|
|
listeners.add(this);
|
|
return;
|
|
}
|
|
|
|
//if fling ended just shortly before, still prevent touch events on child controls
|
|
//in case user tapped just to late to stop scrolling before scroll bounds reached
|
|
if (lastFlingStopTime > 0) {
|
|
if (TimeUtils.nanoTime() - lastFlingStopTime < FLING_STOP_DELAY) {
|
|
listeners.add(this);
|
|
return;
|
|
}
|
|
lastFlingStopTime = 0; //don't need to hold onto this after it has elapsed
|
|
}
|
|
super.buildTouchListeners(screenX, screenY, listeners);
|
|
}
|
|
|
|
@Override
|
|
public boolean pan(float x, float y, float deltaX, float deltaY) {
|
|
setScrollPositions(scrollLeft - deltaX, scrollTop - deltaY);
|
|
return true;
|
|
}
|
|
|
|
public final void draw(Graphics g) {
|
|
g.startClip(0, 0, getWidth(), getHeight());
|
|
super.draw(g);
|
|
g.endClip();
|
|
}
|
|
}
|