From be67e01bfb541de057e8b3698e6dcbc828a69fc6 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Mon, 23 Jun 2025 05:43:48 +0200 Subject: Add collapse button to waypoint groups in Waypoints Screen (#1158) * collapse button * positioning + use that one RGB input i made * changes --- .../item/custom/screen/ColorSelectionWidget.java | 28 +-- .../skyblock/waypoint/WaypointsListWidget.java | 95 ++++---- .../skyblocker/utils/render/gui/ARGBTextInput.java | 249 +++++++++++++++++++++ .../skyblocker/utils/render/gui/RGBTextInput.java | 218 ------------------ 4 files changed, 314 insertions(+), 276 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/ARGBTextInput.java delete mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/RGBTextInput.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ColorSelectionWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ColorSelectionWidget.java index a8a110e7..c60aaebd 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ColorSelectionWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/custom/screen/ColorSelectionWidget.java @@ -8,7 +8,7 @@ import de.hysky.skyblocker.skyblock.item.custom.CustomArmorAnimatedDyes; import de.hysky.skyblocker.utils.Formatters; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.render.gui.ColorPickerWidget; -import de.hysky.skyblocker.utils.render.gui.RGBTextInput; +import de.hysky.skyblocker.utils.render.gui.ARGBTextInput; import it.unimi.dsi.fastutil.floats.FloatConsumer; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; @@ -42,7 +42,7 @@ public class ColorSelectionWidget extends ContainerWidget implements Closeable { private static final String DELAY_TEXT = "skyblocker.armorCustomization.delay"; private final ColorPickerWidget colorPicker; - private final RGBTextInput rgbTextInput; + private final ARGBTextInput argbTextInput; private final AnimatedDyeTimelineWidget timelineWidget; private final CheckboxWidget cycleBackCheckbox; @@ -65,12 +65,12 @@ public class ColorSelectionWidget extends ContainerWidget implements Closeable { colorPicker = new ColorPickerWidget(x + 3, y + 3, height1 * 2, height1); colorPicker.setOnColorChange(this::onPickerColorChanged); - rgbTextInput = new RGBTextInput(0, y + 3, textRenderer, true); - rgbTextInput.setX(colorPicker.getRight() + 5); - rgbTextInput.setOnChange(this::onTextInputColorChanged); + argbTextInput = new ARGBTextInput(0, y + 3, textRenderer, true); + argbTextInput.setX(colorPicker.getRight() + 5); + argbTextInput.setOnChange(this::onTextInputColorChanged); timelineWidget = new AnimatedDyeTimelineWidget(getX() + 3, getBottom() - 18, getWidth() - 6, 15, this::onTimelineFrameSelected); - resetColorButton = ButtonWidget.builder(RESET_COLOR_TEXT, this::onRemoveCustomColor).width(Math.min(150, x + width - rgbTextInput.getRight() - 5)).build(); + resetColorButton = ButtonWidget.builder(RESET_COLOR_TEXT, this::onRemoveCustomColor).width(Math.min(150, x + width - argbTextInput.getRight() - 5)).build(); resetColorButton.setPosition(getRight() - resetColorButton.getWidth() - 3, getY() + 3); @@ -126,11 +126,11 @@ public class ColorSelectionWidget extends ContainerWidget implements Closeable { }); durationSlider.setTooltip(Tooltip.of(DURATION_TOOLTIP_TEXT)); - children = List.of(colorPicker, rgbTextInput, timelineWidget, resetColorButton, animatedCheckbox, notCustomizableText, cycleBackCheckbox, delaySlider, durationSlider); + children = List.of(colorPicker, argbTextInput, timelineWidget, resetColorButton, animatedCheckbox, notCustomizableText, cycleBackCheckbox, delaySlider, durationSlider); } private void onPickerColorChanged(int argb, boolean release) { - rgbTextInput.setRGBColor(argb); + argbTextInput.setARGBColor(argb); if (!animated) { SkyblockerConfigManager.get().general.customDyeColors.put(ItemUtils.getItemUuid(currentItem), ColorHelper.fullAlpha(argb)); } else if (release) { @@ -150,7 +150,7 @@ public class ColorSelectionWidget extends ContainerWidget implements Closeable { } private void onTimelineFrameSelected(int color, float time) { - rgbTextInput.setRGBColor(color); + argbTextInput.setARGBColor(color); colorPicker.setRGBColor(color); } @@ -164,7 +164,7 @@ public class ColorSelectionWidget extends ContainerWidget implements Closeable { SkyblockerConfigManager.get().general.customAnimatedDyes.remove(itemUuid); int color = DyedColorComponent.getColor(currentItem, -1); - rgbTextInput.setRGBColor(color); + argbTextInput.setARGBColor(color); colorPicker.setRGBColor(color); } @@ -186,7 +186,7 @@ public class ColorSelectionWidget extends ContainerWidget implements Closeable { } else { int color = SkyblockerConfigManager.get().general.customDyeColors.getOrDefault(itemUuid, DyedColorComponent.getColor(currentItem, -1)); colorPicker.setRGBColor(color); - rgbTextInput.setRGBColor(color); + argbTextInput.setARGBColor(color); SkyblockerConfigManager.get().general.customAnimatedDyes.remove(itemUuid); } } @@ -205,7 +205,7 @@ public class ColorSelectionWidget extends ContainerWidget implements Closeable { private void changeVisibilities() { colorPicker.visible = customizable; - rgbTextInput.visible = customizable; + argbTextInput.visible = customizable; timelineWidget.visible = customizable && animated; cycleBackCheckbox.visible = customizable && animated; @@ -272,12 +272,12 @@ public class ColorSelectionWidget extends ContainerWidget implements Closeable { } else if (SkyblockerConfigManager.get().general.customDyeColors.containsKey(itemUuid)) { animated = false; int color = SkyblockerConfigManager.get().general.customDyeColors.getInt(itemUuid); - rgbTextInput.setRGBColor(color); + argbTextInput.setARGBColor(color); colorPicker.setRGBColor(color); } else { animated = false; int color = DyedColorComponent.getColor(currentItem, -1); - rgbTextInput.setRGBColor(color); + argbTextInput.setARGBColor(color); colorPicker.setRGBColor(color); } changeVisibilities(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/WaypointsListWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/WaypointsListWidget.java index a7fc30c2..7b79cb4e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/WaypointsListWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/WaypointsListWidget.java @@ -2,6 +2,7 @@ package de.hysky.skyblocker.skyblock.waypoint; import de.hysky.skyblocker.mixins.accessors.CheckboxWidgetAccessor; import de.hysky.skyblocker.utils.Location; +import de.hysky.skyblocker.utils.render.gui.ARGBTextInput; import de.hysky.skyblocker.utils.waypoint.NamedWaypoint; import de.hysky.skyblocker.utils.waypoint.WaypointGroup; import it.unimi.dsi.fastutil.ints.Int2ObjectFunction; @@ -14,16 +15,15 @@ import net.minecraft.text.Text; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ColorHelper; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; +import java.util.*; public class WaypointsListWidget extends ElementListWidget { private final AbstractWaypointsScreen screen; private Location island; private List waypoints; + private final Set collapsedGroups = new HashSet<>(); public WaypointsListWidget(MinecraftClient client, AbstractWaypointsScreen screen, int width, int height, int y, int itemHeight) { super(client, width, height, y, itemHeight); @@ -33,7 +33,7 @@ public class WaypointsListWidget extends ElementListWidget) screen.waypoints.get(island); + collapsedGroups.clear(); + collapsedGroups.addAll(waypoints); updateEntries(); } @@ -76,8 +78,10 @@ public class WaypointsListWidget extends ElementListWidget())); + this(new WaypointGroup("New Group", island, new ArrayList<>()), false); } - public WaypointGroupEntry(WaypointGroup group) { + public WaypointGroupEntry(WaypointGroup group, boolean collapsed) { this.group = group; enabled = CheckboxWidget.builder(Text.literal(""), client.textRenderer).checked(shouldBeChecked()).callback((checkbox, checked) -> group.waypoints().forEach(waypoint -> screen.enabledChanged(waypoint, checked))).build(); nameField = new TextFieldWidget(client.textRenderer, 70, 20, Text.literal("Name")); @@ -122,7 +127,7 @@ public class WaypointsListWidget extends ElementListWidget updateOrdered(checked)).build(); buttonNewWaypoint = ButtonWidget.builder(Text.translatable("skyblocker.waypoints.new"), buttonNewWaypoint -> { - WaypointEntry waypointEntry = new WaypointEntry(this); + WaypointEntry waypointEntry = new WaypointEntry(this); int entryIndex; if (getSelectedOrNull() instanceof WaypointEntry selectedWaypointEntry && selectedWaypointEntry.groupEntry == this) { entryIndex = WaypointsListWidget.this.children().indexOf(selectedWaypointEntry) + 1; @@ -134,6 +139,10 @@ public class WaypointsListWidget extends ElementListWidget { int entryIndex = WaypointsListWidget.this.children().indexOf(this) + 1; @@ -143,7 +152,12 @@ public class WaypointsListWidget extends ElementListWidget { + if (collapsed) collapsedGroups.remove(group); else collapsedGroups.add(group); + updateEntries(); + }).size(11, 11).build(); + children = List.of(enabled, nameField, ordered, buttonNewWaypoint, buttonDelete, collapseWaypoint); } @Override @@ -178,8 +192,9 @@ public class WaypointsListWidget extends ElementListWidget { groupEntry.group.waypoints().remove(waypoint); WaypointsListWidget.this.children().remove(this); @@ -288,42 +305,32 @@ public class WaypointsListWidget extends ElementListWidget> 16) / 255f, ((colorInt & 0x0000FF00) >> 8) / 255f, (colorInt & 0x000000FF) / 255f}; - float alpha = ((colorInt & 0xFF000000) >>> 24) / 255f; - if (Arrays.equals(waypoint.colorComponents, colorComponents) && waypoint.alpha == alpha) return; - waypoint = waypoint.withColor(colorComponents, alpha); - if (index >= 0) { - groupEntry.group.waypoints().set(index, waypoint); - } - } catch (NumberFormatException e) { - Waypoints.LOGGER.warn("[Skyblocker Waypoints] Failed to parse color: {}", colorString, e); - } + private void updateColor(int colorInt) { + int index = groupEntry.group.waypoints().indexOf(waypoint); + float[] colorComponents = {((colorInt & 0x00FF0000) >> 16) / 255f, ((colorInt & 0x0000FF00) >> 8) / 255f, (colorInt & 0x000000FF) / 255f}; + float alpha = ((colorInt & 0xFF000000) >>> 24) / 255f; + if (Arrays.equals(waypoint.colorComponents, colorComponents) && waypoint.alpha == alpha) return; + waypoint = waypoint.withColor(colorComponents, alpha); + if (index >= 0) { + groupEntry.group.waypoints().set(index, waypoint); + } } private int parseEmptiableInt(String value) throws NumberFormatException { return value.isEmpty() || value.equals("-") ? 0 : Integer.parseInt(value); } - @SuppressWarnings("SameParameterValue") - private int parseEmptiableInt(String value, int radix) throws NumberFormatException { - return value.isEmpty() || value.equals("-") ? 0 : Integer.parseInt(value, radix); - } - @Override public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - context.drawTextWithShadow(client.textRenderer, "X:", width / 2 - 56, y + 6, 0xFFFFFF); - context.drawTextWithShadow(client.textRenderer, "Y:", width / 2 - 19, y + 6, 0xFFFFFF); - context.drawTextWithShadow(client.textRenderer, "Z:", width / 2 + 18, y + 6, 0xFFFFFF); - context.drawTextWithShadow(client.textRenderer, "#", x + entryWidth - 105, y + 6, 0xFFFFFF); - enabled.setPosition(x + 10, y + 1); - nameField.setPosition(x + 32, y); - xField.setPosition(width / 2 - 48, y); - yField.setPosition(width / 2 - 11, y); - zField.setPosition(width / 2 + 26, y); + context.drawTextWithShadow(client.textRenderer, "X:", width / 2 - 48, y + 6, 0xFF_FFFFFF); + context.drawTextWithShadow(client.textRenderer, "Y:", width / 2 - 11, y + 6, 0xFF_FFFFFF); + context.drawTextWithShadow(client.textRenderer, "Z:", width / 2 + 26, y + 6, 0xFF_FFFFFF); + context.drawTextWithShadow(client.textRenderer, "#", x + entryWidth - 105, y + 6, 0xFF_FFFFFF); + enabled.setPosition(x + 26, y + 1); + nameField.setPosition(enabled.getRight() + 5, y); + xField.setPosition(width / 2 - 40, y); + yField.setPosition(width / 2 - 3, y); + zField.setPosition(width / 2 + 34, y); colorField.setPosition(x + entryWidth - 99, y); buttonDelete.setPosition(x + entryWidth - 38, y); for (ClickableWidget child : children) { diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ARGBTextInput.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ARGBTextInput.java new file mode 100644 index 00000000..67408169 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ARGBTextInput.java @@ -0,0 +1,249 @@ +package de.hysky.skyblocker.utils.render.gui; + +import com.mojang.logging.LogUtils; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.util.Colors; +import net.minecraft.util.Formatting; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; + +import java.util.Locale; +import java.util.OptionalInt; +import java.util.function.IntConsumer; + +public class ARGBTextInput extends ClickableWidget { + private static final Logger LOGGER = LogUtils.getLogger(); + + private static final Formatting[] FORMATTINGS = new Formatting[] {Formatting.WHITE, Formatting.RED, Formatting.GREEN, Formatting.BLUE}; + private static final String HEXADECIMAL_CHARS = "0123456789aAbBcCdDeEfF"; + + /** + * Length in characters of the input + */ + private final int length; + private final boolean drawBackground; + private final TextRenderer textRenderer; + /** + * Whether the alpha channel can be changed + */ + private final boolean hasAlpha; + /** + * Mask to have full alpha if {@link ARGBTextInput#hasAlpha} is {@code false} + */ + private final int alphaMask; + + private String input; + int index = 0; + + private @Nullable IntConsumer onChange = null; + + /** + * Height and width are automatically computed to be the size of the hex number + some padding if {@code drawBackground} is true. + * If the size needs to be changed, use {@link ARGBTextInput#setWidth(int)} and {@link ARGBTextInput#setHeight(int)}. + * + * @see ARGBTextInput#setOnChange(IntConsumer) + * + * @param x x position + * @param y y position + * @param textRenderer text renderer to render the text (duh!) + * @param drawBackground draws a black background and a white border if true + * @param hasAlpha if the controller allows to change the alpha color. If false alpha is FF. + * + */ + public ARGBTextInput(int x, int y, TextRenderer textRenderer, boolean drawBackground, boolean hasAlpha) { + super(x, y, textRenderer.getWidth(hasAlpha ? "AAAAAAAA" : "AAAAAA") + (drawBackground ? 6 : 0), 10 + (drawBackground ? 4 : 0), Text.of("ARGBTextInput")); + this.drawBackground = drawBackground; + this.textRenderer = textRenderer; + this.length = hasAlpha ? 8 : 6; + this.hasAlpha = hasAlpha; + this.alphaMask = hasAlpha ? 0 : 0xFF000000; + this.input = hasAlpha ? "FFFFFFFF" : "FFFFFF"; + } + + /** + * Constructor without alpha channel control. + *
+ * Height and width are automatically computed to be the size of the hex number + some padding if {@code drawBackground} is true. + * If the size needs to be changed, use {@link ARGBTextInput#setWidth(int)} and {@link ARGBTextInput#setHeight(int)}. + * + * @see ARGBTextInput#setOnChange(IntConsumer) + * + * @param x x position + * @param y y position + * @param textRenderer text renderer to render the text (duh!) + * @param drawBackground draws a black background and a white border if true + * + */ + public ARGBTextInput(int x, int y, TextRenderer textRenderer, boolean drawBackground) { + this(x, y, textRenderer, drawBackground, false); + } + + protected OptionalInt getOptionalARGBColor(String input) { + try { + int i = Integer.parseUnsignedInt(input, 16); + return OptionalInt.of(alphaMask | i); + } catch (NumberFormatException e) { + LOGGER.error("Could not parse rgb color", e); + } + return OptionalInt.empty(); + } + + /** + * @return the color, or white if something somehow went wrong + */ + public int getARGBColor() { + return getOptionalARGBColor(input).orElse(Colors.WHITE); + } + + public void setARGBColor(int argb) { + input = String.format(hasAlpha ? "%08X" : "%06X", argb & (~alphaMask)); + } + + /** + * Sets a consumer that will be called whenever the color is changed by the user (and not when {@link ARGBTextInput#setARGBColor(int)} is called) with the new color. + * The alpha channel will be at 255 (or FF) + * @param onChange the consumer + */ + public void setOnChange(@Nullable IntConsumer onChange) { + this.onChange = onChange; + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + int selectionStart = textRenderer.getWidth(input.substring(0, index)); + int selectionEnd = textRenderer.getWidth(input.substring(0, index + 1)); + int textX = getX() + (drawBackground ? 3 : 0); + int textY = getY() + (getHeight() - textRenderer.fontHeight) / 2; + if (drawBackground) { + context.fill(getX(), getY(), getRight(), getBottom(), isFocused() ? Colors.WHITE: Colors.GRAY); + context.fill(getX() + 1, getY() + 1, getRight() - 1, getBottom() - 1, Colors.BLACK); + } + + if (isFocused()) { + context.fill( + textX + selectionStart, + textY, + textX + selectionEnd, + textY + textRenderer.fontHeight, + 0xFF_00_BB_FF + ); + context.fill( + textX + selectionStart, + textY + textRenderer.fontHeight - 1, + textX + selectionEnd, + textY + textRenderer.fontHeight, + Colors.WHITE + ); + } + context.drawText( + textRenderer, + visitor -> { + int start = hasAlpha ? 0 : 1; + for (int i = 0; i < input.length(); i++) { + if (!visitor.accept(i, isSelected() ? Style.EMPTY.withFormatting(FORMATTINGS[i / 2 + start]) : Style.EMPTY, input.charAt(i))) return false; + } + return true; + }, + textX, + textY, + -1, + true + ); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) {} + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (!isFocused()) return false; + boolean bl = switch (keyCode) { + case GLFW.GLFW_KEY_DELETE -> { + StringBuilder builder = new StringBuilder(input); + builder.setCharAt(index, '0'); + input = builder.toString(); + yield true; + } + case GLFW.GLFW_KEY_BACKSPACE -> { + StringBuilder builder = new StringBuilder(input); + builder.setCharAt(index, '0'); + input = builder.toString(); + index = Math.max(0, index - 1); + yield true; + } + case GLFW.GLFW_KEY_LEFT -> { + index = Math.max(0, index - 1); + yield true; + } + case GLFW.GLFW_KEY_RIGHT -> { + index = Math.min(length - 1, index + 1); + yield true; + } + default -> false; + }; + if (bl) { + callOnChange(); + return true; + } else { + if (Screen.isCopy(keyCode)) { + MinecraftClient.getInstance().keyboard.setClipboard(input); + return true; + } else if (Screen.isPaste(keyCode)) { + String clipboard = MinecraftClient.getInstance().keyboard.getClipboard(); + String s = clipboard.substring(0, 6); + getOptionalARGBColor(s.toUpperCase(Locale.ENGLISH)).ifPresent(color -> { + input = s; + callOnChange(); + }); + return true; + + } + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean charTyped(char chr, int modifiers) { + if (!isFocused()) return false; + if (HEXADECIMAL_CHARS.indexOf(chr) >= 0) { + input = new StringBuilder(input).replace(index, index+1, String.valueOf(chr).toUpperCase(Locale.ENGLISH)).toString(); + index = Math.min(length - 1, index + 1); + callOnChange(); + return true; + } + + return super.charTyped(chr, modifiers); + } + + protected void callOnChange() { + if (onChange != null) { + onChange.accept(getARGBColor()); + } + } + + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + findClickedChar((int) mouseX); + } + + @Override + protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) { + super.onDrag(mouseX, mouseY, deltaX, deltaY); + findClickedChar((int) mouseX); + } + + private void findClickedChar(int mouseX) { + index = Math.clamp(textRenderer.trimToWidth(input, mouseX - getX() - (drawBackground ? 3 : 0)).length(), 0, length - 1); + } + + +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/RGBTextInput.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/RGBTextInput.java deleted file mode 100644 index 64708ca1..00000000 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/RGBTextInput.java +++ /dev/null @@ -1,218 +0,0 @@ -package de.hysky.skyblocker.utils.render.gui; - -import com.mojang.logging.LogUtils; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.widget.ClickableWidget; -import net.minecraft.text.Style; -import net.minecraft.text.Text; -import net.minecraft.util.Colors; -import net.minecraft.util.Formatting; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.glfw.GLFW; -import org.slf4j.Logger; - -import java.util.Locale; -import java.util.OptionalInt; -import java.util.function.IntConsumer; - -public class RGBTextInput extends ClickableWidget { - private static final Logger LOGGER = LogUtils.getLogger(); - - private static final String SAMPLE_TEXT = "AAAAAA"; - private static final Formatting[] FORMATTINGS = new Formatting[] {Formatting.RED, Formatting.GREEN, Formatting.BLUE}; - private static final String HEXADECIMAL_CHARS = "0123456789aAbBcCdDeEfF"; - - private static final int LENGTH = 6; - private final boolean drawBackground; - private final TextRenderer textRenderer; - - private String input = "FFFFFF"; - int index = 0; - - private @Nullable IntConsumer onChange = null; - - /** - * Creates a new widget. - *

- * Height and width are automatically computed to be the size of the hex number + some padding if {@code drawBackground} is true. - * If the size needs to be changed, use {@link RGBTextInput#setWidth(int)} and {@link RGBTextInput#setHeight(int)}. - * - * @see RGBTextInput#setOnChange(IntConsumer) - * - * @param x x position - * @param y y position - * @param textRenderer text renderer to render the text (duh!) - * @param drawBackground draws a black background and a white border if true - * - */ - public RGBTextInput(int x, int y, TextRenderer textRenderer, boolean drawBackground) { - super(x, y, textRenderer.getWidth(SAMPLE_TEXT) + (drawBackground ? 6 : 0), 10 + (drawBackground ? 4 : 0), Text.of("RGBTextInput")); - this.drawBackground = drawBackground; - this.textRenderer = textRenderer; - } - - protected OptionalInt getOptionalRGBColor(String input) { - try { - return OptionalInt.of(0xFF000000 | Integer.parseInt(input, 16)); - } catch (NumberFormatException e) { - LOGGER.error("Could not parse rgb color", e); - } - return OptionalInt.empty(); - } - - /** - * @return the color, or white if something somehow went wrong - */ - public int getRGBColor() { - return getOptionalRGBColor(input).orElse(-1); - } - - public void setRGBColor(int rgb) { - input = String.format("%06X",rgb & 0x00FFFFFF); - } - - /** - * Sets a consumer that will be called whenever the color is changed by the user (and not when {@link RGBTextInput#setRGBColor(int)} is called) with the new color. - * The alpha channel will be at 255 (or FF) - * @param onChange the consumer - */ - public void setOnChange(@Nullable IntConsumer onChange) { - this.onChange = onChange; - } - - @Override - protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { - int selectionStart = textRenderer.getWidth(input.substring(0, index)); - int selectionEnd = textRenderer.getWidth(input.substring(0, index + 1)); - int textX = getX() + (drawBackground ? 3 : 0); - int textY = getY() + (drawBackground ? 3 : 0); - if (drawBackground) { - context.fill(getX(), getY(), getRight(), getBottom(), isFocused() ? Colors.WHITE: Colors.GRAY); - context.fill(getX() + 1, getY() + 1, getRight() - 1, getBottom() - 1, Colors.BLACK); - } - - if (isFocused()) { - context.fill( - textX + selectionStart, - textY, - textX + selectionEnd, - textY + textRenderer.fontHeight, - 0xff00bbff - ); - context.fill( - textX + selectionStart, - textY + textRenderer.fontHeight - 1, - textX + selectionEnd, - textY + textRenderer.fontHeight, - -1 - ); - } - context.drawText( - textRenderer, - visitor -> { - for (int i = 0; i < input.length(); i++) { - if (!visitor.accept(i, isSelected() ? Style.EMPTY.withFormatting(FORMATTINGS[i / 2]) : Style.EMPTY, input.charAt(i))) return false; - } - return true; - }, - textX, - textY, - -1, - true - ); - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (!isFocused()) return false; - boolean bl = switch (keyCode) { - case GLFW.GLFW_KEY_DELETE -> { - StringBuilder builder = new StringBuilder(input); - builder.setCharAt(index, '0'); - input = builder.toString(); - yield true; - } - case GLFW.GLFW_KEY_BACKSPACE -> { - StringBuilder builder = new StringBuilder(input); - builder.setCharAt(index, '0'); - input = builder.toString(); - index = Math.max(0, index - 1); - yield true; - } - case GLFW.GLFW_KEY_LEFT -> { - index = Math.max(0, index - 1); - yield true; - } - case GLFW.GLFW_KEY_RIGHT -> { - index = Math.min(LENGTH - 1, index + 1); - yield true; - } - default -> false; - }; - if (bl) { - callOnChange(); - return true; - } else { - if (Screen.isCopy(keyCode)) { - MinecraftClient.getInstance().keyboard.setClipboard(input); - return true; - } else if (Screen.isPaste(keyCode)) { - String clipboard = MinecraftClient.getInstance().keyboard.getClipboard(); - String s = clipboard.substring(0, 6); - getOptionalRGBColor(s.toUpperCase(Locale.ENGLISH)).ifPresent(color -> { - input = s; - callOnChange(); - }); - return true; - - } - } - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public boolean charTyped(char chr, int modifiers) { - if (!isFocused()) return false; - if (HEXADECIMAL_CHARS.indexOf(chr) >= 0) { - input = new StringBuilder(input).replace(index, index+1, String.valueOf(chr).toUpperCase(Locale.ENGLISH)).toString(); - index = Math.min(LENGTH - 1, index + 1); - callOnChange(); - return true; - } - - return super.charTyped(chr, modifiers); - } - - protected void callOnChange() { - if (onChange != null) { - onChange.accept(getRGBColor()); - } - } - - @Override - public void onClick(double mouseX, double mouseY) { - super.onClick(mouseX, mouseY); - findClickedChar((int) mouseX); - } - - @Override - protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) { - super.onDrag(mouseX, mouseY, deltaX, deltaY); - findClickedChar((int) mouseX); - } - - private void findClickedChar(int mouseX) { - index = Math.clamp(textRenderer.trimToWidth(input, mouseX - getX() - (drawBackground ? 3 : 0)).length(), 0, LENGTH - 1); - } - - -} -- cgit