Implementing components
This commit is contained in:
@@ -15,4 +15,6 @@ public abstract class MinecraftProvider {
|
||||
public abstract int getWidth(String text, int font);
|
||||
|
||||
public abstract void registerBufferedImageTexture(SaturnIdentifier i, BufferedImage bi);
|
||||
|
||||
public abstract String getKeyName(int key);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.saturnclient.ui.components;
|
||||
|
||||
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
|
||||
public class CosmeticPreview extends Element {
|
||||
static int padding = 14;
|
||||
|
||||
SaturnIdentifier sprite;
|
||||
private Runnable onClick;
|
||||
private boolean isSelected;
|
||||
|
||||
public CosmeticPreview(boolean isSelected, SaturnIdentifier sprite, Runnable onClick) {
|
||||
this.sprite = sprite;
|
||||
this.onClick = onClick;
|
||||
this.isSelected = isSelected;
|
||||
|
||||
this.width = 50;
|
||||
this.height = 111;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
|
||||
isSelected ? Theme.ACCENT.value : Theme.PRIMARY.value);
|
||||
|
||||
int texWidth = width - padding;
|
||||
int texHeight = height - padding;
|
||||
|
||||
renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight,
|
||||
Theme.FOREGROUND.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
if (onClick != null) {
|
||||
onClick.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.saturnclient.ui.components;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.ElementRenderer;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.Utils;
|
||||
|
||||
public class ElementRendererImpl extends ElementRenderer {
|
||||
public void draw(List<Element> elements, Element element) {
|
||||
synchronized (elements) {
|
||||
elements.add(element);
|
||||
}
|
||||
|
||||
if (element.animation != null) {
|
||||
element.animation.init(element);
|
||||
}
|
||||
}
|
||||
|
||||
public void render(List<Element> elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY) {
|
||||
for (Element element : elements) {
|
||||
element.playAnimationFrame(elapsed);
|
||||
|
||||
renderScope.getMatrixStack().push();
|
||||
renderScope.setOpacity(element.opacity);
|
||||
renderScope.getMatrixStack().translate(element.x, element.y, 0);
|
||||
renderScope.getMatrixStack().scale(element.scale, element.scale, 1.0f);
|
||||
element.render(renderScope, new ElementContext(elapsed, mouseX, mouseY, element));
|
||||
renderScope.getMatrixStack().pop();
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseClicked(List<Element> elements, double mouseX, double mouseY, int button) {
|
||||
if (button == 0) {
|
||||
for (Element element : new ArrayList<>(elements)) {
|
||||
element.focused = false;
|
||||
|
||||
int adjustedMouseX = (int) mouseX - element.x;
|
||||
int adjustedMouseY = (int) mouseY - element.y;
|
||||
|
||||
element.mouseClicked(adjustedMouseX * element.scale, adjustedMouseY * element.scale, button);
|
||||
|
||||
if (element.opacity > 0 && Utils.isHovering(adjustedMouseX, adjustedMouseY, element.width,
|
||||
element.height, element.scale)) {
|
||||
element.focused = true;
|
||||
element.click((int) (adjustedMouseX * element.scale), (int) (adjustedMouseY * element.scale));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseScrolled(
|
||||
List<Element> elements,
|
||||
double mouseX,
|
||||
double mouseY,
|
||||
double horizontalAmount,
|
||||
double verticalAmount) {
|
||||
for (Element element : new ArrayList<>(elements)) {
|
||||
if (Utils.isHovering((int) mouseX - element.x, (int) mouseY - element.y, element.width, element.height,
|
||||
element.scale)) {
|
||||
element.scroll(
|
||||
(int) mouseX - element.x,
|
||||
(int) mouseY - element.y,
|
||||
horizontalAmount,
|
||||
verticalAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void keyPressed(List<Element> elements, int keyCode, int scanCode, int modifiers) {
|
||||
for (Element element : new ArrayList<>(elements)) {
|
||||
if (element.focused && element.opacity > 0) {
|
||||
element.keyPressed(keyCode, scanCode, modifiers);
|
||||
|
||||
char typedChar = Utils.getCharFromKey(keyCode, modifiers);
|
||||
if (typedChar != '\0') {
|
||||
element.charTyped(typedChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseDragged(List<Element> elements, double mouseX, double mouseY, int button, double deltaX,
|
||||
double deltaY) {
|
||||
for (Element element : new ArrayList<>(elements)) {
|
||||
element.mouseDragged(mouseX - element.x, mouseY - element.y, button, deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseReleased(List<Element> elements, double mouseX, double mouseY, int button) {
|
||||
for (Element element : new ArrayList<>(elements)) {
|
||||
element.mouseReleased(mouseX - element.x, mouseY - element.y, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package org.saturnclient.ui.components;
|
||||
|
||||
import org.saturnclient.modules.Module;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.Utils;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
import org.saturnclient.ui.resources.Textures;
|
||||
import org.saturnclient.ui.screens.ConfigEditor;
|
||||
|
||||
public class SaturnModule extends Element {
|
||||
|
||||
private final Module mod;
|
||||
|
||||
static int p = 10; // padding
|
||||
static int s = 14; // settings icon size
|
||||
static int expand = 5; // settings icon click area
|
||||
|
||||
public SaturnModule(Module mod) {
|
||||
this.mod = mod;
|
||||
this.width = 160;
|
||||
this.height = 50;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
boolean hovered = ctx.isHovering();
|
||||
boolean enabled = mod.isEnabled();
|
||||
|
||||
int iconX = width - s - p;
|
||||
int iconY = height - s - p;
|
||||
|
||||
boolean settingsHover = ctx.isHovering(
|
||||
getSettingsX(),
|
||||
getSettingsY(),
|
||||
getSettingsHitBox(),
|
||||
getSettingsHitBox());
|
||||
|
||||
// Resolve theme colors
|
||||
int bgColor = Theme.PRIMARY.value;
|
||||
int fgColor = hovered && !settingsHover ? Theme.ACCENT.value : Theme.PRIMARY_FG.value;
|
||||
|
||||
int iconBg = enabled ? Theme.ACCENT.value : Theme.PRIMARY.value;
|
||||
int iconFg = enabled ? Theme.ACCENT_FG.value : fgColor;
|
||||
|
||||
int settingsColor = settingsHover
|
||||
? Theme.ACCENT.value
|
||||
: Theme.withAlpha(160, fgColor);
|
||||
|
||||
int h = height - (p * 2);
|
||||
|
||||
// Main background
|
||||
renderScope.drawRoundedRectangle(
|
||||
0, 0,
|
||||
width, height,
|
||||
Theme.WIDGET_RADIUS.value,
|
||||
bgColor);
|
||||
|
||||
// Icon background
|
||||
renderScope.drawRoundedRectangle(
|
||||
p, p,
|
||||
h, h,
|
||||
Theme.WIDGET_RADIUS.value,
|
||||
iconBg);
|
||||
|
||||
// Icon
|
||||
renderScope.drawTexture(
|
||||
mod.getIconTexture(),
|
||||
p + (p / 2),
|
||||
p + (p / 2),
|
||||
0, 0,
|
||||
h - p,
|
||||
h - p,
|
||||
iconFg);
|
||||
|
||||
// Module name
|
||||
renderScope.drawText(
|
||||
0.6f,
|
||||
mod.getName(),
|
||||
p + h + 4,
|
||||
p + 1,
|
||||
Theme.FONT.value,
|
||||
fgColor);
|
||||
|
||||
// Version (right aligned)
|
||||
String version = mod.getVersion();
|
||||
int versionWidth = (int) (Fonts.getWidth(version, Theme.FONT.value) * 0.35f);
|
||||
|
||||
renderScope.drawText(
|
||||
0.35f,
|
||||
version,
|
||||
width - p - versionWidth,
|
||||
p + 1,
|
||||
Theme.FONT.value,
|
||||
fgColor);
|
||||
|
||||
// Tags
|
||||
int xt = p + h + 4;
|
||||
for (String tag : mod.getTags()) {
|
||||
int tagWidth = (int) (Fonts.getWidth(tag, Theme.FONT.value) * 0.4f);
|
||||
|
||||
renderScope.drawRoundedRectangle(
|
||||
xt,
|
||||
p + (h / 2),
|
||||
tagWidth + 6,
|
||||
12,
|
||||
5,
|
||||
Theme.ACCENT.value);
|
||||
|
||||
renderScope.drawText(
|
||||
0.4f,
|
||||
tag,
|
||||
xt + 2,
|
||||
p + (h / 2) + 3,
|
||||
Theme.FONT.value,
|
||||
Theme.ACCENT_FG.value);
|
||||
|
||||
xt += tagWidth + 9;
|
||||
}
|
||||
|
||||
// Settings icon
|
||||
renderScope.drawTexture(
|
||||
Textures.SETTINGS,
|
||||
iconX,
|
||||
iconY,
|
||||
0, 0,
|
||||
s, s,
|
||||
settingsColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
boolean settingsHover = Utils.isHovering(
|
||||
mouseX - getSettingsX(),
|
||||
mouseY - getSettingsY(),
|
||||
getSettingsHitBox(),
|
||||
getSettingsHitBox(),
|
||||
1.0f);
|
||||
|
||||
if (settingsHover) {
|
||||
SaturnClient.client.setScreen(new ConfigEditor(mod.getConfig()));
|
||||
} else {
|
||||
mod.setEnabled(!mod.isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
private int getSettingsX() {
|
||||
return width - s - p - expand;
|
||||
}
|
||||
|
||||
private int getSettingsY() {
|
||||
return height - s - p - expand;
|
||||
}
|
||||
|
||||
private int getSettingsHitBox() {
|
||||
return s + expand * 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package org.saturnclient.ui.components;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
|
||||
import org.saturnclient.config.Config;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.Utils;
|
||||
import org.saturnclient.ui.resources.Textures;
|
||||
import org.saturnclient.ui.screens.ConfigEditor;
|
||||
import org.saturnclient.ui.screens.ModMenu;
|
||||
import org.saturnclient.ui.screens.cosmetics.CloakMenu;
|
||||
import org.saturnclient.ui.screens.store.CloakStore;
|
||||
|
||||
public class Sidebar extends Element {
|
||||
public static class SidebarComponent {
|
||||
Runnable onClick;
|
||||
boolean end;
|
||||
SaturnIdentifier sprite;
|
||||
|
||||
public SidebarComponent(SaturnIdentifier sprite, Runnable onClick, boolean end) {
|
||||
this.onClick = onClick;
|
||||
this.end = end;
|
||||
this.sprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
Runnable onClose;
|
||||
SidebarComponent[] components = {
|
||||
new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> {
|
||||
SaturnClient.client.setScreen(new ModMenu());
|
||||
}, false),
|
||||
new Sidebar.SidebarComponent(Textures.SETTINGS, () -> {
|
||||
SaturnClient.client.setScreen(new ConfigEditor(Config.config));
|
||||
}, false),
|
||||
|
||||
new Sidebar.SidebarComponent(Textures.SHIRT, () -> {
|
||||
SaturnClient.client.setScreen(new CloakMenu());
|
||||
}, false),
|
||||
|
||||
new Sidebar.SidebarComponent(Textures.CLOSE, () -> {
|
||||
onClose.run();
|
||||
}, true),
|
||||
|
||||
new Sidebar.SidebarComponent(Textures.STORE, () -> {
|
||||
SaturnClient.client.setScreen(new CloakStore());
|
||||
}, true),
|
||||
};
|
||||
int active = 0;
|
||||
|
||||
public Sidebar(int active, Runnable onClose) {
|
||||
this.width = 30;
|
||||
this.height = 350;
|
||||
this.active = active;
|
||||
this.onClose = onClose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
int topRow = 0;
|
||||
int bottomRow = 0;
|
||||
|
||||
int gap = 5; // 5px gap between icons
|
||||
int padding = 10;
|
||||
int sp = 30 - padding + gap;
|
||||
int s = 30 - (padding * 2);
|
||||
int s2 = s + padding;
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (SidebarComponent component : components) {
|
||||
int y = component.end ? ((height - (sp * bottomRow)) - (s * 2)) : (padding + (sp * topRow));
|
||||
boolean selected = ctx.isHovering(0, y - (padding / 2), s2, s2) || active == i;
|
||||
|
||||
renderScope.drawRoundedRectangle(padding / 2, y - (padding / 2), s + padding,
|
||||
s + padding, Theme.WIDGET_RADIUS.value, Theme.getBg(selected));
|
||||
|
||||
renderScope.drawTexture(component.sprite, padding, y, 0, 0, s, s, Theme.getFg(selected));
|
||||
|
||||
if (component.end) {
|
||||
bottomRow += 1;
|
||||
} else {
|
||||
topRow += 1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
int topRow = 0;
|
||||
int bottomRow = 0;
|
||||
|
||||
int gap = 5;
|
||||
int padding = 10;
|
||||
int sp = 30 - padding + gap;
|
||||
int s = 30 - (padding * 2);
|
||||
int s2 = s + padding;
|
||||
|
||||
for (SidebarComponent component : components) {
|
||||
int y = component.end ? ((height - (sp * bottomRow)) - (s * 2)) : (padding + (sp * topRow));
|
||||
boolean hovering = Utils.isHovering(mouseX - (padding / 2), mouseY - (y - (padding / 2)), s2, s2, scale);
|
||||
|
||||
if (hovering) {
|
||||
component.onClick.run();
|
||||
break;
|
||||
}
|
||||
|
||||
if (component.end) {
|
||||
bottomRow += 1;
|
||||
} else {
|
||||
topRow += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.saturnclient.ui.components;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
import org.saturnclient.common.minecraft.bindings.SaturnQuaternionf;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
|
||||
import net.minecraft.client.render.DiffuseLighting;
|
||||
import net.minecraft.client.render.entity.EntityRenderDispatcher;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
|
||||
public class SkinPreview extends Element {
|
||||
private boolean negativeAngle = false;
|
||||
private float angle = 0.0f;
|
||||
|
||||
public SkinPreview(float angle, boolean negativeAngle) {
|
||||
this.angle = angle;
|
||||
this.negativeAngle = negativeAngle;
|
||||
}
|
||||
|
||||
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float f,
|
||||
float mouseX,
|
||||
float mouseY, LivingEntity entity, float angle, boolean negativeAngle) {
|
||||
float g = (float) (x1 + x2) / 2.0F;
|
||||
float h = (float) (y1 + y2) / 2.0F;
|
||||
renderScope.enableScissor(0, 6, x2, y2);
|
||||
float i = (float) Math.atan((double) ((g - mouseX) / 40.0F));
|
||||
float j = (float) Math.atan((double) ((h - mouseY) / 40.0F));
|
||||
Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F);
|
||||
float k = entity.bodyYaw;
|
||||
float l = entity.getYaw();
|
||||
float m = entity.getPitch();
|
||||
float n = entity.prevHeadYaw;
|
||||
float o = entity.headYaw;
|
||||
entity.setYaw(angle);
|
||||
entity.bodyYaw = angle;
|
||||
entity.setPitch(-(j * 20.0F));
|
||||
entity.headYaw = negativeAngle ? (i * 40.0F) + 180f : -(i * 40.0F);
|
||||
entity.prevHeadYaw = entity.getHeadYaw();
|
||||
entity.setCustomNameVisible(false);
|
||||
|
||||
float p = entity.getScale();
|
||||
Vector3f vector3f = new Vector3f(0.0F, entity.getHeight() / 2.0F + f * p, 0.0F);
|
||||
float q = (float) size / p;
|
||||
drawEntity(renderScope, g, h, q, vector3f, quaternionf, null, entity);
|
||||
|
||||
entity.setCustomNameVisible(true);
|
||||
entity.bodyYaw = k;
|
||||
entity.setYaw(l);
|
||||
entity.setPitch(m);
|
||||
entity.prevHeadYaw = n;
|
||||
entity.headYaw = o;
|
||||
renderScope.disableScissor();
|
||||
}
|
||||
|
||||
public static void drawEntity(RenderScope renderScope, float x, float y, float size, Vector3f vector3f,
|
||||
Quaternionf quaternionf, @Nullable Quaternionf quaternionf2, LivingEntity entity) {
|
||||
renderScope.getMatrixStack().push();
|
||||
renderScope.getMatrixStack().translate((double) x, (double) y, 50.0);
|
||||
renderScope.getMatrixStack().scale(size, size, -size);
|
||||
renderScope.getMatrixStack().translate(vector3f.x, vector3f.y, vector3f.z);
|
||||
renderScope.getMatrixStack().multiply(new SaturnQuaternionf(quaternionf));
|
||||
renderScope.draw();
|
||||
DiffuseLighting.method_34742();
|
||||
EntityRenderDispatcher entityRenderDispatcher = SaturnClient.client.getEntityRenderDispatcher();
|
||||
|
||||
entityRenderDispatcher.setRenderShadows(false);
|
||||
// TODO add the method draw with vertex consumers
|
||||
// renderScope.draw((vertexConsumers) -> {
|
||||
// entityRenderDispatcher.render(entity, 0.0, 0.0, 0.0, 1.0F, renderScope.getMatrixStack(), vertexConsumers,
|
||||
// 15728880);
|
||||
// });
|
||||
renderScope.draw();
|
||||
entityRenderDispatcher.setRenderShadows(true);
|
||||
renderScope.getMatrixStack().pop();
|
||||
DiffuseLighting.enableGuiDepthLighting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
LivingEntity entity = SaturnClient.client.player;
|
||||
if (entity != null) {
|
||||
drawEntity(renderScope, 0, 0, 75, 78, 30, 0.0625F, ctx.mouseX / scale, ctx.mouseY / scale, entity, angle,
|
||||
negativeAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import org.saturnclient.config.manager.ConfigManager;
|
||||
import org.saturnclient.config.manager.Property;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
|
||||
public class FloatInput extends Input {
|
||||
public Property<Float> prop;
|
||||
|
||||
public FloatInput(Property<Float> prop) {
|
||||
this.width = 120;
|
||||
this.height = Fonts.getHeight();
|
||||
this.prop = prop;
|
||||
this.text = String.valueOf(prop.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void charTyped(char chr) {
|
||||
if ((Character.isDigit(chr) || chr == '.' || (chr == '-' && cursorPosition == 0)) && text.length() < 10) {
|
||||
if (text.equals("0") && cursorPosition == 1) {
|
||||
text = "" + chr;
|
||||
} else {
|
||||
text = text.substring(0, cursorPosition) +
|
||||
chr +
|
||||
text.substring(cursorPosition);
|
||||
|
||||
try {
|
||||
prop.value = text.isEmpty() ? 0 : Float.parseFloat(text);
|
||||
cursorPosition++;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backspace() {
|
||||
try {
|
||||
if (text.length() == 0) {
|
||||
text = "0";
|
||||
cursorPosition = 1;
|
||||
}
|
||||
prop.value = Float.parseFloat(text);
|
||||
ConfigManager.save();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkReset() {
|
||||
if (prop.isReset) {
|
||||
this.text = String.valueOf(prop.value);
|
||||
this.cursorPosition = 0;
|
||||
prop.isReset = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.saturnclient.config.manager.Property;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
|
||||
public class HexInput extends Element {
|
||||
public Property<Integer> prop;
|
||||
|
||||
// Picker state
|
||||
private boolean open = false;
|
||||
private float hue = 0f;
|
||||
private float opacity = 1f;
|
||||
private int selectedX = 0;
|
||||
private int selectedY = 0;
|
||||
|
||||
private boolean draggingOpacity = false;
|
||||
|
||||
// Dimensions
|
||||
private static final int PICKER_WIDTH = 100;
|
||||
private static final int PICKER_HEIGHT = 100;
|
||||
private static final int SLIDER_WIDTH = 100;
|
||||
private static final int SLIDER_HEIGHT = 10;
|
||||
private static final int HUE_SLIDER_HEIGHT = 10;
|
||||
|
||||
public HexInput(Property<Integer> prop) {
|
||||
this.prop = prop;
|
||||
this.width = 58;
|
||||
this.height = 20;
|
||||
syncFromColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
open = !open;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (open) {
|
||||
handlePickerClick(mouseX - width - 8, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
if (open) {
|
||||
handlePickerDrag(mouseX - width - 8, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(double mouseX, double mouseY, int button) {
|
||||
draggingOpacity = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
// Draw the main color rectangle
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, 6, prop.value);
|
||||
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
// Draw the picker relative to (width + 8, 0)
|
||||
int pickerX = width + 8;
|
||||
int pickerY = 0;
|
||||
|
||||
renderColorSquare(renderScope, pickerX, pickerY);
|
||||
renderOpacitySlider(renderScope, pickerX, pickerY - SLIDER_HEIGHT - 10);
|
||||
renderHueSlider(renderScope, pickerX, pickerY + PICKER_HEIGHT + 10);
|
||||
}
|
||||
|
||||
private void renderColorSquare(RenderScope renderScope, int xStart, int yStart) {
|
||||
for (int x = 0; x < PICKER_WIDTH; x++) {
|
||||
for (int y = 0; y < PICKER_HEIGHT; y++) {
|
||||
float saturation = x / (float) PICKER_WIDTH;
|
||||
float value = 1f - y / (float) PICKER_HEIGHT;
|
||||
int color = Color.HSBtoRGB(hue, saturation, value);
|
||||
int alpha = (int) (opacity * 255) & 0xFF;
|
||||
color = (alpha << 24) | (color & 0xFFFFFF);
|
||||
renderScope.fill(xStart + x, yStart + y, xStart + x + 1, yStart + y + 1, color);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw selection square
|
||||
int selX = xStart + selectedX - 4;
|
||||
int selY = yStart + selectedY - 4;
|
||||
renderScope.fill(selX, selY, selX + 8, selY + 8, 0xFF000000 | prop.value);
|
||||
renderScope.drawBorder(selX, selY, 8, 8, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
private void renderOpacitySlider(RenderScope renderScope, int xStart, int yStart) {
|
||||
for (int x = 0; x < SLIDER_WIDTH; x++) {
|
||||
float localOpacity = x / (float) SLIDER_WIDTH;
|
||||
int alpha = (int) (localOpacity * 255) & 0xFF;
|
||||
int color = (alpha << 24) | (prop.value & 0xFFFFFF);
|
||||
renderScope.fill(xStart + x, yStart, xStart + x + 1, yStart + SLIDER_HEIGHT, color);
|
||||
}
|
||||
|
||||
int knobX = xStart + (int) (opacity * SLIDER_WIDTH);
|
||||
renderScope.fill(knobX - 1, yStart - 2, knobX + 1, yStart + SLIDER_HEIGHT + 2, 0xFF000000);
|
||||
}
|
||||
|
||||
private void renderHueSlider(RenderScope renderScope, int xStart, int yStart) {
|
||||
for (int x = 0; x < SLIDER_WIDTH; x++) {
|
||||
float localHue = x / (float) SLIDER_WIDTH;
|
||||
int color = Color.HSBtoRGB(localHue, 1f, 1f);
|
||||
renderScope.fill(xStart + x, yStart, xStart + x + 1, yStart + HUE_SLIDER_HEIGHT, 0xFF000000 | color);
|
||||
}
|
||||
|
||||
int knobX = xStart + (int) (hue * SLIDER_WIDTH);
|
||||
renderScope.fill(knobX - 1, yStart - 2, knobX + 1, yStart + HUE_SLIDER_HEIGHT + 2, 0xFF000000);
|
||||
}
|
||||
|
||||
private void handlePickerClick(double mouseX, double mouseY) {
|
||||
// Color square
|
||||
if (mouseX >= 0 && mouseX < PICKER_WIDTH && mouseY >= 0 && mouseY < PICKER_HEIGHT) {
|
||||
selectedX = (int) mouseX;
|
||||
selectedY = (int) mouseY;
|
||||
updateColorFromSelection();
|
||||
}
|
||||
|
||||
// Opacity slider
|
||||
int opY = -SLIDER_HEIGHT - 10;
|
||||
if (mouseX >= 0 && mouseX < SLIDER_WIDTH && mouseY >= opY && mouseY <= opY + SLIDER_HEIGHT) {
|
||||
updateOpacity(mouseX);
|
||||
draggingOpacity = true;
|
||||
}
|
||||
|
||||
// Hue slider
|
||||
int hueY = PICKER_HEIGHT + 10;
|
||||
if (mouseX >= 0 && mouseX < SLIDER_WIDTH && mouseY >= hueY && mouseY <= hueY + HUE_SLIDER_HEIGHT) {
|
||||
updateHue(mouseX);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePickerDrag(double mouseX, double mouseY) {
|
||||
// Opacity
|
||||
if (draggingOpacity)
|
||||
updateOpacity(mouseX);
|
||||
|
||||
// Color square
|
||||
if (mouseX >= 0 && mouseX < PICKER_WIDTH && mouseY >= 0 && mouseY < PICKER_HEIGHT) {
|
||||
selectedX = (int) mouseX;
|
||||
selectedY = (int) mouseY;
|
||||
updateColorFromSelection();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateColorFromSelection() {
|
||||
float saturation = selectedX / (float) PICKER_WIDTH;
|
||||
float value = 1f - selectedY / (float) PICKER_HEIGHT;
|
||||
int color = Color.HSBtoRGB(hue, saturation, value);
|
||||
int alpha = (int) (opacity * 255) & 0xFF;
|
||||
prop.value = (alpha << 24) | (color & 0xFFFFFF);
|
||||
}
|
||||
|
||||
private void updateHue(double mouseX) {
|
||||
hue = (float) Math.max(0, Math.min(1, mouseX / SLIDER_WIDTH));
|
||||
updateColorFromSelection();
|
||||
}
|
||||
|
||||
private void updateOpacity(double mouseX) {
|
||||
opacity = (float) Math.max(0, Math.min(1, mouseX / SLIDER_WIDTH));
|
||||
updateColorFromSelection();
|
||||
}
|
||||
|
||||
private void syncFromColor() {
|
||||
int rgb = prop.value & 0xFFFFFF;
|
||||
float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, null);
|
||||
hue = hsb[0];
|
||||
selectedX = (int) (hsb[1] * PICKER_WIDTH);
|
||||
selectedY = (int) ((1f - hsb[2]) * PICKER_HEIGHT);
|
||||
int alpha = (prop.value >> 24) & 0xFF;
|
||||
if (alpha == 0)
|
||||
alpha = 255;
|
||||
opacity = alpha / 255f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.config.manager.Key;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
|
||||
public abstract class Input extends Element {
|
||||
String text = "";
|
||||
public int cursorPosition = 0;
|
||||
|
||||
public abstract void checkReset();
|
||||
|
||||
public abstract void backspace();
|
||||
|
||||
public boolean validateBackspace() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
if (keyCode == Key.GLFW_KEY_BACKSPACE) {
|
||||
if (cursorPosition > 0 && validateBackspace()) {
|
||||
text = text.substring(0, cursorPosition - 1) +
|
||||
text.substring(cursorPosition);
|
||||
cursorPosition--;
|
||||
backspace();
|
||||
}
|
||||
} else if (keyCode == Key.GLFW_KEY_LEFT) {
|
||||
if (cursorPosition > 0) {
|
||||
cursorPosition--;
|
||||
}
|
||||
} else if (keyCode == Key.GLFW_KEY_RIGHT) {
|
||||
if (cursorPosition < text.length()) {
|
||||
cursorPosition++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
int scrollOffset = getScrollOffset();
|
||||
String visibleText = text.substring(scrollOffset);
|
||||
|
||||
int newCursorPos = scrollOffset;
|
||||
float textWidth = 0;
|
||||
|
||||
for (int i = 0; i < visibleText.length(); i++) {
|
||||
textWidth = Fonts.getWidth(visibleText.substring(0, i + 1), Theme.FONT.value) * 0.6f;
|
||||
|
||||
if (textWidth > mouseX) {
|
||||
break;
|
||||
}
|
||||
|
||||
newCursorPos++;
|
||||
}
|
||||
|
||||
cursorPosition = newCursorPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
checkReset();
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
|
||||
|
||||
int scrollOffset = getScrollOffset();
|
||||
String visibleText = getVisibleText(scrollOffset);
|
||||
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
|
||||
renderScope.drawText(0.6f, visibleText, 4, 4, Theme.FONT.value, textColor);
|
||||
|
||||
if (focused) {
|
||||
int cursorX = 4 +
|
||||
(int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), Theme.FONT.value)
|
||||
* 0.6f);
|
||||
|
||||
renderScope.drawRect(cursorX, 2, 1, height - 4, 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getScrollOffset() {
|
||||
if (text.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
int maxWidth = width - 4; // Available width for text
|
||||
while (offset < cursorPosition) { // Ensure cursor remains visible
|
||||
String visibleText = text.substring(offset, cursorPosition);
|
||||
if (Fonts.getWidth(visibleText, Theme.FONT.value) > maxWidth) {
|
||||
offset++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
protected String getVisibleText(int scrollOffset) {
|
||||
String visibleText = text.substring(scrollOffset);
|
||||
|
||||
// Ensure text does not overflow past the available width
|
||||
for (int i = 1; i <= visibleText.length(); i++) {
|
||||
if (Fonts.getWidth(visibleText.substring(0, i), Theme.FONT.value) > width - 4) {
|
||||
return visibleText.substring(0, i - 1); // Trim overflow
|
||||
}
|
||||
}
|
||||
return visibleText;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import org.saturnclient.config.manager.ConfigManager;
|
||||
import org.saturnclient.config.manager.Property;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
|
||||
public class IntInput extends Input {
|
||||
public Property<Integer> prop;
|
||||
|
||||
public IntInput(Property<Integer> prop) {
|
||||
this.width = 120;
|
||||
this.height = Fonts.getHeight();
|
||||
this.prop = prop;
|
||||
this.text = String.valueOf(prop.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void charTyped(char chr) {
|
||||
if ((Character.isDigit(chr) || chr == '.' || (chr == '-' && cursorPosition == 0)) && text.length() < 10) {
|
||||
if (text.equals("0") && cursorPosition == 1) {
|
||||
text = "" + chr;
|
||||
} else {
|
||||
text = text.substring(0, cursorPosition) +
|
||||
chr +
|
||||
text.substring(cursorPosition);
|
||||
|
||||
try {
|
||||
prop.value = text.isEmpty() ? 0 : Integer.parseInt(text);
|
||||
cursorPosition++;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backspace() {
|
||||
try {
|
||||
if (text.length() == 0) {
|
||||
text = "0";
|
||||
cursorPosition = 1;
|
||||
}
|
||||
prop.value = Integer.parseInt(text);
|
||||
ConfigManager.save();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkReset() {
|
||||
if (prop.isReset) {
|
||||
this.text = String.valueOf(prop.value);
|
||||
this.cursorPosition = 0;
|
||||
prop.isReset = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import org.saturnclient.common.minecraft.MinecraftProvider;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.config.manager.Key;
|
||||
import org.saturnclient.config.manager.Property;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
|
||||
public class KeybindingSelector extends Element {
|
||||
Property<Integer> prop;
|
||||
|
||||
public KeybindingSelector(Property<Integer> prop) {
|
||||
this.prop = prop;
|
||||
this.width = 120;
|
||||
this.height = Fonts.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
|
||||
String name = MinecraftProvider.PROVIDER.getKeyName(prop.value);
|
||||
|
||||
renderScope.drawText(0.6f,
|
||||
prop.value == -1 ? "<NONE>"
|
||||
: name != null ? name.toUpperCase() : "<Invalid-Key>",
|
||||
4, 4, Theme.FONT.value, textColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
if (keyCode == Key.GLFW_KEY_BACKSPACE) {
|
||||
prop.value = -1;
|
||||
} else {
|
||||
prop.value = keyCode;
|
||||
}
|
||||
focused = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.config.manager.Property;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.Utils;
|
||||
import org.saturnclient.ui.resources.Fonts;
|
||||
|
||||
public class Select extends Element {
|
||||
private Property<Integer> prop;
|
||||
|
||||
public Select(Property<Integer> prop) {
|
||||
this.prop = prop;
|
||||
this.width = 150;
|
||||
this.height = 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawText(0.8f, "<", 0, 0, Theme.FONT.value, Theme.FOREGROUND.value);
|
||||
|
||||
String text = prop.getSelection();
|
||||
|
||||
renderScope.drawText(0.6f, text, (int) (width - ((float) Fonts.getWidth(text, Theme.FONT.value) * 0.6f)) / 2, 1,
|
||||
Theme.FONT.value, Theme.FOREGROUND.value);
|
||||
|
||||
renderScope.drawText(0.8f, ">", width - 10, 0, Theme.FONT.value, Theme.FOREGROUND.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
if (Utils.isHovering(mouseX - (width - 10), mouseY, 10, height, 1.0f)) {
|
||||
prop.next();
|
||||
} else if (Utils.isHovering(mouseX, mouseY, 10, height, 1.0f)) {
|
||||
prop.prev();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.config.manager.Property;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
|
||||
public class Toggle extends Element {
|
||||
private Property<Boolean> prop;
|
||||
|
||||
public Toggle(Property<Boolean> prop) {
|
||||
this.prop = prop;
|
||||
this.width = 60;
|
||||
this.height = 30;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, height,
|
||||
Theme.withAlpha(0.5f, prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value));
|
||||
renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30,
|
||||
prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
prop.value = !prop.value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user