From 39ccb1088ff24daa09fa85de014bd11b531748cf Mon Sep 17 00:00:00 2001 From: brachy84 Date: Sun, 26 May 2024 20:34:54 +0200 Subject: [PATCH 1/3] texture rework --- .../cleanroommc/modularui/test/TestTile.java | 2 +- .../modularui/widgets/CycleButtonWidget.java | 185 ++++++++++++++++-- 2 files changed, 165 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/cleanroommc/modularui/test/TestTile.java b/src/main/java/com/cleanroommc/modularui/test/TestTile.java index 4bae814b7..7d2d2ad52 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestTile.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestTile.java @@ -228,7 +228,7 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) .child(new CycleButtonWidget() .size(14, 14) .length(3) - .texture(GuiTextures.CYCLE_BUTTON_DEMO) + .stateBackground(GuiTextures.CYCLE_BUTTON_DEMO) .value(new IntSyncValue(() -> this.val2, val -> this.val2 = val)) .margin(8, 0)) .child(IKey.str("Hello World").asWidget().height(18))) diff --git a/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java index 8bec4d22f..9968ab711 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java @@ -1,6 +1,5 @@ package com.cleanroommc.modularui.widgets; -import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.drawable.IKey; @@ -10,7 +9,6 @@ import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.UITexture; import com.cleanroommc.modularui.screen.Tooltip; -import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.value.IntValue; import com.cleanroommc.modularui.value.sync.SyncHandler; @@ -20,6 +18,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.IntFunction; @@ -29,8 +28,10 @@ public class CycleButtonWidget extends Widget implements Inte private int length = 1; private IIntValue intValue; private int lastValue = -1; - private IntFunction textureGetter; - private IDrawable texture = IDrawable.EMPTY; + private IDrawable[] background = null; + private IDrawable[] hoverBackground = null; + private IDrawable[] overlay = null; + private IDrawable[] hoverOverlay = null; private final List stateTooltip = new ArrayList<>(); @Override @@ -38,11 +39,6 @@ public void onInit() { if (this.intValue == null) { this.intValue = new IntValue(0); } - if (this.textureGetter == null) { - ModularUI.LOGGER.warn("Texture Getter of {} was not set!", this); - this.textureGetter = val -> IDrawable.EMPTY; - } - this.texture = this.textureGetter.apply(getState()); } @Override @@ -82,7 +78,6 @@ public void setState(int state, boolean setSource) { if (setSource) { this.intValue.setIntValue(state); } - this.texture = this.textureGetter.apply(state); this.lastValue = state; } @@ -107,12 +102,24 @@ public WidgetTheme getWidgetTheme(ITheme theme) { } @Override - public void draw(GuiContext context, WidgetTheme widgetTheme) { - super.draw(context, widgetTheme); + public IDrawable getCurrentBackground(ITheme theme, WidgetTheme widgetTheme) { // make sure texture is up-to-date - getState(); - // draw state texture after background, but before overlay - this.texture.draw(context, 0, 0, getArea().w(), getArea().h(), widgetTheme); + int state = getState(); + if (isHovering()) { + if (this.hoverBackground != null && this.hoverBackground[state] != null) return this.hoverBackground[state]; + return this.background != null && this.background[state] != null ? this.background[state] : super.getCurrentBackground(theme, widgetTheme); + } + return this.background != null && this.background[state] != null ? this.background[state] : super.getCurrentBackground(theme, widgetTheme); + } + + @Override + public IDrawable getCurrentOverlay(ITheme theme, WidgetTheme widgetTheme) { + int state = getState(); + if (isHovering()) { + if (this.hoverOverlay != null && this.hoverOverlay[state] != null) return this.hoverOverlay[state]; + return this.overlay != null && this.overlay[state] != null ? this.overlay[state] : super.getCurrentBackground(theme, widgetTheme); + } + return this.overlay != null && this.overlay[state] != null ? this.overlay[state] : super.getCurrentBackground(theme, widgetTheme); } @Override @@ -127,7 +134,7 @@ public void markTooltipDirty() { for (Tooltip tooltip : this.stateTooltip) { tooltip.markDirty(); } - this.texture = this.textureGetter.apply(getState()); + getState(); } @Override @@ -150,16 +157,134 @@ public CycleButtonWidget value(IIntValue value) { return this; } + @Deprecated public CycleButtonWidget textureGetter(IntFunction textureGetter) { - this.textureGetter = textureGetter; - return this; + throw new UnsupportedOperationException("'textureGetter()' is no longer supported in CycleButtonWidget. Use 'stateBackground()'"); } + @Deprecated public CycleButtonWidget texture(UITexture texture) { - return textureGetter(val -> { + return stateBackground(texture); + } + + /** + * Sets the state dependent background. The images should be vertically stacked images from top to bottom + * Note: The length must be already set! + * + * @param texture background + * @return this + */ + public CycleButtonWidget stateBackground(UITexture texture) { + for (int i = 0; i < this.length; i++) { + float a = 1f / this.length; + this.background[i] = texture.getSubArea(0, i * a, 1, i * a + a); + } + return this; + } + + /** + * Sets the state dependent overlay. The images should be vertically stacked images from top to bottom + * Note: The length must be already set! + * + * @param texture background + * @return this + */ + public CycleButtonWidget stateOverlay(UITexture texture) { + for (int i = 0; i < this.length; i++) { + float a = 1f / this.length; + this.overlay[i] = texture.getSubArea(0, i * a, 1, i * a + a); + } + return this; + } + + /** + * Sets the state dependent hover background. The images should be vertically stacked images from top to bottom + * Note: The length must be already set! + * + * @param texture background + * @return this + */ + public CycleButtonWidget stateHoverBackground(UITexture texture) { + for (int i = 0; i < this.length; i++) { float a = 1f / this.length; - return texture.getSubArea(0, val * a, 1, val * a + a); - }); + this.hoverBackground[i] = texture.getSubArea(0, i * a, 1, i * a + a); + } + return this; + } + + /** + * Sets the state dependent hover overlay. The images should be vertically stacked images from top to bottom + * Note: The length must be already set! + * + * @param texture background + * @return this + */ + public CycleButtonWidget stateHoverOverlay(UITexture texture) { + for (int i = 0; i < this.length; i++) { + float a = 1f / this.length; + this.hoverOverlay[i] = texture.getSubArea(0, i * a, 1, i * a + a); + } + return this; + } + + public CycleButtonWidget stateBackground(int state, IDrawable drawable) { + this.background = addToArray(this.background, drawable, state); + return this; + } + + public CycleButtonWidget stateHoverBackground(int state, IDrawable drawable) { + this.hoverBackground = addToArray(this.hoverBackground, drawable, state); + return this; + } + + public CycleButtonWidget stateOverlay(int state, IDrawable drawable) { + this.overlay = addToArray(this.overlay, drawable, state); + return this; + } + + public CycleButtonWidget stateHoverOverlay(int state, IDrawable drawable) { + this.hoverOverlay = addToArray(this.hoverOverlay, drawable, state); + return this; + } + + public CycleButtonWidget stateBackground(boolean state, IDrawable drawable) { + this.background = addToArray(this.background, drawable, state ? 1 : 0); + return this; + } + + public CycleButtonWidget stateHoverBackground(boolean state, IDrawable drawable) { + this.hoverBackground = addToArray(this.hoverBackground, drawable, state ? 1 : 0); + return this; + } + + public CycleButtonWidget stateOverlay(boolean state, IDrawable drawable) { + this.overlay = addToArray(this.overlay, drawable, state ? 1 : 0); + return this; + } + + public CycleButtonWidget stateHoverOverlay(boolean state, IDrawable drawable) { + this.hoverOverlay = addToArray(this.hoverOverlay, drawable, state ? 1 : 0); + return this; + } + + public > CycleButtonWidget stateBackground(T state, IDrawable drawable) { + this.background = addToArray(this.background, drawable, state.ordinal()); + return this; + } + + public > CycleButtonWidget stateHoverBackground(T state, IDrawable drawable) { + this.hoverBackground = addToArray(this.hoverBackground, drawable, state.ordinal()); + return this; + } + + public > CycleButtonWidget stateOverlay(T state, IDrawable drawable) { + this.overlay = addToArray(this.overlay, drawable, state.ordinal()); + return this; + } + + public > CycleButtonWidget stateHoverOverlay(T state, IDrawable drawable) { + this.hoverOverlay = addToArray(this.hoverOverlay, drawable, state.ordinal()); + return this; } /** @@ -189,9 +314,27 @@ public CycleButtonWidget length(int length) { while (this.stateTooltip.size() > this.length) { this.stateTooltip.remove(this.stateTooltip.size() - 1); } + this.background = checkArray(this.background, length); + this.overlay = checkArray(this.overlay, length); + this.hoverBackground = checkArray(this.hoverBackground, length); + this.hoverOverlay = checkArray(this.hoverOverlay, length); return this; } + private static IDrawable[] checkArray(IDrawable[] array, int length) { + if (array == null) return new IDrawable[length]; + return array.length < length ? Arrays.copyOf(array, length) : array; + } + + private IDrawable[] addToArray(IDrawable[] array, IDrawable drawable, int index) { + if (index < 0) throw new IndexOutOfBoundsException(); + if (array == null || index >= array.length) { + array = new IDrawable[(int) (Math.ceil((index + 1) / 4.0) * 4)]; + } + array[index] = drawable; + return array; + } + public CycleButtonWidget tooltip(int index, Consumer builder) { builder.accept(this.stateTooltip.get(index)); return this; From 71ce4a36ba8dae6f71ea62346a1f17cfedef7c58 Mon Sep 17 00:00:00 2001 From: brachy84 Date: Sat, 27 Jul 2024 20:37:33 +0200 Subject: [PATCH 2/3] "minor" changes - abstract cycle & toggle button - helper method for drawable arrays - add widget property for widget theme override - and some minor things --- .../com/cleanroommc/modularui/api/ITheme.java | 12 +- .../cleanroommc/modularui/api/IThemeApi.java | 6 + .../modularui/api/drawable/IDrawable.java | 11 + .../modularui/api/value/IBoolValue.java | 12 +- .../api/value/sync/IBoolSyncValue.java | 17 +- .../modularui/api/widget/ITooltip.java | 24 +- .../modularui/screen/ModularPanel.java | 2 +- .../cleanroommc/modularui/test/TestTile.java | 2 +- .../modularui/theme/AbstractDefaultTheme.java | 6 +- .../cleanroommc/modularui/theme/Theme.java | 6 +- .../cleanroommc/modularui/theme/ThemeAPI.java | 21 +- .../modularui/theme/ThemeManager.java | 1 + ...nTheme.java => WidgetThemeSelectable.java} | 14 +- .../value/sync/BooleanSyncValue.java | 13 +- .../cleanroommc/modularui/widget/Widget.java | 57 ++-- .../widgets/AbstractCycleButtonWidget.java | 281 ++++++++++++++++ .../modularui/widgets/ButtonWidget.java | 2 +- .../modularui/widgets/CycleButtonWidget.java | 307 ++---------------- .../modularui/widgets/FluidSlot.java | 2 +- .../modularui/widgets/ItemSlot.java | 2 +- .../modularui/widgets/PageButton.java | 6 +- .../modularui/widgets/TextWidget.java | 13 - .../modularui/widgets/ToggleButton.java | 80 +++-- .../textfield/BaseTextFieldWidget.java | 2 +- 24 files changed, 490 insertions(+), 409 deletions(-) rename src/main/java/com/cleanroommc/modularui/theme/{WidgetToggleButtonTheme.java => WidgetThemeSelectable.java} (70%) create mode 100644 src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java diff --git a/src/main/java/com/cleanroommc/modularui/api/ITheme.java b/src/main/java/com/cleanroommc/modularui/api/ITheme.java index f2d302dfd..70a3e150f 100644 --- a/src/main/java/com/cleanroommc/modularui/api/ITheme.java +++ b/src/main/java/com/cleanroommc/modularui/api/ITheme.java @@ -4,7 +4,7 @@ import com.cleanroommc.modularui.theme.WidgetSlotTheme; import com.cleanroommc.modularui.theme.WidgetTextFieldTheme; import com.cleanroommc.modularui.theme.WidgetTheme; -import com.cleanroommc.modularui.theme.WidgetToggleButtonTheme; +import com.cleanroommc.modularui.theme.WidgetThemeSelectable; /** * A theme is parsed from json and contains style information like color or background texture. @@ -48,10 +48,18 @@ static ITheme get(String id) { WidgetTextFieldTheme getTextFieldTheme(); - WidgetToggleButtonTheme getToggleButtonTheme(); + WidgetThemeSelectable getToggleButtonTheme(); WidgetTheme getWidgetTheme(String id); + default T getWidgetTheme(Class clazz, String id) { + WidgetTheme theme = getWidgetTheme(id); + if (clazz.isInstance(theme)) { + return (T) theme; + } + return null; + } + int getOpenCloseAnimationOverride(); boolean getSmoothProgressBarOverride(); diff --git a/src/main/java/com/cleanroommc/modularui/api/IThemeApi.java b/src/main/java/com/cleanroommc/modularui/api/IThemeApi.java index e84d81819..10aec7b7a 100644 --- a/src/main/java/com/cleanroommc/modularui/api/IThemeApi.java +++ b/src/main/java/com/cleanroommc/modularui/api/IThemeApi.java @@ -45,6 +45,12 @@ static IThemeApi get() { */ boolean hasTheme(String id); + /** + * @param id id of the widget theme + * @return if a widget theme with the id is registered + */ + boolean hasWidgetTheme(String id); + /** * Registers a theme json object. Themes from resource packs always have greater priority. * diff --git a/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java b/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java index b9e0f89f1..88700eec3 100644 --- a/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java +++ b/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.api.drawable; +import com.cleanroommc.modularui.drawable.DrawableArray; import com.cleanroommc.modularui.drawable.Icon; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; @@ -17,6 +18,16 @@ */ public interface IDrawable { + static IDrawable of(IDrawable... drawables) { + if (drawables == null || drawables.length == 0) { + return null; + } else if (drawables.length == 1) { + return drawables[0]; + } else { + return new DrawableArray(drawables); + } + } + /** * Draws this drawable at the given position with the given size. * diff --git a/src/main/java/com/cleanroommc/modularui/api/value/IBoolValue.java b/src/main/java/com/cleanroommc/modularui/api/value/IBoolValue.java index a8a3a8420..b0769120d 100644 --- a/src/main/java/com/cleanroommc/modularui/api/value/IBoolValue.java +++ b/src/main/java/com/cleanroommc/modularui/api/value/IBoolValue.java @@ -1,8 +1,18 @@ package com.cleanroommc.modularui.api.value; -public interface IBoolValue extends IValue { +public interface IBoolValue extends IValue, IIntValue { boolean getBoolValue(); void setBoolValue(boolean val); + + @Override + default int getIntValue() { + return getBoolValue() ? 1 : 0; + } + + @Override + default void setIntValue(int val) { + setBoolValue(val == 1); + } } diff --git a/src/main/java/com/cleanroommc/modularui/api/value/sync/IBoolSyncValue.java b/src/main/java/com/cleanroommc/modularui/api/value/sync/IBoolSyncValue.java index 6c3e64852..c73b5dedd 100644 --- a/src/main/java/com/cleanroommc/modularui/api/value/sync/IBoolSyncValue.java +++ b/src/main/java/com/cleanroommc/modularui/api/value/sync/IBoolSyncValue.java @@ -7,7 +7,7 @@ * * @param value type */ -public interface IBoolSyncValue extends IValueSyncHandler, IBoolValue { +public interface IBoolSyncValue extends IValueSyncHandler, IBoolValue, IIntSyncValue { @Override default void setBoolValue(boolean val) { @@ -19,4 +19,19 @@ default void setBoolValue(boolean val, boolean setSource) { } void setBoolValue(boolean value, boolean setSource, boolean sync); + + @Override + default void setIntValue(int value, boolean setSource, boolean sync) { + setBoolValue(value == 1, setSource, sync); + } + + @Override + default int getIntValue() { + return IBoolValue.super.getIntValue(); + } + + @Override + default void setIntValue(int val) { + IBoolValue.super.setIntValue(val); + } } diff --git a/src/main/java/com/cleanroommc/modularui/api/widget/ITooltip.java b/src/main/java/com/cleanroommc/modularui/api/widget/ITooltip.java index 75f668ae7..74aaf00e0 100644 --- a/src/main/java/com/cleanroommc/modularui/api/widget/ITooltip.java +++ b/src/main/java/com/cleanroommc/modularui/api/widget/ITooltip.java @@ -52,18 +52,40 @@ default W getThis() { * @return this */ default W tooltip(Consumer tooltipConsumer) { + return tooltipStatic(tooltipConsumer); + } + + /** + * Helper method to call tooltip setters within a widget tree initialisation. + * Only called once. + * + * @param tooltipConsumer tooltip function + * @return this + */ + default W tooltipStatic(Consumer tooltipConsumer) { tooltipConsumer.accept(tooltip()); return getThis(); } /** * Sets a tooltip builder. The builder will be called every time the tooltip is marked dirty. - * Should be used for dynamic tooltips + * Should be used for dynamic tooltips. * * @param tooltipBuilder tooltip function * @return this */ default W tooltipBuilder(Consumer tooltipBuilder) { + return tooltipDynamic(tooltipBuilder); + } + + /** + * Sets a tooltip builder. The builder will be called every time the tooltip is marked dirty. + * Should be used for dynamic tooltips. + * + * @param tooltipBuilder tooltip function + * @return this + */ + default W tooltipDynamic(Consumer tooltipBuilder) { tooltip().tooltipBuilder(tooltipBuilder); return getThis(); } diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java index 125167cc4..8a67d8689 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java @@ -131,7 +131,7 @@ public boolean hasParent() { } @Override - public WidgetTheme getWidgetTheme(ITheme theme) { + public WidgetTheme getWidgetThemeInternal(ITheme theme) { return theme.getPanelTheme(); } diff --git a/src/main/java/com/cleanroommc/modularui/test/TestTile.java b/src/main/java/com/cleanroommc/modularui/test/TestTile.java index 7d2d2ad52..632a9cf8d 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestTile.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestTile.java @@ -227,7 +227,7 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) .coverChildrenHeight() .child(new CycleButtonWidget() .size(14, 14) - .length(3) + .stateCount(3) .stateBackground(GuiTextures.CYCLE_BUTTON_DEMO) .value(new IntSyncValue(() -> this.val2, val -> this.val2 = val)) .margin(8, 0)) diff --git a/src/main/java/com/cleanroommc/modularui/theme/AbstractDefaultTheme.java b/src/main/java/com/cleanroommc/modularui/theme/AbstractDefaultTheme.java index 246d05ec5..f0b820f59 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/AbstractDefaultTheme.java +++ b/src/main/java/com/cleanroommc/modularui/theme/AbstractDefaultTheme.java @@ -11,7 +11,7 @@ public abstract class AbstractDefaultTheme implements ITheme { private WidgetSlotTheme itemSlot; private WidgetSlotTheme fluidSlot; private WidgetTextFieldTheme textField; - private WidgetToggleButtonTheme toggleButtonTheme; + private WidgetThemeSelectable toggleButtonTheme; @Override public ITheme getParentTheme() { @@ -59,9 +59,9 @@ public WidgetTextFieldTheme getTextFieldTheme() { } @Override - public WidgetToggleButtonTheme getToggleButtonTheme() { + public WidgetThemeSelectable getToggleButtonTheme() { if (this.toggleButtonTheme == null) { - this.toggleButtonTheme = (WidgetToggleButtonTheme) getWidgetTheme(Theme.TOGGLE_BUTTON); + this.toggleButtonTheme = (WidgetThemeSelectable) getWidgetTheme(Theme.TOGGLE_BUTTON); } return this.toggleButtonTheme; } diff --git a/src/main/java/com/cleanroommc/modularui/theme/Theme.java b/src/main/java/com/cleanroommc/modularui/theme/Theme.java index 674274e65..701806095 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/Theme.java +++ b/src/main/java/com/cleanroommc/modularui/theme/Theme.java @@ -29,7 +29,7 @@ public class Theme implements ITheme { private final WidgetSlotTheme itemSlotTheme; private final WidgetSlotTheme fluidSlotTheme; private final WidgetTextFieldTheme textFieldTheme; - private final WidgetToggleButtonTheme toggleButtonTheme; + private final WidgetThemeSelectable toggleButtonTheme; private int openCloseAnimationOverride = -1; private Boolean smoothProgressBarOverride = null; @@ -61,7 +61,7 @@ public class Theme implements ITheme { this.itemSlotTheme = (WidgetSlotTheme) this.widgetThemes.get(ITEM_SLOT); this.fluidSlotTheme = (WidgetSlotTheme) this.widgetThemes.get(FLUID_SLOT); this.textFieldTheme = (WidgetTextFieldTheme) this.widgetThemes.get(TEXT_FIELD); - this.toggleButtonTheme = (WidgetToggleButtonTheme) this.widgetThemes.get(TOGGLE_BUTTON); + this.toggleButtonTheme = (WidgetThemeSelectable) this.widgetThemes.get(TOGGLE_BUTTON); } void setOpenCloseAnimationOverride(int override) { @@ -111,7 +111,7 @@ public WidgetTextFieldTheme getTextFieldTheme() { } @Override - public WidgetToggleButtonTheme getToggleButtonTheme() { + public WidgetThemeSelectable getToggleButtonTheme() { return this.toggleButtonTheme; } diff --git a/src/main/java/com/cleanroommc/modularui/theme/ThemeAPI.java b/src/main/java/com/cleanroommc/modularui/theme/ThemeAPI.java index 3b1600d06..f7788c280 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/ThemeAPI.java +++ b/src/main/java/com/cleanroommc/modularui/theme/ThemeAPI.java @@ -23,7 +23,7 @@ public class ThemeAPI implements IThemeApi { public static final String DEFAULT = "DEFAULT"; public static final ITheme DEFAULT_DEFAULT = new DefaultTheme(); - private final Object2ObjectMap THEMES = new Object2ObjectOpenHashMap<>(); + private final Object2ObjectMap themes = new Object2ObjectOpenHashMap<>(); protected final Object2ObjectMap> defaultThemes = new Object2ObjectOpenHashMap<>(); protected final Object2ObjectMap defaultWidgetThemes = new Object2ObjectOpenHashMap<>(); protected final Object2ObjectMap widgetThemeFunctions = new Object2ObjectOpenHashMap<>(); @@ -36,8 +36,8 @@ private ThemeAPI() { registerWidgetTheme(Theme.ITEM_SLOT, new WidgetSlotTheme(GuiTextures.SLOT_ITEM, Color.withAlpha(Color.WHITE.main, 0x60)), WidgetSlotTheme::new); registerWidgetTheme(Theme.FLUID_SLOT, new WidgetSlotTheme(GuiTextures.SLOT_FLUID, Color.withAlpha(Color.WHITE.main, 0x60)), WidgetSlotTheme::new); registerWidgetTheme(Theme.TEXT_FIELD, new WidgetTextFieldTheme(0xFF2F72A8), (parent, json, fallback) -> new WidgetTextFieldTheme(parent, fallback, json)); - registerWidgetTheme(Theme.TOGGLE_BUTTON, new WidgetToggleButtonTheme(GuiTextures.MC_BUTTON, GuiTextures.MC_BUTTON_HOVERED, Color.WHITE.main, Color.WHITE.main, true, - GuiTextures.MC_BUTTON_DISABLED, IDrawable.NONE, Color.WHITE.main, Color.WHITE.main, true), WidgetToggleButtonTheme::new); + registerWidgetTheme(Theme.TOGGLE_BUTTON, new WidgetThemeSelectable(GuiTextures.MC_BUTTON, GuiTextures.MC_BUTTON_HOVERED, Color.WHITE.main, Color.WHITE.main, true, + GuiTextures.MC_BUTTON_DISABLED, IDrawable.NONE, Color.WHITE.main, Color.WHITE.main, true), WidgetThemeSelectable::new); } @Override @@ -47,12 +47,17 @@ public ITheme getDefaultTheme() { @Override public @NotNull ITheme getTheme(String id) { - return this.THEMES.getOrDefault(id, getDefaultTheme()); + return this.themes.getOrDefault(id, getDefaultTheme()); } @Override public boolean hasTheme(String id) { - return this.THEMES.containsKey(id); + return this.themes.containsKey(id); + } + + @Override + public boolean hasWidgetTheme(String id) { + return this.widgetThemeFunctions.containsKey(id); } @Override @@ -105,14 +110,14 @@ public void registerWidgetTheme(String id, WidgetTheme defaultTheme, WidgetTheme // Internals void registerTheme(ITheme theme) { - if (this.THEMES.containsKey(theme.getId())) { + if (this.themes.containsKey(theme.getId())) { throw new IllegalArgumentException("Theme with id " + theme.getId() + " already exists!"); } - this.THEMES.put(theme.getId(), theme); + this.themes.put(theme.getId(), theme); } void onReload() { - this.THEMES.clear(); + this.themes.clear(); this.jsonScreenThemes.clear(); registerTheme(DEFAULT_DEFAULT); } diff --git a/src/main/java/com/cleanroommc/modularui/theme/ThemeManager.java b/src/main/java/com/cleanroommc/modularui/theme/ThemeManager.java index 689288a8f..5dfef5f50 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/ThemeManager.java +++ b/src/main/java/com/cleanroommc/modularui/theme/ThemeManager.java @@ -294,6 +294,7 @@ private Theme deserialize() { widgetThemes.put(entry.getKey(), entry.getValue().parse(parentWidgetTheme, widgetThemeJson, jsonBuilder.getJson())); } Theme theme = new Theme(this.id, parent, widgetThemes); + // TODO: bad implementation if (jsonBuilder.getJson().has("openCloseAnimation")) { theme.setOpenCloseAnimationOverride(jsonBuilder.getJson().get("openCloseAnimation").getAsInt()); } diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetToggleButtonTheme.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeSelectable.java similarity index 70% rename from src/main/java/com/cleanroommc/modularui/theme/WidgetToggleButtonTheme.java rename to src/main/java/com/cleanroommc/modularui/theme/WidgetThemeSelectable.java index b8da684b8..cdc1d043a 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/WidgetToggleButtonTheme.java +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeSelectable.java @@ -6,21 +6,21 @@ import com.google.gson.JsonObject; import org.jetbrains.annotations.Nullable; -public class WidgetToggleButtonTheme extends WidgetTheme { +public class WidgetThemeSelectable extends WidgetTheme { private final WidgetTheme selected; - public WidgetToggleButtonTheme(@Nullable IDrawable background, @Nullable IDrawable hoverBackground, - int color, int textColor, boolean textShadow, - @Nullable IDrawable selectedBackground, @Nullable IDrawable selectedHoverBackground, - int selectedColor, int selectedTextColor, boolean selectedTextShadow) { + public WidgetThemeSelectable(@Nullable IDrawable background, @Nullable IDrawable hoverBackground, + int color, int textColor, boolean textShadow, + @Nullable IDrawable selectedBackground, @Nullable IDrawable selectedHoverBackground, + int selectedColor, int selectedTextColor, boolean selectedTextShadow) { super(background, hoverBackground, color, textColor, textShadow); this.selected = new WidgetTheme(selectedBackground, selectedHoverBackground, selectedColor, selectedTextColor, selectedTextShadow); } - public WidgetToggleButtonTheme(WidgetTheme parent, JsonObject json, JsonObject fallback) { + public WidgetThemeSelectable(WidgetTheme parent, JsonObject json, JsonObject fallback) { super(parent, json, fallback); - WidgetToggleButtonTheme parentWTBT = (WidgetToggleButtonTheme) parent; + WidgetThemeSelectable parentWTBT = (WidgetThemeSelectable) parent; IDrawable selectedBackground = JsonHelper.deserializeWithFallback(json, fallback, IDrawable.class, parentWTBT.getSelected().getBackground(), "selectedBackground"); IDrawable selectedHoverBackground = JsonHelper.deserializeWithFallback(json, fallback, IDrawable.class, parentWTBT.getSelected().getHoverBackground(), "selectedHoverBackground"); int selectedColor = JsonHelper.getColorWithFallback(json, fallback, parentWTBT.getSelected().getColor(), "selectedColor"); diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/BooleanSyncValue.java b/src/main/java/com/cleanroommc/modularui/value/sync/BooleanSyncValue.java index 9b28fa474..2e1ffac9a 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/BooleanSyncValue.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/BooleanSyncValue.java @@ -1,7 +1,6 @@ package com.cleanroommc.modularui.value.sync; import com.cleanroommc.modularui.api.value.sync.IBoolSyncValue; -import com.cleanroommc.modularui.api.value.sync.IIntSyncValue; import com.cleanroommc.modularui.api.value.sync.IStringSyncValue; import com.cleanroommc.modularui.network.NetworkUtils; import com.cleanroommc.modularui.utils.BooleanConsumer; @@ -14,7 +13,7 @@ import java.util.function.BooleanSupplier; -public class BooleanSyncValue extends ValueSyncHandler implements IBoolSyncValue, IIntSyncValue, IStringSyncValue { +public class BooleanSyncValue extends ValueSyncHandler implements IBoolSyncValue, IStringSyncValue { private final BooleanSupplier getter; private final BooleanConsumer setter; @@ -87,16 +86,6 @@ public void read(PacketBuffer buffer) { setBoolValue(buffer.readBoolean(), true, false); } - @Override - public void setIntValue(int value, boolean setSource, boolean sync) { - setBoolValue(value == 1, setSource, sync); - } - - @Override - public int getIntValue() { - return this.cache ? 1 : 0; - } - @Override public void setStringValue(String value, boolean setSource, boolean sync) { setBoolValue(Boolean.getBoolean(value), setSource, sync); diff --git a/src/main/java/com/cleanroommc/modularui/widget/Widget.java b/src/main/java/com/cleanroommc/modularui/widget/Widget.java index 71e11fdac..a5f2fc376 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/Widget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/Widget.java @@ -1,11 +1,11 @@ package com.cleanroommc.modularui.widget; import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.IThemeApi; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.layout.IResizeable; import com.cleanroommc.modularui.api.value.IValue; import com.cleanroommc.modularui.api.widget.*; -import com.cleanroommc.modularui.drawable.DrawableArray; import com.cleanroommc.modularui.factory.GuiData; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.ModularScreen; @@ -54,6 +54,7 @@ public class Widget> implements IWidget, IPositioned, ITo @Nullable private IDrawable hoverBackground = null; @Nullable private IDrawable hoverOverlay = null; @Nullable private Tooltip tooltip; + @Nullable private String widgetThemeOverride = null; // listener @Nullable private List guiActionListeners; @Nullable private Consumer onUpdateListener; @@ -222,47 +223,37 @@ public void markTooltipDirty() { } } - public W background(IDrawable... background) { - if (background == null || background.length == 0) { - this.background = null; - } else if (background.length == 1) { - this.background = background[0]; - } else { - this.background = new DrawableArray(background); + @ApiStatus.OverrideOnly + protected WidgetTheme getWidgetThemeInternal(ITheme theme) { + return theme.getFallback(); + } + + @ApiStatus.NonExtendable + @Override + public final WidgetTheme getWidgetTheme(ITheme theme) { + if (this.widgetThemeOverride != null) { + return theme.getWidgetTheme(this.widgetThemeOverride); } + return getWidgetThemeInternal(theme); + } + + public W background(IDrawable... background) { + this.background = IDrawable.of(background); return getThis(); } public W overlay(IDrawable... overlay) { - if (overlay == null || overlay.length == 0) { - this.overlay = null; - } else if (overlay.length == 1) { - this.overlay = overlay[0]; - } else { - this.overlay = new DrawableArray(overlay); - } + this.overlay = IDrawable.of(overlay); return getThis(); } public W hoverBackground(IDrawable... background) { - if (background == null || background.length == 0) { - this.hoverBackground = null; - } else if (background.length == 1) { - this.hoverBackground = background[0]; - } else { - this.hoverBackground = new DrawableArray(background); - } + this.hoverBackground = IDrawable.of(background); return getThis(); } public W hoverOverlay(IDrawable... overlay) { - if (overlay == null || overlay.length == 0) { - this.hoverOverlay = null; - } else if (overlay.length == 1) { - this.hoverOverlay = overlay[0]; - } else { - this.hoverOverlay = new DrawableArray(overlay); - } + this.hoverOverlay = IDrawable.of(overlay); return getThis(); } @@ -274,6 +265,14 @@ public W disableHoverOverlay() { return hoverOverlay(IDrawable.NONE); } + public W widgetTheme(String s) { + if (!IThemeApi.get().hasWidgetTheme(s)) { + throw new IllegalArgumentException("No widget theme for id '" + s + "' exists."); + } + this.widgetThemeOverride = s; + return getThis(); + } + // -------------- // === Events === // -------------- diff --git a/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java new file mode 100644 index 000000000..68cce111f --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java @@ -0,0 +1,281 @@ +package com.cleanroommc.modularui.widgets; + +import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.value.IBoolValue; +import com.cleanroommc.modularui.api.value.IEnumValue; +import com.cleanroommc.modularui.api.value.IIntValue; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.drawable.UITexture; +import com.cleanroommc.modularui.screen.Tooltip; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.value.IntValue; +import com.cleanroommc.modularui.value.sync.SyncHandler; +import com.cleanroommc.modularui.widget.Widget; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.IntFunction; + +public class AbstractCycleButtonWidget> extends Widget implements Interactable { + + private int stateCount = 1; + private IIntValue intValue; + private int lastValue = -1; + protected IDrawable[] background = null; + protected IDrawable[] hoverBackground = null; + protected IDrawable[] overlay = null; + protected IDrawable[] hoverOverlay = null; + private final List stateTooltip = new ArrayList<>(); + + @Override + public void onInit() { + if (this.intValue == null) { + this.intValue = new IntValue(0); + } + } + + @Override + public boolean isValidSyncHandler(SyncHandler syncHandler) { + this.intValue = castIfTypeElseNull(syncHandler, IIntValue.class); + return this.intValue != null; + } + + protected int getState() { + int val = this.intValue.getIntValue(); + if (val != this.lastValue) { + setState(val, false); + } + return val; + } + + public void next() { + int state = (getState() + 1) % this.stateCount; + + setState(state, true); + } + + public void prev() { + int state = getState(); + if (--state == -1) { + state = this.stateCount - 1; + } + setState(state, true); + } + + public void setState(int state, boolean setSource) { + if (state < 0 || state >= this.stateCount) { + throw new IndexOutOfBoundsException("CycleButton state out of bounds"); + } + if (setSource) { + this.intValue.setIntValue(state); + } + this.lastValue = state; + } + + @Override + public @NotNull Result onMousePressed(int mouseButton) { + switch (mouseButton) { + case 0: + next(); + Interactable.playButtonClickSound(); + return Result.SUCCESS; + case 1: + prev(); + Interactable.playButtonClickSound(); + return Result.SUCCESS; + } + return Result.IGNORE; + } + + @Override + public WidgetTheme getWidgetThemeInternal(ITheme theme) { + return theme.getButtonTheme(); + } + + @Override + public IDrawable getCurrentBackground(ITheme theme, WidgetTheme widgetTheme) { + // make sure texture is up-to-date + int state = getState(); + if (isHovering() && this.hoverBackground != null && this.hoverBackground[state] != null) { + return this.hoverBackground[state]; + } + return this.background != null && this.background[state] != null ? this.background[state] : super.getCurrentBackground(theme, widgetTheme); + } + + @Override + public IDrawable getCurrentOverlay(ITheme theme, WidgetTheme widgetTheme) { + int state = getState(); + if (isHovering() && this.hoverOverlay != null && this.hoverOverlay[state] != null) { + return this.hoverOverlay[state]; + } + return this.overlay != null && this.overlay[state] != null ? this.overlay[state] : super.getCurrentBackground(theme, widgetTheme); + } + + @Override + public boolean hasTooltip() { + int state = getState(); + return super.hasTooltip() || (this.stateTooltip.size() > state && !this.stateTooltip.get(state).isEmpty()); + } + + @Override + public void markTooltipDirty() { + super.markTooltipDirty(); + for (Tooltip tooltip : this.stateTooltip) { + tooltip.markDirty(); + } + getState(); + } + + @Override + public @Nullable Tooltip getTooltip() { + Tooltip tooltip = super.getTooltip(); + if (tooltip == null || tooltip.isEmpty()) { + return this.stateTooltip.get(getState()); + } + return tooltip; + } + + protected W value(IIntValue value) { + this.intValue = value; + setValue(value); + if (value instanceof IEnumValue enumValue) { + stateCount(enumValue.getEnumClass().getEnumConstants().length); + } else if (value instanceof IBoolValue) { + stateCount(2); + } + return getThis(); + } + + /** + * Sets the state dependent background. The images should be vertically stacked images from top to bottom + * Note: The length must be already set! + * + * @param texture background + * @return this + */ + public W stateBackground(UITexture texture) { + for (int i = 0; i < this.stateCount; i++) { + float a = 1f / this.stateCount; + this.background[i] = texture.getSubArea(0, i * a, 1, i * a + a); + } + return getThis(); + } + + /** + * Sets the state dependent overlay. The images should be vertically stacked images from top to bottom + * Note: The length must be already set! + * + * @param texture background + * @return this + */ + public W stateOverlay(UITexture texture) { + for (int i = 0; i < this.stateCount; i++) { + float a = 1f / this.stateCount; + this.overlay[i] = texture.getSubArea(0, i * a, 1, i * a + a); + } + return getThis(); + } + + /** + * Sets the state dependent hover background. The images should be vertically stacked images from top to bottom + * Note: The length must be already set! + * + * @param texture background + * @return this + */ + public W stateHoverBackground(UITexture texture) { + for (int i = 0; i < this.stateCount; i++) { + float a = 1f / this.stateCount; + this.hoverBackground[i] = texture.getSubArea(0, i * a, 1, i * a + a); + } + return getThis(); + } + + /** + * Sets the state dependent hover overlay. The images should be vertically stacked images from top to bottom + * Note: The length must be already set! + * + * @param texture background + * @return this + */ + public W stateHoverOverlay(UITexture texture) { + for (int i = 0; i < this.stateCount; i++) { + float a = 1f / this.stateCount; + this.hoverOverlay[i] = texture.getSubArea(0, i * a, 1, i * a + a); + } + return getThis(); + } + + /** + * Adds a line to the tooltip + */ + protected W addTooltip(int state, IDrawable tooltip) { + if (state >= this.stateTooltip.size() || state < 0) { + throw new IndexOutOfBoundsException(); + } + this.stateTooltip.get(state).addLine(tooltip); + return getThis(); + } + + /** + * Adds a line to the tooltip + */ + protected W addTooltip(int state, String tooltip) { + return addTooltip(state, IKey.str(tooltip)); + } + + protected W stateCount(int stateCount) { + this.stateCount = stateCount; + // adjust tooltip buffer size + while (this.stateTooltip.size() < this.stateCount) { + this.stateTooltip.add(new Tooltip(this)); + } + while (this.stateTooltip.size() > this.stateCount) { + this.stateTooltip.remove(this.stateTooltip.size() - 1); + } + this.background = checkArray(this.background, stateCount); + this.overlay = checkArray(this.overlay, stateCount); + this.hoverBackground = checkArray(this.hoverBackground, stateCount); + this.hoverOverlay = checkArray(this.hoverOverlay, stateCount); + return getThis(); + } + + private static IDrawable[] checkArray(IDrawable[] array, int length) { + if (array == null) return new IDrawable[length]; + return array.length < length ? Arrays.copyOf(array, length) : array; + } + + protected IDrawable[] addToArray(IDrawable[] array, IDrawable[] drawable, int index) { + return addToArray(array, IDrawable.of(drawable), index); + } + + protected IDrawable[] addToArray(IDrawable[] array, IDrawable drawable, int index) { + if (index < 0) throw new IndexOutOfBoundsException(); + if (array == null || index >= array.length) { + IDrawable[] copy = new IDrawable[(int) (Math.ceil((index + 1) / 4.0) * 4)]; + if (array != null) { + System.arraycopy(array, 0, copy, 0, array.length); + } + array = copy; + } + array[index] = drawable; + return array; + } + + protected W tooltip(int index, Consumer builder) { + builder.accept(this.stateTooltip.get(index)); + return getThis(); + } + + protected W tooltipBuilder(int index, Consumer builder) { + this.stateTooltip.get(index).tooltipBuilder(builder); + return getThis(); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java index 521f0500a..18b61f73b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java @@ -44,7 +44,7 @@ public boolean isValidSyncHandler(SyncHandler syncHandler) { } @Override - public WidgetTheme getWidgetTheme(ITheme theme) { + public WidgetTheme getWidgetThemeInternal(ITheme theme) { return theme.getButtonTheme(); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java index 9968ab711..fb638a54b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java @@ -1,347 +1,96 @@ package com.cleanroommc.modularui.widgets; -import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IDrawable; -import com.cleanroommc.modularui.api.drawable.IKey; -import com.cleanroommc.modularui.api.value.IBoolValue; -import com.cleanroommc.modularui.api.value.IEnumValue; import com.cleanroommc.modularui.api.value.IIntValue; -import com.cleanroommc.modularui.api.widget.Interactable; -import com.cleanroommc.modularui.drawable.UITexture; import com.cleanroommc.modularui.screen.Tooltip; -import com.cleanroommc.modularui.theme.WidgetTheme; -import com.cleanroommc.modularui.value.IntValue; -import com.cleanroommc.modularui.value.sync.SyncHandler; -import com.cleanroommc.modularui.widget.Widget; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.function.Consumer; -import java.util.function.IntFunction; - -public class CycleButtonWidget extends Widget implements Interactable { - - private int length = 1; - private IIntValue intValue; - private int lastValue = -1; - private IDrawable[] background = null; - private IDrawable[] hoverBackground = null; - private IDrawable[] overlay = null; - private IDrawable[] hoverOverlay = null; - private final List stateTooltip = new ArrayList<>(); - - @Override - public void onInit() { - if (this.intValue == null) { - this.intValue = new IntValue(0); - } - } - - @Override - public boolean isValidSyncHandler(SyncHandler syncHandler) { - this.intValue = castIfTypeElseNull(syncHandler, IIntValue.class); - return this.intValue != null; - } - - private int getState() { - int val = this.intValue.getIntValue(); - if (val != this.lastValue) { - setState(val, false); - } - return val; - } - - public void next() { - int state = getState(); - if (++state == this.length) { - state = 0; - } - setState(state, true); - } - - public void prev() { - int state = getState(); - if (--state == -1) { - state = this.length - 1; - } - setState(state, true); - } - public void setState(int state, boolean setSource) { - if (state < 0 || state >= this.length) { - throw new IndexOutOfBoundsException("CycleButton state out of bounds"); - } - if (setSource) { - this.intValue.setIntValue(state); - } - this.lastValue = state; - } - - @Override - public @NotNull Result onMousePressed(int mouseButton) { - switch (mouseButton) { - case 0: - next(); - Interactable.playButtonClickSound(); - return Result.SUCCESS; - case 1: - prev(); - Interactable.playButtonClickSound(); - return Result.SUCCESS; - } - return Result.IGNORE; - } +public class CycleButtonWidget extends AbstractCycleButtonWidget { @Override - public WidgetTheme getWidgetTheme(ITheme theme) { - return theme.getButtonTheme(); - } - - @Override - public IDrawable getCurrentBackground(ITheme theme, WidgetTheme widgetTheme) { - // make sure texture is up-to-date - int state = getState(); - if (isHovering()) { - if (this.hoverBackground != null && this.hoverBackground[state] != null) return this.hoverBackground[state]; - return this.background != null && this.background[state] != null ? this.background[state] : super.getCurrentBackground(theme, widgetTheme); - } - return this.background != null && this.background[state] != null ? this.background[state] : super.getCurrentBackground(theme, widgetTheme); - } - - @Override - public IDrawable getCurrentOverlay(ITheme theme, WidgetTheme widgetTheme) { - int state = getState(); - if (isHovering()) { - if (this.hoverOverlay != null && this.hoverOverlay[state] != null) return this.hoverOverlay[state]; - return this.overlay != null && this.overlay[state] != null ? this.overlay[state] : super.getCurrentBackground(theme, widgetTheme); - } - return this.overlay != null && this.overlay[state] != null ? this.overlay[state] : super.getCurrentBackground(theme, widgetTheme); - } - - @Override - public boolean hasTooltip() { - int state = getState(); - return super.hasTooltip() || (this.stateTooltip.size() > state && !this.stateTooltip.get(state).isEmpty()); - } - - @Override - public void markTooltipDirty() { - super.markTooltipDirty(); - for (Tooltip tooltip : this.stateTooltip) { - tooltip.markDirty(); - } - getState(); - } - - @Override - public @Nullable Tooltip getTooltip() { - Tooltip tooltip = super.getTooltip(); - if (tooltip == null || tooltip.isEmpty()) { - return this.stateTooltip.get(getState()); - } - return tooltip; - } - public CycleButtonWidget value(IIntValue value) { - this.intValue = value; - setValue(value); - if (value instanceof IEnumValue enumValue) { - length(enumValue.getEnumClass().getEnumConstants().length); - } else if (value instanceof IBoolValue) { - length(2); - } - return this; - } - - @Deprecated - public CycleButtonWidget textureGetter(IntFunction textureGetter) { - throw new UnsupportedOperationException("'textureGetter()' is no longer supported in CycleButtonWidget. Use 'stateBackground()'"); - } - - @Deprecated - public CycleButtonWidget texture(UITexture texture) { - return stateBackground(texture); - } - - /** - * Sets the state dependent background. The images should be vertically stacked images from top to bottom - * Note: The length must be already set! - * - * @param texture background - * @return this - */ - public CycleButtonWidget stateBackground(UITexture texture) { - for (int i = 0; i < this.length; i++) { - float a = 1f / this.length; - this.background[i] = texture.getSubArea(0, i * a, 1, i * a + a); - } - return this; - } - - /** - * Sets the state dependent overlay. The images should be vertically stacked images from top to bottom - * Note: The length must be already set! - * - * @param texture background - * @return this - */ - public CycleButtonWidget stateOverlay(UITexture texture) { - for (int i = 0; i < this.length; i++) { - float a = 1f / this.length; - this.overlay[i] = texture.getSubArea(0, i * a, 1, i * a + a); - } - return this; - } - - /** - * Sets the state dependent hover background. The images should be vertically stacked images from top to bottom - * Note: The length must be already set! - * - * @param texture background - * @return this - */ - public CycleButtonWidget stateHoverBackground(UITexture texture) { - for (int i = 0; i < this.length; i++) { - float a = 1f / this.length; - this.hoverBackground[i] = texture.getSubArea(0, i * a, 1, i * a + a); - } - return this; - } - - /** - * Sets the state dependent hover overlay. The images should be vertically stacked images from top to bottom - * Note: The length must be already set! - * - * @param texture background - * @return this - */ - public CycleButtonWidget stateHoverOverlay(UITexture texture) { - for (int i = 0; i < this.length; i++) { - float a = 1f / this.length; - this.hoverOverlay[i] = texture.getSubArea(0, i * a, 1, i * a + a); - } - return this; + return super.value(value); } public CycleButtonWidget stateBackground(int state, IDrawable drawable) { this.background = addToArray(this.background, drawable, state); - return this; + return getThis(); } public CycleButtonWidget stateHoverBackground(int state, IDrawable drawable) { this.hoverBackground = addToArray(this.hoverBackground, drawable, state); - return this; + return getThis(); } public CycleButtonWidget stateOverlay(int state, IDrawable drawable) { this.overlay = addToArray(this.overlay, drawable, state); - return this; + return getThis(); } public CycleButtonWidget stateHoverOverlay(int state, IDrawable drawable) { this.hoverOverlay = addToArray(this.hoverOverlay, drawable, state); - return this; + return getThis(); } public CycleButtonWidget stateBackground(boolean state, IDrawable drawable) { - this.background = addToArray(this.background, drawable, state ? 1 : 0); - return this; + return stateBackground(state ? 1 : 0, drawable); } public CycleButtonWidget stateHoverBackground(boolean state, IDrawable drawable) { - this.hoverBackground = addToArray(this.hoverBackground, drawable, state ? 1 : 0); - return this; + return stateHoverBackground(state ? 1 : 0, drawable); } public CycleButtonWidget stateOverlay(boolean state, IDrawable drawable) { - this.overlay = addToArray(this.overlay, drawable, state ? 1 : 0); - return this; + return stateOverlay(state ? 1 : 0, drawable); } public CycleButtonWidget stateHoverOverlay(boolean state, IDrawable drawable) { - this.hoverOverlay = addToArray(this.hoverOverlay, drawable, state ? 1 : 0); - return this; + return stateHoverOverlay(state ? 1 : 0, drawable); } public > CycleButtonWidget stateBackground(T state, IDrawable drawable) { - this.background = addToArray(this.background, drawable, state.ordinal()); - return this; + return stateBackground(state.ordinal(), drawable); } public > CycleButtonWidget stateHoverBackground(T state, IDrawable drawable) { - this.hoverBackground = addToArray(this.hoverBackground, drawable, state.ordinal()); - return this; + return stateHoverBackground(state.ordinal(), drawable); } public > CycleButtonWidget stateOverlay(T state, IDrawable drawable) { - this.overlay = addToArray(this.overlay, drawable, state.ordinal()); - return this; + return stateOverlay(state.ordinal(), drawable); } public > CycleButtonWidget stateHoverOverlay(T state, IDrawable drawable) { - this.hoverOverlay = addToArray(this.hoverOverlay, drawable, state.ordinal()); - return this; + return stateHoverOverlay(state.ordinal(), drawable); } - /** - * Adds a line to the tooltip - */ - public CycleButtonWidget addTooltip(int state, IDrawable tooltip) { - if (state >= this.stateTooltip.size() || state < 0) { - throw new IndexOutOfBoundsException(); - } - this.stateTooltip.get(state).addLine(tooltip); - return this; - } - - /** - * Adds a line to the tooltip - */ + @Override public CycleButtonWidget addTooltip(int state, String tooltip) { - return addTooltip(state, IKey.str(tooltip)); + return super.addTooltip(state, tooltip); } - public CycleButtonWidget length(int length) { - this.length = length; - // adjust tooltip buffer size - while (this.stateTooltip.size() < this.length) { - this.stateTooltip.add(new Tooltip(this)); - } - while (this.stateTooltip.size() > this.length) { - this.stateTooltip.remove(this.stateTooltip.size() - 1); - } - this.background = checkArray(this.background, length); - this.overlay = checkArray(this.overlay, length); - this.hoverBackground = checkArray(this.hoverBackground, length); - this.hoverOverlay = checkArray(this.hoverOverlay, length); - return this; + @Override + public CycleButtonWidget addTooltip(int state, IDrawable tooltip) { + return super.addTooltip(state, tooltip); } - private static IDrawable[] checkArray(IDrawable[] array, int length) { - if (array == null) return new IDrawable[length]; - return array.length < length ? Arrays.copyOf(array, length) : array; + public CycleButtonWidget length(int length) { + return stateCount(length); } - private IDrawable[] addToArray(IDrawable[] array, IDrawable drawable, int index) { - if (index < 0) throw new IndexOutOfBoundsException(); - if (array == null || index >= array.length) { - array = new IDrawable[(int) (Math.ceil((index + 1) / 4.0) * 4)]; - } - array[index] = drawable; - return array; + @Override + public CycleButtonWidget stateCount(int stateCount) { + return super.stateCount(stateCount); } + @Override public CycleButtonWidget tooltip(int index, Consumer builder) { - builder.accept(this.stateTooltip.get(index)); - return this; + return super.tooltip(index, builder); } + @Override public CycleButtonWidget tooltipBuilder(int index, Consumer builder) { - this.stateTooltip.get(index).tooltipBuilder(builder); - return this; + return super.tooltipBuilder(index, builder); } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/FluidSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/FluidSlot.java index 8fc155751..43a49c71d 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/FluidSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/FluidSlot.java @@ -154,7 +154,7 @@ public void draw(GuiContext context, WidgetTheme widgetTheme) { } @Override - public WidgetSlotTheme getWidgetTheme(ITheme theme) { + public WidgetSlotTheme getWidgetThemeInternal(ITheme theme) { return theme.getFluidSlotTheme(); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java index e98776614..ac282ff4c 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java @@ -100,7 +100,7 @@ public void drawForeground(GuiContext context) { } @Override - public WidgetSlotTheme getWidgetTheme(ITheme theme) { + public WidgetSlotTheme getWidgetThemeInternal(ITheme theme) { return theme.getItemSlotTheme(); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java b/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java index 3e9ea6d57..05a0cc206 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java @@ -6,7 +6,7 @@ import com.cleanroommc.modularui.drawable.DrawableArray; import com.cleanroommc.modularui.drawable.TabTexture; import com.cleanroommc.modularui.theme.WidgetTheme; -import com.cleanroommc.modularui.theme.WidgetToggleButtonTheme; +import com.cleanroommc.modularui.theme.WidgetThemeSelectable; import com.cleanroommc.modularui.widget.Widget; import org.jetbrains.annotations.NotNull; @@ -24,8 +24,8 @@ public PageButton(int index, PagedWidget.Controller controller) { } @Override - public WidgetTheme getWidgetTheme(ITheme theme) { - WidgetToggleButtonTheme widgetTheme = theme.getToggleButtonTheme(); + public WidgetTheme getWidgetThemeInternal(ITheme theme) { + WidgetThemeSelectable widgetTheme = theme.getToggleButtonTheme(); return isActive() ? widgetTheme : widgetTheme.getSelected(); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java index 58772471f..0041640c8 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java @@ -1,10 +1,8 @@ package com.cleanroommc.modularui.widgets; -import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.drawable.TextRenderer; import com.cleanroommc.modularui.screen.viewport.GuiContext; -import com.cleanroommc.modularui.theme.Theme; import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.widget.Widget; @@ -19,7 +17,6 @@ public class TextWidget extends Widget { private float scale = 1f; public boolean colorChanged = false, shadowChanged = false; - private String widgetTheme = Theme.FALLBACK; public TextWidget(IKey key) { this.key = key; @@ -37,11 +34,6 @@ public void draw(GuiContext context, WidgetTheme widgetTheme) { renderer.draw(this.key.get()); } - @Override - public WidgetTheme getWidgetTheme(ITheme theme) { - return theme.getWidgetTheme(this.widgetTheme); - } - private TextRenderer simulate(float maxWidth) { Box padding = getArea().getPadding(); TextRenderer renderer = TextRenderer.SHARED; @@ -118,9 +110,4 @@ public TextWidget shadow(boolean shadow) { this.shadow = shadow; return this; } - - public TextWidget widgetTheme(String widgetTheme) { - this.widgetTheme = widgetTheme; - return this; - } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java b/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java index 9f53f0f67..166a76687 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java @@ -3,69 +3,67 @@ import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.value.IBoolValue; -import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.screen.Tooltip; import com.cleanroommc.modularui.theme.WidgetTheme; -import com.cleanroommc.modularui.theme.WidgetToggleButtonTheme; -import com.cleanroommc.modularui.widget.Widget; +import com.cleanroommc.modularui.theme.WidgetThemeSelectable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import java.util.function.Consumer; -public class ToggleButton extends Widget implements Interactable { +public class ToggleButton extends AbstractCycleButtonWidget { - private IBoolValue boolValue; - private IDrawable selectedBackground; - private IDrawable selectedHoverBackground; - - @Override - public @NotNull Result onMousePressed(int mouseButton) { - if (mouseButton == 0 || mouseButton == 1) { - this.boolValue.setBoolValue(!this.boolValue.getBoolValue()); - Interactable.playButtonClickSound(); - return Result.SUCCESS; - } - return Result.ACCEPT; + public ToggleButton() { + stateCount(2); } @Override - public WidgetTheme getWidgetTheme(ITheme theme) { - WidgetToggleButtonTheme widgetTheme = theme.getToggleButtonTheme(); + public WidgetTheme getWidgetThemeInternal(ITheme theme) { + WidgetThemeSelectable widgetTheme = theme.getToggleButtonTheme(); return isValueSelected() ? widgetTheme.getSelected() : widgetTheme; } - @Override - public @Nullable IDrawable getBackground() { - if (isValueSelected()) { - return this.selectedBackground; - } - return super.getBackground(); + public boolean isValueSelected() { + return getState() == 1; } - @Override - public @Nullable IDrawable getHoverBackground() { - if (isValueSelected()) { - return this.selectedHoverBackground; - } - return super.getHoverBackground(); + public ToggleButton value(IBoolValue boolValue) { + return super.value(boolValue); } - public boolean isValueSelected() { - return this.boolValue.getBoolValue(); + public ToggleButton selectedBackground(IDrawable... selectedBackground) { + this.background = addToArray(this.background, selectedBackground, 1); + return this; } - public ToggleButton value(IBoolValue boolValue) { - this.boolValue = boolValue; - setValue(boolValue); + public ToggleButton selectedHoverBackground(IDrawable... selectedHoverBackground) { + this.hoverBackground = addToArray(this.hoverBackground, selectedHoverBackground, 1); return this; } - public ToggleButton selectedBackground(IDrawable selectedBackground) { - this.selectedBackground = selectedBackground; + @Override + public ToggleButton background(IDrawable... background) { + this.background = addToArray(this.background, background, 0); return this; } - public ToggleButton selectedHoverBackground(IDrawable selectedHoverBackground) { - this.selectedHoverBackground = selectedHoverBackground; + @Override + public ToggleButton hoverBackground(IDrawable... background) { + this.hoverBackground = addToArray(this.hoverBackground, background, 0); return this; } + + public ToggleButton addTooltip(boolean selected, String tooltip) { + return super.addTooltip(selected ? 1 : 0, tooltip); + } + + public ToggleButton addTooltip(boolean selected, IDrawable tooltip) { + return super.addTooltip(selected ? 1 : 0, tooltip); + } + + public ToggleButton tooltip(boolean selected, Consumer builder) { + return super.tooltip(selected ? 1 : 0, builder); + } + + public ToggleButton tooltipBuilder(boolean selected, Consumer builder) { + return super.tooltipBuilder(selected ? 1 : 0, builder); + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java index 846483bf7..72d87d141 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java @@ -100,7 +100,7 @@ public void drawText(GuiContext context) { } @Override - public WidgetTextFieldTheme getWidgetTheme(ITheme theme) { + public WidgetTextFieldTheme getWidgetThemeInternal(ITheme theme) { return theme.getTextFieldTheme(); } From e8d7ecad81de9319faea5241ea6bd19e5b7172d7 Mon Sep 17 00:00:00 2001 From: brachy84 Date: Thu, 22 Aug 2024 20:20:40 +0200 Subject: [PATCH 3/3] minor stuff --- .../drawable/AdaptableUITexture.java | 2 +- .../modularui/drawable/UITexture.java | 14 +++++------ .../widgets/AbstractCycleButtonWidget.java | 1 + .../modularui/widgets/ToggleButton.java | 24 ++++++++++++------- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java b/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java index 342ba1a92..5698905a2 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java @@ -25,7 +25,7 @@ public class AdaptableUITexture extends UITexture { @Override public AdaptableUITexture getSubArea(float uStart, float vStart, float uEnd, float vEnd) { - return new AdaptableUITexture(this.location, calcU(uStart), calcV(vStart), calcU(uEnd), calcV(vEnd), this.canApplyTheme, this.imageWidth, this.imageHeight, this.bl, this.bt, this.br, this.bb, this.tiled); + return new AdaptableUITexture(this.location, lerpU(uStart), lerpV(vStart), lerpU(uEnd), lerpV(vEnd), this.canApplyTheme, this.imageWidth, this.imageHeight, this.bl, this.bt, this.br, this.bb, this.tiled); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java b/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java index afb2d3ed8..c7d68d304 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java @@ -1,11 +1,11 @@ package com.cleanroommc.modularui.drawable; import com.cleanroommc.modularui.ModularUI; -import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.Interpolations; import com.cleanroommc.modularui.utils.JsonHelper; import com.cleanroommc.modularui.widget.sizer.Area; @@ -111,19 +111,19 @@ public UITexture getSubArea(Area bounds) { * @return relative sub area */ public UITexture getSubArea(float uStart, float vStart, float uEnd, float vEnd) { - return new UITexture(this.location, calcU(uStart), calcV(vStart), calcU(uEnd), calcV(vEnd), this.canApplyTheme); + return new UITexture(this.location, lerpU(uStart), lerpV(vStart), lerpU(uEnd), lerpV(vEnd), this.canApplyTheme); } public ResourceLocation getLocation() { return this.location; } - protected final float calcU(float uNew) { - return (this.u1 - this.u0) * uNew + this.u0; + protected final float lerpU(float u) { + return Interpolations.lerp(this.u0, this.u1, u); } - protected final float calcV(float vNew) { - return (this.v1 - this.v0) * vNew + this.v0; + protected final float lerpV(float v) { + return Interpolations.lerp(this.v0, this.v1, v); } @SideOnly(Side.CLIENT) @@ -142,7 +142,7 @@ public void draw(float x, float y, float width, float height) { } public void drawSubArea(float x, float y, float width, float height, float uStart, float vStart, float uEnd, float vEnd) { - GuiDraw.drawTexture(this.location, x, y, x + width, y + height, calcU(uStart), calcV(vStart), calcU(uEnd), calcV(vEnd)); + GuiDraw.drawTexture(this.location, x, y, x + width, y + height, lerpU(uStart), lerpV(vStart), lerpU(uEnd), lerpV(vEnd)); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java index 68cce111f..8f7f37c57 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java @@ -77,6 +77,7 @@ public void setState(int state, boolean setSource) { this.intValue.setIntValue(state); } this.lastValue = state; + markTooltipDirty(); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java b/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java index 166a76687..5e19a4993 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java @@ -30,24 +30,30 @@ public ToggleButton value(IBoolValue boolValue) { } public ToggleButton selectedBackground(IDrawable... selectedBackground) { - this.background = addToArray(this.background, selectedBackground, 1); - return this; + return background(true, selectedBackground); } public ToggleButton selectedHoverBackground(IDrawable... selectedHoverBackground) { - this.hoverBackground = addToArray(this.hoverBackground, selectedHoverBackground, 1); - return this; + return hoverBackground(true, selectedHoverBackground); } @Override - public ToggleButton background(IDrawable... background) { - this.background = addToArray(this.background, background, 0); - return this; + public ToggleButton background(IDrawable... selectedBackground) { + return background(false, selectedBackground); } @Override - public ToggleButton hoverBackground(IDrawable... background) { - this.hoverBackground = addToArray(this.hoverBackground, background, 0); + public ToggleButton hoverBackground(IDrawable... selectedHoverBackground) { + return hoverBackground(false, selectedHoverBackground); + } + + public ToggleButton background(boolean selected, IDrawable... background) { + this.background = addToArray(this.background, background, selected ? 1 : 0); + return this; + } + + public ToggleButton hoverBackground(boolean selected, IDrawable... background) { + this.hoverBackground = addToArray(this.hoverBackground, background, selected ? 1 : 0); return this; }