diff --git a/src/configlib/java/fr/alexdoru/configlib/lib/gui/ColorSelectionGuiScreen.java b/src/configlib/java/fr/alexdoru/configlib/lib/gui/ColorSelectionGuiScreen.java index 220d2fc0..7280e8c7 100644 --- a/src/configlib/java/fr/alexdoru/configlib/lib/gui/ColorSelectionGuiScreen.java +++ b/src/configlib/java/fr/alexdoru/configlib/lib/gui/ColorSelectionGuiScreen.java @@ -1,73 +1,107 @@ package fr.alexdoru.configlib.lib.gui; +import fr.alexdoru.configlib.lib.gui.elements.ColorGuiButton; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.client.config.GuiSlider; +import org.lwjgl.input.Keyboard; import java.awt.Color; import java.io.IOException; -import java.lang.reflect.Field; -import java.util.function.Consumer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; -public class ColorSelectionGuiScreen extends GuiScreen { +public class ColorSelectionGuiScreen extends GuiScreen implements GuiSlider.ISlider { private static final ResourceLocation BLUR = new ResourceLocation("configlib", "blur.json"); + private static final int BUTTON_WIDTH = 150; + private static final int BUTTON_HEIGHT = 20; + private static final int BUTTONS_GAP = 4; + private static final int TOP_BOTTOM_MARGIN = 16; + private final GuiScreen parent; - private final Field field; + private final ColorGuiButton colorButton; private final int initialColor; - private final int defaultColor; - private GuiSlider sliderRed; - private GuiSlider sliderGreen; - private GuiSlider sliderBlue; private final boolean hasAlpha; - private GuiSlider sliderAlpha; - private final Consumer setter; - - public ColorSelectionGuiScreen(ConfigGuiScreen parentScreen, Field field, int defaultColor, Consumer setter) throws IllegalAccessException { - this.parent = parentScreen; - this.field = field; - this.initialColor = ((int) field.get(null)); - this.defaultColor = defaultColor; - this.hasAlpha = ((defaultColor >> 24) & 0xff) != 0; - this.setter = setter; + private final List colorSliders = new ArrayList<>(); + + private int selectedSliderIndex = -1; + private int allButtonsHeight; + + public ColorSelectionGuiScreen(GuiScreen parent, ColorGuiButton colorButton) { + this.parent = parent; + this.colorButton = colorButton; + this.initialColor = colorButton.getColor(); + this.hasAlpha = ((colorButton.getDefaultColor() >> 24) & 0xff) != 0; } @Override public void initGui() { - this.buttonList.add(this.sliderRed = new GuiSlider(1, getxCenter() - 150, getButtonYPos(2), 150, 20, "Red: ", "", 0.0D, 255.0D, (this.initialColor >> 16 & 0xFF), false, true)); - this.buttonList.add(this.sliderGreen = new GuiSlider(2, getxCenter() - 150, getButtonYPos(3), 150, 20, "Green: ", "", 0.0D, 255.0D, (this.initialColor >> 8 & 0xFF), false, true)); - this.buttonList.add(this.sliderBlue = new GuiSlider(3, getxCenter() - 150, getButtonYPos(4), 150, 20, "Blue: ", "", 0.0D, 255.0D, (this.initialColor & 0xFF), false, true)); - if (this.hasAlpha) { - this.buttonList.add(this.sliderAlpha = new GuiSlider(4, getxCenter() - 150, getButtonYPos(5), 150, 20, "Alpha: ", "", 0.0D, 255.0D, (this.initialColor >> 24 & 0xFF), false, true)); + final int color = colorButton.getColor(); + final int centerX = this.width / 2; + final int slidersX = centerX - BUTTON_WIDTH; + final List colorChannels = new ArrayList<>(Arrays.asList("Red", "Green", "Blue")); + if (hasAlpha) colorChannels.add("Alpha"); + this.allButtonsHeight = ((colorChannels.size() + 2) * (BUTTON_HEIGHT + BUTTONS_GAP) - BUTTONS_GAP) + TOP_BOTTOM_MARGIN + BUTTON_HEIGHT; + int drawY = (this.height - allButtonsHeight) / 2; + buttonList.clear(); + colorSliders.clear(); + for (int i = 0; i < colorChannels.size(); ++i) { + final int value = getColorChannelValue(color, i); + final GuiSlider slider = new GuiSlider(i + 1, slidersX, drawY, BUTTON_WIDTH, BUTTON_HEIGHT, colorChannels.get(i) + ": ", "", 0, 255, value, false, true, this); + this.colorSliders.add(slider); + this.buttonList.add(slider); + drawY += BUTTON_HEIGHT + BUTTONS_GAP; } - this.buttonList.add(new GuiButton(5, getxCenter() - 150, getButtonYPos(this.hasAlpha ? 6 : 5), 150, 20, "Reset default color")); - this.buttonList.add(new GuiButton(6, getxCenter() - 150 / 2, getButtonYPos(this.hasAlpha ? 8 : 7), 150, 20, "Done")); + this.buttonList.add(new GuiButton(5, slidersX, drawY, BUTTON_WIDTH, BUTTON_HEIGHT, "Undo Changes")); + drawY += BUTTON_HEIGHT + BUTTONS_GAP; + this.buttonList.add(new GuiButton(6, slidersX, drawY, BUTTON_WIDTH, BUTTON_HEIGHT, "Reset to Default")); + drawY += BUTTON_HEIGHT + TOP_BOTTOM_MARGIN; + this.buttonList.add(new GuiButton(7, centerX - BUTTON_WIDTH / 2, drawY, BUTTON_WIDTH, BUTTON_HEIGHT, "Done")); this.mc.entityRenderer.loadShader(BLUR); super.initGui(); + this.selectedSliderIndex = -1; } @Override public void drawScreen(int mouseX, int mouseY, float partialTicks) { - try { - this.field.set(null, getCurrentColor()); - this.setter.accept(getCurrentColor()); - } catch (IllegalAccessException e) { - throw new RuntimeException("Couldn't set color!"); + final int firstSliderY = colorSliders.get(0).yPosition; + final int lineLength = allButtonsHeight - BUTTON_HEIGHT - TOP_BOTTOM_MARGIN; + final int colorBoxLeft = this.width / 2 + 10; + final int colorBoxTop = firstSliderY; + final int colorBoxRight = colorBoxLeft + lineLength; + final int colorBoxBottom = colorBoxTop + lineLength; + if (GuiUtil.beginClearRect(mc, colorBoxLeft, colorBoxTop, colorBoxRight, colorBoxBottom)) { + drawDefaultBackground(); + GuiUtil.endClearRect(); } + drawCenteredString(fontRendererObj, colorButton.getCategory() + " - " + colorButton.getName(), this.width / 2, firstSliderY - TOP_BOTTOM_MARGIN - fontRendererObj.FONT_HEIGHT / 2, Color.WHITE.getRGB()); super.drawScreen(mouseX, mouseY, partialTicks); - final int sideLength = 10 + (20 + 4) * (this.hasAlpha ? 4 : 3) + 20 + 10; - final int leftX = getxCenter() + 10; - final int topY = getButtonYPos(2) - 10; - GuiUtil.drawBoxWithOutline(leftX, topY, leftX + sideLength, topY + sideLength, 255 << 24 | getCurrentColor(), Color.BLACK.getRGB()); + if (this.selectedSliderIndex != -1) { + final GuiSlider slider = colorSliders.get(this.selectedSliderIndex); + GuiUtil.drawBoxOutline(slider.xPosition-1, slider.yPosition-1, slider.xPosition+slider.width+1, slider.yPosition+slider.height+1, 0xFF3C6EFF); + } + GuiUtil.drawBoxWithOutline(colorBoxLeft, colorBoxTop, colorBoxRight, colorBoxBottom, hasAlpha ? colorButton.getColor() : (colorButton.getColor() | (0xFF << 24)), Color.BLACK.getRGB()); + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { + this.selectedSliderIndex = -1; + super.mouseClicked(mouseX, mouseY, mouseButton); } @Override protected void actionPerformed(GuiButton button) { - if (button.id == 5) { - this.resetDefaultColor(); - } else if (button.id == 6) { - this.mc.displayGuiScreen(parent); + if (button.id >= 1 && button.id <= 4) { + this.selectedSliderIndex = button.id-1; + return; + } + switch (button.id) { + case 5: updateToColor(initialColor); break; + case 6: updateToColor(colorButton.getDefaultColor()); break; + case 7: mc.displayGuiScreen(parent); break; } } @@ -77,14 +111,25 @@ protected void keyTyped(char typedChar, int keyCode) throws IOException { this.mc.displayGuiScreen(parent); return; } + if (keyCode == Keyboard.KEY_TAB) { + if ((++this.selectedSliderIndex) >= this.colorSliders.size()) + this.selectedSliderIndex = 0; // change to -1 if you want a "none selected buffer" between cycles + return; + } + else if (this.selectedSliderIndex != -1 && (keyCode == Keyboard.KEY_LEFT || keyCode == Keyboard.KEY_RIGHT)) { + final GuiSlider slider = colorSliders.get(this.selectedSliderIndex); + final int value = keyCode == Keyboard.KEY_LEFT ? -1 : 1; + slider.setValue(slider.getValueInt() + value); + slider.updateSlider(); + return; + } super.keyTyped(typedChar, keyCode); } @Override public void onGuiClosed() { try { - this.field.set(null, getCurrentColor()); - this.setter.accept(getCurrentColor()); + colorButton.saveColorToField(); } catch (IllegalAccessException e) { throw new RuntimeException("Couldn't set color!"); } @@ -97,32 +142,31 @@ public boolean doesGuiPauseGame() { return false; } - private int getCurrentColor() { - if (this.hasAlpha) { - return this.sliderAlpha.getValueInt() << 24 | this.sliderRed.getValueInt() << 16 | this.sliderGreen.getValueInt() << 8 | this.sliderBlue.getValueInt(); + private void updateToColor(int newColor) { + for (final GuiSlider slider : colorSliders) { + slider.setValue(getColorChannelValue(newColor, slider.id-1)); + slider.updateSlider(); } - return this.sliderRed.getValueInt() << 16 | this.sliderGreen.getValueInt() << 8 | this.sliderBlue.getValueInt(); } - private void resetDefaultColor() { - this.sliderRed.setValue(this.defaultColor >> 16 & 0xFF); - this.sliderRed.updateSlider(); - this.sliderGreen.setValue(this.defaultColor >> 8 & 0xFF); - this.sliderGreen.updateSlider(); - this.sliderBlue.setValue(this.defaultColor & 0xFF); - this.sliderBlue.updateSlider(); - if (this.hasAlpha) { - this.sliderAlpha.setValue(this.defaultColor >> 24 & 0xFF); - this.sliderAlpha.updateSlider(); - } + @Override + public void onChangeSliderValue(GuiSlider slider) { + colorButton.setColor(getColorWithUpdatedChannel(colorButton.getColor(), slider.id-1, slider.getValueInt())); } - private int getButtonYPos(int i) { - return this.height / 8 + 24 * (i + 1); + private static int getColorWithUpdatedChannel(int color, int channel, int channelValue) { + final int shiftBy = getColorChannelBitShift(channel); + final int mask = 0xFF << shiftBy; + return (color & ~mask) | ((channelValue & 0xFF) << shiftBy); } - private int getxCenter() { - return this.width / 2; + private static int getColorChannelValue(int color, int channel) { + return (color >> getColorChannelBitShift(channel)) & 0xFF; } + private static int getColorChannelBitShift(int channel) { + if (channel < 0 || channel > 3) + throw new IllegalArgumentException("Argument must be one of: (0, 1, 2, 3)"); + return channel == 3 ? 24 : (16 - (channel << 3)); // 8 * (2-channel) + } } diff --git a/src/configlib/java/fr/alexdoru/configlib/lib/gui/GuiUtil.java b/src/configlib/java/fr/alexdoru/configlib/lib/gui/GuiUtil.java index 9b945a23..524f2314 100644 --- a/src/configlib/java/fr/alexdoru/configlib/lib/gui/GuiUtil.java +++ b/src/configlib/java/fr/alexdoru/configlib/lib/gui/GuiUtil.java @@ -1,26 +1,39 @@ package fr.alexdoru.configlib.lib.gui; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; +import net.minecraft.client.renderer.GlStateManager; +import org.lwjgl.opengl.GL11; public final class GuiUtil { private GuiUtil() {} + public static void drawBoxOutline(int left, int top, int right, int bottom, int color) { + drawVerticalLine(left, top, bottom, color); + drawVerticalLine(right - 1, top, bottom, color); + drawHorizontalLine(left, right, top, color); + drawHorizontalLine(left, right, bottom - 1, color); + } + public static void drawBoxWithOutline(int left, int top, int right, int bot, int boxColor, int borderColor) { - drawHorizontalLine(left, right - 1, top, borderColor); - drawHorizontalLine(left, right - 1, bot - 1, borderColor); - drawVerticalLine(left, top - 1, bot - 1, borderColor); - drawVerticalLine(right - 1, top - 1, bot - 1, borderColor); Gui.drawRect(left + 1, top + 1, right - 1, bot - 1, boxColor); + drawBoxOutline(left, top, right, bot, borderColor); } + /* + * because of that changes to drawHorizontalLine and drawVerticalLine + * you might need to update a lot of other rendering code that uses it + * example: line 237 in 'ConfigGuiScreen' should be: + * GuiUtil.drawHorizontalLine(GUI_INSIDE_LEFT, GUI_INSIDE_RIGHT, GUI_INSIDE_TOP + fontRendererObj.FONT_HEIGHT + 1, colorPalette.BELOW_TITLE_LINE); + * */ public static void drawHorizontalLine(int startX, int endX, int y, int color) { if (endX < startX) { final int i = startX; startX = endX; endX = i; } - Gui.drawRect(startX, y, endX + 1, y + 1, color); + Gui.drawRect(startX, y, endX, y + 1, color); } public static void drawVerticalLine(int x, int startY, int endY, int color) { @@ -29,7 +42,7 @@ public static void drawVerticalLine(int x, int startY, int endY, int color) { startY = endY; endY = i; } - Gui.drawRect(x, startY + 1, x + 1, endY, color); + Gui.drawRect(x, startY, x + 1, endY, color); } public static int brightenColor(int color, float amount) { @@ -43,4 +56,34 @@ public static int brightenColor(int color, float amount) { return (a << 24) | (r << 16) | (g << 8) | b; } + /** + * Begins the stencil clear rect. + * Call {@link #endClearRect()} after rendering if this returned {@code true}. + * @return {@code true} if this operation worked + */ + public static boolean beginClearRect(Minecraft mc, int left, int top, int right, int bottom) { + if (!mc.getFramebuffer().isStencilEnabled() && !mc.getFramebuffer().enableStencil()) return false; + GL11.glEnable(GL11.GL_STENCIL_TEST); + GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); + + GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0xFF); + GL11.glStencilOp(GL11.GL_REPLACE, GL11.GL_REPLACE, GL11.GL_REPLACE); + + GlStateManager.colorMask(false, false, false, false); + + Gui.drawRect(left, top, right, bottom, 0xFFFFFFFF); + + GlStateManager.colorMask(true, true, true, true); + + GL11.glStencilFunc(GL11.GL_NOTEQUAL, 1, 0xFF); // skip hole + GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); + + return true; + // GL11.glDisable(GL11.GL_STENCIL_TEST); + } + + /** See {@link #beginClearRect(Minecraft, int, int, int, int)} */ + public static void endClearRect() { + GL11.glDisable(GL11.GL_STENCIL_TEST); + } } diff --git a/src/configlib/java/fr/alexdoru/configlib/lib/gui/elements/ColorGuiButton.java b/src/configlib/java/fr/alexdoru/configlib/lib/gui/elements/ColorGuiButton.java index 6393822b..089ac5e8 100644 --- a/src/configlib/java/fr/alexdoru/configlib/lib/gui/elements/ColorGuiButton.java +++ b/src/configlib/java/fr/alexdoru/configlib/lib/gui/elements/ColorGuiButton.java @@ -12,6 +12,7 @@ public class ColorGuiButton extends ConfigGuiButton { private final ConfigGuiScreen parentScreen; private final ClickGuiButton button; + private final String name; private int color; private final int defaultColor; @@ -21,6 +22,8 @@ public ColorGuiButton(ConfigGuiScreen configGuiScreen, Field field, ConfigProper this.color = (int) this.field.get(null); this.defaultColor = defaultColor; this.button = new ClickGuiButton(0, 0, 0, mc.fontRendererObj.getStringWidth(" Disabled "), 20, "Change"); + // SHOULD NOT BE THE WAY WE GET NAME + this.name = annotation.name(); } @Override @@ -38,10 +41,23 @@ public void draw(ColorPalette colorPalette, int drawX, int drawY, int mouseX, in public boolean mouseClicked(int mouseX, int mouseY, int mouseButton) throws IllegalAccessException { if (mouseButton == 0 && button.mousePressed(mc, mouseX, mouseY)) { button.playPressSound(mc.getSoundHandler()); - mc.displayGuiScreen(new ColorSelectionGuiScreen(parentScreen, field, defaultColor, color -> this.color = color)); + mc.displayGuiScreen(new ColorSelectionGuiScreen(parentScreen, this)); return true; } return false; } + // THIS METHOD SHOULD BE DECLARED IN 'ConfigGuiButton' + public String getName() { + return name; + } + + public void setColor(int color) { this.color = color; } + public int getColor() { return this.color; } + + public int getDefaultColor() { return this.defaultColor; } + + public void saveColorToField() throws IllegalAccessException { + this.field.set(null, this.color); + } }