diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/ColorPickerIncludedScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/ColorPickerIncludedScreen.java index 531b9f90..e2ef616b 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/ColorPickerIncludedScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/ColorPickerIncludedScreen.java @@ -1,11 +1,90 @@ package dev.hephaestus.glowcase.client.gui.screen.ingame; -import dev.hephaestus.glowcase.client.gui.widget.ingame.ColorPickerWidget; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; +import dev.hephaestus.glowcase.client.util.ColorUtil; import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import org.lwjgl.glfw.GLFW; +/** + * Main interface for any Screen wishing to implement a {@link ColorPickerWidget}.

+ * Each ColorPickerIncludedScreen has *one* Color Picker widget which gets shared among all things using it.
+ * Steps for adding a Color Picker to a screen: + *
    + *
  1. Implement this interface, and return a private ColorPickerWidget in {@link ColorPickerIncludedScreen#getColorPickerWidget()}.
  2. + *
  3. Initialize your ColorPickerWidget in {@link Screen#init()} via {@link ColorPickerWidget#builder(ColorPickerIncludedScreen)}. You do *not* need to add it as a renderableWidget.
  4. + *
  5. Include {@link ColorPickerIncludedScreen#extractColorPicker(GuiGraphicsExtractor, int, int, float)} at the very bottom of {@link Screen#extractRenderState(GuiGraphicsExtractor, int, int, float)} to ensure it renders atop of everything else.
  6. + *
  7. Include {@link ColorPickerIncludedScreen#mouseClickedColorPicker(MouseButtonEvent, boolean)} at the very top of {@link Screen#mouseClicked(MouseButtonEvent, boolean)}, return true if color picker was clicked, otherwise continue with method.
  8. + *
  9. Include {@link ColorPickerIncludedScreen#keyPressedColorPicker(KeyEvent)} at the very top of {@link Screen#keyPressed(KeyEvent)}, return true if color picker key pressed, otherwise continue with method.
  10. + *
  11. Booyah!
  12. + *
+ * @author Superkat32 + */ public interface ColorPickerIncludedScreen { - ColorPickerWidget colorPickerWidget(); - void toggleColorPicker(boolean active); - void insertHexTag(String hex); - void insertFormattingTag(ChatFormatting formatting); + ColorPickerWidget getColorPickerWidget(); + + default ColorPickerWidget createColorPickerWidget() { + return ColorPickerWidget.builder(this).build(); + } + + default void extractColorPicker(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + this.getColorPickerWidget().extractRenderState(graphics, mouseX, mouseY, delta); + } + + default boolean mouseClickedColorPicker(MouseButtonEvent event, boolean doubleClick) { + double mouseX = event.x(); + double mouseY = event.y(); + ColorPickerWidget colorPickerWidget = this.getColorPickerWidget(); + + if (colorPickerWidget.isActive() && colorPickerWidget.visible) { + if (colorPickerWidget.isMouseOver(mouseX, mouseY)) { + colorPickerWidget.mouseClicked(event, doubleClick); + Screen self = (Screen) this; + self.setFocused(colorPickerWidget); + self.setDragging(true); + return true; + } else if (colorPickerWidget.targetElement == null || !colorPickerWidget.targetElement.isMouseOver(mouseX, mouseY)) { + this.hideColorPickerWidget(); + } + } + return false; + } + + default boolean keyPressedColorPicker(KeyEvent event) { + int keyCode = event.key(); + ColorPickerWidget colorPickerWidget = this.getColorPickerWidget(); + if (colorPickerWidget.isActive()) { + Screen self = (Screen) this; + switch (keyCode) { + case GLFW.GLFW_KEY_ENTER, GLFW.GLFW_KEY_KP_ENTER -> colorPickerWidget.confirm(); + case GLFW.GLFW_KEY_ESCAPE -> colorPickerWidget.cancel(); + default -> { + GuiEventListener pickerTarget = colorPickerWidget.targetElement; + if (pickerTarget != null) { + self.setFocused(pickerTarget); + pickerTarget.keyPressed(event); + return true; + } + } + } + self.setFocused(null); + return true; + } + return false; + } + + default void hideColorPickerWidget() { + this.getColorPickerWidget().hide(); + } + + default void insertHexTag(String hex) {} + default void insertFormattingTag(ChatFormatting formatting) {} + default void insertHexColor(int color) { + String hex = ColorUtil.toHex(color); + this.insertHexTag(hex); + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/NoteEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/NoteEditScreen.java index 6e8d6253..f4614d78 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/NoteEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/NoteEditScreen.java @@ -2,7 +2,7 @@ import com.mojang.datafixers.util.Pair; import dev.hephaestus.glowcase.Glowcase; -import dev.hephaestus.glowcase.client.gui.widget.ingame.ColorPickerWidget; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; import dev.hephaestus.glowcase.client.util.NoteTextColorResource; import dev.hephaestus.glowcase.item.component.NoteComponent; import dev.hephaestus.glowcase.packet.C2SEditNoteItem; @@ -187,10 +187,7 @@ protected void init() { }).bounds(width / 2 + BG_WIDTH / 2 - (BG_WIDTH / 2 - 3), height / 2 + BG_HEIGHT / 2 + offset, BG_WIDTH / 2 - 3, 20).build(); - this.colorPickerWidget = ColorPickerWidget.builder(this, 216, 10).size(182, 104).build(); - this.colorPickerWidget.toggle(false); //start deactivated - - this.addRenderableWidget(colorPickerWidget); + this.colorPickerWidget = this.createColorPickerWidget(); addRenderableWidget(changeAlignment); addRenderableWidget(doneButton); @@ -329,6 +326,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo } graphics.disableScissor(); + this.extractColorPicker(graphics, mouseX, mouseY, delta); } @Override @@ -347,12 +345,7 @@ public boolean keyPressed(KeyEvent event) { boolean result; int keyCode = event.key(); - if (this.colorPickerWidget.active && (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_ESCAPE)) { - if (keyCode == GLFW.GLFW_KEY_ENTER) { - this.colorPickerWidget.confirmColor(); - } else { - this.colorPickerWidget.cancel(); - } + if (keyPressedColorPicker(event)) { result = true; } else { setFocused(null); @@ -444,18 +437,7 @@ public boolean charTyped(CharacterEvent event) { public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { double mouseX = event.x(); double mouseY = event.y(); - if (colorPickerWidget.active && colorPickerWidget.visible) { - if (colorPickerWidget.isMouseOver(mouseX, mouseY)) { - colorPickerWidget.mouseClicked(event, doubleClick); - this.setFocused(colorPickerWidget); - this.setDragging(true); - return true; - } else { - if (!this.colorPickerWidget.targetElement.isMouseOver(mouseX, mouseY)) { - toggleColorPicker(false); - } - } - } + if (this.mouseClickedColorPicker(event, doubleClick)) return true; boolean withinX = (mouseX >= width / 2f - BG_WIDTH / 2f && mouseX <= width / 2f + BG_WIDTH / 2f); boolean withinY = (mouseY >= height / 2f - BG_HEIGHT / 2f && mouseY <= height / 2f + BG_HEIGHT / 2f); @@ -597,13 +579,8 @@ public CustomPacketPayload getUpdatePayload() { } @Override - public ColorPickerWidget colorPickerWidget() { - return colorPickerWidget; - } - - @Override - public void toggleColorPicker(boolean active) { - colorPickerWidget.toggle(active); + public ColorPickerWidget getColorPickerWidget() { + return this.colorPickerWidget; } @Override diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/OutlineBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/OutlineBlockEditScreen.java index 4ae93263..38b65a81 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/OutlineBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/OutlineBlockEditScreen.java @@ -3,19 +3,23 @@ import com.google.common.primitives.Ints; import dev.hephaestus.glowcase.block.entity.OutlineBlockEntity; import dev.hephaestus.glowcase.client.gui.widget.ingame.GlowcaseEditBox; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.HexColorEditBox; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; import dev.hephaestus.glowcase.packet.C2SEditOutlineBlock; import dev.hephaestus.glowcase.util.InputFilters; import dev.hephaestus.glowcase.util.TextUtils; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.StringWidget; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.core.Vec3i; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextColor; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import org.jetbrains.annotations.Nullable; import java.util.concurrent.atomic.AtomicInteger; -public class OutlineBlockEditScreen extends BlockEditorScreen { +public class OutlineBlockEditScreen extends BlockEditorScreen implements ColorPickerIncludedScreen { private StringWidget offsetTextWidget; private StringWidget scaleTextWidget; private StringWidget colorTextWidget; @@ -27,9 +31,11 @@ public class OutlineBlockEditScreen extends BlockEditorScreen { - TextColor.parseColor(this.colorWidget.getValue()) - .ifSuccess(color -> this.blockEntity.color = color.getValue() | 0xFF000000); - }); + this.colorPickerWidget = this.createColorPickerWidget(); + + this.colorWidget = HexColorEditBox.builder(this.minecraft.font, width / 2 - 65, widgetY.getAndAdd(lineOffset), + () -> this.blockEntity.color, color -> this.blockEntity.color = color + ) + .setWidth(50) + .setColorPickerWidget(this.colorPickerWidget) + .build(); this.widthWidget = new GlowcaseEditBox(this.minecraft.font, width / 2 - 65, widgetY.getAndAdd(lineOffset), 50, 20, Component.empty()); this.widthWidget.setValue(String.valueOf(this.blockEntity.width)); @@ -147,8 +155,31 @@ public void init() { this.addRenderableWidget(this.widthWidget); } + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + super.extractRenderState(graphics, mouseX, mouseY, delta); + this.extractColorPicker(graphics, mouseX, mouseY, delta); + } + + @Override + public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { + if (this.mouseClickedColorPicker(event, doubleClick)) return true; + return super.mouseClicked(event, doubleClick); + } + + @Override + public boolean keyPressed(KeyEvent event) { + if (this.keyPressedColorPicker(event)) return true; + return super.keyPressed(event); + } + @Override public @Nullable CustomPacketPayload getUpdatePayload() { return C2SEditOutlineBlock.of(blockEntity); } + + @Override + public ColorPickerWidget getColorPickerWidget() { + return this.colorPickerWidget; + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java index 2b8c3f20..b9d7b844 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/PopupBlockEditScreen.java @@ -3,6 +3,8 @@ import dev.hephaestus.glowcase.block.entity.HyperlinkBlockEntity; import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.HexColorEditBox; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; import dev.hephaestus.glowcase.packet.C2SEditPopupBlock; import dev.hephaestus.glowcase.util.TextUtils; import net.minecraft.client.Minecraft; @@ -14,20 +16,21 @@ import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextColor; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.util.Mth; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; //TODO: multi-character selection at some point? it may be a bit complex but it'd be nice -public class PopupBlockEditScreen extends BlockEditorScreen { +public class PopupBlockEditScreen extends BlockEditorScreen implements ColorPickerIncludedScreen { private TextFieldHelper selectionManager; private int currentRow; private long ticksSinceOpened = 0; private EditBox titleEntryWidget; private Button changeAlignment; - private EditBox colorEntryWidget; + private HexColorEditBox colorEntryWidget; + + private ColorPickerWidget colorPickerWidget; public PopupBlockEditScreen(PopupBlockEntity blockEntity) { super(blockEntity); @@ -76,14 +79,13 @@ public void init() { )); }).bounds(120 + innerPadding, 20 + innerPadding, 160, 20).build(); - this.colorEntryWidget = new EditBox(this.minecraft.font, 280 + innerPadding * 2, 20 + innerPadding, 50, 20, Component.empty()); - this.colorEntryWidget.setValue("#" + Integer.toHexString(this.blockEntity.color & 0x00FFFFFF)); - this.colorEntryWidget.setResponder(string -> { - TextColor.parseColor(this.colorEntryWidget.getValue()).ifSuccess(color -> { - this.blockEntity.color = color == null ? 0xFFFFFFFF : color.getValue() | 0xFF000000; - this.blockEntity.renderDirty = true; - }); - }); + this.colorPickerWidget = createColorPickerWidget(); + this.colorEntryWidget = HexColorEditBox.builder(this.minecraft.font, 280 + innerPadding, 20 + innerPadding, + () -> this.blockEntity.color, color -> { this.blockEntity.color = color; this.blockEntity.renderDirty = true; } + ) + .setWidth(50) + .setColorPickerWidget(this.colorPickerWidget) + .build(); this.addRenderableWidget(this.titleEntryWidget); this.addRenderableWidget(this.changeAlignment); @@ -169,6 +171,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo } graphics.pose().popMatrix(); + this.extractColorPicker(graphics, mouseX, mouseY, delta); } @Override @@ -186,6 +189,7 @@ public boolean charTyped(CharacterEvent event) { @Override public boolean keyPressed(KeyEvent event) { int keyCode = event.key(); + if (this.keyPressedColorPicker(event)) return true; if (this.titleEntryWidget.canConsumeInput()) { if (keyCode == GLFW.GLFW_KEY_ESCAPE) { this.onClose(); @@ -296,6 +300,7 @@ public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { double mouseX = event.x(); double mouseY = event.y(); int topOffset = (int) (40 + 2 * this.width / 100F); + if (this.mouseClickedColorPicker(event, doubleClick)) return true; if (!this.titleEntryWidget.mouseClicked(event, doubleClick)) { this.titleEntryWidget.setFocused(false); } @@ -354,4 +359,9 @@ public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { return super.mouseClicked(event, doubleClick); } } + + @Override + public ColorPickerWidget getColorPickerWidget() { + return this.colorPickerWidget; + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/SpriteBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/SpriteBlockEditScreen.java index 4e0a0ea9..325978d2 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/SpriteBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/SpriteBlockEditScreen.java @@ -4,6 +4,8 @@ import dev.hephaestus.glowcase.block.entity.TextBlockEntity; import dev.hephaestus.glowcase.client.gui.widget.ingame.GlowcaseEditBox; import dev.hephaestus.glowcase.client.gui.widget.ingame.SuggestionListWidget; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.HexColorEditBox; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; import dev.hephaestus.glowcase.packet.C2SEditSpriteBlock; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -14,7 +16,6 @@ import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextColor; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.ResourceManager; @@ -24,16 +25,17 @@ import java.util.List; import java.util.function.Function; -public class SpriteBlockEditScreen extends BlockEditorScreen { +public class SpriteBlockEditScreen extends BlockEditorScreen implements ColorPickerIncludedScreen { private EditBox spriteWidget; private Button spriteWidgetHelpButton; private Button rotationWidget; private Button zOffsetToggle; - private EditBox colorEntryWidget; + private HexColorEditBox colorEntryWidget; private EditBox scaleEntryWidget; private List spriteHelpTooltipText; + private ColorPickerWidget colorPickerWidget; private SuggestionListWidget suggestionWidget; private List validSprites = new ArrayList<>(); @@ -73,13 +75,14 @@ public void init() { this.zOffsetToggle.setMessage(Component.literal(this.blockEntity.zOffset.name())); }).bounds(width / 2 - 90, height / 2 + 5, 180, 20).build(); - this.colorEntryWidget = new EditBox(this.minecraft.font, width / 2 - 90, height / 2 + 35, 180, 20, Component.empty()); - this.colorEntryWidget.setValue("#" + String.format("%1$06X", this.blockEntity.color & 0x00FFFFFF)); - this.colorEntryWidget.setResponder(string -> { - TextColor.parseColor(this.colorEntryWidget.getValue()).ifSuccess(color -> { - this.blockEntity.color = color == null ? 0xFFFFFFFF : color.getValue() | 0xFF000000; - }); - }); + this.colorPickerWidget = this.createColorPickerWidget(); + + this.colorEntryWidget = HexColorEditBox.builder(this.minecraft.font, width / 2 - 90, height / 2 + 35, + () -> this.blockEntity.color, color -> this.blockEntity.color = color + ) + .setWidth(180) + .setColorPickerWidget(this.colorPickerWidget) + .build(); this.scaleEntryWidget = new EditBox(this.minecraft.font, width / 2 - 90, height / 2 + 65, 180, 20, Component.empty()); this.scaleEntryWidget.setValue(String.valueOf(this.blockEntity.scale)); @@ -143,6 +146,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo setTooltip(this.spriteHelpTooltipText); }*/ + this.extractColorPicker(graphics, mouseX, mouseY, delta); // render the list over everything suggestionWidget.extractRenderState(graphics, mouseX, mouseY, delta); } @@ -151,6 +155,9 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { double mouseX = event.x(); double mouseY = event.y(); + + if (mouseClickedColorPicker(event, doubleClick)) return true; + if (suggestionWidget.isMouseOver(mouseX, mouseY) && spriteWidget.isFocused()) { return suggestionWidget.mouseClicked(event, doubleClick); } else { @@ -185,6 +192,7 @@ public boolean keyPressed(KeyEvent event) { if (suggestionWidget.keyPressed(event)) { return true; } + if (keyPressedColorPicker(event)) return true; return super.keyPressed(event); } @@ -194,4 +202,9 @@ public CustomPacketPayload getUpdatePayload() { blockEntity.setChanged(); return C2SEditSpriteBlock.of(blockEntity); } + + @Override + public ColorPickerWidget getColorPickerWidget() { + return this.colorPickerWidget; + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java index 412db662..e00dca71 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockEditScreen.java @@ -1,9 +1,11 @@ package dev.hephaestus.glowcase.client.gui.screen.ingame; +import dev.hephaestus.glowcase.Glowcase; import dev.hephaestus.glowcase.block.entity.TextBlockEntity; -import dev.hephaestus.glowcase.client.gui.widget.ingame.ColorPickerWidget; import dev.hephaestus.glowcase.client.gui.widget.ingame.GlowcaseEditBox; -import dev.hephaestus.glowcase.client.util.ColorUtil; +import dev.hephaestus.glowcase.client.gui.widget.ingame.IconButtonWidget; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.HexColorEditBox; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; import dev.hephaestus.glowcase.packet.C2SEditTextBlock; import dev.hephaestus.glowcase.util.InputFilters; import dev.hephaestus.glowcase.util.ParseUtil; @@ -26,7 +28,6 @@ import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; -import java.awt.*; import java.util.List; import java.util.function.Consumer; @@ -37,14 +38,14 @@ public class TextBlockEditScreen extends TextEditorScreen implements BlockEditor private final TextBlockEntity textBlockEntity; private List textWidgets; - private List colorListeners; private TextFieldHelper selectionManager; - private EditBox colorEntryWidget; + private HexColorEditBox colorEntryWidget; + private HexColorEditBox backgroundColorEntryWidget; + private int currentRow; private long ticksSinceOpened = 0; private ColorPickerWidget colorPickerWidget; - private Color colorEntryPreColorPicker; //used for color picker cancel button public TextBlockEditScreen(TextBlockEntity textBlockEntity) { this.textBlockEntity = textBlockEntity; @@ -66,47 +67,48 @@ public void init() { int middle = width / 2; - var scaleSlider = new TextScale.SliderWidget(textBlockEntity, middle - 203, innerPadding, 113, 20); - addFormattingButtons(middle - 90 + 6, innerPadding, 0, 20, 2); + var scaleSlider = new TextScale.SliderWidget(textBlockEntity, middle - 203 - 5, innerPadding, 113, 20); + addFormattingButtons(middle - 90 + 6 - 5, innerPadding, 0, 20, 2); - this.colorEntryWidget = new EditBox(this.minecraft.font, middle + 54, innerPadding, 64, 20, Component.empty()); - this.colorEntryWidget.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.color"))); - this.colorEntryWidget.setValue(ColorUtil.toAlphaHex(this.textBlockEntity.color)); - this.colorEntryWidget.setResponder(string -> { - ColorUtil.parse(string, this.textBlockEntity.color).ifSuccess(newColor -> { - final int color = (Math.max(newColor >>> 24, 0x1A) << 24) | (newColor & ColorUtil.RGB_MASK); + this.colorPickerWidget = this.createColorPickerWidget(); - this.textBlockEntity.color = color; - // make sure it doesn't update from the color picker updating the text - if (this.colorEntryWidget.isFocused()) { - this.colorPickerWidget.setColor(new Color(color)); + this.colorEntryWidget = HexColorEditBox.builder(this.minecraft.font, middle + 54 - 5, innerPadding, + () -> this.textBlockEntity.color, color -> { + this.textBlockEntity.color = color; + this.textBlockEntity.rebake(true); } + ) + .setEditableAlpha(true) + .setColorPickerWidget(this.colorPickerWidget) + .build(); + this.colorEntryWidget.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.color_argb"))); + + this.backgroundColorEntryWidget = HexColorEditBox.builder(this.minecraft.font, middle + 54 + 64 + 6 - 5, innerPadding, + () -> this.textBlockEntity.backgroundColor, color -> { + this.textBlockEntity.backgroundColor = color; this.textBlockEntity.rebake(true); - }); - }); - - this.colorPickerWidget = ColorPickerWidget.builder(this, 216, 10).size(182, 104).build(); - this.colorPickerWidget.toggle(false); //start deactivated - - var moreOptionsButton = Button.builder( - Component.translatable("gui.glowcase.more"), - button -> { - var optionsScreen = new TextBlockOptionsScreen(this, textBlockEntity); - Minecraft.getInstance().setScreen(optionsScreen); - }) - .bounds(middle + 124, innerPadding, 80, 20) + } + ) + .setEditableAlpha(true) + .setColorPickerWidget(this.colorPickerWidget) .build(); + this.backgroundColorEntryWidget.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.background_color_argb"))); - this.textWidgets = List.of( - this.colorEntryWidget - ); + Button moreOptionsButton = IconButtonWidget.builder(Glowcase.id("properties"), button -> { + TextBlockOptionsScreen optionsScreen = new TextBlockOptionsScreen(this, textBlockEntity); + Minecraft.getInstance().setScreen(optionsScreen); + }) + .dimensions(middle + 124 + 64 + 6 - 5, innerPadding, 20, 20, 16, 16) + .build(); + moreOptionsButton.setTooltip(Tooltip.create(Component.translatable("gui.glowcase.extra_properties"))); - this.colorListeners = List.of( - this.colorEntryWidget + this.textWidgets = List.of( + this.colorEntryWidget, + this.backgroundColorEntryWidget ); - this.addRenderableWidget(colorPickerWidget); this.addRenderableWidget(this.colorEntryWidget); + this.addRenderableWidget(this.backgroundColorEntryWidget); this.addRenderableWidget(scaleSlider); this.addRenderableWidget(moreOptionsButton); } @@ -141,6 +143,7 @@ private void checkRow() { } } + // FIXME - Text shadow to false is not represented in editor @Override public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { super.extractRenderState(graphics, mouseX, mouseY, delta); @@ -198,7 +201,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo graphics.pose().popMatrix(); - colorPickerWidget.extractRenderState(graphics, mouseX, mouseY, delta); + this.extractColorPicker(graphics, mouseX, mouseY, delta); } @Override @@ -216,31 +219,13 @@ public boolean charTyped(CharacterEvent event) { public boolean keyPressed(KeyEvent event) { var keyCode = event.key(); - if (this.colorPickerWidget.active) { - switch (keyCode) { - case GLFW.GLFW_KEY_ENTER, GLFW.GLFW_KEY_KP_ENTER -> this.colorPickerWidget.confirmColor(); - case GLFW.GLFW_KEY_ESCAPE -> this.colorPickerWidget.cancel(); - default -> { - final GuiEventListener listener = this.colorPickerWidget.targetElement; - if (listener != null) { - this.setFocused(listener); - return listener.keyPressed(event); - } - } - } - - this.toggleColorPicker(false); - this.setFocused(null); - - return true; - } + if (this.keyPressedColorPicker(event)) return true; if (this.getFocused() != null) { if (this.getFocused().keyPressed(event)) { return true; } - this.toggleColorPicker(false); this.setFocused(null); if (keyCode == GLFW.GLFW_KEY_ESCAPE) { @@ -280,7 +265,7 @@ public boolean keyPressed(KeyEvent event) { return true; } else { - //formatting hotkeys + // Formatting hotkeys if (event.hasControlDown()) { if (keyCode == GLFW.GLFW_KEY_B) { insertTag(TagRegistry.SAFE.getTag("bold"), true); @@ -292,9 +277,9 @@ public boolean keyPressed(KeyEvent event) { insertTag(TagRegistry.SAFE.getTag("underline"), true); return true; } else if (keyCode == GLFW.GLFW_KEY_5 || keyCode == GLFW.GLFW_KEY_S) { - //There isn't a commonly agreed upon hotkey for strikethrough unlike the rest above - //apparently 5 is commonly used for strikethrough ¯\_(ツ)_/¯ - //Google Docs and Microsoft Word have 5 in their hotkeys, while Discord has S in its hotkey + // There isn't a commonly agreed upon hotkey for strikethrough unlike the rest above + // apparently 5 is commonly used for strikethrough ¯\_(ツ)_/¯ + // Google Docs and Microsoft Word have 5 in their hotkeys, while Discord has S in its hotkey insertTag(TagRegistry.SAFE.getTag("strikethrough"), true); return true; } else if (keyCode == GLFW.GLFW_KEY_O) { @@ -344,31 +329,6 @@ private void deleteLine() { this.textBlockEntity.rebake(true); } - private void colorListenerClicked(EditBox textWidget) { - this.colorPickerWidget.setPosition(Math.min(textWidget.getX(), width - colorPickerWidget.getWidth()), textWidget.getY() + textWidget.getHeight()); - this.colorPickerWidget.setTargetElement(textWidget); - this.colorPickerWidget.setOnAccept(picker -> { - textWidget.setValue(ColorUtil.toAlphaHex(picker.getCurrentColor().getRGB())); - }); - this.colorPickerWidget.setOnCancel(picker -> { - picker.setColor(this.colorEntryPreColorPicker); - textWidget.setValue(ColorUtil.toAlphaHex(this.colorEntryPreColorPicker.getRGB())); - }); - this.colorPickerWidget.setChangeListener(color -> { - final int newColor = ColorUtil.transferAlpha(this.colorEntryPreColorPicker.getRGB(), color.getRGB()); - textWidget.setValue(ColorUtil.toAlphaHex(newColor)); - }); - this.colorPickerWidget.setPresetListener((color, formatting) -> { - this.colorPickerWidget.setColor(color); - }); - ColorUtil.parse(textWidget.getValue(), ColorUtil.WHITE).ifSuccess(color -> { - final Color pickerColor = new Color(color); - this.colorEntryPreColorPicker = pickerColor; - this.colorPickerWidget.setColor(pickerColor); - }).ifError(textColorError -> this.colorEntryPreColorPicker = this.colorPickerWidget.getCurrentColor()); - toggleColorPicker(true); - } - @Override public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { double mouseX = event.x(); @@ -380,27 +340,11 @@ public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { continue; } this.setFocused(text); - if (this.colorListeners.contains(text)) { - this.colorListenerClicked(text); - } - if (this.colorPickerWidget.targetElement != text) { - text.setFocused(false); - } break; } - if (colorPickerWidget.active && colorPickerWidget.visible) { - if (colorPickerWidget.isMouseOver(mouseX, mouseY)) { - colorPickerWidget.mouseClicked(event, doubleClick); - this.setFocused(colorPickerWidget); - this.setDragging(true); - return true; - } else { - if (!this.colorPickerWidget.targetElement.isMouseOver(mouseX, mouseY)) { - toggleColorPicker(false); - } - } - } + if (mouseClickedColorPicker(event, doubleClick)) return true; + if (mouseY > topOffset) { this.currentRow = Mth.clamp((int) (mouseY - topOffset) / 12, 0, this.textBlockEntity.lines.size() - 1); this.setFocused(null); @@ -455,15 +399,10 @@ public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { } @Override - public ColorPickerWidget colorPickerWidget() { + public ColorPickerWidget getColorPickerWidget() { return this.colorPickerWidget; } - @Override - public void toggleColorPicker(boolean active) { - this.colorPickerWidget.toggle(active); - } - @Override TextFieldHelper getSelectionManager() { return this.selectionManager; diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java index 248b7276..bdf87961 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextBlockOptionsScreen.java @@ -2,7 +2,8 @@ import dev.hephaestus.glowcase.block.entity.TextBlockEntity; import dev.hephaestus.glowcase.client.gui.screen.ingame.TextBlockEditScreen.TextScale; -import dev.hephaestus.glowcase.client.util.ColorUtil; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.HexColorEditBox; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; import dev.hephaestus.glowcase.packet.C2SEditTextBlock; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -10,12 +11,13 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.ContainerObjectSelectionList; import net.minecraft.client.gui.components.CycleButton; -import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.layouts.HeaderAndFooterLayout; import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; @@ -23,9 +25,10 @@ import java.util.List; -public class TextBlockOptionsScreen extends BlockEditorScreen { +public class TextBlockOptionsScreen extends BlockEditorScreen implements ColorPickerIncludedScreen { private final Screen returnScreen; + private ColorPickerWidget colorPickerWidget; public final HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this); public TextOptionList options; @@ -98,32 +101,30 @@ public void init() { ) ); - this.options.addHeaders(Component.translatable("gui.glowcase.color"), Component.translatable("gui.glowcase.background_color")); - var colorEditBox = new EditBox( - this.font, - Button.DEFAULT_WIDTH, - Button.DEFAULT_HEIGHT, - Component.translatable("gui.glowcase.color") - ); - colorEditBox.setValue(ColorUtil.toAlphaHex(this.blockEntity.color)); - colorEditBox.setResponder(string -> ColorUtil.parse(string, blockEntity.color) - .ifSuccess(newColor -> { - blockEntity.color = newColor; - blockEntity.rebake(true); - })); - - var backgroundEditBox = new EditBox( - this.font, - Button.DEFAULT_WIDTH, - Button.DEFAULT_HEIGHT, - Component.translatable("gui.glowcase.background_color") - ); - backgroundEditBox.setValue(ColorUtil.toAlphaHex(this.blockEntity.backgroundColor)); - backgroundEditBox.setResponder(string -> ColorUtil.parse(string, blockEntity.backgroundColor) - .ifSuccess(newColor -> { - blockEntity.backgroundColor = newColor; - blockEntity.rebake(true); - })); + // Note: Color picker position is messed up on this screen, but it still works otherwise + this.colorPickerWidget = createColorPickerWidget(); + this.options.addHeaders(Component.translatable("gui.glowcase.color_argb"), Component.translatable("gui.glowcase.background_color_argb")); + HexColorEditBox colorEditBox = HexColorEditBox.builder(this.minecraft.font, 0, 0, + () -> this.blockEntity.color, color -> { + this.blockEntity.color = color; + this.blockEntity.rebake(true); + } + ) + .setEditableAlpha(true) + .setWidth(Button.DEFAULT_WIDTH) + .setColorPickerWidget(this.colorPickerWidget) + .build(); + + HexColorEditBox backgroundEditBox = HexColorEditBox.builder(this.minecraft.font, 0, 0, + () -> this.blockEntity.backgroundColor, color -> { + this.blockEntity.backgroundColor = color; + this.blockEntity.rebake(true); + } + ) + .setWidth(Button.DEFAULT_WIDTH) + .setEditableAlpha(true) + .setColorPickerWidget(this.colorPickerWidget) + .build(); this.options.add(colorEditBox, backgroundEditBox); @@ -159,6 +160,29 @@ public void onClose() { this.minecraft.setScreen(this.returnScreen); } + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + super.extractRenderState(graphics, mouseX, mouseY, delta); + this.extractColorPicker(graphics, mouseX, mouseY, delta); + } + + @Override + public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { + if (this.mouseClickedColorPicker(event, doubleClick)) return true; + return super.mouseClicked(event, doubleClick); + } + + @Override + public boolean keyPressed(KeyEvent event) { + if (this.keyPressedColorPicker(event)) return true; + return super.keyPressed(event); + } + + @Override + public ColorPickerWidget getColorPickerWidget() { + return this.colorPickerWidget; + } + @NullMarked public static class TextOptionList extends ContainerObjectSelectionList { public TextOptionList(Minecraft minecraft, int width, int height, int y) { diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextEditorScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextEditorScreen.java index de428bd5..3f096102 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextEditorScreen.java +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/TextEditorScreen.java @@ -1,6 +1,7 @@ package dev.hephaestus.glowcase.client.gui.screen.ingame; -import dev.hephaestus.glowcase.client.gui.widget.ingame.ColorPickerWidget; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; +import dev.hephaestus.glowcase.client.util.ColorUtil; import eu.pb4.placeholders.api.parsers.tag.TagRegistry; import eu.pb4.placeholders.api.parsers.tag.TextTag; import net.minecraft.ChatFormatting; @@ -47,24 +48,31 @@ protected void addFormattingButtons(int x, int y, int innerPadding, int buttonSi buttonX += buttonSize + buttonPadding; // + 4? (only works on padding of 2) this.colorText = Button.builder(Component.literal("\uD83D\uDD8C"), action -> { - ColorPickerWidget colorPickerWidget = colorPickerWidget(); - colorPickerWidget.setPosition(216, 10); - colorPickerWidget.setTargetElement(this.colorText); - colorPickerWidget.setOnAccept(picker -> { - picker.insertColor(picker.color); - picker.toggle(false); + ColorPickerWidget colorPickerWidget = this.getColorPickerWidget(); + colorPickerWidget.target(this.colorText, ColorUtil.RED, false, true, pickedColor -> {}); + colorPickerWidget.setConfirmListener(this::insertHexColor); + colorPickerWidget.setPresetListener(preset -> { + if (preset.getPresetFormatting() != null) this.insertFormattingTag(preset.getPresetFormatting()); + colorPickerWidget.hide(); }); - colorPickerWidget.setOnCancel(picker -> picker.toggle(false)); - colorPickerWidget.setPresetListener((color, formatting) -> { - if(formatting != null) { - insertFormattingTag(formatting); - } else { - insertHexTag(ColorPickerWidget.getHexCode(color)); - } - this.toggleColorPicker(false); - }); - colorPickerWidget.setChangeListener(null); - toggleColorPicker(!colorPickerWidget.active); +// ColorPickerWidget colorPickerWidget = getColorPickerWidget(); +// colorPickerWidget.setPosition(216, 10); +// colorPickerWidget.setTargetElement(this.colorText); +// colorPickerWidget.setOnAccept(picker -> { +// picker.insertColor(picker.color); +// picker.toggle(false); +// }); +// colorPickerWidget.setOnCancel(picker -> picker.toggle(false)); +// colorPickerWidget.setPresetListener((color, formatting) -> { +// if(formatting != null) { +// insertFormattingTag(formatting); +// } else { +// insertHexTag(ColorPickerWidget.getHexCode(color)); +// } +// this.toggleColorPickerWidget(false); +// }); +// colorPickerWidget.setChangeListener(null); +// toggleColorPickerWidget(!colorPickerWidget.active); }).bounds(buttonX, buttonY, buttonSize, buttonSize).build(); widgets = new Button[]{ diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/ColorPickerWidget.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/ColorPickerWidget.java deleted file mode 100644 index c5c38aaf..00000000 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/ColorPickerWidget.java +++ /dev/null @@ -1,607 +0,0 @@ -package dev.hephaestus.glowcase.client.gui.widget.ingame; - -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.vertex.VertexConsumer; -import dev.hephaestus.glowcase.client.gui.screen.ingame.ColorPickerIncludedScreen; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.ChatFormatting; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.AbstractButton; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.narration.NarrationElementOutput; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.input.InputWithModifiers; -import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.state.gui.GuiElementRenderState; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import org.apache.commons.compress.utils.Lists; -import org.jetbrains.annotations.Nullable; -import org.joml.Matrix3x2fStack; - -import java.awt.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -public class ColorPickerWidget extends AbstractButton { - static final Identifier CONFIRM_TEXTURE = Identifier.withDefaultNamespace("pending_invite/accept"); - static final Identifier CONFIRM_HIGHLIGHTED_TEXTURE = Identifier.withDefaultNamespace("pending_invite/accept_highlighted"); - static final Identifier CANCEL_TEXTURE = Identifier.withDefaultNamespace("pending_invite/reject"); - static final Identifier CANCEL_HIGHLIGHTED_TEXTURE = Identifier.withDefaultNamespace("pending_invite/reject_highlighted"); - - public final ColorPickerIncludedScreen screen; - public GuiEventListener targetElement; - public Color color = Color.red; - public boolean includePresets = true; - public ArrayList presetWidgets = Lists.newArrayList(); - public boolean confirmOrCancelButtonDown = false; - public IconButtonWidget confirmButton; - public IconButtonWidget cancelButton; - private Consumer changeListener; - private BiConsumer presetListener; - private Consumer onAccept; - private Consumer onCancel; - - private boolean mouseDown = false; - private int presetY, presetSize, presetPadding, presetHeight; - private boolean presetDown = false; - private int previewX, previewY, previewWidth, previewHeight; - private int hueX, hueY, hueWidth, hueHeight; - private boolean hueDown = false; - private int satLightX, satLightY, satLightWidth, satLightHeight; - private boolean satLightDown = false; - public int hueThumbX; - public int satLightThumbX, satLightThumbY; - - private float[] HSL; - private float hue; - private float saturation; - private float light; - - public static ColorPickerWidget.Builder builder(ColorPickerIncludedScreen screen, int x, int y) { - return new ColorPickerWidget.Builder(screen, x, y); - } - - public ColorPickerWidget(ColorPickerIncludedScreen screen, int x, int y, int width, int height, Component message) { - super(x, y, width, height, message); - this.screen = screen; - - this.confirmButton = IconButtonWidget.builder(CONFIRM_TEXTURE, action -> this.confirmColor()) - .hoverIcon(CONFIRM_HIGHLIGHTED_TEXTURE).build(); - - this.cancelButton = IconButtonWidget.builder(CANCEL_TEXTURE, action -> this.cancel()) - .hoverIcon(CANCEL_HIGHLIGHTED_TEXTURE).build(); - - updatePositions(); - updateHSL(); - updateThumbPositions(); - } - - public void setTargetElement(GuiEventListener element) { - this.targetElement = element; - } - - public void setIncludePresets(boolean shouldInclude) { - this.includePresets = shouldInclude; - } - - public void setPresets(boolean includeDefaultPresets, List addedPresets) { - if (includeDefaultPresets) { - addDefaultPresets(); - } - if (!addedPresets.isEmpty()) { - for (Color preset : addedPresets) { - this.presetWidgets.add(ColorPresetWidget.fromColor(this, preset)); - } - } - } - - public void confirmColor() { - if (this.onAccept != null) { - this.onAccept.accept(this); - } - - this.toggle(false); - } - - public void cancel() { - if (this.onCancel != null) { - this.onCancel.accept(this); - } - - this.toggle(false); - } - - public void toggle(boolean active) { - this.active = active; - this.visible = active; - if (this.active) { - this.updatePositions(); - this.updateHSL(); - this.updateThumbPositions(); - } - } - - public void insertColor(Color color) { - String hex = getHexCode(color); - this.screen.insertHexTag(hex); - } - - public void insertFormatting(ChatFormatting formatting) { - this.screen.insertFormattingTag(formatting); - } - - public void setColor(Color color) { - this.color = color; - this.updateHSL(); - this.updateThumbPositions(); - } - - @Override - protected void extractContents(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { - if (!visible) return; - updateHSL(); - - //graphics.setShaderColor(1f, 1f, 1f, this.alpha); - /*RenderSystem.enableBlend(); - RenderSystem.enableDepthTest();*/ - Matrix3x2fStack matrices = graphics.pose(); - //graphics.applyBlur(); - - graphics.nextStratum(); - matrices.pushMatrix(); - - int x = this.getX(); - int y = this.getY(); - int z = 1; - int width = this.getWidth(); - int height = this.getHeight(); - - //background - graphics.blit(RenderPipelines.GUI_TEXTURED, Identifier.withDefaultNamespace("textures/gui/inworld_menu_list_background.png"), x, y, 0, 0, width, height, 32, 32); - if (this.isHoveredOrFocused()) { - //outline - drawOutline(graphics, x, y, width, height, Color.white); - } - - //color picker stuff - updatePositions(); - this.confirmButton.setPosition(x + width - presetSize - presetPadding, y + height - presetSize - 2, z + 1, presetSize, presetSize + 2); - this.cancelButton.setPosition(x + width - presetSize * 2 - presetPadding * 2 - 1, y + height - presetSize - 2, z + 1, presetSize, presetSize + 2); - - drawColorPreview(graphics, previewX, previewY, previewWidth, previewHeight); - drawSatLight(graphics, satLightX, satLightY, satLightWidth, satLightHeight); - drawHueBar(graphics, hueX, hueY, hueWidth, hueHeight, z + 1); - if (this.includePresets) { - //sorta dynamic but also really specific to keep it all aligned - //I'm not going to worry about it a lot though because I do not see the custom preset thing being used a lot if at all - drawPresets(graphics, mouseX, mouseY, delta, previewX, presetY, y + height - presetY, z + 1, presetSize, width / (presetSize + presetPadding), presetPadding); - } - - this.confirmButton.extractRenderState(graphics, mouseX, mouseY, delta); - this.cancelButton.extractRenderState(graphics, mouseX, mouseY, delta); - - - matrices.popMatrix(); - - //graphics.setShaderColor(1f, 1f, 1f, 1f); - } - - public void updatePositions() { - int x = this.getX(); - int y = this.getY(); - int width = this.getWidth(); - int height = this.getHeight(); - - presetSize = (int) (height / 6.5); - presetPadding = 2; - presetHeight = presetSize * 2 + presetPadding * 2; - - previewX = x + 2; - previewY = y + 2; - previewWidth = width / 3; - previewHeight = height - 16 - (includePresets ? presetHeight : 0); - satLightX = previewX + previewWidth + 2; - satLightY = y + 2; - satLightWidth = width - previewWidth - 6; - satLightHeight = previewHeight; - hueX = x + 2; - hueY = previewY + previewHeight + 2; - hueWidth = width - 4; - hueHeight = height - previewHeight - 6 - (includePresets ? presetHeight : 0); - presetY = hueY + hueHeight + presetPadding; - } - - private void drawColorPreview(GuiGraphicsExtractor graphics, int x, int y, int width, int height) { - graphics.fill(x, y, x + width, y + height, this.color.getRGB()); - } - - private void drawHueBar(GuiGraphicsExtractor graphics, int x, int y, int width, int height, int z) { - //rainbow gradient - int[] colors = new int[]{ - Color.red.getRGB(), Color.yellow.getRGB(), Color.green.getRGB(), - Color.cyan.getRGB(), Color.blue.getRGB(), Color.magenta.getRGB(), - Color.red.getRGB() - }; - - int maxColors = colors.length - 1; - for (int color = 0; color < maxColors; color++) { - sidewaysGradient( - graphics, - x + (width / maxColors * (color)), y, - width / maxColors, height, - colors[color], colors[color + 1] - ); - } - - //thumb - graphics.fill(hueThumbX - 3, y - 1, hueThumbX + 3, y + height + 1, getRgbFromHueThumb()); - drawOutline(graphics, hueThumbX - 3, y - 1, 6, height + 2, Color.white); - } - - private void drawSatLight(GuiGraphicsExtractor graphics, int x, int y, int width, int height) { - //white to current color's hue, left to right - sidewaysGradient(graphics, x, y, width, height, Color.white.getRGB(), getRgbFromHueThumb()); - - //transparent to black, top to bottom - graphics.fillGradient(x, y, x + width, y + height, 0x00000000, Color.black.getRGB()); - - //thumb - graphics.fill(satLightThumbX - 4, satLightThumbY - 4, satLightThumbX + 4, satLightThumbY + 4, this.color.getRGB()); - drawOutline(graphics, satLightThumbX - 4, satLightThumbY - 4, 8, 8, Color.white); - } - - private void drawOutline(GuiGraphicsExtractor graphics, int x, int y, int width, int height, Color outlineColor) { - int color = outlineColor.getRGB(); - graphics.fill(x, y, x + width, y + 1, color); - graphics.fill(x, y, x + 1, y + height, color); - graphics.fill(x + width, y, x + width - 1, y + height, color); - graphics.fill(x, y + height, x + width, y + height - 1, color); - } - - private void sidewaysGradient(GuiGraphicsExtractor graphics, int x, int y, int width, int height, int startColor, int endColor) { - graphics.guiRenderState.addGuiElement(new GuiElementRenderState() { - - @Override - public ScreenRectangle bounds() { - return new ScreenRectangle(x, y, width, height).transformMaxBounds(graphics.pose()); - } - - @Override - public void buildVertices(VertexConsumer vertices) { - Matrix3x2fStack matrix = graphics.pose(); - vertices.addVertexWith2DPose(matrix, x, y).setColor(startColor); - vertices.addVertexWith2DPose(matrix, x, y + height).setColor(startColor); - vertices.addVertexWith2DPose(matrix, x + width, y + height).setColor(endColor); - vertices.addVertexWith2DPose(matrix, x + width, y).setColor(endColor); - } - - @Override - public RenderPipeline pipeline() { - return RenderPipelines.GUI; - } - - @Override - public TextureSetup textureSetup() { - return TextureSetup.noTexture(); - } - - @Override - public @Nullable ScreenRectangle scissorArea() { - return null; - } - }); - } - - private void drawPresets(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta, int x, int y, int height, int z, int presetSize, int presetsPerLine, int presetPadding) { - int presetX = x; - int presetY = y; - int renderedPresets = 0; - for (ColorPresetWidget preset : this.presetWidgets) { - preset.setPosition(presetX, presetY, z, presetSize); - preset.extractRenderState(graphics, mouseX, mouseY, delta); - presetX += presetSize + presetPadding; - renderedPresets++; - if (renderedPresets % presetsPerLine == 0) { - presetY += presetSize + presetPadding; - if (presetY > y + height) { //prevent overflow - return; - } - presetX = x; - } - } - } - - //done manually to keep list order instead of looping through Formatting.values() - public void addDefaultPresets() { - ColorPresetWidget darkRed = ColorPresetWidget.fromFormatting(this, ChatFormatting.DARK_RED); - ColorPresetWidget red = ColorPresetWidget.fromFormatting(this, ChatFormatting.RED); - ColorPresetWidget gold = ColorPresetWidget.fromFormatting(this, ChatFormatting.GOLD); - ColorPresetWidget yellow = ColorPresetWidget.fromFormatting(this, ChatFormatting.YELLOW); - ColorPresetWidget green = ColorPresetWidget.fromFormatting(this, ChatFormatting.GREEN); - ColorPresetWidget darkGreen = ColorPresetWidget.fromFormatting(this, ChatFormatting.DARK_GREEN); - ColorPresetWidget aqua = ColorPresetWidget.fromFormatting(this, ChatFormatting.AQUA); - ColorPresetWidget darkAqua = ColorPresetWidget.fromFormatting(this, ChatFormatting.DARK_AQUA); - ColorPresetWidget blue = ColorPresetWidget.fromFormatting(this, ChatFormatting.BLUE); - ColorPresetWidget darkBlue = ColorPresetWidget.fromFormatting(this, ChatFormatting.DARK_BLUE); - ColorPresetWidget lightPurple = ColorPresetWidget.fromFormatting(this, ChatFormatting.LIGHT_PURPLE); - ColorPresetWidget darkPurple = ColorPresetWidget.fromFormatting(this, ChatFormatting.DARK_PURPLE); - ColorPresetWidget white = ColorPresetWidget.fromFormatting(this, ChatFormatting.WHITE); - ColorPresetWidget grey = ColorPresetWidget.fromFormatting(this, ChatFormatting.GRAY); - ColorPresetWidget darkGrey = ColorPresetWidget.fromFormatting(this, ChatFormatting.DARK_GRAY); - ColorPresetWidget black = ColorPresetWidget.fromFormatting(this, ChatFormatting.BLACK); - this.presetWidgets.addAll(List.of(darkRed, red, gold, yellow, green, darkGreen, aqua, darkAqua, - blue, darkBlue, lightPurple, darkPurple, white, grey, darkGrey, black)); - } - - @Override - public void onClick(MouseButtonEvent event, boolean doubleClick) { - this.mouseDown = true; - this.satLightDown = false; - this.hueDown = false; - this.presetDown = false; - this.confirmOrCancelButtonDown = false; - setColorFromMouse(event, doubleClick); - } - - public void setColorFromMouse(MouseButtonEvent event, boolean doubleClick) { - int colorAlpha = color.getAlpha(); - - double mouseX = event.x(); - double mouseY = event.y(); - if (clickedSatLight(mouseX, mouseY)) { - setSatLightFromMouse(mouseX, mouseY); - } else if (clickedHue(mouseX, mouseY)) { - setHueFromMouse(mouseX); - } else if (this.confirmButton.isMouseOver(mouseX, mouseY)) { - if (satLightDown || hueDown || presetDown || confirmOrCancelButtonDown) return; - this.confirmButton.onClick(event, doubleClick); - confirmOrCancelButtonDown = true; - } else if (this.cancelButton.isMouseOver(mouseX, mouseY)) { - if (satLightDown || hueDown || presetDown || confirmOrCancelButtonDown) return; - this.cancelButton.onClick(event, doubleClick); - confirmOrCancelButtonDown = true; - } else { - //clickedPreset also sets the preset to avoid an extra calculation - checkAndSetPreset(event, doubleClick); - } - - if (this.changeListener != null) { - this.changeListener.accept(this.color); - } - } - - public boolean clickedSatLight(double mouseX, double mouseY) { - if (hueDown || presetDown || confirmOrCancelButtonDown) return false; - - if (mouseX >= satLightX - && mouseX <= satLightX + satLightWidth - && mouseY >= satLightY - && mouseY <= satLightY + satLightHeight) { - satLightDown = true; - } - - if (satLightDown) { - satLightThumbX = (int) Math.clamp(mouseX, satLightX, satLightX + satLightWidth); - satLightThumbY = (int) Math.clamp(mouseY, satLightY, satLightY + satLightHeight); - } - return satLightDown; - } - - public boolean clickedHue(double mouseX, double mouseY) { - if (satLightDown || presetDown || confirmOrCancelButtonDown) return false; - - if (mouseY >= hueY && mouseY <= hueY + hueHeight - && mouseX >= hueX && mouseX <= hueX + hueWidth) { - hueDown = true; - } - - if (hueDown) { - hueThumbX = (int) Math.clamp(mouseX, hueX, hueX + hueWidth); - } - return hueDown; - } - - public boolean checkAndSetPreset(MouseButtonEvent event, boolean doubleClick) { - if (satLightDown || hueDown || presetDown || confirmOrCancelButtonDown) return false; - - //just checks for each preset here, and also sets here so it doesn't have to check again - for (ColorPresetWidget preset : this.presetWidgets) { - if (preset.isMouseOver(event.x(), event.y())) { - preset.onClick(event, doubleClick); - //even though the preset closes the color picker, - //this is added to prevent spamming tags when holding down the mouse button - presetDown = true; - } - } - return presetDown; - } - - @Override - protected void onDrag(MouseButtonEvent event, double dx, double dy) { - if (mouseDown || isMouseOver(event.x(), event.y())) { - setColorFromMouse(event, false); - } - } - - @Override - public void onRelease(MouseButtonEvent event) { - this.mouseDown = false; - } - - @Override - public boolean isMouseOver(double mouseX, double mouseY) { - return super.isMouseOver(mouseX, mouseY); - } - - @Override - public void onPress(InputWithModifiers input) { - } - - - public void setSatLightFromMouse(double mouseX, double mouseY) { - if (mouseX < satLightX) { - this.saturation = 0f; - } else if (mouseX > satLightX + satLightWidth) { - this.saturation = 1f; - } else { - float newSat = (float) (mouseX - satLightX) / satLightWidth; - this.saturation = Math.clamp(newSat, 0f, 1f); - } - - if (mouseY < satLightY) { - this.light = 1f; - } else if (mouseY > satLightY + satLightHeight) { - this.light = 0f; - } else { - float newLight = (float) (mouseY - satLightY) / satLightHeight; - this.light = Math.clamp(1f - newLight, 0f, 1f); - } - - setColorFromHSL(); - } - - public void setHueFromMouse(double mouseX) { - if (mouseX < hueX) { - this.hue = 0f; - } else if (mouseX > hueX + hueWidth) { - this.hue = 1f; - } else { - float newHue = (float) (mouseX - hueX) / hueWidth; - this.hue = Math.clamp(newHue, 0f, 1f); - } - - setColorFromHSL(); - } - - public void updateThumbPositions() { - this.satLightThumbX = getSatLightThumbX(); - this.satLightThumbY = getSatLightThumbY(); - this.hueThumbX = getHueThumbX(); - } - - private int getSatLightThumbX() { - int min = satLightX; - int max = satLightX + satLightWidth; - int value = (int) (min + (satLightWidth * this.saturation)); - return Math.clamp(value, min, max); - } - - private int getSatLightThumbY() { - int min = satLightY; - int max = satLightY + satLightHeight; - int value = (int) (min + (satLightHeight * (1.0f - this.light))); - return Math.clamp(value, min, max); - } - - private int getHueThumbX() { - int min = hueX; - int max = hueX + hueWidth; - int value = (int) (min + hueWidth * this.hue); - return Math.clamp(value, min, max); - } - - public Color getCurrentColor() { - return this.color; - } - - public void setColorFromHSL() { - float trueHue = (float) (hueThumbX - hueX) / hueWidth; - this.color = Color.getHSBColor(trueHue, this.saturation, this.light); - } - - public int getRgbFromHueThumb() { - float trueHue = (float) (hueThumbX - hueX) / hueWidth; - return Color.HSBtoRGB(trueHue, 1, 1); - } - - public void updateHSL() { - this.HSL = getHSL(); - this.hue = HSL[0]; - this.saturation = HSL[1]; - this.light = HSL[2]; - } - - protected float[] getHSL() { - return Color.RGBtoHSB(this.color.getRed(), this.color.getGreen(), this.color.getBlue(), null); - } - - @Override - public void updateWidgetNarration(NarrationElementOutput builder) { - this.defaultButtonNarrationText(builder); - } - - public void setChangeListener(Consumer changeListener) { - this.changeListener = changeListener; - } - - public void setPresetListener(BiConsumer presetListener) { - this.presetListener = presetListener; - } - - public void setOnAccept(Consumer onAccept) { - this.onAccept = onAccept; - } - - public void setOnCancel(Consumer onCancel) { - this.onCancel = onCancel; - } - - public BiConsumer getPresetListener() { - return presetListener; - } - - public static String getHexCode(Color color) { - return "#" + String.format("%1$06X", color.getRGB() & 0x00FFFFFF); - } - - @Environment(EnvType.CLIENT) - public static class Builder { - private final ColorPickerIncludedScreen screen; - private final int x; - private final int y; - private int width = 150; - private int height = 200; - private boolean includePresets = true; - private boolean includeDefaultPresets = true; - private final List presets = Lists.newArrayList(); - - public Builder(ColorPickerIncludedScreen screen, int x, int y) { - this.screen = screen; - this.x = x; - this.y = y; - } - - public ColorPickerWidget.Builder size(int width, int height) { - this.width = width; - this.height = height; - return this; - } - - public ColorPickerWidget.Builder includePresets(boolean shouldInclude) { - this.includePresets = shouldInclude; - return this; - } - - public ColorPickerWidget.Builder withPreset(boolean includeDefault, Color... presets) { - this.includeDefaultPresets = includeDefault; - this.presets.addAll(Arrays.asList(presets)); - return this; - } - - public ColorPickerWidget build() { - ColorPickerWidget colorPickerWidget = new ColorPickerWidget(this.screen, this.x, this.y, this.width, this.height, Component.nullToEmpty("")); - colorPickerWidget.setIncludePresets(this.includePresets); - if (this.includePresets) { - colorPickerWidget.setPresets(this.includeDefaultPresets, this.presets); - } - return colorPickerWidget; - } - } -} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/ColorPresetWidget.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/ColorPresetWidget.java deleted file mode 100644 index 2533b6dc..00000000 --- a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/ColorPresetWidget.java +++ /dev/null @@ -1,84 +0,0 @@ -package dev.hephaestus.glowcase.client.gui.widget.ingame; - -import net.minecraft.ChatFormatting; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.AbstractButton; -import net.minecraft.client.gui.narration.NarrationElementOutput; -import net.minecraft.client.input.InputWithModifiers; -import net.minecraft.network.chat.Component; -import org.jetbrains.annotations.Nullable; - -import java.awt.*; -import java.util.function.BiConsumer; - -public class ColorPresetWidget extends AbstractButton { - public final ColorPickerWidget colorPickerWidget; - public final Color color; - @Nullable - public ChatFormatting formatting = null; - public int z = 0; - - public ColorPresetWidget(ColorPickerWidget colorPicker, int x, int y, int width, int height, Color color) { - super(x, y, width, height, Component.nullToEmpty("")); - this.colorPickerWidget = colorPicker; - this.color = color; - } - - public void setPosition(int x, int y, int z, int size) { - this.setX(x); - this.setY(y); - this.setSize(size, size); - this.z = z; - } - - public static ColorPresetWidget fromFormatting(ColorPickerWidget colorPicker, ChatFormatting formatting) { - if (formatting.isColor()) { - //noinspection DataFlowIssue - ColorPresetWidget presetWidget = new ColorPresetWidget(colorPicker, 0, 0, 0, 0, new Color(formatting.getColor())); - presetWidget.formatting = formatting; - return presetWidget; - } - return new ColorPresetWidget(colorPicker, 0, 0, 0, 0, Color.white); //fallback - } - - public static ColorPresetWidget fromColor(ColorPickerWidget colorPicker, Color color) { - return new ColorPresetWidget(colorPicker, 0, 0, 0, 0, color); - } - - @Override - protected void extractContents(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { - graphics.fill(this.getX(), this.getY(), this.getX() + this.getWidth(), this.getY() + this.getHeight(), this.color.getRGB()); - if (isMouseOver(mouseX, mouseY)) { - drawOutline(graphics, this.getX() - 1, this.getY() - 1, this.getWidth() + 2, this.getHeight() + 2, this.z + 1); - } - } - - private void drawOutline(GuiGraphicsExtractor graphics, int x, int y, int width, int height, int z) { - int color = Color.white.getRGB(); - graphics.fill(x, y, x + width, y + 1, color); - graphics.fill(x, y, x + 1, y + height, color); - graphics.fill(x + width, y, x + width - 1, y + height, color); - graphics.fill(x, y + height, x + width, y + height - 1, color); - } - - @Override - public void onPress(InputWithModifiers input) { - BiConsumer presetListener = this.colorPickerWidget.getPresetListener(); - if (presetListener != null) { - presetListener.accept(this.color, this.formatting != null && this.formatting.isColor() ? this.formatting : null); - } else { - if (this.formatting != null && formatting.isColor()) { - this.colorPickerWidget.color = this.color; - this.colorPickerWidget.toggle(false); - } else { - this.colorPickerWidget.setColor(this.color); - } - } - - } - - @Override - protected void updateWidgetNarration(NarrationElementOutput builder) { - - } -} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/HexColorEditBox.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/HexColorEditBox.java new file mode 100644 index 00000000..a7ccf651 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/HexColorEditBox.java @@ -0,0 +1,138 @@ +package dev.hephaestus.glowcase.client.gui.widget.ingame.color; + +import dev.hephaestus.glowcase.client.gui.widget.ingame.GlowcaseEditBox; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.functional.ColorGetter; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.functional.ColorSetter; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.ColorPickerWidget; +import dev.hephaestus.glowcase.client.util.ColorUtil; +import net.minecraft.client.gui.Font; +import net.minecraft.client.input.CharacterEvent; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Nullable; + +/** + * An EditBox Widget built for HEX colors, with automatic color (as an integer) getter & setter, and built-in {@link ColorPickerWidget} support. + * @author Superkat32 + */ +public class HexColorEditBox extends GlowcaseEditBox { + @Nullable + public final ColorPickerWidget colorPickerWidget; + public final ColorGetter colorGetter; + public final ColorSetter colorSetter; + public int color; + public boolean editableAlpha; + + public static HexColorEditBox.Builder builder(Font textRenderer, int x, int y, ColorGetter colorGetter, ColorSetter colorSetter) { + return new Builder(textRenderer, x, y, colorGetter, colorSetter); + } + + public HexColorEditBox( + Font textRenderer, int x, int y, int width, int height, + boolean editableAlpha, @Nullable ColorPickerWidget colorPickerWidget, + ColorGetter colorGetter, ColorSetter colorSetter + ) { + super(textRenderer, x, y, width, height, Component.empty()); + this.colorPickerWidget = colorPickerWidget; + this.colorGetter = colorGetter; + this.colorSetter = colorSetter; + this.color = colorGetter.get(); + this.editableAlpha = editableAlpha; + + this.setResponder(string -> { + ColorUtil.parse(string, this.color).ifSuccess(this.colorSetter::set); + }); + + this.setTextFromColor(); + } + + @Override + public void onClick(MouseButtonEvent event, boolean doubleClick) { + super.onClick(event, doubleClick); + if (this.colorPickerWidget != null) { + this.colorPickerWidget.target(this, this.color, this.editableAlpha, this::setColor); + } + } + + @Override + public boolean charTyped(CharacterEvent event) { + if(super.charTyped(event)) { + if (this.colorPickerWidget != null) { + ColorUtil.parse(this.getValue(), this.color).ifSuccess(parsedColor -> { + this.color = parsedColor; + this.colorPickerWidget.setColor(parsedColor); + }); + } + return true; + } + return false; + } + + public void setTextFromColor() { + String hexString = this.formatColorAsHex(this.color); + this.setValue(hexString); + } + + public String formatColorAsHex(int color) { + if (this.editableAlpha) return ColorUtil.toAlphaHex(color); + return ColorUtil.toHex(color); + } + + public void setColor(int color) { + this.color = color; + this.setTextFromColor(); + } + + public static class Builder { + private static final int DEFAULT_WIDTH = 50; + private static final int DEFAULT_ALPHA_WIDTH = 64; + + private final ColorGetter colorGetter; + private final ColorSetter colorSetter; + private final Font textRenderer; + private final int x, y; + + private int width = DEFAULT_WIDTH; + private int height = 20; + private boolean editableAlpha = false; + private ColorPickerWidget colorPickerWidget = null; + + public Builder(Font textRenderer, int x, int y, ColorGetter colorGetter, ColorSetter colorSetter) { + this.colorGetter = colorGetter; + this.colorSetter = colorSetter; + this.textRenderer = textRenderer; + this.x = x; + this.y = y; + } + + public Builder setWidth(int width) { + this.width = width; + return this; + } + + public Builder setHeight(int height) { + this.height = height; + return this; + } + + public Builder setEditableAlpha(boolean editableAlpha) { + this.editableAlpha = editableAlpha; + if (this.width == DEFAULT_WIDTH) this.width = DEFAULT_ALPHA_WIDTH; + return this; + } + + public Builder setColorPickerWidget(ColorPickerWidget colorPickerWidget) { + this.colorPickerWidget = colorPickerWidget; + return this; + } + + public HexColorEditBox build() { + return new HexColorEditBox( + this.textRenderer, this.x, this.y, this.width, this.height, + this.editableAlpha, this.colorPickerWidget, + this.colorGetter, this.colorSetter + ); + } + } + +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/ColorGetter.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/ColorGetter.java new file mode 100644 index 00000000..1f4602ed --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/ColorGetter.java @@ -0,0 +1,5 @@ +package dev.hephaestus.glowcase.client.gui.widget.ingame.color.functional; + +public interface ColorGetter { + int get(); +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/ColorSetter.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/ColorSetter.java new file mode 100644 index 00000000..8e97df43 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/ColorSetter.java @@ -0,0 +1,5 @@ +package dev.hephaestus.glowcase.client.gui.widget.ingame.color.functional; + +public interface ColorSetter { + void set(int color); +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/PickerPresetListener.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/PickerPresetListener.java new file mode 100644 index 00000000..488e3d50 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/functional/PickerPresetListener.java @@ -0,0 +1,7 @@ +package dev.hephaestus.glowcase.client.gui.widget.ingame.color.functional; + +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker.PickerPreset; + +public interface PickerPresetListener { + void onPresetClick(PickerPreset preset); +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/ColorPickerWidget.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/ColorPickerWidget.java new file mode 100644 index 00000000..183adaa3 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/ColorPickerWidget.java @@ -0,0 +1,490 @@ +package dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker; + +import com.mojang.blaze3d.platform.cursor.CursorTypes; +import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.client.gui.screen.ingame.ColorPickerIncludedScreen; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.functional.ColorSetter; +import dev.hephaestus.glowcase.client.gui.widget.ingame.color.functional.PickerPresetListener; +import dev.hephaestus.glowcase.client.util.ColorUtil; +import dev.hephaestus.glowcase.client.util.GuiGraphicsUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.AbstractButton; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.InputWithModifiers; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.util.ARGB; +import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; + +import java.util.List; +import java.util.function.Consumer; + +/** + * Main color picker widget, including a preview box, saturation & value box, hue slider, optional alpha slider, and color presets.

+ * Each {@link ColorPickerIncludedScreen} has *one* Color Picker, which each widget uses. This means that position, showAlpha, and color values will be updated constantly.

+ * @author Superkat32 + */ +public class ColorPickerWidget extends AbstractButton { + // To keep things simple (and keep me sane), sizes are fully locked and hardcoded + // Width calculated by: ((presetScale + padding) * presetsPerLine) + padding -> ((16 + 2) * 10) + 2 + // Height calculated by: Whatever my heart desired at the moment -> 116 and 146, apparently + // Alpha height difference: (presetScale + padding) + (alphaHeight + padding) -> (16 + 2) + (10 + 2) + private static final int DEFAULT_WIDTH = 182; + private static final int DEFAULT_HEIGHT = 116; + private static final int DEFAULT_ALPHA_HEIGHT = 146; + private static final Identifier BACKGROUND_TEXTURE = Identifier.withDefaultNamespace("textures/gui/inworld_menu_list_background.png"); + + // Different alpha textures have different tile sizes to better match their background + private static final Identifier ALPHA_PREVIEW_TEXTURE = Glowcase.id("color_picker/alpha_preview"); + private static final Identifier ALPHA_SLIDER_TEXTURE = Glowcase.id("color_picker/alpha_slider"); + private static final Identifier ALPHA_THUMB_TEXTURE = Glowcase.id("color_picker/alpha_thumb"); + private static final Identifier ALPHA_PRESET_TEXTURE = Glowcase.id("color_picker/alpha_preset"); + + public final ColorPickerIncludedScreen screen; + @Nullable + public GuiEventListener targetElement = null; + @Nullable + public ColorSetter pickedColorListener = null; + @Nullable + public PickerPresetListener presetListener = null; + @Nullable + public Consumer confirmListener = null; + public boolean showAlpha = false; + + public final List clickableAreas; + public PickerArea previewArea; + public PickerArea satValueArea; + public PickerArea hueArea; + public PickerArea alphaArea; + public PickerArea presetsArea; + public List formattingPresetAreas; + public List alphaPresetAreas; + public List buttonAreas; + + @Nullable + public PickerArea currentClickedArea = null; // Used for allowing mouse drags beyond an area's boundaries + + public float hue, saturation, value, alpha; + public float prevHue, prevSaturation, prevValue, prevAlpha; // Used for cancelling, *not* interpolation + + public static ColorPickerWidget.Builder builder(ColorPickerIncludedScreen screen) { + return new Builder(screen); + } + + public ColorPickerWidget(ColorPickerIncludedScreen screen, int x, int y, int width, int height, Component message) { + super(x, y, width, height, message); + this.screen = screen; + + this.previewArea = new PickerArea(this, false); + this.satValueArea = new PickerArea(this, xLerp -> this.saturation = xLerp, yLerp -> this.value = 1f - yLerp); + this.hueArea = new PickerArea(this, xLerp -> this.hue = xLerp); + this.alphaArea = new PickerArea(this, xLerp -> this.alpha = xLerp); + this.presetsArea = new PickerArea(this, false); + this.formattingPresetAreas = PickerPreset.createFormattingPresets(this); + this.alphaPresetAreas = PickerPreset.createAlphaPresets(this); + this.buttonAreas = PickerButton.createButtons(this); + this.updateAreas(); + + this.clickableAreas = List.of(this.satValueArea, this.hueArea, this.alphaArea); + this.hide(); // Start hidden + } + + // Preset & button width & height: 16 + // Presets per row: 10 (2 rows by default, 3 rows if alpha is shown) + // Hue & Alpha slider heights: 10 + // Preview & Sat/value picker: Whatever height is left + // Preview width: 1/3 of picker padded width + // Sat/value picker: Whatever width is left from preview + // Padding among everything: 2 + public void updateAreas() { + int paddedX = this.getX() + 2; + int bottomY = this.getY() + this.getHeight() - 2; + int paddedWidth = this.getWidth() - 4; + + int presetsHeight = (16) * (this.showAlpha ? 3 : 2) + (this.showAlpha ? 4 : 2); + this.presetsArea.set(paddedX, bottomY - presetsHeight, paddedWidth, presetsHeight); + this.alphaArea.set(paddedX, presetsArea.getY() - 10 - 2, paddedWidth, this.showAlpha ? 10 : 0); + this.hueArea.set(paddedX, alphaArea.getY() - (showAlpha ? 10 + 2 : 0), paddedWidth, 10); + + int remainingHeight = this.hueArea.getY() - this.getY() - 4; + int remainingY = hueArea.getY() - remainingHeight - 2; + this.previewArea.set(paddedX, remainingY, paddedWidth / 3, remainingHeight); + int satValueWidth = this.getX() + this.getWidth() - previewArea.getX2() - 4; + this.satValueArea.set(previewArea.getX2() + 2, remainingY, satValueWidth, remainingHeight); + + for (int i = 0; i < this.formattingPresetAreas.size(); i++) { + PickerPreset preset = this.formattingPresetAreas.get(i); + int x = paddedX + ((i % 10) * 18); + int y = presetsArea.getY() + (18 * (i / 10)); + preset.set(x, y, 16, 16); + } + + if (this.showAlpha) { + for (int i = 0; i < this.alphaPresetAreas.size(); i++) { + PickerPreset preset = this.alphaPresetAreas.get(i); + int x = paddedX + ((i % 10) * 18); + int y = presetsArea.getY() + (18 * ((i / 10) + 2)); + preset.set(x, y, 16, 16); + } + } + + for (int i = 0; i < this.buttonAreas.size(); i++) { + // First button is confirm, second is cancel, so start from far right and shift left + PickerButton button = this.buttonAreas.get(i); + int x = this.getX() + this.getWidth() - (18 * (i + 1)); + int y = this.presetsArea.getY() + (18 * (this.showAlpha ? 2 : 1)); + button.set(x, y, 16, 16); + } + } + + // Position the thumbs based on the current color + public void updateAreaThumbs() { + this.hueArea.lerpThumbX(this.hue); + this.satValueArea.lerpThumbX(this.saturation); + this.satValueArea.lerpThumbY(1f - this.value); + if (this.showAlpha) this.alphaArea.lerpThumbX(this.alpha); + } + + // region R.E.P.O - Render, Extract, and Position Operations + @Override + protected void extractContents(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + // Background + graphics.blit(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE, this.getX(), this.getY(), 0, 0, this.getWidth(), this.getHeight(), 32, 32); + + // Render areas + this.extractPreviewArea(this.previewArea, graphics); + this.extractSatValueArea(this.satValueArea, graphics, mouseX, mouseY); + this.extractHueArea(this.hueArea, graphics, mouseX, mouseY); + if (showAlpha) this.extractAlphaArea(this.alphaArea, graphics, mouseX, mouseY); + for (PickerPreset preset : this.formattingPresetAreas) { + this.extractPresetArea(preset, graphics, false, mouseX, mouseY); + } + if (showAlpha) { + for (PickerPreset preset : this.alphaPresetAreas) { + this.extractPresetArea(preset, graphics, true, mouseX, mouseY); + } + } + for (PickerButton button : buttonAreas) { + this.extractButtonArea(button, graphics, mouseX, mouseY); + } + + // Render thumbs (to ensure they are above everything despite overlaps) + this.extractThumb(graphics, this.hueArea.getThumbX(), this.hueArea.getY() + 5, 6, 12, this.getHueColor()); + if (showAlpha) this.extractThumb(graphics, this.alphaArea.getThumbX(), this.alphaArea.getY() + 5, 6, 12, this.getColor(), true); + this.extractThumb(graphics, this.satValueArea.getThumbX(), this.satValueArea.getThumbY(), 8, 8, this.getColorNoAlpha()); + } + + public void extractPreviewArea(PickerArea area, GuiGraphicsExtractor graphics) { + // Alpha background texture (if needed) + if (this.showAlpha) GuiGraphicsUtil.drawPreciseTile(graphics, ALPHA_PREVIEW_TEXTURE, area.getX(), area.getY(), area.getX2(), area.getY2(), 8, 7); + // Current color + graphics.fill(area.getX(), area.getY(), area.getX2(), area.getY2(), this.getColor()); + } + + public void extractSatValueArea(PickerArea area, GuiGraphicsExtractor graphics, int mouseX, int mouseY) { + // White to current hue, left to right + GuiGraphicsUtil.extractHorizontalGradient(graphics, area.getX(), area.getY(), area.getX2(), area.getY2(), ColorUtil.WHITE, this.getHueColor()); + // Transparent to black, top to bottom + graphics.fillGradient(area.getX(), area.getY(), area.getX2(), area.getY2(), ColorUtil.TRANSPARENT, ColorUtil.BLACK); + // Outline + if (area.shouldOutline(mouseX, mouseY)) { + graphics.outline(area.getX(), area.getY(), area.getWidth(), area.getHeight(), ColorUtil.WHITE); + graphics.requestCursor(CursorTypes.POINTING_HAND); + } + } + + public void extractHueArea(PickerArea area, GuiGraphicsExtractor graphics, int mouseX, int mouseY) { + // Hue gradient, starting and ending on red + GuiGraphicsUtil.extractHueGradient(graphics, area.getX(), area.getY(), area.getX2(), area.getY2()); + // Outline + if (area.shouldOutline(mouseX, mouseY)) { + graphics.outline(area.getX(), area.getY(), area.getWidth(), area.getHeight(), ColorUtil.WHITE); + graphics.requestCursor(CursorTypes.POINTING_HAND); + } + } + + public void extractAlphaArea(PickerArea area, GuiGraphicsExtractor graphics, int mouseX, int mouseY) { + // Alpha background texture + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ALPHA_SLIDER_TEXTURE, area.getX(), area.getY(), area.getWidth(), area.getHeight()); + // Transparent to current color, left to right + GuiGraphicsUtil.extractHorizontalGradient(graphics, area.getX(), area.getY(), area.getX2(), area.getY2(), ColorUtil.TRANSPARENT, this.getColorNoAlpha()); + // Outline + if (area.shouldOutline(mouseX, mouseY)) { + graphics.outline(area.getX(), area.getY(), area.getWidth(), area.getHeight(), ColorUtil.WHITE); + graphics.requestCursor(CursorTypes.POINTING_HAND); + } + } + + public void extractPresetArea(PickerPreset preset, GuiGraphicsExtractor graphics, boolean alphaPreset, int mouseX, int mouseY) { + // Alpha background texture (if needed) + if (alphaPreset) graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ALPHA_PRESET_TEXTURE, preset.getX(), preset.getY(), preset.getWidth(), preset.getHeight()); + graphics.fill(preset.getX(), preset.getY(), preset.getX2(), preset.getY2(), preset.getPresetColor()); + // Alpha preset tooltip + if (alphaPreset) { + // I'll be honest I have no clue how this works, I just got lucky + String hex = String.format("%1$02X", ARGB.alpha(preset.getPresetColor())); + graphics.text(Minecraft.getInstance().font, hex, preset.getX() + 1, preset.getY() + 7, ColorUtil.WHITE); + } + // Outline + if (preset.shouldOutline(mouseX, mouseY)) { + graphics.outline(preset.getX(), preset.getY(), preset.getWidth(), preset.getHeight(), ColorUtil.WHITE); + graphics.requestCursor(CursorTypes.POINTING_HAND); + } + } + + public void extractButtonArea(PickerButton button, GuiGraphicsExtractor graphics, int mouseX, int mouseY) { + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, button.getTexture(mouseX, mouseY), button.getX() - button.getPadX(), button.getY() - button.getPadY(), button.getWidth() + button.getPadScale(), button.getHeight() + button.getPadScale()); + if (button.isMouseOver(mouseX, mouseY)) graphics.requestCursor(CursorTypes.POINTING_HAND); + } + + public void extractThumb(GuiGraphicsExtractor graphics, int thumbX, int thumbY, int thumbWidth, int thumbHeight, int thumbColor) { + this.extractThumb(graphics, thumbX, thumbY, thumbWidth, thumbHeight, thumbColor, false); + } + + public void extractThumb(GuiGraphicsExtractor graphics, int thumbX, int thumbY, int thumbWidth, int thumbHeight, int thumbColor, boolean alphaBackground) { + int halfWidth = thumbWidth / 2; + int halfHeight = thumbHeight / 2; + + int x = thumbX - halfWidth; + int y = thumbY - halfHeight; + int x2 = thumbX + halfWidth; + int y2 = thumbY + halfHeight; + + if (alphaBackground) graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ALPHA_THUMB_TEXTURE, x, y, thumbWidth, thumbHeight); + graphics.fill(x, y, x2, y2, thumbColor); + graphics.outline(x, y, thumbWidth, thumbHeight, ColorUtil.WHITE); + } + + @Override + protected void handleCursor(GuiGraphicsExtractor graphics) { + // NO-OP + } + + // endregion + + public void target(AbstractWidget widget, int initColor, boolean showAlpha, ColorSetter pickedColorListener) { + this.target(widget, initColor, showAlpha, false, pickedColorListener); + } + + /** + * Position & set up the Color Picker based on a widget and its needs. Positioning accounts for screen size, ensuring the color picker never goes off-screen. + * @param widget The Widget to position the Color Picker too. + * @param initColor The initial color the Color Picker should be, and should revert to if canceled/undone. + * @param showAlpha Whether the alpha should be editable, and the alpha slider visible. + * @param rightAligned If the Color Picker Widget should align itself to the right side of the widget instead of the left side . + * @param pickedColorListener What to do with the picked color, as an integer. + */ + public void target(AbstractWidget widget, int initColor, boolean showAlpha, boolean rightAligned, ColorSetter pickedColorListener) { + Screen screen = Minecraft.getInstance().screen; + this.visible = true; + this.active = true; + this.targetElement = widget; + + // Ensure at least 2 pixels of padding between screen edges + int x = Math.min(screen.width - this.getWidth() - 2, widget.getX()); + if (rightAligned) x = Math.max(2, widget.getX() + widget.getWidth() - this.getWidth() + 2); + int y = widget.getY() + widget.getHeight(); // Beneath widget + if (y + this.getHeight() > screen.height - 2) y = widget.getY() - this.getHeight(); // Above widget + this.setX(x); + this.setY(y); + + this.setPrevColor(initColor); + this.setColor(initColor); + + this.showAlpha = showAlpha; + this.setHeight(this.showAlpha ? DEFAULT_ALPHA_HEIGHT : DEFAULT_HEIGHT); + + this.pickedColorListener = pickedColorListener; + this.presetListener = preset -> { // Default preset behaviour, just set the picker color + if (preset.isAlphaPreset()) { + this.alpha = preset.getAlpha(); + this.updateAreaThumbs(); + } else { + this.setColor(preset.getPresetColor(), false); + } + }; + this.confirmListener = null; + + this.updateAreas(); + this.updateAreaThumbs(); + } + + public void setPresetListener(PickerPresetListener presetListener) { + this.presetListener = presetListener; + } + + public void setConfirmListener(Consumer confirmListener) { + this.confirmListener = confirmListener; + } + + public void setColor(int color) { + this.setColor(color, true); + } + + /** + * Set the Color Picker's color from an integer. Intended for something like the {@link dev.hephaestus.glowcase.client.gui.widget.ingame.color.HexColorEditBox} when a user edits the HEX string, and the color picker needs to reflect that update. + */ + public void setColor(int color, boolean setAlpha) { + float[] HSBA = ColorUtil.ARGBToHSBA(color); + this.hue = HSBA[0]; + this.saturation = HSBA[1]; + this.value = HSBA[2]; + if (setAlpha) this.alpha = HSBA[3]; + + this.updateAreaThumbs(); + } + + /** + * The color to revert/undo to in case the user presses cancel. + */ + public void setPrevColor(int color) { + float[] HSBA = ColorUtil.ARGBToHSBA(color); + this.prevHue = HSBA[0]; + this.prevSaturation = HSBA[1]; + this.prevValue = HSBA[2]; + this.prevAlpha = HSBA[3]; + } + + public void confirm() { + if (this.confirmListener != null) this.confirmListener.accept(this.getColor()); + this.hide(); + } + + public void cancel() { + int prevColor = ColorUtil.HSBAtoARGB(this.prevHue, this.prevSaturation, this.prevValue, this.showAlpha ? this.prevAlpha : 1f); + this.setColor(prevColor); + this.onColorPicked(); + this.hide(); + } + + public void hide() { + this.active = false; + this.visible = false; + this.setFocused(false); + + this.pickedColorListener = null; + this.presetListener = null; + this.confirmListener = null; + } + + @Override + public void onClick(@NonNull MouseButtonEvent event, boolean doubleClick) { + this.tryClickAreas(event); + } + + @Override + protected void onDrag(@NonNull MouseButtonEvent event, double dx, double dy) { + this.tryClickAreas(event); + } + + @Override + public void onRelease(@NonNull MouseButtonEvent event) { + if (this.currentClickedArea != null) { + this.currentClickedArea.mouseReleased(); + this.currentClickedArea = null; + } + } + + public void tryClickAreas(MouseButtonEvent event) { + double x = event.x(); + double y = event.y(); + + // Current clicked area gets full click priority + if (this.currentClickedArea != null) { + if (this.currentClickedArea.mouseClicked(x, y)) { + this.onColorPicked(); + } + return; + } + + // No area is currently clicked, so search for one to click, and return if found + for (PickerArea area : clickableAreas) { + if (area.mouseClicked(x, y)) { + this.currentClickedArea = area; + this.onColorPicked(); + return; + } + } + + for (PickerPreset preset : formattingPresetAreas) { + if (preset.mouseClicked(x, y)) { + this.currentClickedArea = preset; + this.onColorPicked(); + return; + } + } + + for (PickerPreset preset : alphaPresetAreas) { + if (preset.mouseClicked(x, y)) { + this.currentClickedArea = preset; + this.onColorPicked(); + return; + } + } + + for (PickerButton button : buttonAreas) { + if (button.mouseClicked(x, y)) { + this.currentClickedArea = button; + return; + } + } + } + + public void onPresetClick(PickerPreset preset) { + if (this.presetListener != null) { + this.presetListener.onPresetClick(preset); + } + } + + public void onColorPicked() { + if (this.pickedColorListener != null) { + int pickedColor = this.showAlpha ? this.getColor() : this.getColorNoAlpha(); + this.pickedColorListener.set(pickedColor); + } + } + + /** + * @return The Color Picker's picked color, as an integer + */ + public int getColor() { + float returnedAlpha = this.showAlpha ? this.alpha : 1f; + return ColorUtil.HSBAtoARGB(this.hue, this.saturation, this.value, returnedAlpha); + } + + public int getColorNoAlpha() { + return ColorUtil.HSBtoRGB(this.hue, this.saturation, this.value); + } + + public int getHueColor() { + return ColorUtil.HSBtoRGB(this.hue, 1f, 1f); + } + + @Override + public void onPress(@NonNull InputWithModifiers input) { + // NO-OP + } + + @Override + protected void updateWidgetNarration(@NonNull NarrationElementOutput output) { + // NO-OP + } + + public static class Builder { + private final ColorPickerIncludedScreen screen; + + public Builder(ColorPickerIncludedScreen screen) { + this.screen = screen; + } + + public ColorPickerWidget build() { + return new ColorPickerWidget(screen, 0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT, Component.nullToEmpty("")); + } + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerArea.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerArea.java new file mode 100644 index 00000000..6ebc1289 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerArea.java @@ -0,0 +1,160 @@ +package dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker; + +import net.minecraft.util.Mth; +import org.jetbrains.annotations.Nullable; + +/** + * Clickable area within a {@link ColorPickerWidget}, such as the hue slider, saturation/light picker, alpha slider, and presets.

+ * Allows for definable position & dimensions for a clickable area, and a listener for what to do when clicked (e.g. set ColorPicker hue/sat./light/alpha). + * + * @see ColorPickerWidget + */ +public class PickerArea { + private final ColorPickerWidget colorPicker; + @Nullable + private final AreaSetter xLerpSetter; + @Nullable + private final AreaSetter yLerpSetter; + private final boolean clickable; + private int x, y, width, height; + private int thumbX, thumbY; + private boolean hasMouseDown = false; + + public PickerArea(ColorPickerWidget colorPicker, boolean clickable) { + this(colorPicker, false, null, null); + } + + public PickerArea(ColorPickerWidget colorPicker, @Nullable AreaSetter xLerp) { + this(colorPicker, true, xLerp, null); + } + + public PickerArea(ColorPickerWidget colorPicker, @Nullable AreaSetter xLerp, @Nullable AreaSetter yLerp) { + this(colorPicker, true, xLerp, yLerp); + } + + public PickerArea(ColorPickerWidget colorPicker, boolean clickable, @Nullable AreaSetter xLerp, @Nullable AreaSetter yLerp) { + this.colorPicker = colorPicker; + this.clickable = clickable; + this.xLerpSetter = xLerp; + this.yLerpSetter = yLerp; + } + + public boolean mouseClicked(double mouseX, double mouseY) { + if (!this.clickable) return false; + + if (isMouseOver(mouseX, mouseY)) { + this.hasMouseDown = true; + } + + if (this.hasMouseDown) { + // Assume horizontal / x movement is possible + if (this.xLerpSetter != null) { + this.thumbX = (int) Mth.clamp(mouseX, this.getX(), this.getX2()); + + float xLerp = calcLerp(mouseX, this.getX(), this.getX2()); + this.xLerpSetter.set(xLerp); + } + + // Assume vertical / y movement is possible + if (this.yLerpSetter != null) { + this.thumbY = (int) Mth.clamp(mouseY, this.getY(), this.getY2()); + + float yLerp = calcLerp(mouseY, this.getY(), this.getY2()); + this.yLerpSetter.set(yLerp); + } + } + + return this.hasMouseDown; + } + + public void mouseReleased() { + this.hasMouseDown = false; + } + + public boolean isMouseOver(double mouseX, double mouseY) { + return (mouseY > this.getY() && mouseY < this.getY2()) + && (mouseX > this.getX() && mouseX < this.getX2()); + } + + public boolean shouldOutline(int mouseX, int mouseY) { + // Outline if mouse down, or if the mouse over AND no other area has the mouse down + return this.hasMouseDown() || (this.isMouseOver(mouseX, mouseY) && this.getColorPicker().currentClickedArea == null); + } + + private float calcLerp(double mousePos, int minPos, int maxPos) { + if (mousePos < minPos) return 0f; + if (mousePos > maxPos) return 1f; + float width = maxPos - minPos; + return Mth.clamp(((float) mousePos - minPos) / width, 0f, 1f); + } + + public void lerpThumbX(float lerp) { + this.thumbX = (int) Mth.clamp(this.getX() + (this.getWidth() * lerp), this.getX(), this.getX2()); + } + + public void lerpThumbY(float lerp) { + this.thumbY = (int) Mth.clamp(this.getY() + (this.getHeight() * lerp), this.getY(), this.getY2()); + } + + // region Getters & Setters + public void set(int x, int y, int width, int height) { + this.setPos(x, y); + this.setDimensions(width, height); + } + + public void setPos(int x, int y) { + this.x = x; + this.y = y; + } + + public void setDimensions(int width, int height) { + this.width = width; + this.height = height; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getX2() { + return this.getX() + this.getWidth(); + } + + public int getY2() { + return this.getY() + this.getHeight(); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getThumbX() { + return thumbX; + } + + public int getThumbY() { + return thumbY; + } + + public boolean hasMouseDown() { + return this.hasMouseDown; + } + + public ColorPickerWidget getColorPicker() { + return colorPicker; + } + + // endregion + + public interface AreaSetter { + void set(float value); + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerButton.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerButton.java new file mode 100644 index 00000000..9b467aed --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerButton.java @@ -0,0 +1,60 @@ +package dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker; + +import net.minecraft.resources.Identifier; + +import java.util.List; + +public class PickerButton extends PickerArea { + private static final Identifier CONFIRM_TEXTURE = Identifier.withDefaultNamespace("pending_invite/accept"); + private static final Identifier CONFIRM_HIGHLIGHTED_TEXTURE = Identifier.withDefaultNamespace("pending_invite/accept_highlighted"); + private static final Identifier CANCEL_TEXTURE = Identifier.withDefaultNamespace("pending_invite/reject"); + private static final Identifier CANCEL_HIGHLIGHTED_TEXTURE = Identifier.withDefaultNamespace("pending_invite/reject_highlighted"); + + private final Identifier texture; + private final Identifier hoverTexture; + private final Runnable clickListener; + // Padding on the texture rendering because the icons aren't (and physically can't be) centered on the pixel grid + private final int padX, padY, padScale; + + public static List createButtons(ColorPickerWidget colorPickerWidget) { + return List.of( + new PickerButton(colorPickerWidget, CONFIRM_TEXTURE, CONFIRM_HIGHLIGHTED_TEXTURE, 1, 1, 2, colorPickerWidget::confirm), + new PickerButton(colorPickerWidget, CANCEL_TEXTURE, CANCEL_HIGHLIGHTED_TEXTURE, 2, 1, 3, colorPickerWidget::cancel) + ); + } + + public PickerButton(ColorPickerWidget colorPicker, Identifier texture, Identifier hoverTexture, int padX, int padY, int padScale, Runnable clickListener) { + super(colorPicker, true, null, null); + this.texture = texture; + this.hoverTexture = hoverTexture; + this.padX = padX; + this.padY = padY; + this.padScale = padScale; + this.clickListener = clickListener; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY) { + if (this.isMouseOver(mouseX, mouseY)) { + this.clickListener.run(); + return true; + } + return super.mouseClicked(mouseX, mouseY); + } + + public Identifier getTexture(int mouseX, int mouseY) { + return this.isMouseOver(mouseX, mouseY) ? this.hoverTexture : this.texture; + } + + public int getPadX() { + return padX; + } + + public int getPadY() { + return padY; + } + + public int getPadScale() { + return padScale; + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerPreset.java b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerPreset.java new file mode 100644 index 00000000..e76312aa --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/widget/ingame/color/picker/PickerPreset.java @@ -0,0 +1,79 @@ +package dev.hephaestus.glowcase.client.gui.widget.ingame.color.picker; + +import net.minecraft.ChatFormatting; +import net.minecraft.util.ARGB; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class PickerPreset extends PickerArea { + public static final ChatFormatting[] FORMATTING_PRESETS = new ChatFormatting[]{ + ChatFormatting.DARK_RED, ChatFormatting.RED, ChatFormatting.GOLD, ChatFormatting.YELLOW, + ChatFormatting.GREEN, ChatFormatting.DARK_GREEN, ChatFormatting.AQUA, ChatFormatting.DARK_AQUA, + ChatFormatting.BLUE, ChatFormatting.DARK_BLUE, ChatFormatting.LIGHT_PURPLE, ChatFormatting.DARK_PURPLE, + ChatFormatting.WHITE, ChatFormatting.GRAY, ChatFormatting.DARK_GRAY, ChatFormatting.BLACK + }; + + public static final Integer[] ALPHA_PRESETS = new Integer[]{ + 0x00000000, 0x40000000, 0x55000000, 0x77000000, 0x99000000, 0xAA000000, 0xCC000000, 0xFF000000 + }; + + public static List createFormattingPresets(ColorPickerWidget colorPicker) { + List presets = new ArrayList<>(); + for (ChatFormatting formattingPreset : FORMATTING_PRESETS) { + //noinspection DataFlowIssue - We can assume that the presets in this array have their color ints + presets.add(new PickerPreset(colorPicker, ARGB.color(1f, formattingPreset.getColor()), formattingPreset, false)); + } + return presets; + } + + public static List createAlphaPresets(ColorPickerWidget colorPicker) { + List presets = new ArrayList<>(); + for (Integer alphaColor : ALPHA_PRESETS) { + presets.add(new PickerPreset(colorPicker, alphaColor, null, true)); + } + return presets; + } + + private final int presetColor; + @Nullable + private final ChatFormatting presetFormatting; + private final boolean isAlphaPreset; + + public PickerPreset(ColorPickerWidget colorPicker, int presetColor, @Nullable ChatFormatting presetFormatting, boolean isAlphaPreset) { + super(colorPicker, true, null, null); + this.presetColor = presetColor; + this.presetFormatting = presetFormatting; + this.isAlphaPreset = isAlphaPreset; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY) { + if(super.mouseClicked(mouseX, mouseY)) { + this.getColorPicker().onPresetClick(this); + return true; + } + return false; + } + + public int getPresetColor() { + if (this.presetFormatting != null && this.presetFormatting.isColor() && this.presetFormatting.getColor() != null) { + // Needed otherwise it's transparent for some reason + return ARGB.color(1f, this.presetFormatting.getColor()); + } + return this.presetColor; + } + + public @Nullable ChatFormatting getPresetFormatting() { + return presetFormatting; + } + + public boolean isAlphaPreset() { + return isAlphaPreset; + } + + public float getAlpha() { + return ARGB.alphaFloat(this.presetColor); + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/util/ColorUtil.java b/src/main/java/dev/hephaestus/glowcase/client/util/ColorUtil.java index 2649758e..570b7d2f 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/util/ColorUtil.java +++ b/src/main/java/dev/hephaestus/glowcase/client/util/ColorUtil.java @@ -2,6 +2,7 @@ import com.mojang.serialization.DataResult; import net.minecraft.ChatFormatting; +import net.minecraft.util.ARGB; /** * @author Ampflower @@ -16,7 +17,16 @@ public final class ColorUtil { public static final int RGB_MASK = (1 << RGB_BITS) - 1; public static final int ALPHA_MASK = CHANNEL_MASK << RGB_BITS; + + public static final int RED = 0xFFFF0000; + public static final int YELLOW = 0xFFFFFF00; + public static final int GREEN = 0xFF00FF00; + public static final int CYAN = 0xFF00FFFF; + public static final int BLUE = 0xFF0000FF; + public static final int MAGENTA = 0xFFFF00FF; + public static final int WHITE = ALPHA_MASK | RGB_MASK; + public static final int BLACK = 0xFF000000; public static final int TRANSPARENT = 0; public static int transferAlpha(int oldColor, int newColor) { @@ -80,6 +90,10 @@ public static String toAlphaHex(int color) { return String.format("#%1$08X", color); } + public static String toHex(int color) { + return "#" + String.format("%1$06X", color & 0x00FFFFFF); + } + private static int upcast(int color) { int r = (color & 0x000F) * 0x00000011; int g = (color & 0x00F0) * 0x00000110; @@ -87,4 +101,120 @@ private static int upcast(int color) { int a = (color & 0xF000) * 0x00011000; return r | g | b | a; } + + /** + * Convert an integer color to hue, saturation, brightness/value, and alpha. + */ + public static float[] ARGBToHSBA(int color) { + int red = ARGB.red(color); + int green = ARGB.green(color); + int blue = ARGB.blue(color); + float[] HSBA = new float[4]; + RGBtoHSB(red, green, blue, HSBA); + + HSBA[3] = ARGB.alphaFloat(color); + return HSBA; + } + + /** + * Converts RGB to HSB (hue, saturation, brightness/value).

+ * + * Custom method here used in favor of java.awt.Color's to prevent possible crashes on Mac. + * @see java.awt.Color#RGBtoHSB(int, int, int, float[]) + */ + public static float[] RGBtoHSB(int r, int g, int b, float[] hsbvals) { + float hue, saturation, brightness; + if (hsbvals == null) { + hsbvals = new float[3]; + } + int cmax = (r > g) ? r : g; + if (b > cmax) cmax = b; + int cmin = (r < g) ? r : g; + if (b < cmin) cmin = b; + + brightness = ((float) cmax) / 255.0f; + if (cmax != 0) + saturation = ((float) (cmax - cmin)) / ((float) cmax); + else + saturation = 0; + if (saturation == 0) + hue = 0; + else { + float redc = ((float) (cmax - r)) / ((float) (cmax - cmin)); + float greenc = ((float) (cmax - g)) / ((float) (cmax - cmin)); + float bluec = ((float) (cmax - b)) / ((float) (cmax - cmin)); + if (r == cmax) + hue = bluec - greenc; + else if (g == cmax) + hue = 2.0f + redc - bluec; + else + hue = 4.0f + greenc - redc; + hue = hue / 6.0f; + if (hue < 0) + hue = hue + 1.0f; + } + hsbvals[0] = hue; + hsbvals[1] = saturation; + hsbvals[2] = brightness; + return hsbvals; + } + + /** + * Convert hue, saturation, brightness/value, and alpha to an integer of ARGB. + */ + public static int HSBAtoARGB(float hue, float saturation, float brightness, float alpha) { + return ARGB.color(alpha, HSBtoRGB(hue, saturation,brightness)); + } + + /** + * Converts HSB (hue, saturation, brightness/value) to RGB.

+ * + * Custom method here used in favor of java.awt.Color's to prevent possible crashes on Mac. + * @see java.awt.Color#HSBtoRGB(float, float, float) + */ + public static int HSBtoRGB(float hue, float saturation, float brightness) { + int r = 0, g = 0, b = 0; + if (saturation == 0) { + r = g = b = (int) (brightness * 255.0f + 0.5f); + } else { + float h = (hue - (float)Math.floor(hue)) * 6.0f; + float f = h - (float)java.lang.Math.floor(h); + float p = brightness * (1.0f - saturation); + float q = brightness * (1.0f - saturation * f); + float t = brightness * (1.0f - (saturation * (1.0f - f))); + switch ((int) h) { + case 0: + r = (int) (brightness * 255.0f + 0.5f); + g = (int) (t * 255.0f + 0.5f); + b = (int) (p * 255.0f + 0.5f); + break; + case 1: + r = (int) (q * 255.0f + 0.5f); + g = (int) (brightness * 255.0f + 0.5f); + b = (int) (p * 255.0f + 0.5f); + break; + case 2: + r = (int) (p * 255.0f + 0.5f); + g = (int) (brightness * 255.0f + 0.5f); + b = (int) (t * 255.0f + 0.5f); + break; + case 3: + r = (int) (p * 255.0f + 0.5f); + g = (int) (q * 255.0f + 0.5f); + b = (int) (brightness * 255.0f + 0.5f); + break; + case 4: + r = (int) (t * 255.0f + 0.5f); + g = (int) (p * 255.0f + 0.5f); + b = (int) (brightness * 255.0f + 0.5f); + break; + case 5: + r = (int) (brightness * 255.0f + 0.5f); + g = (int) (p * 255.0f + 0.5f); + b = (int) (q * 255.0f + 0.5f); + break; + } + } + return 0xff000000 | (r << 16) | (g << 8) | (b << 0); + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/util/GuiGraphicsUtil.java b/src/main/java/dev/hephaestus/glowcase/client/util/GuiGraphicsUtil.java new file mode 100644 index 00000000..ecfd4ac9 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/util/GuiGraphicsUtil.java @@ -0,0 +1,143 @@ +package dev.hephaestus.glowcase.client.util; + +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.state.gui.GuiElementRenderState; +import net.minecraft.client.renderer.texture.AbstractTexture; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.data.AtlasIds; +import net.minecraft.resources.Identifier; +import org.joml.Matrix3x2fStack; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class GuiGraphicsUtil { + public static final int[] HUE_GRADIENT_COLORS = new int[]{ + ColorUtil.RED, ColorUtil.YELLOW, ColorUtil.GREEN, ColorUtil.CYAN, ColorUtil.BLUE, ColorUtil.MAGENTA, ColorUtil.RED + }; + + /** + * Fill a horizontal gradient between two colors. + * @apiNote Do not replace the position floats with integers! {@link GuiGraphicsUtil#extractHueGradient(GuiGraphicsExtractor, int, int, int, int)} relies on float position to ensure its gradients can be fully rendered across any specified integer length, instead of requiring the length to be perfectly divisible by a specific number (7 I think) or else it leaves an empty gap. + * @see GuiGraphicsExtractor#fillGradient + */ + public static void extractHorizontalGradient(GuiGraphicsExtractor graphics, float x0, float y0, float x1, float y1, int startColor, int endColor) { + graphics.guiRenderState.addGuiElement( + new GuiElementRenderState() { + @Override + public void buildVertices(@NonNull VertexConsumer vertexConsumer) { + Matrix3x2fStack matrix = graphics.pose(); + vertexConsumer.addVertexWith2DPose(matrix, x0, y0).setColor(startColor); + vertexConsumer.addVertexWith2DPose(matrix, x0, y1).setColor(startColor); + vertexConsumer.addVertexWith2DPose(matrix, x1, y1).setColor(endColor); + vertexConsumer.addVertexWith2DPose(matrix, x1, y0).setColor(endColor); + } + + @Override + public @NonNull RenderPipeline pipeline() { + return RenderPipelines.GUI; + } + + @Override + public @NonNull TextureSetup textureSetup() { + return TextureSetup.noTexture(); + } + + @Override + public @Nullable ScreenRectangle scissorArea() { + return null; + } + + @Override + public @NonNull ScreenRectangle bounds() { + return new ScreenRectangle((int) x0, (int) y0, (int) (x1 - x0), (int) (y1 - y0)).transformMaxBounds(graphics.pose()); + } + } + ); + } + + /** + * Render a hue gradient, starting and ending on red. + * @apiNote Do not replace the width float with an integer! See {@link GuiGraphicsUtil#extractHorizontalGradient(GuiGraphicsExtractor, float, float, float, float, int, int)}'s API Note for details. + */ + public static void extractHueGradient(GuiGraphicsExtractor graphics, int x0, int y0, int x1, int y1) { + float width = x1 - x0; + int colorCount = HUE_GRADIENT_COLORS.length - 1; + for (int i = 0; i < colorCount; i++) { + extractHorizontalGradient( + graphics, + x0 + (width / colorCount * i), y0, + x0 + (width / colorCount * (i + 1)), y1, + HUE_GRADIENT_COLORS[i], HUE_GRADIENT_COLORS[i + 1] + ); + } + } + + /** + * Tile a texture across an area with precise texture placement. Allows for better tiling of textures within an integer bounds than otherwise possible. + */ + public static void drawPreciseTile(GuiGraphicsExtractor graphics, Identifier spriteId, int x0, int y0, int x1, int y1, float rows, float columns) { + TextureAtlasSprite sprite = Minecraft.getInstance().getAtlasManager().getAtlasOrThrow(AtlasIds.GUI).getSprite(spriteId); + AbstractTexture spriteTexture = Minecraft.getInstance().getTextureManager().getTexture(sprite.atlasLocation()); + float width = x1 - x0; + float height = y1 - y0; + float tileWidth = width / columns; + float tileHeight = height / rows; + for (int iX = 0; iX < columns; iX++) { + float tileX = x0 + (tileWidth * iX); + for (int iY = 0; iY < rows; iY++) { + float tileY = y0 + (tileHeight * iY); + drawPreciseTexture( + graphics, spriteTexture, + tileX, tileY, tileX + tileWidth, tileY + tileHeight, + sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1() + ); + } + } + } + + /** + * Draw a texture with precise placement beyond the normal integer grid of GUI rendering. + */ + public static void drawPreciseTexture(GuiGraphicsExtractor graphics, AbstractTexture texture, float x0, float y0, float x1, float y1, float u0, float u1, float v0, float v1) { + graphics.guiRenderState.addGuiElement( + new GuiElementRenderState() { + @Override + public void buildVertices(@NonNull VertexConsumer vertexConsumer) { + Matrix3x2fStack matrix = graphics.pose(); + int color = -1; + vertexConsumer.addVertexWith2DPose(matrix, x0, y0).setUv(u0, v0).setColor(color); + vertexConsumer.addVertexWith2DPose(matrix, x0, y1).setUv(u0, v1).setColor(color); + vertexConsumer.addVertexWith2DPose(matrix, x1, y1).setUv(u1, v1).setColor(color); + vertexConsumer.addVertexWith2DPose(matrix, x1, y0).setUv(u1, v0).setColor(color); + } + + @Override + public @NonNull RenderPipeline pipeline() { + return RenderPipelines.GUI_TEXTURED; + } + + @Override + public @NonNull TextureSetup textureSetup() { + return TextureSetup.singleTexture(texture.getTextureView(), texture.getSampler()); + } + + @Override + public @Nullable ScreenRectangle scissorArea() { + return null; + } + + @Override + public @NonNull ScreenRectangle bounds() { + return new ScreenRectangle((int) x0, (int) y0, (int) (x1 - x0), (int) (y1 - y0)).transformMaxBounds(graphics.pose()); + } + } + ); + } + +} diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index cd13ddd9..007fc95a 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -101,8 +101,10 @@ "gui.glowcase.volume": "Volume", "gui.glowcase.pitch": "Pitch", "gui.glowcase.yaw": "Yaw", - "gui.glowcase.color": "Color", - "gui.glowcase.background_color": "Background Color", + "gui.glowcase.color_argb": "Color (ARGB)", + "gui.glowcase.color": "Color (RGB)", + "gui.glowcase.background_color_argb": "Background Color (ARGB)", + "gui.glowcase.background_color": "Background Color (RGB)", "gui.glowcase.repeat_delay": "Repeat Delay", "gui.glowcase.distance": "Distance", "gui.glowcase.sound_category": "%s", @@ -179,5 +181,6 @@ "gui.glowcase.render_distance_value.infinite": "Render Distance: Infinite", "gui.glowcase.text_alignment": "Text Alignment", "gui.glowcase.text_shadow": "Text Shadow", - "gui.glowcase.more": "More..." + "gui.glowcase.more": "More...", + "gui.glowcase.extra_properties": "Extra Properties" } diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preset.png b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preset.png new file mode 100644 index 00000000..137d044f Binary files /dev/null and b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preset.png differ diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preset.png.mcmeta b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preset.png.mcmeta new file mode 100644 index 00000000..920b97f9 --- /dev/null +++ b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preset.png.mcmeta @@ -0,0 +1,10 @@ +{ + "gui": { + "scaling": { + "type": "tile", + "width": 16, + "height": 16, + "border": 2 + } + } +} diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preview.png b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preview.png new file mode 100644 index 00000000..137d044f Binary files /dev/null and b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preview.png differ diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preview.png.mcmeta b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preview.png.mcmeta new file mode 100644 index 00000000..fdc0c74a --- /dev/null +++ b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_preview.png.mcmeta @@ -0,0 +1,10 @@ +{ + "gui": { + "scaling": { + "type": "tile", + "width": 20, + "height": 20, + "border": 2 + } + } +} diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_slider.png b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_slider.png new file mode 100644 index 00000000..137d044f Binary files /dev/null and b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_slider.png differ diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_slider.png.mcmeta b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_slider.png.mcmeta new file mode 100644 index 00000000..3fdfad94 --- /dev/null +++ b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_slider.png.mcmeta @@ -0,0 +1,10 @@ +{ + "gui": { + "scaling": { + "type": "tile", + "width": 10, + "height": 10, + "border": 2 + } + } +} diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_thumb.png b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_thumb.png new file mode 100644 index 00000000..137d044f Binary files /dev/null and b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_thumb.png differ diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_thumb.png.mcmeta b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_thumb.png.mcmeta new file mode 100644 index 00000000..0382014c --- /dev/null +++ b/src/main/resources/assets/glowcase/textures/gui/sprites/color_picker/alpha_thumb.png.mcmeta @@ -0,0 +1,10 @@ +{ + "gui": { + "scaling": { + "type": "tile", + "width": 12, + "height": 12, + "border": 2 + } + } +} diff --git a/src/main/resources/assets/glowcase/textures/gui/sprites/properties.png b/src/main/resources/assets/glowcase/textures/gui/sprites/properties.png new file mode 100644 index 00000000..46df3000 Binary files /dev/null and b/src/main/resources/assets/glowcase/textures/gui/sprites/properties.png differ