package gminers.glasspane.component.button;
import gminers.glasspane.HorzAlignment;
import gminers.glasspane.VertAlignment;
import gminers.glasspane.component.Focusable;
import gminers.glasspane.component.text.PaneLabel;
import gminers.glasspane.event.ComponentActivateEvent;
import gminers.glasspane.event.FocusGainedEvent;
import gminers.glasspane.listener.PaneEventHandler;
import gminers.kitchensink.Rendering;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.lang3.Validate;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import com.google.common.collect.Lists;
/**
* Implements a clickable button.
*
* @author Aesen Vismea
*
*/
@FieldDefaults(level = AccessLevel.PROTECTED)
@ToString
@Getter
@Setter
public class PaneButton
extends PaneLabel
implements Focusable {
/**
* Whether or not this button should render as enabled, and should be allowed to be clicked and have the focus.
*/
boolean enabled = true;
/**
* The color of this button. If set to 0xFFFFFF, renders the same as a default Minecraft button.
*/
int buttonColor = 0xFFFFFF;
/**
* The color of the text of this button when it is disabled.
*/
int disabledColor = 0xA0A0A0;
/**
* The color of the text of this button when hovering.
*/
int hoveredColor = 0xFFFFA0;
public PaneButton() {
this("Button");
}
public PaneButton(final String text) {
this.text = text;
color = 0xE0E0E0;
alignmentX = HorzAlignment.MIDDLE;
alignmentY = VertAlignment.MIDDLE;
width = 200;
height = 20;
}
@Override
protected void doTick() {
super.doTick();
// if we're disabled and focused, drop the focus
if (!enabled) {
if (getParent() != null) {
if (getParent().getFocusedComponent() == this) {
getParent().setFocusedComponent(null);
}
}
}
}
@Override
protected void doRender(final int mouseX, final int mouseY, final float partialTicks) {
// determine the u and v offsets we want
final int u = 0;
int v = 0;
final boolean hover = Mouse.isInsideWindow() && withinBounds(mouseX, mouseY);
if (enabled) {
v += 40;
if (buttonColor != 0xFFFFFF) {
v += 80;
}
if (hover || (this instanceof PaneToggleButton && ((PaneToggleButton) this).isSelected())) {
v += 40;
}
}
// bind the widgets file
Minecraft.getMinecraft().renderEngine.bindTexture(RESOURCE);
// unpack the button color
int r = buttonColor >> 16 & 255;
int g = buttonColor >> 8 & 255;
final int b = buttonColor & 255;
if (this instanceof PaneToggleButton && ((PaneToggleButton) this).isSelected()) {
r -= hover ? 32 : 64;
g -= hover ? 32 : 64;
}
// apply the button color
GL11.glColor3f(r / 255f, g / 255f, b / 255f);
renderStretchyTexturedRect(0, 0, u, v, width, height, 220, 40);
// if we're focused, draw a blue border over the normal black one
GL11.glTranslatef(0, 0, 0.001f);
if (getParent() != null) {
if (getParent().getFocusedComponent() == this) {
final int fv = 200;
renderStretchyTexturedRect(0, 0, u, fv, width, height, 220, 40);
}
}
// change the label's color, if needed
final int trueColor = color;
if (!enabled) {
color = disabledColor;
} else if (hover) {
color = hoveredColor;
}
// render the label
GL11.glTranslatef(0, 0, 0.001f);
if (alignmentX == HorzAlignment.RIGHT && !text.endsWith(" ")) {
text = text + " ";
} else if (alignmentX == HorzAlignment.LEFT && !text.startsWith(" ")) {
text = " " + text;
}
labelRender(mouseX, mouseY, partialTicks);
color = trueColor;
}
@Override
public String getText() {
if (alignmentX == HorzAlignment.RIGHT && text.endsWith(" "))
return text.substring(0, text.length() - 1);
else if (alignmentX == HorzAlignment.LEFT && text.startsWith(" ")) return text.substring(1);
return text;
}
protected void labelRender(final int mouseX, final int mouseY, final float partialTicks) {
super.doRender(mouseX, mouseY, partialTicks);
}
private List<Runnable> activationListeners = Lists.newArrayList();
/**
* Convenience method to easily register click listeners.
*/
public void registerActivationListener(final Runnable r) {
Validate.notNull(r, "Runnable cannot be null");
activationListeners.add(r);
}
/**
* Convenience method to easily unregister click listeners.
*/
public void unregisterActivationListener(final Runnable r) {
activationListeners.remove(r);
}
@PaneEventHandler
public void onActivate(final ComponentActivateEvent e) {
if (enabled) {
for (final Runnable r : activationListeners) {
r.run();
}
Minecraft.getMinecraft().getSoundHandler()
.playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
} else {
e.consume();
}
}
@PaneEventHandler
public void onFocus(final FocusGainedEvent e) {
if (!enabled) {
e.consume(); // refuse focus if we're disabled
}
}
public static void renderStretchyTexturedRect(int x, int y, int u, int v, int width, int height, int texWidth,
int texHeight) {
int hWidth = width / 2;
int hHeight = height / 2;
// render the button - this method of rendering gives buttons a max sensible size of 436x84 (for a 220x40 texture)
// for comparison, a GuiButton can only go up to 390x30
// rendering nine-patch style with a tiled center would allow theoretically infinite button sizes, but that's unnecessary
Rendering.drawTexturedModalRect(x, y, u, v, hWidth, hHeight, 0);
Rendering.drawTexturedModalRect(x + hWidth, y, u + (texWidth - hWidth), v, hWidth, hHeight, 0);
Rendering.drawTexturedModalRect(x, y + hHeight, u, v + (texHeight - hHeight), hWidth, hHeight, 0);
Rendering.drawTexturedModalRect(x + hWidth, y + hHeight, u + (texWidth - hWidth), v + (texHeight - hHeight),
hWidth, hHeight, 0);
}
/**
* Creates a 'done' button - a button which will call revert() on it's parent GlassPane when clicked, and appears centered near the
* bottom of the screen.
*
* @return A newly created 'done' button.
*/
public static PaneButton createDoneButton() {
final PaneButton done = new PaneButton("Done");
done.setAutoPositionX(true);
done.setRelativeX(0.5);
done.setRelativeXOffset(-100);
done.setAutoPositionY(true);
done.setRelativeY(1.0);
done.setRelativeYOffset(-30);
done.registerActivationListener(new Runnable() {
@Override
public void run() {
done.getGlassPane().revert();
}
});
return done;
}
}