Skip to content
Original file line number Diff line number Diff line change
@@ -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<Integer> setter;

public ColorSelectionGuiScreen(ConfigGuiScreen parentScreen, Field field, int defaultColor, Consumer<Integer> 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<GuiSlider> 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<String> 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;
}
}

Expand All @@ -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!");
}
Expand All @@ -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)
}
}
55 changes: 49 additions & 6 deletions src/configlib/java/fr/alexdoru/configlib/lib/gui/GuiUtil.java
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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) {
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -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);
}
}
Loading