diff options
Diffstat (limited to 'src/main/java/dev/isxander/yacl3/gui')
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java | 38 | ||||
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/YACLScreen.java | 37 | ||||
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/controllers/ColorController.java | 133 | ||||
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java | 459 | ||||
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/controllers/ControllerPopupWidget.java | 39 | ||||
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/controllers/PopupControllerScreen.java | 70 | ||||
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/image/YACLImageReloadListener.java | 3 | ||||
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java | 22 | ||||
-rw-r--r-- | src/main/java/dev/isxander/yacl3/gui/utils/YACLRenderHelper.java (renamed from src/main/java/dev/isxander/yacl3/gui/utils/ButtonTextureRenderer.java) | 13 |
9 files changed, 794 insertions, 20 deletions
diff --git a/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java b/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java index 6f92749..1f7c29b 100644 --- a/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java +++ b/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java @@ -1,7 +1,8 @@ package dev.isxander.yacl3.gui; +import com.mojang.blaze3d.vertex.VertexConsumer; import dev.isxander.yacl3.api.utils.Dimension; -import dev.isxander.yacl3.gui.utils.ButtonTextureRenderer; +import dev.isxander.yacl3.gui.utils.YACLRenderHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -9,8 +10,10 @@ import net.minecraft.client.gui.components.Renderable; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.sounds.SoundEvents; +import org.joml.Matrix4f; import java.awt.Color; @@ -75,7 +78,7 @@ public abstract class AbstractWidget implements GuiEventListener, Renderable, Na int width = x2 - x1; int height = y2 - y1; - ButtonTextureRenderer.render(graphics, x1, y1, width, height, enabled, hovered); + YACLRenderHelper.renderButtonTexture(graphics, x1, y1, width, height, enabled, hovered); } protected void drawOutline(GuiGraphics graphics, int x1, int y1, int x2, int y2, int width, int color) { @@ -85,6 +88,37 @@ public abstract class AbstractWidget implements GuiEventListener, Renderable, Na graphics.fill(x1, y1, x1 + width, y2, color); } + protected void fillSidewaysGradient(GuiGraphics graphics, int x1, int y1, int x2, int y2, int startColor, int endColor) { + //Fills a gradient, left to right + //Uses practically the same method as the GuiGraphics class, but with the x/y moved + //Has a custom "z" value in case needed for later + VertexConsumer vertex = graphics.bufferSource().getBuffer(RenderType.gui()); + Matrix4f matrix4f = graphics.pose().last().pose(); + vertex.vertex(matrix4f, x1, y1, 0).color(startColor).endVertex(); + vertex.vertex(matrix4f, x1, y2, 0).color(startColor).endVertex(); + vertex.vertex(matrix4f, x2, y2, 0).color(endColor).endVertex(); + vertex.vertex(matrix4f, x2, y1, 0).color(endColor).endVertex(); + } + + + protected void drawRainbowGradient(GuiGraphics graphics, int x1, int y1, int x2, int y2) { + //Draws a rainbow gradient, left to right + 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()}; //all the colors in the gradient + int width = x2 - x1; + int maxColors = colors.length - 1; + for (int color = 0; color < maxColors; color++) { + //First checks if the final color is being rendered, if true -> uses x2 int instead of x1 + //if false -> it adds the width divided by the max colors multiplied by the current color plus one to the x1 int + //the x2 int for the fillSidewaysGradient is the same formula, excluding the additional plus one. + //The gradient colors is determined by the color int and the color int plus one, which is why red is in the colors array twice + fillSidewaysGradient(graphics, + x1 + (width / maxColors * color), y1, + color == maxColors - 1 ? x2 : x1 + (width / maxColors * (color + 1)), y2, + colors[color], colors[color + 1]); + } + } + protected int multiplyColor(int hex, float amount) { Color color = new Color(hex, true); diff --git a/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java b/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java index ec70d60..1d3be9f 100644 --- a/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java +++ b/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java @@ -7,19 +7,21 @@ import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.utils.Dimension; import dev.isxander.yacl3.api.utils.MutableDimension; import dev.isxander.yacl3.api.utils.OptionUtils; -import dev.isxander.yacl3.gui.tab.ScrollableNavigationBar; +import dev.isxander.yacl3.gui.controllers.PopupControllerScreen; +import dev.isxander.yacl3.gui.controllers.ControllerPopupWidget; import dev.isxander.yacl3.gui.tab.ListHolderWidget; +import dev.isxander.yacl3.gui.tab.ScrollableNavigationBar; import dev.isxander.yacl3.gui.tab.TabExt; import dev.isxander.yacl3.gui.utils.GuiUtils; import dev.isxander.yacl3.impl.utils.YACLConstants; import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.MultiLineLabel; import net.minecraft.client.gui.components.Tooltip; -import net.minecraft.client.gui.components.tabs.Tab; import net.minecraft.client.gui.components.tabs.TabManager; import net.minecraft.client.gui.components.tabs.TabNavigationBar; import net.minecraft.client.gui.navigation.ScreenRectangle; @@ -34,7 +36,6 @@ import org.jetbrains.annotations.Nullable; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; public class YACLScreen extends Screen { @@ -52,6 +53,9 @@ public class YACLScreen extends Screen { private boolean pendingChanges; + public ControllerPopupWidget<?> currentPopupController = null; + public boolean popupControllerVisible = false; + public YACLScreen(YetAnotherConfigLib config, Screen parent) { super(config.title()); this.config = config; @@ -87,6 +91,33 @@ public class YACLScreen extends Screen { config.initConsumer().accept(this); } + public void addPopupControllerWidget(ControllerPopupWidget<?> controllerPopupWidget) { + + //Safety check for the color picker + if (currentPopupController != null) { + clearPopupControllerWidget(); + } + + currentPopupController = controllerPopupWidget; + popupControllerVisible = true; + + OptionListWidget optionListWidget = null; + if(this.tabNavigationBar.getTabManager().getCurrentTab() instanceof CategoryTab categoryTab) { + optionListWidget = categoryTab.optionList.getList(); + } + if(optionListWidget != null) { + this.minecraft.setScreen(new PopupControllerScreen(this, controllerPopupWidget)); + } + } + + public void clearPopupControllerWidget() { + if(Minecraft.getInstance().screen instanceof PopupControllerScreen popupControllerScreen) { + popupControllerScreen.onClose(); + } + popupControllerVisible = false; + currentPopupController = null; + } + /*? if <=1.20.4 {*/ @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { diff --git a/src/main/java/dev/isxander/yacl3/gui/controllers/ColorController.java b/src/main/java/dev/isxander/yacl3/gui/controllers/ColorController.java index 56e6d30..3c0a5fc 100644 --- a/src/main/java/dev/isxander/yacl3/gui/controllers/ColorController.java +++ b/src/main/java/dev/isxander/yacl3/gui/controllers/ColorController.java @@ -8,6 +8,7 @@ import dev.isxander.yacl3.gui.AbstractWidget; import dev.isxander.yacl3.gui.YACLScreen; import dev.isxander.yacl3.gui.controllers.string.IStringController; import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; +import dev.isxander.yacl3.platform.YACLConfig; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; @@ -101,10 +102,13 @@ public class ColorController implements IStringController<Color> { public static class ColorControllerElement extends StringControllerElement { private final ColorController colorController; + private ColorPickerWidget colorPickerWidget; protected MutableDimension<Integer> colorPreviewDim; - private final List<Character> allowedChars; + public boolean hoveredOverColorPreview = false; + private boolean colorPickerVisible = false; + private int previewOutlineFadeTicks = 0; public ColorControllerElement(ColorController control, YACLScreen screen, Dimension<Integer> dim) { super(control, screen, dim, true); @@ -114,13 +118,18 @@ public class ColorController implements IStringController<Color> { @Override protected void drawValueText(GuiGraphics graphics, int mouseX, int mouseY, float delta) { + hovered = isMouseOver(mouseX, mouseY); + if (isHovered()) { - colorPreviewDim.move(-inputFieldBounds.width() - 5, 0); + colorPreviewDim.move(-inputFieldBounds.width() - 8, -2); + colorPreviewDim.expand(4, 4); + previewOutlineFadeTicks++; super.drawValueText(graphics, mouseX, mouseY, delta); } graphics.fill(colorPreviewDim.x(), colorPreviewDim.y(), colorPreviewDim.xLimit(), colorPreviewDim.yLimit(), colorController.option().pendingValue().getRGB()); - drawOutline(graphics, colorPreviewDim.x(), colorPreviewDim.y(), colorPreviewDim.xLimit(), colorPreviewDim.yLimit(), 1, 0xFF000000); + Color outlineColor = getPreviewOutlineColor(hoveredOverColorPreview || isMouseOverColorPreview(mouseX, mouseY)); + drawOutline(graphics, colorPreviewDim.x(), colorPreviewDim.y(), colorPreviewDim.xLimit(), colorPreviewDim.yLimit(), 1, outlineColor.getRGB()); } @Override @@ -193,6 +202,14 @@ public class ColorController implements IStringController<Color> { int previewSize = (dim.height() - getYPadding() * 2) / 2; colorPreviewDim = Dimension.ofInt(dim.xLimit() - getXPadding() - previewSize, dim.centerY() - previewSize / 2, previewSize, previewSize); + + if(colorPickerWidget != null) { + colorPickerWidget.setDimension(colorPickerWidget.getDimension().withY(this.getDimension().y())); + //checks if the color controller is being partially rendered offscreen + if(this.getDimension().y() < screen.tabArea.top() || this.getDimension().yLimit() > screen.tabArea.bottom()) { + removeColorPicker(); + } + } } @Override @@ -210,11 +227,121 @@ public class ColorController implements IStringController<Color> { @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { if (super.mouseClicked(mouseX, mouseY, button)) { + //Detects if the user has clicked the color preview + if(isMouseOverColorPreview(mouseX, mouseY)) { + playDownSound(); + createOrRemoveColorPicker(); + if(YACLConfig.HANDLER.instance().showColorPickerIndicator) { + YACLConfig.HANDLER.instance().showColorPickerIndicator = false; + YACLConfig.HANDLER.save(); + } + } caretPos = Math.max(1, caretPos); setSelectionLength(); return true; } + return false; } + + public boolean isMouseOverColorPreview(double mouseX, double mouseY) { + return colorPreviewDim.isPointInside((int) mouseX, (int) mouseY); + } + + public void createOrRemoveColorPicker() { + colorPickerVisible = !colorPickerVisible; + if(colorPickerVisible) { + colorPickerWidget = createColorPicker(); + screen.addPopupControllerWidget(colorPickerWidget); + } else { + removeColorPicker(); + } + } + + @Override + public void unfocus() { + if(colorPickerVisible) { + removeColorPicker(); + } + previewOutlineFadeTicks = 0; + super.unfocus(); + } + + public Color getPreviewOutlineColor(boolean colorPreviewHovered) { + Color outlineColor = new Color(0xFF000000); + Color highlightedColor = getHighlightedOutlineColor(); + + if(!hovered && !colorPreviewHovered) { + previewOutlineFadeTicks = 0; + return outlineColor; + } + + int fadeInTicks = 80; + int fadeOutTicks = fadeInTicks + 120; + + if(colorPreviewHovered) { + //white/light grey if the color preview is being hovered + previewOutlineFadeTicks = 0; + return highlightedColor; + } else if(YACLConfig.HANDLER.instance().showColorPickerIndicator) { + if(previewOutlineFadeTicks <= fadeInTicks) { + //fade to white + return getFadedColor(outlineColor, highlightedColor, previewOutlineFadeTicks, fadeInTicks); + } else if (previewOutlineFadeTicks <= fadeOutTicks) { + //fade to black + return getFadedColor(highlightedColor, outlineColor, previewOutlineFadeTicks - fadeInTicks, fadeOutTicks - fadeInTicks); + } + + if(previewOutlineFadeTicks >= fadeInTicks + fadeOutTicks + 10) { + //reset fade + previewOutlineFadeTicks = 0; + } + } + + return outlineColor; + } + + private Color getFadedColor(Color original, Color fadeToColor, int fadeTick, int maxFadeTicks) { + int red = fadeToColor.getRed() - original.getRed(); + int green = fadeToColor.getGreen() - original.getGreen(); + int blue = fadeToColor.getBlue() - original.getBlue(); + return new Color( + original.getRed() + ((red * fadeTick) / maxFadeTicks), + original.getGreen() + ((green * fadeTick) / maxFadeTicks), + original.getBlue() + ((blue * fadeTick) / maxFadeTicks) + ); + } + + private Color getHighlightedOutlineColor() { + //Brightness detector in case a developer has their starting color bright + //Makes the outline indicating to a user that the mini color preview can be clicked a light grey rather than white + //For reference, there is about a 10 digit moving room in saturation and light + Color pendingValue = colorController.option().pendingValue(); + float[] HSL = Color.RGBtoHSB(pendingValue.getRed(), pendingValue.getGreen(), pendingValue.getBlue(), null); + Color highlightedColor = new Color(0xFFFFFFFF); + if(HSL[1] < 0.1f && HSL[2] > 0.9f) { + highlightedColor = new Color(0xFFC6C6C6); + } + return highlightedColor; + } + + public ColorPickerWidget colorPickerWidget() { + return colorPickerWidget; + } + + public boolean colorPickerVisible() { + return colorPickerVisible; + } + + public ColorPickerWidget createColorPicker() { + return new ColorPickerWidget(colorController, screen, getDimension(), this); + } + + public void removeColorPicker() { + screen.clearPopupControllerWidget(); + this.colorPickerVisible = false; + this.colorPickerWidget = null; + this.hoveredOverColorPreview = false; //set to false in favor of the manual checking here to be done + } } } diff --git a/src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java b/src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java new file mode 100644 index 0000000..c7664dc --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java @@ -0,0 +1,459 @@ +package dev.isxander.yacl3.gui.controllers; + +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.api.utils.MutableDimension; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.gui.utils.YACLRenderHelper; +import dev.isxander.yacl3.platform.YACLPlatform; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; + +import java.awt.*; + +public class ColorPickerWidget extends ControllerPopupWidget<ColorController> { + /*? if >1.20.1 {*/ + private static final ResourceLocation COLOR_PICKER_LOCATION = YACLPlatform.rl("controller/colorpicker"); + private static final ResourceLocation TRANSPARENT_TEXTURE_LOCATION = YACLPlatform.rl("controller/transparent"); + /*? } else {*//* + // nineslice and repeating only work on a 256x atlas + private static final ResourceLocation COLOR_PICKER_ATLAS = YACLPlatform.rl("textures/gui/colorpicker-atlas.png"); + *//*?}*/ + + private final ColorController controller; + private final ColorController.ColorControllerElement entryWidget; + protected MutableDimension<Integer> colorPickerDim; + protected MutableDimension<Integer> previewColorDim; + protected MutableDimension<Integer> saturationLightDim; + protected MutableDimension<Integer> hueGradientDim; + protected MutableDimension<Integer> alphaGradientDim; + private boolean mouseDown; + private boolean hueSliderDown; + private boolean satLightGradientDown; + private boolean alphaSliderDown; + private int hueThumbX; + private int satLightThumbX; + private int alphaThumbX; + private boolean charTyped; + + //The width of the outline between each color picker element(color preview, saturation/light gradient, hue gradient) + //Note: Additional space may need to be manually made upon increasing the outline + private final int outline = 1; + + //The main color preview's portion of the color picker as a whole + //example: if previewPortion is set to 7, then the color preview will take up + //a 7th of the color picker's width + private final int previewPortion = 7; + + //The height in pixels of the hue slider + //example: if the sliderHeight is set to 7, then the hue slider will be 7 pixels, with some extra padding between + //the color preview and the HSL gradient to allow for an outline(determined by the "outline" int) + private final int sliderHeight = 7; + + //The x padding between the color preview and saturation/light gradient. + //Does NOT account for the outline on its own + private final int paddingX = 1; + + //The y padding between the hue gradient and color preview & saturation/light gradient. + //Does NOT account for the outline on its own + private final int paddingY = 3; + + + private float[] HSL; + private float hue; + private float saturation; + private float light; + private int alpha; + + public ColorPickerWidget(ColorController control, YACLScreen screen, Dimension<Integer> dim, ColorController.ColorControllerElement entryWidget) { + super(control, screen, dim, entryWidget); + this.controller = control; + this.entryWidget = entryWidget; + + setDimension(dim); + + updateHSL(); + setThumbX(); + } + + @Override + public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { + updateHSL(); + + int thumbWidth = 4; + int thumbHeight = 4; + + graphics.pose().pushPose(); + graphics.pose().translate(0, 0, 10); // render over text + + //Background + /*? if >1.20.3 { */ + graphics.blitSprite(COLOR_PICKER_LOCATION, colorPickerDim.x() - 5, colorPickerDim.y() - 5, 1, colorPickerDim.width() + 10, colorPickerDim.height() + 10); + /*? } else {*//* + graphics.blitNineSliced(COLOR_PICKER_ATLAS, colorPickerDim.x() - 5, colorPickerDim.y() - 5, colorPickerDim.width() + 10, colorPickerDim.height() + 10, 3, 236, 34, 0, 0); + *//*?}*/ + + //Main color preview + //outline + graphics.fill(previewColorDim.x() - outline, previewColorDim.y() - outline, previewColorDim.xLimit() + outline, previewColorDim.yLimit() + outline, Color.black.getRGB()); + //transparent texture - must be rendered BEFORE the main color preview + if(controller.allowAlpha()) { + /*? if >1.20.3 { */ + graphics.blitSprite(TRANSPARENT_TEXTURE_LOCATION, previewColorDim.x(), previewColorDim.y(), 3, previewColorDim.width(), previewColorDim.height()); + /*? } else {*//* + graphics.blitRepeating(COLOR_PICKER_ATLAS, previewColorDim.x(), previewColorDim.y(), previewColorDim.width(), previewColorDim.height(), 236, 0, 8, 8); + *//*?}*/ + } + //main color preview + graphics.fill(previewColorDim.x(), previewColorDim.y(), previewColorDim.xLimit(), previewColorDim.yLimit(), controller.option().pendingValue().getRGB()); + + //Saturation/light gradient + //outline + graphics.fill(saturationLightDim.x() - outline, saturationLightDim.y() - outline, saturationLightDim.xLimit() + outline, saturationLightDim.yLimit() + outline, Color.black.getRGB()); + //White to pending color's RGB from hue, left to right + fillSidewaysGradient(graphics, saturationLightDim.x(), saturationLightDim.y(), saturationLightDim.xLimit(), saturationLightDim.yLimit(), 0xFFFFFFFF, (int) getRgbFromHueX()); + //Transparent to black, top to bottom + graphics.fillGradient(saturationLightDim.x(), saturationLightDim.y(), saturationLightDim.xLimit(), saturationLightDim.yLimit(), 0x00000000, 0xFF000000); + //Sat/light thumb shadow + graphics.fill(satLightThumbX - thumbWidth / 2 - 2, getSatLightThumbY() + thumbHeight / 2 + 2, satLightThumbX + thumbWidth / 2 + 1, getSatLightThumbY() - thumbHeight / 2 - 1, 0xFF404040); + //Sat/light thumb - extra 1 pixel on left and top to make it centered + graphics.fill(satLightThumbX - thumbWidth / 2 - 1, getSatLightThumbY() + thumbHeight / 2 + 1, satLightThumbX + thumbWidth / 2, getSatLightThumbY() - thumbHeight / 2, -1); + + //Hue gradient + //outline + graphics.fill(hueGradientDim.x() - outline, hueGradientDim.y() - outline, hueGradientDim.xLimit() + outline, hueGradientDim.yLimit() + outline, Color.black.getRGB()); + //Hue rainbow gradient + drawRainbowGradient(graphics, hueGradientDim.x(), hueGradientDim.y(), hueGradientDim.xLimit(), hueGradientDim.yLimit()); + //Hue slider thumb shadow + graphics.fill(hueThumbX - thumbWidth / 2 - 1, hueGradientDim.y() - outline - 1, hueThumbX + thumbWidth / 2 + 1, hueGradientDim.yLimit() + outline + 1, 0xFF404040); + //Hue slider thumb + graphics.fill(hueThumbX - thumbWidth / 2, hueGradientDim.y() - outline, hueThumbX + thumbWidth / 2, hueGradientDim.yLimit() + outline, -1); + + if(controller.allowAlpha()) { + //outline + graphics.fill(alphaGradientDim.x() - outline, alphaGradientDim.y() - outline, alphaGradientDim.xLimit() + outline, alphaGradientDim.yLimit() + outline, Color.black.getRGB()); + //Transparent texture + /*? if >1.20.3 { */ + graphics.blitSprite(TRANSPARENT_TEXTURE_LOCATION, alphaGradientDim.x(), alphaGradientDim.y(), 3, alphaGradientDim.width(), sliderHeight); + /*? } else {*//* + graphics.blitRepeating(COLOR_PICKER_ATLAS, alphaGradientDim.x(), alphaGradientDim.y(), alphaGradientDim.width(), sliderHeight, 236, 0, 8, 8); + *//*?}*/ + //Pending color to transparent + fillSidewaysGradient(graphics, alphaGradientDim.x(), alphaGradientDim.y(), alphaGradientDim.xLimit(), alphaGradientDim.yLimit(), getRgbWithoutAlpha(), 0x00000000); + //Alpha slider thumb shadow + graphics.fill(alphaThumbX - thumbWidth / 2 - 1, alphaGradientDim.y() - outline - 1, alphaThumbX + thumbWidth / 2 + 1, alphaGradientDim.yLimit() + outline + 1, 0xFF404040); + //Alpha slider thumb + graphics.fill(alphaThumbX - thumbWidth / 2, alphaGradientDim.y() - outline, alphaThumbX + thumbWidth / 2, alphaGradientDim.yLimit() + outline, -1); + } + + //graphics.blitRepeating(COLOR_PICKER_ATLAS, colorPickerDim.x(), colorPickerDim.y(), colorPickerDim.width(), colorPickerDim.height(), 237, 0, 4, 4); + + graphics.pose().popPose(); + } + + public boolean clickedHueSlider(double mouseX, double mouseY) { + if (satLightGradientDown || alphaSliderDown) return false; + + if (mouseY >= hueGradientDim.y() && mouseY <= hueGradientDim.yLimit()) { + if (mouseX >= hueGradientDim.x() && mouseX <= hueGradientDim.xLimit()) { + hueSliderDown = true; + } + } + + if (hueSliderDown) { + hueThumbX = (int) Mth.clamp(mouseX, hueGradientDim.x(), hueGradientDim.xLimit()); + } + + return hueSliderDown; + } + + public boolean clickedSatLightGradient(double mouseX, double mouseY) { + if (hueSliderDown || alphaSliderDown) return false; + + if (mouseX >= saturationLightDim.x() && mouseX <= saturationLightDim.xLimit()) { + if (mouseY >= saturationLightDim.y() && mouseY <= saturationLightDim.yLimit()) { + satLightGradientDown = true; + } + } + + if(satLightGradientDown) { + satLightThumbX = (int) Mth.clamp(mouseX, saturationLightDim.x(), saturationLightDim.xLimit()); + } + + return satLightGradientDown; + } + + public boolean clickedAlphaSlider(double mouseX, double mouseY) { + if (satLightGradientDown || hueSliderDown) return false; + + if (mouseX >= alphaGradientDim.x() && mouseX <= alphaGradientDim.xLimit()) { + if (mouseY >= alphaGradientDim.y() && mouseY <= alphaGradientDim.yLimit()) { + alphaSliderDown = true; + } + } + + if (alphaSliderDown) { + alphaThumbX = (int) Mth.clamp(mouseX, alphaGradientDim.x(), alphaGradientDim.xLimit()); + } + + return alphaSliderDown; + } + + public void setColorFromMouseClick(double mouseX, double mouseY) { + if (clickedSatLightGradient(mouseX, mouseY)) { + setSatLightFromMouse(mouseX, mouseY); + } else if (clickedHueSlider(mouseX, mouseY)) { + setHueFromMouse(mouseX); + } else if (controller.allowAlpha() && clickedAlphaSlider(mouseX, mouseY)) { + setAlphaFromMouse(mouseX); + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isMouseOver(mouseX, mouseY)) { + mouseDown = true; + hueSliderDown = false; + satLightGradientDown = false; + alphaSliderDown = false; + setColorFromMouseClick(mouseX, mouseY); + return true; + } else if (entryWidget.isMouseOver(mouseX, mouseY)) { + return entryWidget.mouseClicked(mouseX, mouseY, button); + } else { + close(); //removes color picker + return false; + } + } + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + mouseDown = false; + return false; + } + @Override + public boolean isMouseOver(double mouseX, double mouseY) { + //Checks if the mouse is either over the color picker or the color controller + //The addition/subtraction of the outline and extra 3 pixels is to account for both the outline and the background + if (mouseX >= colorPickerDim.x() - outline - 3 && mouseX <= colorPickerDim.xLimit() + outline + 3 + && mouseY >= colorPickerDim.y() - outline - 3 && mouseY <= colorPickerDim.yLimit() + outline + 3) { + return true; + } + return false; + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + if (mouseDown || isMouseOver(mouseX, mouseY)) { + setColorFromMouseClick(mouseX, mouseY); + return true; + } + return entryWidget.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean charTyped(char chr, int modifiers) { + //Done to allow for typing whilst the color picker is visible + charTyped = true; + return entryWidget.charTyped(chr, modifiers); + } + + @Override + public void setDimension(Dimension<Integer> dim) { + super.setDimension(dim); + + int colorPickerHeight = (dim.height() * 2) + 7; + int colorPickerX = dim.centerX() - getXPadding() * 2; + int colorPickerY = dim.y() - colorPickerHeight - sliderHeight; + int alphaSliderHeight = 0; + if (controller.allowAlpha()) { + alphaSliderHeight = sliderHeight + outline + paddingY; + colorPickerHeight += alphaSliderHeight; + colorPickerY -= alphaSliderHeight; + } + + //Check if the color picker should be moved to beneath the controller + //Add additional numbers after colorPickerY to reduce the "strictness" of this detection + if (colorPickerY < screen.tabArea.top()) { + colorPickerY = dim.yLimit() + sliderHeight; + } + + //A single dimension for the entire color picker as a whole + //Division is used for the main color preview, saturation/light picker, and hue slider to determine their dimensions + colorPickerDim = Dimension.ofInt(colorPickerX, colorPickerY, dim.xLimit() - colorPickerX, colorPickerHeight); + + previewColorDim = Dimension.ofInt(colorPickerDim.x(), colorPickerDim.y(), (colorPickerDim.x() + (colorPickerDim.xLimit() / previewPortion) - paddingX) - colorPickerDim.x(), (colorPickerDim.yLimit() - sliderHeight - paddingY) - colorPickerDim.y() - alphaSliderHeight); + saturationLightDim = Dimension.ofInt(colorPickerDim.x() + (colorPickerDim.xLimit() / previewPortion) + paddingX + 1, colorPickerDim.y(), colorPickerDim.xLimit() - (colorPickerDim.x() + (colorPickerDim.xLimit() / previewPortion) + paddingX + 1), (colorPickerDim.yLimit() - sliderHeight - paddingY) - colorPickerDim.y() - alphaSliderHeight); + hueGradientDim = Dimension.ofInt(colorPickerDim.x(), colorPickerDim.yLimit() - sliderHeight - alphaSliderHeight, colorPickerDim.width(), sliderHeight); + if (controller.allowAlpha()) { + alphaGradientDim = Dimension.ofInt(hueGradientDim.x(), hueGradientDim.y() + alphaSliderHeight, hueGradientDim.width(), sliderHeight); + } + } + + @Override + public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + entryWidget.hoveredOverColorPreview = entryWidget.isMouseOverColorPreview(mouseX, mouseY); + } + + @Override + public void close() { + entryWidget.removeColorPicker(); + } + + @Override + public Component popupTitle() { + return Component.translatable("yacl.control.color.color_picker_title"); + } + + public void setThumbX() { + //Sets the thumb x for both hue and sat/light + hueThumbX = getHueThumbX(); + satLightThumbX = getSatLightThumbX(); + if (controller.allowAlpha()) { + alphaThumbX = getAlphaThumbX(); + } + } + + protected int getHueThumbX() { + int min = hueGradientDim.x(); + int max = hueGradientDim.xLimit(); + int value = (int) (min + hueGradientDim.width() * this.hue); + + return Mth.clamp(value, min, max); + } + + protected int getSatLightThumbX() { + int min = saturationLightDim.x(); + int max = saturationLightDim.xLimit(); + int value = (int) (min + (saturationLightDim.width() * this.saturation)); + + return Mth.clamp(value, min, max); + } + + protected int getSatLightThumbY() { + int min = saturationLightDim.y(); + int max = saturationLightDim.yLimit(); + int value = (int) (min + (saturationLightDim.height() * (1.0f - this.light))); + + return Mth.clamp(value, min, max); + } + + protected int getAlphaThumbX() { + int min = alphaGradientDim.x(); + int max = alphaGradientDim.xLimit(); + int value = max - (alphaGradientDim.width() * this.alpha / 255); + + return Mth.clamp(value, min, max); + } + + public void setHueFromMouse(double mouseX) { + //Changes the hue of the pending color based on the mouseX's pos. + //relative to the colorPickerDim's x/xLimit + if(mouseX < hueGradientDim.x()) { + this.hue = 0f; + } else if (mouseX > hueGradientDim.xLimit()) { + this.hue = 1f; + } else { + float newHue = (float) (mouseX - hueGradientDim.x()) / hueGradientDim.width(); + + this.hue = Mth.clamp(newHue, 0f, 1f); + } + + setColorControllerFromHSL(); + } + + public void setSatLightFromMouse(double mouseX, double mouseY) { + if(mouseX < saturationLightDim.x()) { + this.saturation = 0f; + } else if (mouseX > saturationLightDim.xLimit()) { + this.saturation = 1f; + } else { + float newSat = (float) (mouseX - saturationLightDim.x()) / saturationLightDim.width(); + + this.saturation = Mth.clamp(newSat, 0f, 1.0f); + } + + if(mouseY < saturationLightDim.y()) { + this.light = 1f; + } else if (mouseY > saturationLightDim.yLimit()) { + this.light = 0f; + } else { + float newLight = (float) (mouseY - saturationLightDim.y()) / saturationLightDim.height(); + + this.light = Mth.clamp(1f - newLight, 0f, 1.0f); + } + + setColorControllerFromHSL(); + } + + public void setAlphaFromMouse(double mouseX) { + //Changes the alpha of the pending color based on the mouseX's pos. + if(mouseX < alphaGradientDim.x()) { + this.alpha = 255; + } else if (mouseX > alphaGradientDim.xLimit()) { + this.alpha = 0; + } else { + int newAlpha = (int) ((mouseX - alphaGradientDim.xLimit()) / alphaGradientDim.width() * -255); + + this.alpha = Mth.clamp(newAlpha, 0, 255); + } + + setColorControllerFromHSL(); + } + + public void setColorControllerFromHSL() { + //Updates the current color controller's pending value based from HSL to RGB + float trueHue = (float) (hueThumbX - colorPickerDim.x()) / colorPickerDim.width(); + Color color = Color.getHSBColor(trueHue, saturation, light); + Color returnColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); + controller.option().requestSet(returnColor); + } + + protected void updateHSL() { + this.HSL = getHSL(); + this.hue = hue(); + this.saturation = saturation(); + this.light = light(); + this.alpha = getAlpha(); + if(charTyped) { + setThumbX(); + charTyped = false; + } + } + + protected float[] getHSL() { + Color pendingValue = controller.option().pendingValue(); + return Color.RGBtoHSB(pendingValue.getRed(), pendingValue.getGreen(), pendingValue.getBlue(), null); + } + + protected float hue() { + //Gets the hue of the pending value + return HSL[0]; + } + + protected float saturation() { + //Gets the saturation of the pending value + return HSL[1]; + } + + protected float light() { + //Gets the light/brightness/value(has a few different names, all refer to the same thing) of the pending value + return HSL[2]; + } + + protected int getAlpha() { + return controller.option().pendingValue().getAlpha(); + } + + protected float getRgbFromHueX() { + float trueHue = (float) (hueThumbX - colorPickerDim.x()) / colorPickerDim.width(); + + return Color.HSBtoRGB(trueHue, 1, 1); + } + + protected int getRgbWithoutAlpha() { + Color pendingColor = controller.option().pendingValue(); + Color returnColor = new Color(pendingColor.getRed(), pendingColor.getGreen(), pendingColor.getBlue(), 255); + return returnColor.getRGB(); + } +} diff --git a/src/main/java/dev/isxander/yacl3/gui/controllers/ControllerPopupWidget.java b/src/main/java/dev/isxander/yacl3/gui/controllers/ControllerPopupWidget.java new file mode 100644 index 0000000..e2f19bc --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/gui/controllers/ControllerPopupWidget.java @@ -0,0 +1,39 @@ +package dev.isxander.yacl3.gui.controllers; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.YACLScreen; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.network.chat.Component; + +public abstract class ControllerPopupWidget<T extends Controller<?>> extends ControllerWidget<Controller<?>> implements GuiEventListener { + public final ControllerWidget<?> entryWidget; + public ControllerPopupWidget(T control, YACLScreen screen, Dimension<Integer> dim, ControllerWidget<?> entryWidget) { + super(control, screen, dim); + this.entryWidget = entryWidget; + } + + public ControllerWidget<?> entryWidget() { + return entryWidget; + } + + public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {} + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return entryWidget.keyPressed(keyCode, scanCode, modifiers); + } + + public void close() {} + + public Component popupTitle() { + return Component.translatable("yacl.control.text.blank"); + } + + @Override + protected int getHoveredControlWidth() { + return 0; + } + +} diff --git a/src/main/java/dev/isxander/yacl3/gui/controllers/PopupControllerScreen.java b/src/main/java/dev/isxander/yacl3/gui/controllers/PopupControllerScreen.java new file mode 100644 index 0000000..f6a5db3 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/gui/controllers/PopupControllerScreen.java @@ -0,0 +1,70 @@ +package dev.isxander.yacl3.gui.controllers; + +import dev.isxander.yacl3.gui.YACLScreen; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; + +public class PopupControllerScreen extends Screen { + private final YACLScreen backgroundYaclScreen; + private final ControllerPopupWidget<?> controllerPopup; + public PopupControllerScreen(YACLScreen backgroundYaclScreen, ControllerPopupWidget<?> controllerPopup) { + super(controllerPopup.popupTitle()); //Gets narrated by the narrator + this.backgroundYaclScreen = backgroundYaclScreen; + this.controllerPopup = controllerPopup; + } + + + @Override + protected void init() { + this.addRenderableWidget(this.controllerPopup); + } + + @Override + public void resize(Minecraft minecraft, int width, int height) { + minecraft.setScreen(backgroundYaclScreen); + } + + @Override + public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { + controllerPopup.renderBackground(graphics, mouseX, mouseY, delta); + this.backgroundYaclScreen.render(graphics, -1, -1, delta); //mouseX/Y set to -1 to prevent hovering outlines + + super.render(graphics, mouseX, mouseY, delta); + } + + @Override + public void renderBackground( + GuiGraphics guiGraphics + /*? if >1.20.1 {*/, + int mouseX, + int mouseY, + float partialTick + /*?}*/ + ) { + + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, /*? if >1.20.1 {*/ double scrollX, /*?}*/ double scrollY) { + backgroundYaclScreen.mouseScrolled(mouseX, mouseY, /*? if >1.20.1 {*/ scrollX, /*?}*/ scrollY); //mouseX & mouseY are needed here + return super.mouseScrolled(mouseX, mouseY, /*? if >1.20.1 {*/ scrollX, /*?}*/ scrollY); + } + + @Override + public boolean charTyped(char codePoint, int modifiers) { + return controllerPopup.charTyped(codePoint, modifiers); + } + + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return controllerPopup.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public void onClose() { + this.minecraft.screen = backgroundYaclScreen; + } + +} diff --git a/src/main/java/dev/isxander/yacl3/gui/image/YACLImageReloadListener.java b/src/main/java/dev/isxander/yacl3/gui/image/YACLImageReloadListener.java index b6524a7..1852d98 100644 --- a/src/main/java/dev/isxander/yacl3/gui/image/YACLImageReloadListener.java +++ b/src/main/java/dev/isxander/yacl3/gui/image/YACLImageReloadListener.java @@ -1,6 +1,7 @@ package dev.isxander.yacl3.gui.image; import dev.isxander.yacl3.impl.utils.YACLConstants; +import dev.isxander.yacl3.platform.YACLPlatform; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; @@ -104,7 +105,7 @@ public class YACLImageReloadListener /*? if fabric {*/ @Override public ResourceLocation getFabricId() { - return new ResourceLocation("yet_another_config_lib_v3", "image_reload_listener"); + return YACLPlatform.rl("image_reload_listener"); } /*?}*/ } diff --git a/src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java b/src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java index f458557..61c07c7 100644 --- a/src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java +++ b/src/main/java/dev/isxander/yacl3/gui/tab/ScrollableNavigationBar.java @@ -30,7 +30,7 @@ public class ScrollableNavigationBar extends TabNavigationBar { this.accessor = (TabNavigationBarAccessor) this; // add tab tooltips to the tab buttons - for (TabButton tabButton : accessor.getTabButtons()) { + for (TabButton tabButton : accessor.yacl$getTabButtons()) { if (tabButton.tab() instanceof TabExt tab) { tabButton.setTooltip(tab.getTooltip()); } @@ -39,8 +39,8 @@ public class ScrollableNavigationBar extends TabNavigationBar { @Override public void arrangeElements() { - ImmutableList<TabButton> tabButtons = accessor.getTabButtons(); - int noScrollWidth = accessor.getWidth() - NAVBAR_MARGIN*2; + ImmutableList<TabButton> tabButtons = accessor.yacl$getTabButtons(); + int noScrollWidth = accessor.yacl$getWidth() - NAVBAR_MARGIN*2; int allTabsWidth = 0; // first pass: set the width of each tab button @@ -63,12 +63,12 @@ public class ScrollableNavigationBar extends TabNavigationBar { allTabsWidth = noScrollWidth; } - Layout layout = ((TabNavigationBarAccessor) this).getLayout(); + Layout layout = ((TabNavigationBarAccessor) this).yacl$getLayout(); layout.arrangeElements(); layout.setY(0); scrollOffset = 0; - layout.setX(Math.max((accessor.getWidth() - allTabsWidth) / 2, NAVBAR_MARGIN)); + layout.setX(Math.max((accessor.yacl$getWidth() - allTabsWidth) / 2, NAVBAR_MARGIN)); this.maxScrollOffset = Math.max(0, allTabsWidth - noScrollWidth); } @@ -95,7 +95,7 @@ public class ScrollableNavigationBar extends TabNavigationBar { } public void setScrollOffset(int scrollOffset) { - Layout layout = ((TabNavigationBarAccessor) this).getLayout(); + Layout layout = ((TabNavigationBarAccessor) this).yacl$getLayout(); layout.setX(layout.getX() + this.scrollOffset); this.scrollOffset = Mth.clamp(scrollOffset, 0, maxScrollOffset); @@ -117,12 +117,16 @@ public class ScrollableNavigationBar extends TabNavigationBar { protected void ensureVisible(TabButton tabButton) { if (tabButton.getX() < NAVBAR_MARGIN) { this.setScrollOffset(this.scrollOffset - (NAVBAR_MARGIN - tabButton.getX())); - } else if (tabButton.getX() + tabButton.getWidth() > accessor.getWidth() - NAVBAR_MARGIN) { - this.setScrollOffset(this.scrollOffset + (tabButton.getX() + tabButton.getWidth() - (accessor.getWidth() - NAVBAR_MARGIN))); + } else if (tabButton.getX() + tabButton.getWidth() > accessor.yacl$getWidth() - NAVBAR_MARGIN) { + this.setScrollOffset(this.scrollOffset + (tabButton.getX() + tabButton.getWidth() - (accessor.yacl$getWidth() - NAVBAR_MARGIN))); } } public ImmutableList<Tab> getTabs() { - return accessor.getTabs(); + return accessor.yacl$getTabs(); + } + + public TabManager getTabManager() { + return accessor.yacl$getTabManager(); } } diff --git a/src/main/java/dev/isxander/yacl3/gui/utils/ButtonTextureRenderer.java b/src/main/java/dev/isxander/yacl3/gui/utils/YACLRenderHelper.java index aa52a3f..b49557b 100644 --- a/src/main/java/dev/isxander/yacl3/gui/utils/ButtonTextureRenderer.java +++ b/src/main/java/dev/isxander/yacl3/gui/utils/YACLRenderHelper.java @@ -1,10 +1,11 @@ package dev.isxander.yacl3.gui.utils; import com.mojang.blaze3d.systems.RenderSystem; +import dev.isxander.yacl3.platform.YACLPlatform; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.resources.ResourceLocation; -public class ButtonTextureRenderer { +public class YACLRenderHelper { /*? if >1.20.1 {*/ private static final net.minecraft.client.gui.components.WidgetSprites SPRITES = new net.minecraft.client.gui.components.WidgetSprites( new ResourceLocation("widget/button"), // normal @@ -16,7 +17,7 @@ public class ButtonTextureRenderer { private static final ResourceLocation SLIDER_LOCATION = new ResourceLocation("textures/gui/slider.png"); *//*?}*/ - public static void render(GuiGraphics graphics, int x, int y, int width, int height, boolean enabled, boolean focused) { + public static void renderButtonTexture(GuiGraphics graphics, int x, int y, int width, int height, boolean enabled, boolean focused) { /*? if >1.20.1 {*/ graphics.blitSprite(SPRITES.get(enabled, focused), x, y, width, height); /*?} else {*//* @@ -31,4 +32,12 @@ public class ButtonTextureRenderer { graphics.blitNineSliced(SLIDER_LOCATION, x, y, width, height, 20, 4, 200, 20, 0, textureV); *//*?}*/ } + + public static ResourceLocation getSpriteLocation(String path) { + /*? if >1.20.3 {*/ + return YACLPlatform.rl(path); + /*? } else {*//* + return YACLPlatform.rl("textures/gui/sprites/" + path + ".png"); + *//*?}*/ + } } |