package gminers.glasspane.component;
import gminers.glasspane.PaneBB;
import gminers.glasspane.event.KeyTypedEvent;
import gminers.glasspane.event.MouseWheelEvent;
import gminers.glasspane.listener.PaneEventHandler;
import gminers.kitchensink.Rendering;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.Validate;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
/**
* A panel that can be scrolled by the user.
*
* @author Aesen Vismea
*
*/
public class PaneScrollPanel
extends PaneContainer {
private float momentum = 0.0f;
private float incomingMomentum = 0.0f;
private float offset = 0.0f;
/**
* Whether or not to draw a ShadowPanel-style shadow.
*/
@Getter @Setter private boolean shadowed = true;
/**
* The depth of the shadow, if enabled.
*/
@Getter @Setter private int shadowDepth = 3;
@SuppressWarnings("deprecation")
public PaneScrollPanel() {
// Workaround: Scroll Panel causes horrible white flickering when it's not clipped to size. I don't know why.
setClipToSize(true);
}
@PaneEventHandler
public void onKeyPress(final KeyTypedEvent e) {
if (e.getKeyCode() == Keyboard.KEY_NEXT) {
incomingMomentum -= height / 2f;
} else if (e.getKeyCode() == Keyboard.KEY_PRIOR) {
incomingMomentum += height / 2f;
} else if (e.getKeyCode() == Keyboard.KEY_DOWN) {
incomingMomentum -= 2;
} else if (e.getKeyCode() == Keyboard.KEY_UP) {
incomingMomentum += 2;
} else if (e.getKeyCode() == Keyboard.KEY_HOME) {
incomingMomentum = ((getMinimumChildY() - offset) / 2f) + (-momentum);
} else if (e.getKeyCode() == Keyboard.KEY_END) {
incomingMomentum = ((-(getMaximumChildEdgeY() - height) - offset) / 2f) + (-momentum);
}
}
@PaneEventHandler
public void onMouseWheel(final MouseWheelEvent e) {
incomingMomentum += (e.getDistance() / 4f);
}
public int getMinimumChildY() {
if (components.isEmpty()) return 0;
int rtrn = Integer.MAX_VALUE;
for (final PaneComponent pc : components) {
rtrn = Math.min(rtrn, pc.getY());
}
return rtrn;
}
public int getMaximumChildEdgeY() {
if (components.isEmpty()) return 0;
int rtrn = Integer.MIN_VALUE;
for (final PaneComponent pc : components) {
rtrn = Math.max(rtrn, pc.getEdgeY());
}
return rtrn;
}
@Override
protected void doTick() {
if (getMaximumChildEdgeY() <= getHeight()) {
momentum = 0;
offset = 0;
} else {
float in = incomingMomentum / 2f;
in = Math.min(Math.max(24, Math.abs(momentum)), Math.abs(in)) * Math.signum(in);
incomingMomentum -= in;
if (incomingMomentum == Float.NaN || Math.abs(incomingMomentum) < 0.001) {
incomingMomentum = 0;
}
momentum += in;
momentum = momentum / 1.5f;
if (momentum == Float.NaN || Math.abs(momentum) < 0.001) {
momentum = 0;
}
offset += momentum;
if (offset > 0) {
offset = momentum;
} else if (offset < -((getMaximumChildEdgeY() + 8) - height)) {
offset = -((getMaximumChildEdgeY() + 8) - height) + momentum;
}
}
}
private final PaneBB goat = new PaneBB();
@Override
protected void doRender(final int mouseX, final int mouseY, final float partialTicks) {
final int col = 0x88000000;
if (shadowed) {
Rendering.drawRect(0, 0, width, height, col);
}
final float ofs = (float) (Math.floor(offset * 2f) / 2f);
final int iOfs = (int) offset;
final int pX = getPX();
final int pY = getPY();
GL11.glPushMatrix();
GL11.glTranslatef(0, ofs, 0);
for (final PaneComponent pc : components) {
if (intersects(goat.mimic(pc).translate(x, iOfs + y))) {
pc.render(mouseX - pX, (mouseY - pY), partialTicks);
}
}
GL11.glPopMatrix();
if (shadowed) {
GL11.glDisable(GL11.GL_SCISSOR_TEST);
Rendering.drawGradientRect(0, 0, width, shadowDepth, 0xFF000000, 0x00000000, 0);
Rendering.drawGradientRect(0, height - shadowDepth, width, height, 0x00000000, 0xFF000000, 0);
GL11.glEnable(GL11.GL_SCISSOR_TEST);
}
GL11.glScissor(getAbsoluteX(getGlassPane().getWidth()), getAbsoluteY(getGlassPane().getHeight()),
getAbsoluteWidth(getGlassPane().getWidth()), getAbsoluteHeight(getGlassPane().getHeight()));
Rendering.drawRect(width - 4, 0, width, height, col);
final int diff = getMaximumChildEdgeY() - getMinimumChildY();
float percentage = -offset / diff;
if (Float.isInfinite(percentage) || Float.isNaN(percentage)) {
percentage = 0.0f;
}
final float viewportPercentage = Math.min(1.0f, diff > 0 ? (float) height / (float) diff : 1);
final int barHeight = height - 2;
int segHeight = (int) (barHeight * viewportPercentage);
if (segHeight <= 0) {
segHeight = 1;
}
final int segY = (int) (Math.floor((barHeight * percentage) * 2f) / 2f);
Rendering.drawRect(width - 3, segY + 1, width - 1, segY + 1 + segHeight, 0x88FFFFFF);
}
@Override
protected int getPY() {
return (int) (super.getPY() + Math.floor(offset));
}
public boolean isChildVisible(final PaneComponent pc) {
Validate.isTrue(components.contains(pc), "Attempt to call isChildVisible with a non-child");
return intersects(goat.mimic(pc).translate(x, ((int) offset) + y));
}
}