Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color Picker For Color Controllers #140

Merged
merged 44 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
42151c5
Initial work on color picker - gradient rendering
Superkat32 Aug 26, 2023
bcb418e
Official color picker element
Superkat32 Aug 30, 2023
a19daaa
Merge remote-tracking branch 'origin/1.20.x/dev' into 1.20.x/dev
Superkat32 Aug 30, 2023
a1354e1
Slider x calculation
Superkat32 Aug 31, 2023
ff42a68
Some "z-fighting" fixing
Superkat32 Sep 9, 2023
250e581
Sound effect changes
Superkat32 Sep 10, 2023
781c112
Back to work! - Some refactors I don't remember the point of
Superkat32 Nov 5, 2023
68d5d59
Merge remote-tracking branch 'origin/1.20.x/dev' into 1.20.x/dev
Superkat32 Nov 5, 2023
4481714
Beginning of the end (Huge improvements, technical challenge overcome)
Superkat32 Nov 11, 2023
3c8de1d
Bug fixes, some gradient refactoring, small commented out code cleanup
Superkat32 Nov 11, 2023
2e084a6
Mouse click checkpoint
Superkat32 Nov 18, 2023
e333c1c
Mouse click checkpoint 2
Superkat32 Nov 20, 2023
b7b7f90
Color picker widget correct mouse clicking
Superkat32 Nov 24, 2023
9506f96
Mouse clicking cleanup
Superkat32 Nov 24, 2023
dd03a25
Even more cleanup
Superkat32 Nov 24, 2023
245406d
Color picker - clicking does cool things now
Superkat32 Nov 25, 2023
f1c44f5
Initial work on color picker redesign
Superkat32 Dec 4, 2023
494145d
Merge remote-tracking branch 'origin/1.20.x/dev' into 1.20.x/dev
Superkat32 Dec 6, 2023
3bf9187
Color picker visual redesign
Superkat32 Dec 8, 2023
2415fe6
Saturation/Light Picking + Bug fixes + Better mini preview outline
Superkat32 Dec 12, 2023
1eb57d7
Typing bug fix and temporary float color picker fix
Superkat32 Dec 15, 2023
6e9fd05
Merge remote-tracking branch 'origin/1.20.x/dev' into 1.20.x/dev
Superkat32 Dec 15, 2023
66532ab
Color picker texture background
Superkat32 Dec 17, 2023
5fa302e
Some comments I guess?
Superkat32 Jan 1, 2024
1bae4ab
Popup Color Picker Screen
Superkat32 Jan 2, 2024
d396fc5
General improvements to the Popup Color Picker Screen
Superkat32 Jan 3, 2024
c1c6299
Color Picker Test category, beginning of alpha in color picker, move …
Superkat32 Jan 4, 2024
10af328
Finished Alpha slider
Superkat32 Jan 4, 2024
f2678a5
Cleanup and bug fixes
Superkat32 Jan 5, 2024
de0f766
More cleanup
Superkat32 Jan 5, 2024
5534d17
Optimize imports
Superkat32 Jan 9, 2024
4abb47f
Color Picker Changes 1
Superkat32 Jan 13, 2024
aca4770
Extracted PopupColorPickerScreen.java to PopupControllerScreen
Superkat32 Jan 13, 2024
3255fb6
Color preview outline fading
Superkat32 Jan 20, 2024
fdd7285
Popup Controller Future Proofing 1
Superkat32 Jan 20, 2024
d0a00de
Color picker y is now controlled by the color controller
Superkat32 Jan 21, 2024
eb2b1c8
Fix option widget list being given
Superkat32 Jan 21, 2024
a191850
ColorPickerWidget
Superkat32 Feb 4, 2024
66a5f57
Attempt 2 at fixing weird clearing popup controller loop
Superkat32 Feb 15, 2024
2ae1f24
Merge remote-tracking branch 'upstream/1.20.x/dev' into 1.20.x/dev
Superkat32 Mar 2, 2024
8134ba6
Color picker indicator automatically disables upon discovery
Superkat32 Mar 2, 2024
8f02d6b
Moving color picker beneath controller better detection implementatio…
Superkat32 Mar 2, 2024
855c022
Merge remote-tracking branch 'refs/remotes/upstream/multiversion/dev'…
isXander Apr 11, 2024
d129a74
Add multi-version support
isXander Apr 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions common/src/main/java/dev/isxander/yacl3/platform/YACLConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dev.isxander.yacl3.platform;

import dev.isxander.yacl3.config.v2.api.ConfigClassHandler;
import dev.isxander.yacl3.config.v2.api.SerialEntry;
import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder;

public class YACLConfig {
public static final ConfigClassHandler<YACLConfig> HANDLER = ConfigClassHandler.createBuilder(YACLConfig.class)
.id(YACLPlatform.rl("config"))
.serializer(config -> GsonConfigSerializerBuilder.create(config)
.setPath(YACLPlatform.getConfigDir().resolve("yacl.json5"))
.setJson5(true)
.build())
.build();

@SerialEntry
public boolean showColorPickerIndicator = true;
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"gui": {
"scaling": {
"type": "nine_slice",
"width": 236,
"height": 34,
"border": 3
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"gui": {
"scaling": {
"type": "tile",
"width": 7,
"height": 7,
"border": 3
}
}
}
38 changes: 36 additions & 2 deletions src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
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;
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;

Expand Down Expand Up @@ -75,7 +78,7 @@ protected void drawButtonRect(GuiGraphics graphics, int x1, int y1, int x2, int
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) {
Expand All @@ -85,6 +88,37 @@ protected void drawOutline(GuiGraphics graphics, int x1, int y1, int x2, int y2,
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);

Expand Down
37 changes: 34 additions & 3 deletions src/main/java/dev/isxander/yacl3/gui/YACLScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@
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;
Expand All @@ -34,7 +36,6 @@

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

public class YACLScreen extends Screen {
Expand All @@ -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;
Expand Down Expand Up @@ -87,6 +91,33 @@ protected void init() {
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) {
Expand Down
133 changes: 130 additions & 3 deletions src/main/java/dev/isxander/yacl3/gui/controllers/ColorController.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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;
Expand Down Expand Up @@ -101,10 +102,13 @@ public AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widget

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);
Expand All @@ -114,13 +118,18 @@ public ColorControllerElement(ColorController control, YACLScreen screen, Dimens

@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
Expand Down Expand Up @@ -193,6 +202,14 @@ public void setDimension(Dimension<Integer> dim) {

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
Expand All @@ -210,11 +227,121 @@ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
@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
}
}
}
Loading
Loading