diff --git a/dependencies.gradle b/dependencies.gradle index fb254407f..c1ce7fa30 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -25,6 +25,6 @@ dependencies { embed 'org.mariuszgromada.math:MathParser.org-mXparser:6.1.0' implementation(rfg.deobf("curse.maven:baubles-227083:2518667")) - implementation rfg.deobf("curse.maven:neverenoughanimation-1062347:6533650-sources-6533651") + compileOnlyApi rfg.deobf("curse.maven:neverenoughanimation-1062347:6533650-sources-6533651") //implementation("com.cleanroommc:neverenoughanimations:1.0.6") { transitive false } } diff --git a/src/main/java/com/cleanroommc/modularui/ModularUIConfig.java b/src/main/java/com/cleanroommc/modularui/ModularUIConfig.java index 13ffdcf62..0c55ace59 100644 --- a/src/main/java/com/cleanroommc/modularui/ModularUIConfig.java +++ b/src/main/java/com/cleanroommc/modularui/ModularUIConfig.java @@ -13,11 +13,11 @@ public class ModularUIConfig { public static int defaultScrollSpeed = 30; @Config.Comment("If progress bar should step in texture pixels or screen pixels. (Screen pixels are way smaller and therefore smoother)") - public static boolean smoothProgressBar = true; + public static boolean smoothProgressBar = false; // Default direction @Config.Comment("Default tooltip position around the widget or its panel.") - public static RichTooltip.Pos tooltipPos = RichTooltip.Pos.VERTICAL; + public static RichTooltip.Pos tooltipPos = RichTooltip.Pos.NEXT_TO_MOUSE; @Config.Comment("If true, pressing ESC key in the text field will restore the last text instead of confirming current one.") public static boolean escRestoreLastText = false; diff --git a/src/main/java/com/cleanroommc/modularui/api/ITheme.java b/src/main/java/com/cleanroommc/modularui/api/ITheme.java index fe46e8db5..71cbbad8c 100644 --- a/src/main/java/com/cleanroommc/modularui/api/ITheme.java +++ b/src/main/java/com/cleanroommc/modularui/api/ITheme.java @@ -1,10 +1,15 @@ package com.cleanroommc.modularui.api; -import com.cleanroommc.modularui.screen.RichTooltip; -import com.cleanroommc.modularui.theme.WidgetSlotTheme; -import com.cleanroommc.modularui.theme.WidgetTextFieldTheme; +import com.cleanroommc.modularui.theme.SelectableTheme; +import com.cleanroommc.modularui.theme.SlotTheme; +import com.cleanroommc.modularui.theme.TextFieldTheme; import com.cleanroommc.modularui.theme.WidgetTheme; -import com.cleanroommc.modularui.theme.WidgetThemeSelectable; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; +import com.cleanroommc.modularui.theme.WidgetThemeKey; + +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collection; /** * A theme is parsed from json and contains style information like color or background texture. @@ -36,31 +41,22 @@ static ITheme get(String id) { */ ITheme getParentTheme(); - WidgetTheme getFallback(); - - WidgetTheme getPanelTheme(); + @UnmodifiableView + Collection> getWidgetThemes(); - WidgetTheme getButtonTheme(); + WidgetThemeEntry getFallback(); - WidgetSlotTheme getItemSlotTheme(); + WidgetThemeEntry getPanelTheme(); - WidgetSlotTheme getFluidSlotTheme(); + WidgetThemeEntry getButtonTheme(); - WidgetTextFieldTheme getTextFieldTheme(); + WidgetThemeEntry getItemSlotTheme(); - WidgetThemeSelectable getToggleButtonTheme(); + WidgetThemeEntry getFluidSlotTheme(); - WidgetTheme getWidgetTheme(String id); - - default T getWidgetTheme(Class clazz, String id) { - WidgetTheme theme = getWidgetTheme(id); - if (clazz.isInstance(theme)) { - return (T) theme; - } - return null; - } + WidgetThemeEntry getTextFieldTheme(); - boolean getSmoothProgressBarOverride(); + WidgetThemeEntry getToggleButtonTheme(); - RichTooltip.Pos getTooltipPosOverride(); + WidgetThemeEntry getWidgetTheme(WidgetThemeKey key); } diff --git a/src/main/java/com/cleanroommc/modularui/api/IThemeApi.java b/src/main/java/com/cleanroommc/modularui/api/IThemeApi.java index af46eebfc..e1267a20e 100644 --- a/src/main/java/com/cleanroommc/modularui/api/IThemeApi.java +++ b/src/main/java/com/cleanroommc/modularui/api/IThemeApi.java @@ -1,46 +1,89 @@ package com.cleanroommc.modularui.api; +import com.cleanroommc.modularui.drawable.GuiTextures; import com.cleanroommc.modularui.screen.ModularScreen; +import com.cleanroommc.modularui.theme.SelectableTheme; +import com.cleanroommc.modularui.theme.SlotTheme; +import com.cleanroommc.modularui.theme.TextFieldTheme; import com.cleanroommc.modularui.theme.ThemeAPI; +import com.cleanroommc.modularui.theme.ThemeBuilder; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeKey; +import com.cleanroommc.modularui.theme.WidgetThemeKeyBuilder; import com.cleanroommc.modularui.theme.WidgetThemeParser; import com.cleanroommc.modularui.utils.JsonBuilder; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; import java.util.List; /** * An API interface for Themes. */ +@ApiStatus.NonExtendable public interface IThemeApi { // widget themes - String FALLBACK = "default"; - String PANEL = "panel"; - String BUTTON = "button"; - String ITEM_SLOT = "itemSlot"; - String FLUID_SLOT = "fluidSlot"; - String TEXT_FIELD = "textField"; - String TOGGLE_BUTTON = "toggleButton"; + WidgetThemeKey FALLBACK = get().widgetThemeKeyBuilder("default", WidgetTheme.class) + .defaultTheme(WidgetTheme.darkTextNoShadow(18, 18, null)) + .register(); + + WidgetThemeKey PANEL = get().widgetThemeKeyBuilder("panel", WidgetTheme.class) + .defaultTheme(WidgetTheme.darkTextNoShadow(176, 166, GuiTextures.MC_BACKGROUND)) + .register(); + + WidgetThemeKey BUTTON = get().widgetThemeKeyBuilder("button", WidgetTheme.class) + .defaultTheme(WidgetTheme.whiteTextShadow(18, 18, GuiTextures.MC_BUTTON)) + .defaultHoverTheme(WidgetTheme.whiteTextShadow(18, 18, GuiTextures.MC_BUTTON_HOVERED)) + .register(); + + WidgetThemeKey ITEM_SLOT = get().widgetThemeKeyBuilder("itemSlot", SlotTheme.class) + .defaultTheme(new SlotTheme(GuiTextures.SLOT_ITEM)) + .register(); + + WidgetThemeKey FLUID_SLOT = get().widgetThemeKeyBuilder("fluidSlot", SlotTheme.class) + .defaultTheme(new SlotTheme(GuiTextures.SLOT_FLUID)) + .register(); + + WidgetThemeKey TEXT_FIELD = get().widgetThemeKeyBuilder("textField", TextFieldTheme.class) + .defaultTheme(new TextFieldTheme(0xFF2F72A8, 0xFF5F5F5F)) + .register(); + + WidgetThemeKey TOGGLE_BUTTON = get().widgetThemeKeyBuilder("toggleButton", SelectableTheme.class) + .defaultTheme(SelectableTheme.whiteTextShadow(18, 18, GuiTextures.MC_BUTTON, GuiTextures.MC_BUTTON_DISABLED)) + .defaultHoverTheme(SelectableTheme.whiteTextShadow(18, 18, GuiTextures.MC_BUTTON_HOVERED, GuiTextures.MC_BUTTON_DISABLED)) + .register(); + + // sub widget themes + WidgetThemeKey ITEM_SLOT_PLAYER = ITEM_SLOT.createSubKey("player"); + WidgetThemeKey ITEM_SLOT_PLAYER_HOTBAR = ITEM_SLOT_PLAYER.createSubKey("playerHotbar"); + WidgetThemeKey ITEM_SLOT_PLAYER_MAIN_INV = ITEM_SLOT_PLAYER.createSubKey("playerMainInventory"); + WidgetThemeKey ITEM_SLOT_PLAYER_OFFHAND = ITEM_SLOT_PLAYER.createSubKey("playerOffhand"); + WidgetThemeKey ITEM_SLOT_PLAYER_ARMOR = ITEM_SLOT_PLAYER.createSubKey("playerArmor"); + + String HOVER_SUFFIX = ":hover"; // properties String PARENT = "parent"; + String DEFAULT_WIDTH = "defaultWidth"; + String DEFAULT_HEIGHT = "defaultHeight"; String BACKGROUND = "background"; - String HOVER_BACKGROUND = "hoverBackground"; String COLOR = "color"; String TEXT_COLOR = "textColor"; String TEXT_SHADOW = "textShadow"; + String ICON_COLOR = "iconColor"; String SLOT_HOVER_COLOR = "slotHoverColor"; String MARKED_COLOR = "markedColor"; String HINT_COLOR = "hintColor"; String SELECTED_BACKGROUND = "selectedBackground"; - String SELECTED_HOVER_BACKGROUND = "selectedHoverBackground"; String SELECTED_COLOR = "selectedColor"; String SELECTED_TEXT_COLOR = "selectedTextColor"; String SELECTED_TEXT_SHADOW = "selectedTextShadow"; + String SELECTED_ICON_COLOR = "selectedIconColor"; /** * @return the default api implementation @@ -70,20 +113,24 @@ 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. + * Json builders are used here as they are much easier to merge as opposed to normal java objects. * * @param id id of the theme * @param json theme data */ void registerTheme(String id, JsonBuilder json); + /** + * Registers a theme json object. Themes from resource packs always have greater priority. + * + * @param themeBuilder theme data + */ + default void registerTheme(ThemeBuilder themeBuilder) { + registerTheme(themeBuilder.getId(), themeBuilder); + } + /** * Gets all currently from java side registered theme json's for a theme. * @@ -133,11 +180,20 @@ default void registerThemeForScreen(String owner, String name, String theme) { void registerThemeForScreen(String screen, String theme); /** - * Register a widget theme. + * Registers a widget theme. It is recommended to store the resulting key in a static variable and make it accessible by public methods. * - * @param id id of the widget theme - * @param defaultTheme the fallback widget theme - * @param parser the widget theme json parser function + * @param id id of the widget theme + * @param defaultTheme the fallback widget theme + * @param defaultHoverTheme the fallback hover widget theme + * @param parser the widget theme json parser function. This is usually another constructor. + * @return key to access the widget theme */ - void registerWidgetTheme(String id, WidgetTheme defaultTheme, WidgetThemeParser parser); + WidgetThemeKey registerWidgetTheme(String id, T defaultTheme, T defaultHoverTheme, WidgetThemeParser parser); + + default WidgetThemeKeyBuilder widgetThemeKeyBuilder(String id, Class type) { + return new WidgetThemeKeyBuilder<>(id); + } + + @UnmodifiableView + List> getWidgetThemeKeys(); } 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 5633a39c1..29afbe373 100644 --- a/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java +++ b/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java @@ -5,6 +5,8 @@ import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; +import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.widget.Widget; import com.cleanroommc.modularui.widget.sizer.Area; @@ -14,8 +16,9 @@ import org.jetbrains.annotations.Nullable; /** - * An object which can be drawn. This is mainly used for backgrounds and overlays in + * An object which can be drawn at any size. This is mainly used for backgrounds and overlays in * {@link com.cleanroommc.modularui.api.widget.IWidget}. + * To draw at a fixed size, use {@link IIcon} (see {@link #asIcon()}). */ public interface IDrawable { @@ -30,7 +33,8 @@ static IDrawable of(IDrawable... drawables) { } /** - * Draws this drawable at the given position with the given size. + * Draws this drawable at the given position with the given size. It's the implementors responsibility to properly apply the widget + * theme by calling {@link #applyColor(int)} before drawing. * * @param context current context to draw with * @param x x position @@ -43,25 +47,8 @@ static IDrawable of(IDrawable... drawables) { void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme); /** - * @deprecated use {@link #draw(GuiContext, int, int, int, int, WidgetTheme)} - */ - @SideOnly(Side.CLIENT) - @Deprecated - default void draw(GuiContext context, int x, int y, int width, int height) { - draw(context, x, y, width, height, WidgetTheme.getDefault()); - } - - /** - * @deprecated use {@link #drawAtZero(GuiContext, int, int, WidgetTheme)} - */ - @SideOnly(Side.CLIENT) - @Deprecated - default void drawAtZero(GuiContext context, int width, int height) { - drawAtZero(context, width, height, WidgetTheme.getDefault()); - } - - /** - * Draws this drawable at the current (0|0) with the given size. + * Draws this drawable at the current (0|0) with the given size. This is useful inside widgets since GL is transformed to their + * position when they are drawing. * * @param context gui context * @param width draw width @@ -73,15 +60,6 @@ default void drawAtZero(GuiContext context, int width, int height, WidgetTheme w draw(context, 0, 0, width, height, widgetTheme); } - /** - * @deprecated use {@link #draw(GuiContext, Area, WidgetTheme)} - */ - @SideOnly(Side.CLIENT) - @Deprecated - default void draw(GuiContext context, Area area) { - draw(context, area, WidgetTheme.getDefault()); - } - /** * Draws this drawable in a given area. * @@ -95,16 +73,8 @@ default void draw(GuiContext context, Area area, WidgetTheme widgetTheme) { } /** - * @deprecated use {@link #drawAtZero(GuiContext, Area, WidgetTheme)} - */ - @Deprecated - @SideOnly(Side.CLIENT) - default void drawAtZero(GuiContext context, Area area) { - drawAtZero(context, area, WidgetTheme.getDefault()); - } - - /** - * Draws this drawable at the current (0|0) with the given area's size. + * Draws this drawable at the current (0|0) with the given area's size. This is useful inside widgets since GL is transformed to their + * position when they are drawing. * * @param context gui context * @param area draw area @@ -122,6 +92,21 @@ default boolean canApplyTheme() { return false; } + /** + * Applies the theme color to OpenGL if this drawable can have theme colors applied. This is determined by {@link #canApplyTheme()}. + * If this drawable does not allow theme colors, it will reset the current color (to white). + * This method should be called before drawing. + * + * @param themeColor theme color to apply (usually {@link WidgetTheme#getColor()}) + */ + default void applyColor(int themeColor) { + if (canApplyTheme()) { + Color.setGlColor(themeColor); + } else { + Color.setGlColorOpaque(Color.WHITE.main); + } + } + /** * @return a widget with this drawable as a background */ @@ -167,8 +152,8 @@ public DrawableWidget(IDrawable drawable) { @SideOnly(Side.CLIENT) @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { - this.drawable.drawAtZero(context, getArea(), widgetTheme); + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { + this.drawable.drawAtZero(context, getArea(), getActiveWidgetTheme(widgetTheme, isHovering())); } } } diff --git a/src/main/java/com/cleanroommc/modularui/api/drawable/IIcon.java b/src/main/java/com/cleanroommc/modularui/api/drawable/IIcon.java index 2a1897421..ab1382441 100644 --- a/src/main/java/com/cleanroommc/modularui/api/drawable/IIcon.java +++ b/src/main/java/com/cleanroommc/modularui/api/drawable/IIcon.java @@ -40,10 +40,19 @@ default IDrawable getRootDrawable() { return drawable; } + /** + * This returns a hoverable wrapper of this icon. This is only used in {@link com.cleanroommc.modularui.drawable.text.RichText RichText}. + * This allows this icon to have its own tooltip. + */ default HoverableIcon asHoverable() { return new HoverableIcon(this); } + /** + * This returns an interactable wrapper of this icon. This is only used in + * {@link com.cleanroommc.modularui.drawable.text.RichText RichText}. This allows this icon to be able to listen to clicks and other + * inputs. + */ default InteractableIcon asInteractable() { return new InteractableIcon(this); } diff --git a/src/main/java/com/cleanroommc/modularui/api/drawable/IKey.java b/src/main/java/com/cleanroommc/modularui/api/drawable/IKey.java index 9124c14d0..5358fff31 100644 --- a/src/main/java/com/cleanroommc/modularui/api/drawable/IKey.java +++ b/src/main/java/com/cleanroommc/modularui/api/drawable/IKey.java @@ -200,6 +200,10 @@ default void draw(GuiContext context, int x, int y, int width, int height, Widge } @Override + default boolean canApplyTheme() { + return true; + } + default TextWidget asWidget() { return new TextWidget<>(this); } diff --git a/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java b/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java index d4b18ab13..660c3ede6 100644 --- a/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java +++ b/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java @@ -6,7 +6,7 @@ import com.cleanroommc.modularui.drawable.Stencil; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.widget.sizer.Area; import com.cleanroommc.modularui.widget.sizer.Flex; @@ -49,12 +49,12 @@ public interface IWidget extends IGuiElement { * @param context gui context * @param widgetTheme widget theme of this widget */ - void drawBackground(ModularGuiContext context, WidgetTheme widgetTheme); + void drawBackground(ModularGuiContext context, WidgetThemeEntry widgetTheme); /** * Draws additional stuff in this widget. * x = 0 and y = 0 is now in the top left corner of this widget. - * Do NOT override this method, it is never called. Use {@link #draw(ModularGuiContext, WidgetTheme)} instead. + * Do NOT override this method, it is never called. Use {@link #draw(ModularGuiContext, WidgetThemeEntry)} instead. * * @param context gui context */ @@ -66,13 +66,13 @@ default void draw(ModularGuiContext context) { } /** - * Draws extra elements of this widget. Called after {@link #drawBackground(ModularGuiContext, WidgetTheme)} and before - * {@link #drawOverlay(ModularGuiContext, WidgetTheme)} + * Draws extra elements of this widget. Called after {@link #drawBackground(ModularGuiContext, WidgetThemeEntry)} and before + * {@link #drawOverlay(ModularGuiContext, WidgetThemeEntry)} * * @param context gui context * @param widgetTheme widget theme */ - void draw(ModularGuiContext context, WidgetTheme widgetTheme); + void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme); /** * Draws the overlay of this theme. @@ -80,7 +80,7 @@ default void draw(ModularGuiContext context) { * @param context gui context * @param widgetTheme widget theme */ - void drawOverlay(ModularGuiContext context, WidgetTheme widgetTheme); + void drawOverlay(ModularGuiContext context, WidgetThemeEntry widgetTheme); /** * Draws foreground elements of this widget. For example tooltips. @@ -98,7 +98,7 @@ default Object getAdditionalHoverInfo(IViewportStack viewportStack, int mouseX, return null; } - default WidgetTheme getWidgetTheme(ITheme theme) { + default WidgetThemeEntry getWidgetTheme(ITheme theme) { return theme.getFallback(); } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java b/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java index 0ef6c6587..0eff5d976 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java @@ -14,8 +14,8 @@ public class AdaptableUITexture extends UITexture { /** * Use {@link UITexture#builder()} with {@link Builder#adaptable(int, int)} */ - AdaptableUITexture(ResourceLocation location, float u0, float v0, float u1, float v1, boolean background, int imageWidth, int imageHeight, int bl, int bt, int br, int bb, boolean tiled) { - super(location, u0, v0, u1, v1, background); + AdaptableUITexture(ResourceLocation location, float u0, float v0, float u1, float v1, ColorType colorType, int imageWidth, int imageHeight, int bl, int bt, int br, int bb, boolean tiled) { + super(location, u0, v0, u1, v1, colorType); this.imageWidth = imageWidth; this.imageHeight = imageHeight; this.bl = bl; @@ -27,7 +27,7 @@ public class AdaptableUITexture extends UITexture { @Override public AdaptableUITexture getSubArea(float uStart, float vStart, float uEnd, float vEnd) { - 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); + return new AdaptableUITexture(this.location, lerpU(uStart), lerpV(vStart), lerpU(uEnd), lerpV(vEnd), this.colorType, 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/Circle.java b/src/main/java/com/cleanroommc/modularui/drawable/Circle.java index 5f0e667d9..091305197 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/Circle.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/Circle.java @@ -70,6 +70,7 @@ public Circle segments(int segments) { @SideOnly(Side.CLIENT) @Override public void draw(GuiContext context, int x0, int y0, int width, int height, WidgetTheme widgetTheme) { + applyColor(widgetTheme.getColor()); GuiDraw.drawEllipse(x0, y0, width, height, this.colorInner, this.colorOuter, this.segments); } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/ColorType.java b/src/main/java/com/cleanroommc/modularui/drawable/ColorType.java new file mode 100644 index 000000000..88870ebbf --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/drawable/ColorType.java @@ -0,0 +1,38 @@ +package com.cleanroommc.modularui.drawable; + +import com.cleanroommc.modularui.theme.WidgetTheme; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; + +import java.util.Map; +import java.util.function.ToIntFunction; + +public class ColorType { + + private static final Map COLOR_TYPES = new Object2ObjectOpenHashMap<>(); + + public static ColorType get(String name) { + return COLOR_TYPES.getOrDefault(name, DEFAULT); + } + + public static final ColorType DEFAULT = new ColorType("default", WidgetTheme::getColor); + public static final ColorType TEXT = new ColorType("text", WidgetTheme::getTextColor); + public static final ColorType ICON = new ColorType("icon", WidgetTheme::getIconColor); + + private final String name; + private final ToIntFunction colorGetter; + + public ColorType(String name, ToIntFunction colorGetter) { + this.name = name; + this.colorGetter = colorGetter; + COLOR_TYPES.put(name, this); + } + + public String getName() { + return name; + } + + public int getColor(WidgetTheme theme) { + return colorGetter.applyAsInt(theme); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/drawable/DynamicDrawable.java b/src/main/java/com/cleanroommc/modularui/drawable/DynamicDrawable.java index 98ba0e6d3..ba198f308 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/DynamicDrawable.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/DynamicDrawable.java @@ -38,7 +38,7 @@ public boolean canApplyTheme() { if (drawable != null) { return drawable.canApplyTheme(); } else { - return IDrawable.super.canApplyTheme(); + return false; } } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/GuiTextures.java b/src/main/java/com/cleanroommc/modularui/drawable/GuiTextures.java index ba433e449..a58082f6d 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/GuiTextures.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/GuiTextures.java @@ -87,7 +87,8 @@ public interface GuiTextures { .location(ModularUI.ID, "gui/background/vanilla_background") .imageSize(195, 136) .adaptable(4) - .name("vanilla_background").canApplyTheme() + .name("vanilla_background") + .defaultColorType() .build(); UITexture MC_BUTTON = UITexture.builder() @@ -96,6 +97,7 @@ public interface GuiTextures { .uv(0f, 0f, 1f, 0.5f) .adaptable(2).tiled() .name("mc_button") + .defaultColorType() .build(); UITexture MC_BUTTON_PRESSED = UITexture.builder() @@ -104,6 +106,7 @@ public interface GuiTextures { .uv(0f, 0.5f, 1f, 1f) .adaptable(2).tiled() .name("mc_button_pressed") + .defaultColorType() .build(); UITexture MC_BUTTON_HOVERED = UITexture.builder() @@ -125,9 +128,10 @@ public interface GuiTextures { UITexture MC_BUTTON_DISABLED = UITexture.builder() .location(ModularUI.ID, "gui/widgets/mc_button_disabled") .imageSize(16, 16) - .uv(0f, 0f, 1f, 0.5f) + .fullImage() .adaptable(1).tiled() .name("mc_button_disabled") + .defaultColorType() .build(); UITexture BUTTON_CLEAN = UITexture.builder() @@ -155,6 +159,7 @@ public interface GuiTextures { .location(ModularUI.ID, "gui/slot/item") .imageSize(18, 18) .adaptable(1) + .canApplyTheme() .name("slot_item") .build(); @@ -162,17 +167,20 @@ public interface GuiTextures { .location(ModularUI.ID, "gui/slot/fluid") .imageSize(18, 18) .adaptable(1) + .canApplyTheme() .name("slot_fluid") .build(); UITexture PROGRESS_ARROW = UITexture.builder() .location(ModularUI.ID, "gui/widgets/progress_bar_arrow") .imageSize(20, 40) + .canApplyTheme() .build(); UITexture PROGRESS_CYCLE = UITexture.builder() .location(ModularUI.ID, "gui/widgets/progress_bar_mixer") .imageSize(20, 40) + .canApplyTheme() .build(); UITexture CYCLE_BUTTON_DEMO = UITexture.builder() @@ -184,8 +192,8 @@ public interface GuiTextures { UITexture CROSS = UITexture.fullImage(ModularUI.ID, "gui/icons/cross"); UITexture CROSS_TINY = UITexture.fullImage(ModularUI.ID, "gui/icons/cross_tiny"); - TabTexture TAB_TOP = TabTexture.of(UITexture.fullImage(ModularUI.ID, "gui/tab/tabs_top", true), GuiAxis.Y, false, 28, 32, 4); - TabTexture TAB_BOTTOM = TabTexture.of(UITexture.fullImage(ModularUI.ID, "gui/tab/tabs_bottom", true), GuiAxis.Y, true, 28, 32, 4); - TabTexture TAB_LEFT = TabTexture.of(UITexture.fullImage(ModularUI.ID, "gui/tab/tabs_left", true), GuiAxis.X, false, 32, 28, 4); - TabTexture TAB_RIGHT = TabTexture.of(UITexture.fullImage(ModularUI.ID, "gui/tab/tabs_right", true), GuiAxis.X, true, 32, 28, 4); + TabTexture TAB_TOP = TabTexture.of(UITexture.fullImage(ModularUI.ID, "gui/tab/tabs_top", ColorType.DEFAULT), GuiAxis.Y, false, 28, 32, 4); + TabTexture TAB_BOTTOM = TabTexture.of(UITexture.fullImage(ModularUI.ID, "gui/tab/tabs_bottom", ColorType.DEFAULT), GuiAxis.Y, true, 28, 32, 4); + TabTexture TAB_LEFT = TabTexture.of(UITexture.fullImage(ModularUI.ID, "gui/tab/tabs_left", ColorType.DEFAULT), GuiAxis.X, false, 32, 28, 4); + TabTexture TAB_RIGHT = TabTexture.of(UITexture.fullImage(ModularUI.ID, "gui/tab/tabs_right", ColorType.DEFAULT), GuiAxis.X, true, 32, 28, 4); } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/HueBar.java b/src/main/java/com/cleanroommc/modularui/drawable/HueBar.java index c98b24ed0..311caec9f 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/HueBar.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/HueBar.java @@ -25,6 +25,7 @@ public HueBar(GuiAxis axis) { @Override public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { + applyColor(widgetTheme.getColor()); int size = this.axis.isHorizontal() ? width : height; float step = size / 6f; int previous = COLORS[5]; diff --git a/src/main/java/com/cleanroommc/modularui/drawable/Icon.java b/src/main/java/com/cleanroommc/modularui/drawable/Icon.java index 1c7fd0bd4..877ae2532 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/Icon.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/Icon.java @@ -23,7 +23,6 @@ public class Icon implements IIcon, IJsonSerializable { private int width = 0, height = 0; private Alignment alignment = Alignment.Center; private final Box margin = new Box(); - private int color = 0; public Icon(IDrawable drawable) { this.drawable = drawable; @@ -68,9 +67,6 @@ public void draw(GuiContext context, int x, int y, int width, int height, Widget y += (int) (height * this.alignment.y - this.height * this.alignment.y); height = this.height; } - if (this.color != 0 && this.color != widgetTheme.getColor()) { - widgetTheme = widgetTheme.withColor(this.color); - } this.drawable.draw(context, x, y, width, height, widgetTheme); } @@ -105,11 +101,6 @@ public Icon center() { return alignment(Alignment.Center); } - public Icon color(int color) { - this.color = color; - return this; - } - public Icon margin(int left, int right, int top, int bottom) { this.margin.all(left, right, top, bottom); return this; diff --git a/src/main/java/com/cleanroommc/modularui/drawable/IconRenderer.java b/src/main/java/com/cleanroommc/modularui/drawable/IconRenderer.java index 309469cfd..e3ee50ae5 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/IconRenderer.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/IconRenderer.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.List; +@Deprecated public class IconRenderer { public static final IconRenderer SHARED = new IconRenderer(); @@ -105,7 +106,7 @@ public void drawMeasuredLines(GuiContext context, List lines) { for (IIcon icon : lines) { int x = icon.getWidth() > 0 ? getStartX(icon.getWidth()) : this.x; if (!this.simulate) { - icon.draw(context, x, y, maxWidth, icon.getHeight(), WidgetTheme.getDefault()); + icon.draw(context, x, y, maxWidth, icon.getHeight(), WidgetTheme.getDefault().getTheme()); } y += (int) ((icon.getHeight() + this.linePadding) * this.scale); } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/IngredientDrawable.java b/src/main/java/com/cleanroommc/modularui/drawable/IngredientDrawable.java index 53b28336a..617a139d3 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/IngredientDrawable.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/IngredientDrawable.java @@ -14,6 +14,7 @@ public class IngredientDrawable implements IDrawable, IJsonSerializable { private ItemStack[] items; + private int cycleTime = 1000; public IngredientDrawable(Ingredient ingredient) { this(ingredient.getMatchingStacks()); @@ -27,7 +28,8 @@ public IngredientDrawable(ItemStack... items) { @Override public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { if (this.items.length == 0) return; - ItemStack item = this.items[(int) (Minecraft.getSystemTime() % (1000 * this.items.length)) / 1000]; + applyColor(widgetTheme.getColor()); + ItemStack item = this.items[(int) (Minecraft.getSystemTime() % (this.cycleTime * this.items.length)) / this.cycleTime]; if (item != null) { GuiDraw.drawItem(item, x, y, width, height, context.getCurrentDrawingZ()); } @@ -44,4 +46,19 @@ public void setItems(ItemStack... items) { public void setItems(Ingredient ingredient) { setItems(ingredient.getMatchingStacks()); } + + public int getCycleTime() { + return cycleTime; + } + + /** + * Sets how many milliseconds each item shows up + * + * @param cycleTime time per item in milliseconds + * @return this + */ + public IngredientDrawable cycleTime(int cycleTime) { + this.cycleTime = cycleTime; + return this; + } } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/ItemDrawable.java b/src/main/java/com/cleanroommc/modularui/drawable/ItemDrawable.java index 044e43e98..c45f1797c 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/ItemDrawable.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/ItemDrawable.java @@ -70,6 +70,7 @@ public ItemStack getItem() { @SideOnly(Side.CLIENT) @Override public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { + applyColor(widgetTheme.getColor()); GuiDraw.drawItem(this.item, x, y, width, height, context.getCurrentDrawingZ()); } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/Rectangle.java b/src/main/java/com/cleanroommc/modularui/drawable/Rectangle.java index 710e6daff..2b9169e5f 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/Rectangle.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/Rectangle.java @@ -72,11 +72,7 @@ public Rectangle setCanApplyTheme(boolean canApplyTheme) { @SideOnly(Side.CLIENT) @Override public void draw(GuiContext context, int x0, int y0, int width, int height, WidgetTheme widgetTheme) { - if (canApplyTheme()) { - Color.setGlColor(widgetTheme.getColor()); - } else { - Color.setGlColorOpaque(Color.WHITE.main); - } + applyColor(widgetTheme.getColor()); if (this.cornerRadius <= 0) { GuiDraw.drawRect(x0, y0, width, height, this.colorTL, this.colorTR, this.colorBL, this.colorBR); return; diff --git a/src/main/java/com/cleanroommc/modularui/drawable/SpriteDrawable.java b/src/main/java/com/cleanroommc/modularui/drawable/SpriteDrawable.java index f891a57c4..22802c61c 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/SpriteDrawable.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/SpriteDrawable.java @@ -10,6 +10,7 @@ public class SpriteDrawable implements IDrawable { private final TextureAtlasSprite sprite; + private boolean canApplyTheme = false; public SpriteDrawable(TextureAtlasSprite sprite) { this.sprite = sprite; @@ -17,6 +18,7 @@ public SpriteDrawable(TextureAtlasSprite sprite) { @Override public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { + applyColor(widgetTheme.getColor()); GuiDraw.drawSprite(this.sprite, x, y, width, height); } @@ -29,4 +31,14 @@ public Widget asWidget() { public Icon asIcon() { return IDrawable.super.asIcon().size(this.sprite.getIconWidth(), this.sprite.getIconHeight()); } + + @Override + public boolean canApplyTheme() { + return this.canApplyTheme; + } + + public SpriteDrawable canApplyTheme(boolean canApplyTheme) { + this.canApplyTheme = canApplyTheme; + return this; + } } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/Stencil.java b/src/main/java/com/cleanroommc/modularui/drawable/Stencil.java index e2e8213a3..035f0a4f8 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/Stencil.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/Stencil.java @@ -13,6 +13,10 @@ import java.awt.Rectangle; +/** + * A util class for stencil stack used as a scissor stack. The reason for using stencils over scissors is that scissors can not have + * transformation applied and therefore don't work with 3D holo UI's. + */ public class Stencil { // Stores a stack of areas that are transformed, so it represents the actual area diff --git a/src/main/java/com/cleanroommc/modularui/drawable/TiledUITexture.java b/src/main/java/com/cleanroommc/modularui/drawable/TiledUITexture.java index 8f3a138f1..28a9dccf8 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/TiledUITexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/TiledUITexture.java @@ -11,8 +11,8 @@ public class TiledUITexture extends UITexture { /** * Use {@link UITexture#builder()} with {@link Builder#tiled()} */ - TiledUITexture(ResourceLocation location, float u0, float v0, float u1, float v1, int imageWidth, int imageHeight, boolean canApplyTheme) { - super(location, u0, v0, u1, v1, canApplyTheme); + TiledUITexture(ResourceLocation location, float u0, float v0, float u1, float v1, int imageWidth, int imageHeight, ColorType colorType) { + super(location, u0, v0, u1, v1, colorType); this.imageWidth = imageWidth; this.imageHeight = imageHeight; } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java b/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java index 7168f2dd6..cdec63cf5 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java @@ -2,7 +2,6 @@ import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.api.IJsonSerializable; -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; @@ -17,10 +16,11 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import org.jetbrains.annotations.Nullable; public class UITexture implements IDrawable, IJsonSerializable { - public static final UITexture DEFAULT = fullImage("gui/options_background", true); + public static final UITexture DEFAULT = fullImage("gui/options_background", ColorType.DEFAULT); private static final ResourceLocation ICONS_LOCATION = new ResourceLocation(ModularUI.ID, "textures/gui/icons.png"); @@ -30,6 +30,7 @@ static UITexture icon(String name, int x, int y, int w, int h) { .location(ICONS_LOCATION) .imageSize(256, 256) .xy(x, y, w, h) + .iconColorType() .name(name) .build(); } @@ -43,20 +44,20 @@ static UITexture icon(String name, int x, int y) { public final ResourceLocation location; public final float u0, v0, u1, v1; - public final boolean canApplyTheme; + @Nullable public final ColorType colorType; /** * Creates a drawable texture * - * @param location location of the texture - * @param u0 x offset of the image (0-1) - * @param v0 y offset of the image (0-1) - * @param u1 x end offset of the image (0-1) - * @param v1 y end offset of the image (0-1) - * @param canApplyTheme if theme colors can modify how this texture is drawn + * @param location location of the texture + * @param u0 x offset of the image (0-1) + * @param v0 y offset of the image (0-1) + * @param u1 x end offset of the image (0-1) + * @param v1 y end offset of the image (0-1) + * @param colorType a function to get which color from a widget theme should be used to color this texture. Can be null. */ - public UITexture(ResourceLocation location, float u0, float v0, float u1, float v1, boolean canApplyTheme) { - this.canApplyTheme = canApplyTheme; + public UITexture(ResourceLocation location, float u0, float v0, float u1, float v1, @Nullable ColorType colorType) { + this.colorType = colorType; boolean png = !location.getPath().endsWith(".png"); boolean textures = !location.getPath().startsWith("textures/"); if (png || textures) { @@ -76,27 +77,27 @@ public static Builder builder() { } public static UITexture fullImage(ResourceLocation location) { - return new UITexture(location, 0, 0, 1, 1, false); + return new UITexture(location, 0, 0, 1, 1, null); } public static UITexture fullImage(String location) { - return fullImage(new ResourceLocation(location), false); + return fullImage(new ResourceLocation(location), null); } public static UITexture fullImage(String mod, String location) { - return fullImage(new ResourceLocation(mod, location), false); + return fullImage(new ResourceLocation(mod, location), null); } - public static UITexture fullImage(ResourceLocation location, boolean canApplyTheme) { - return new UITexture(location, 0, 0, 1, 1, canApplyTheme); + public static UITexture fullImage(ResourceLocation location, ColorType colorType) { + return new UITexture(location, 0, 0, 1, 1, colorType); } - public static UITexture fullImage(String location, boolean canApplyTheme) { - return fullImage(new ResourceLocation(location), canApplyTheme); + public static UITexture fullImage(String location, ColorType colorType) { + return fullImage(new ResourceLocation(location), colorType); } - public static UITexture fullImage(String mod, String location, boolean canApplyTheme) { - return fullImage(new ResourceLocation(mod, location), canApplyTheme); + public static UITexture fullImage(String mod, String location, ColorType colorType) { + return fullImage(new ResourceLocation(mod, location), colorType); } public UITexture getSubArea(Area bounds) { @@ -113,7 +114,7 @@ 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, lerpU(uStart), lerpV(vStart), lerpU(uEnd), lerpV(vEnd), this.canApplyTheme); + return new UITexture(this.location, lerpU(uStart), lerpV(vStart), lerpU(uEnd), lerpV(vEnd), this.colorType); } public ResourceLocation getLocation() { @@ -131,11 +132,7 @@ protected final float lerpV(float v) { @SideOnly(Side.CLIENT) @Override public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { - if (canApplyTheme()) { - Color.setGlColor(widgetTheme.getColor()); - } else { - Color.setGlColorOpaque(Color.WHITE.main); - } + applyColor(this.colorType != null ? this.colorType.getColor(widgetTheme) : ColorType.DEFAULT.getColor(widgetTheme)); draw((float) x, y, width, height); } @@ -145,7 +142,7 @@ public void draw(float x, float y, float width, float height) { @Deprecated public void drawSubArea(float x, float y, float width, float height, float uStart, float vStart, float uEnd, float vEnd) { - drawSubArea(x, y, width, height, uStart, vStart, uEnd, vEnd, WidgetTheme.getDefault()); + drawSubArea(x, y, width, height, uStart, vStart, uEnd, vEnd, WidgetTheme.getDefault().getTheme()); } public void drawSubArea(float x, float y, float width, float height, float uStart, float vStart, float uEnd, float vEnd, WidgetTheme widgetTheme) { @@ -159,7 +156,7 @@ public void drawSubArea(float x, float y, float width, float height, float uStar @Override public boolean canApplyTheme() { - return this.canApplyTheme; + return colorType != null; } public static UITexture parseFromJson(JsonObject json) { @@ -197,7 +194,12 @@ public static UITexture parseFromJson(JsonObject json) { if (JsonHelper.getBoolean(json, false, "tiled")) { builder.tiled(); } - builder.canApplyTheme(JsonHelper.getBoolean(json, false, "canApplyTheme")); + String colorTypeName = JsonHelper.getString(json, null, "colorType", "color"); + if (colorTypeName != null) { + builder.colorType(ColorType.get(colorTypeName)); + } else if (JsonHelper.getBoolean(json, false, "canApplyTheme")) { + builder.canApplyTheme(); + } return builder.build(); } @@ -213,7 +215,7 @@ public boolean saveToJson(JsonObject json) { json.addProperty("v0", this.v0); json.addProperty("u1", this.u1); json.addProperty("v1", this.v1); - json.addProperty("canApplyTheme", this.canApplyTheme); + if (this.colorType != null) json.addProperty("colorType", this.colorType.getName()); return true; } @@ -237,7 +239,7 @@ public static class Builder { private int bl = 0, bt = 0, br = 0, bb = 0; private String name; private boolean tiled = false; - private boolean canApplyTheme = false; + private ColorType colorType = null; /** * @param loc location of the image to draw @@ -265,7 +267,7 @@ public Builder location(String path) { } /** - * Set the image size. Required for {@link #tiled()}, {@link #adaptable(int, int)} and {@link #uv(int, int, int, int)} + * Set the image size. Required for {@link #tiled()}, {@link #adaptable(int, int)} and {@link #xy(int, int, int, int)} * * @param w image width * @param h image height @@ -373,16 +375,63 @@ public Builder adaptable(int border) { /** * Specify if theme color should apply to this texture. + * + * @see #defaultColorType() */ public Builder canApplyTheme() { - return canApplyTheme(true); + return defaultColorType(); } - public Builder canApplyTheme(boolean canApplyTheme) { - this.canApplyTheme = canApplyTheme; + /** + * Sets a function which defines how theme color is applied to this texture. Null means no color will be applied. + * + *
  • Background textures should use {@link ColorType#DEFAULT} or {@link #defaultColorType()}
  • + *
  • White icons (only has a shape and some grey shading) should use {@link ColorType#ICON} or {@link #iconColorType()}
  • + *
  • Text should use {@link ColorType#TEXT} or {@link #textColorType()}
  • + *
  • Everything else (f.e. colored icons and overlays) should use null
  • + *
    + * + * @param colorType function which defines how theme color is applied to this texture + * @return this + */ + public Builder colorType(@Nullable ColorType colorType) { + this.colorType = colorType; return this; } + /** + * Sets this texture to use default theme color. + * Usually used for background textures (grey shaded). + * + * @return this + * @see #colorType(ColorType) + */ + public Builder defaultColorType() { + return colorType(ColorType.DEFAULT); + } + + /** + * Sets this texture to use text theme color. + * Usually used for texts. + * + * @return this + * @see #colorType(ColorType) + */ + public Builder textColorType() { + return colorType(ColorType.TEXT); + } + + /** + * Sets this texture to use icon theme color. + * Usually used for grey shaded icons without color. + * + * @return this + * @see #colorType(ColorType) + */ + public Builder iconColorType() { + return colorType(ColorType.ICON); + } + /** * Registers the texture with a name, so it can be used in json without creating the texture again. * By default, theme color is applicable. @@ -435,13 +484,13 @@ private UITexture create() { if (this.mode == Mode.RELATIVE) { if (this.u0 < 0 || this.v0 < 0 || this.u1 > 1 || this.v1 > 1) throw new IllegalArgumentException("UV values must be 0 - 1"); - if (this.bl > 0 || this.bt > 0 || this.br > 0|| this.bb > 0) { - return new AdaptableUITexture(this.location, this.u0, this.v0, this.u1, this.v1, this.canApplyTheme, this.iw, this.ih, this.bl, this.bt, this.br, this.bb, this.tiled); + if (this.bl > 0 || this.bt > 0 || this.br > 0 || this.bb > 0) { + return new AdaptableUITexture(this.location, this.u0, this.v0, this.u1, this.v1, this.colorType, this.iw, this.ih, this.bl, this.bt, this.br, this.bb, this.tiled); } if (this.tiled) { - return new TiledUITexture(this.location, this.u0, this.v0, this.u1, this.v1, this.iw, this.ih, this.canApplyTheme); + return new TiledUITexture(this.location, this.u0, this.v0, this.u1, this.v1, this.iw, this.ih, this.colorType); } - return new UITexture(this.location, this.u0, this.v0, this.u1, this.v1, this.canApplyTheme); + return new UITexture(this.location, this.u0, this.v0, this.u1, this.v1, this.colorType); } throw new IllegalStateException(); } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/text/ComposedLine.java b/src/main/java/com/cleanroommc/modularui/drawable/text/ComposedLine.java index beb1c9f8f..4334e9cb0 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/text/ComposedLine.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/text/ComposedLine.java @@ -48,7 +48,7 @@ public void draw(GuiContext context, FontRenderer fr, float x, float y, int colo } else if (o instanceof IIcon icon) { float drawY = getHeight(fr) / 2f - icon.getHeight() / 2f; int w = icon.getWidth() > 0 ? icon.getWidth() : availableWidth; - icon.draw(context, (int) x, (int) (y + drawY), w, icon.getHeight(), IThemeApi.get().getDefaultTheme().getFallback()); + icon.draw(context, (int) x, (int) (y + drawY), w, icon.getHeight(), IThemeApi.get().getDefaultTheme().getFallback().getTheme()); if (icon instanceof IHoverable hoverable) { hoverable.setRenderedAt((int) x, (int) (y + drawY)); } diff --git a/src/main/java/com/cleanroommc/modularui/screen/ClientScreenHandler.java b/src/main/java/com/cleanroommc/modularui/screen/ClientScreenHandler.java index 210d8f71c..c46eafe93 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ClientScreenHandler.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ClientScreenHandler.java @@ -538,6 +538,8 @@ public static void drawDebugScreen(@Nullable ModularScreen muiScreen, @Nullable GuiDraw.drawText("Mouse Pos: " + mouseX + ", " + mouseY, 5, lineY, scale, color, true); lineY -= shift; GuiDraw.drawText("FPS: " + fpsCounter.getFps(), 5, lineY, scale, color, true); + lineY -= shift; + GuiDraw.drawText("Theme ID: " + context.getTheme().getId(), 5, lineY, scale, color, true); LocatedWidget locatedHovered = muiScreen.getPanelManager().getTopWidgetLocated(true); if (locatedHovered != null) { drawSegmentLine(lineY -= 4, scale, color); diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java index 656097867..e632ed3bd 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java @@ -19,7 +19,7 @@ import com.cleanroommc.modularui.screen.viewport.GuiViewportStack; import com.cleanroommc.modularui.screen.viewport.LocatedWidget; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.HoveredWidgetList; import com.cleanroommc.modularui.utils.Interpolation; import com.cleanroommc.modularui.utils.Interpolations; @@ -179,7 +179,7 @@ public boolean hasParent() { } @Override - public WidgetTheme getWidgetThemeInternal(ITheme theme) { + public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { return theme.getPanelTheme(); } @@ -708,16 +708,6 @@ public LocatedWidget getTopHoveringLocated(boolean debug) { return null; } - @Override - public int getDefaultHeight() { - return 166; - } - - @Override - public int getDefaultWidth() { - return 176; - } - final void setPanelGuiContext(@NotNull ModularGuiContext context) { setContext(context); if (!context.getScreen().isOverlay()) { diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java b/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java index b30eabac9..7d3260214 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java @@ -111,7 +111,6 @@ private ModularScreen(@NotNull String owner, @Nullable Function getGuiActionClass(IGuiAction action) { } public ITheme getCurrentTheme() { + if (this.currentTheme == null) { + useTheme(null); + } return this.currentTheme; } diff --git a/src/main/java/com/cleanroommc/modularui/screen/RichTooltip.java b/src/main/java/com/cleanroommc/modularui/screen/RichTooltip.java index 707b0a3ef..3d66183d5 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/RichTooltip.java +++ b/src/main/java/com/cleanroommc/modularui/screen/RichTooltip.java @@ -156,8 +156,7 @@ public Rectangle determineTooltipArea(RichText text, GuiContext context, TextRen Pos pos = this.pos; if (pos == null) { - pos = context.isMuiContext() ? context.getMuiContext().getScreen().getCurrentTheme().getTooltipPosOverride() : null; - if (pos == null) pos = ModularUIConfig.tooltipPos; + pos = ModularUIConfig.tooltipPos; } if (pos == Pos.FIXED) { return new Rectangle(this.x, this.y, width, height); diff --git a/src/main/java/com/cleanroommc/modularui/test/EventHandler.java b/src/main/java/com/cleanroommc/modularui/test/EventHandler.java index 79f0fd1eb..5d44b72c1 100644 --- a/src/main/java/com/cleanroommc/modularui/test/EventHandler.java +++ b/src/main/java/com/cleanroommc/modularui/test/EventHandler.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.test; +import com.cleanroommc.modularui.api.IThemeApi; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.drawable.IIcon; import com.cleanroommc.modularui.api.drawable.IKey; @@ -8,6 +9,9 @@ import com.cleanroommc.modularui.factory.ClientGUI; import com.cleanroommc.modularui.screen.RichTooltipEvent; import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.ReloadThemeEvent; +import com.cleanroommc.modularui.theme.SelectableTheme; +import com.cleanroommc.modularui.theme.ThemeBuilder; import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.Color; @@ -19,6 +23,15 @@ public class EventHandler { public static boolean enabledRichTooltipEventTest = false; + public static final String TEST_THEME = "mui:test_theme"; + private static final ThemeBuilder testTheme = new ThemeBuilder<>(TEST_THEME) + .defaultColor(Color.BLUE_ACCENT.brighter(0)) + .widgetTheme(IThemeApi.TOGGLE_BUTTON, new SelectableTheme.Builder<>() + .color(Color.BLUE_ACCENT.brighter(0)) + .selectedColor(Color.WHITE.main) + .selectedIconColor(Color.RED.brighter(0))) + .widgetThemeHover(IThemeApi.TOGGLE_BUTTON, new SelectableTheme.Builder<>() + .selectedIconColor(Color.DEEP_PURPLE.brighter(0))); private static final IIcon tooltipLine = new IDrawable() { @Override @@ -57,4 +70,9 @@ public void onRichTooltip(RichTooltipEvent.Pre event) { .moveCursorToEnd(); } } + + @SubscribeEvent + public static void onThemeTooltip(ReloadThemeEvent.Pre event) { + IThemeApi.get().registerTheme(testTheme); + } } diff --git a/src/main/java/com/cleanroommc/modularui/test/TestGuis.java b/src/main/java/com/cleanroommc/modularui/test/TestGuis.java index 82fd499c8..7bbb1c520 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestGuis.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestGuis.java @@ -27,6 +27,7 @@ import com.cleanroommc.modularui.utils.fakeworld.ArraySchema; import com.cleanroommc.modularui.utils.fakeworld.FakeEntity; import com.cleanroommc.modularui.utils.fakeworld.ISchema; +import com.cleanroommc.modularui.value.BoolValue; import com.cleanroommc.modularui.value.StringValue; import com.cleanroommc.modularui.widget.DraggableWidget; import com.cleanroommc.modularui.widget.Widget; @@ -35,6 +36,7 @@ import com.cleanroommc.modularui.widgets.SchemaWidget; import com.cleanroommc.modularui.widgets.SortableListWidget; import com.cleanroommc.modularui.widgets.TextWidget; +import com.cleanroommc.modularui.widgets.ToggleButton; import com.cleanroommc.modularui.widgets.TransformWidget; import com.cleanroommc.modularui.widgets.layout.Column; import com.cleanroommc.modularui.widgets.layout.Flow; @@ -64,7 +66,26 @@ public class TestGuis extends CustomModularScreen { @Override public @NotNull ModularPanel buildUI(ModularGuiContext context) { - return buildSpriteAndEntityUI(context); + return buildToggleUI(context); + } + + public @NotNull ModularPanel buildToggleUI(ModularGuiContext context) { + useTheme(EventHandler.TEST_THEME); + boolean[] states = new boolean[60]; + return new ModularPanel("toggle") + .size(150) + .padding(7) + .child(new ListWidget<>() + .sizeRel(1f) + .children(10, i -> Flow.row() + .coverChildren() + .children(6, j -> { + final int index = i * 6 + j; + return new ToggleButton() + .overlay(GuiTextures.BOOKMARK) + .value(new BoolValue.Dynamic(() -> states[index], val -> states[index] = val)) + .margin(2); + }))); } public @NotNull ModularPanel buildAnimationUI(ModularGuiContext context) { diff --git a/src/main/java/com/cleanroommc/modularui/test/TestTile.java b/src/main/java/com/cleanroommc/modularui/test/TestTile.java index e3ba22403..3203212c8 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestTile.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestTile.java @@ -16,7 +16,7 @@ import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.screen.UISettings; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.Interpolation; @@ -592,8 +592,8 @@ private SpecialButton(AnimatedText animatedKey) { } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { - this.animatedKey.draw(context, 0, 0, getArea().w(), getArea().h(), widgetTheme); + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { + this.animatedKey.draw(context, 0, 0, getArea().w(), getArea().h(), getActiveWidgetTheme(widgetTheme, isHovering())); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/theme/AbstractDefaultTheme.java b/src/main/java/com/cleanroommc/modularui/theme/AbstractDefaultTheme.java deleted file mode 100644 index de92c2f03..000000000 --- a/src/main/java/com/cleanroommc/modularui/theme/AbstractDefaultTheme.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.cleanroommc.modularui.theme; - -import com.cleanroommc.modularui.ModularUIConfig; -import com.cleanroommc.modularui.api.ITheme; -import com.cleanroommc.modularui.screen.RichTooltip; - -public abstract class AbstractDefaultTheme implements ITheme { - - private WidgetTheme panel; - private WidgetTheme button; - private WidgetSlotTheme itemSlot; - private WidgetSlotTheme fluidSlot; - private WidgetTextFieldTheme textField; - private WidgetThemeSelectable toggleButtonTheme; - - @Override - public ITheme getParentTheme() { - return null; - } - - @Override - public WidgetTheme getPanelTheme() { - if (this.panel == null) { - this.panel = getWidgetTheme(Theme.PANEL); - } - return this.panel; - } - - @Override - public WidgetTheme getButtonTheme() { - if (this.button == null) { - this.button = getWidgetTheme(Theme.BUTTON); - } - return this.button; - } - - @Override - public WidgetSlotTheme getItemSlotTheme() { - if (this.itemSlot == null) { - this.itemSlot = (WidgetSlotTheme) getWidgetTheme(Theme.ITEM_SLOT); - } - return this.itemSlot; - } - - @Override - public WidgetSlotTheme getFluidSlotTheme() { - if (this.fluidSlot == null) { - this.fluidSlot = (WidgetSlotTheme) getWidgetTheme(Theme.FLUID_SLOT); - } - return this.fluidSlot; - } - - @Override - public WidgetTextFieldTheme getTextFieldTheme() { - if (this.textField == null) { - this.textField = (WidgetTextFieldTheme) getWidgetTheme(Theme.TEXT_FIELD); - } - return this.textField; - } - - @Override - public WidgetThemeSelectable getToggleButtonTheme() { - if (this.toggleButtonTheme == null) { - this.toggleButtonTheme = (WidgetThemeSelectable) getWidgetTheme(Theme.TOGGLE_BUTTON); - } - return this.toggleButtonTheme; - } - - @Override - public boolean getSmoothProgressBarOverride() { - return ModularUIConfig.smoothProgressBar; - } - - @Override - public RichTooltip.Pos getTooltipPosOverride() { - return ModularUIConfig.tooltipPos; - } -} diff --git a/src/main/java/com/cleanroommc/modularui/theme/AbstractTheme.java b/src/main/java/com/cleanroommc/modularui/theme/AbstractTheme.java new file mode 100644 index 000000000..ffc63b141 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/AbstractTheme.java @@ -0,0 +1,89 @@ +package com.cleanroommc.modularui.theme; + +import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.IThemeApi; + +public abstract class AbstractTheme implements ITheme { + + private final String id; + private final ITheme parentTheme; + + private WidgetThemeEntry fallback; + private WidgetThemeEntry panel; + private WidgetThemeEntry button; + private WidgetThemeEntry itemSlot; + private WidgetThemeEntry fluidSlot; + private WidgetThemeEntry textField; + private WidgetThemeEntry toggleButtonTheme; + + protected AbstractTheme(String id, ITheme parentTheme) { + this.id = id; + this.parentTheme = parentTheme; + } + + @Override + public String getId() { + return id; + } + + @Override + public ITheme getParentTheme() { + return parentTheme; + } + + @Override + public WidgetThemeEntry getFallback() { + if (this.fallback == null) { + this.fallback = getWidgetTheme(IThemeApi.FALLBACK); + } + return this.fallback; + } + + @Override + public WidgetThemeEntry getPanelTheme() { + if (this.panel == null) { + this.panel = getWidgetTheme(IThemeApi.PANEL); + } + return this.panel; + } + + @Override + public WidgetThemeEntry getButtonTheme() { + if (this.button == null) { + this.button = getWidgetTheme(IThemeApi.BUTTON); + } + return this.button; + } + + @Override + public WidgetThemeEntry getItemSlotTheme() { + if (this.itemSlot == null) { + this.itemSlot = getWidgetTheme(IThemeApi.ITEM_SLOT); + } + return this.itemSlot; + } + + @Override + public WidgetThemeEntry getFluidSlotTheme() { + if (this.fluidSlot == null) { + this.fluidSlot = getWidgetTheme(IThemeApi.FLUID_SLOT); + } + return this.fluidSlot; + } + + @Override + public WidgetThemeEntry getTextFieldTheme() { + if (this.textField == null) { + this.textField = getWidgetTheme(IThemeApi.TEXT_FIELD); + } + return this.textField; + } + + @Override + public WidgetThemeEntry getToggleButtonTheme() { + if (this.toggleButtonTheme == null) { + this.toggleButtonTheme = getWidgetTheme(IThemeApi.TOGGLE_BUTTON); + } + return this.toggleButtonTheme; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/DefaultTheme.java b/src/main/java/com/cleanroommc/modularui/theme/DefaultTheme.java new file mode 100644 index 000000000..39d18cb98 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/DefaultTheme.java @@ -0,0 +1,47 @@ +package com.cleanroommc.modularui.theme; + +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collection; +import java.util.Collections; + +public class DefaultTheme extends AbstractTheme { + + public static final DefaultTheme INSTANCE = new DefaultTheme(); + + private final WidgetThemeMap widgetThemes = new WidgetThemeMap(); + private boolean initialized = false; + + private DefaultTheme() { + super(ThemeAPI.DEFAULT_ID, null); + } + + private void initialize() { + if (!initialized) { + initialized = true; + for (WidgetThemeKey key1 : ThemeAPI.INSTANCE.getWidgetThemeKeys()) { + this.widgetThemes.put(key1, entryOfKey(key1)); + } + } + } + + @Override + public @UnmodifiableView Collection> getWidgetThemes() { + initialize(); + return Collections.unmodifiableCollection(this.widgetThemes.values()); + } + + @Override + public WidgetThemeEntry getWidgetTheme(WidgetThemeKey key) { + initialize(); + WidgetThemeEntry widgetTheme = this.widgetThemes.getTheme(key); + while (widgetTheme == null && key.isSubWidgetTheme()) { + widgetTheme = this.widgetThemes.getTheme(key.getParent()); + } + return widgetTheme; + } + + private static WidgetThemeEntry entryOfKey(WidgetThemeKey key) { + return new WidgetThemeEntry<>(key, key.getDefaultValue(), key.getDefaultHoverValue()); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/ReloadThemeEvent.java b/src/main/java/com/cleanroommc/modularui/theme/ReloadThemeEvent.java index f7cbee8bf..4a5fbb4af 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/ReloadThemeEvent.java +++ b/src/main/java/com/cleanroommc/modularui/theme/ReloadThemeEvent.java @@ -4,9 +4,7 @@ public class ReloadThemeEvent extends Event { - public static class Pre extends ReloadThemeEvent { - } + public static class Pre extends ReloadThemeEvent {} - public static class Post extends ReloadThemeEvent { - } + public static class Post extends ReloadThemeEvent {} } diff --git a/src/main/java/com/cleanroommc/modularui/theme/SelectableTheme.java b/src/main/java/com/cleanroommc/modularui/theme/SelectableTheme.java new file mode 100644 index 000000000..75379e287 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/SelectableTheme.java @@ -0,0 +1,85 @@ +package com.cleanroommc.modularui.theme; + +import com.cleanroommc.modularui.api.IThemeApi; +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.drawable.DrawableSerialization; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.JsonBuilder; +import com.cleanroommc.modularui.utils.JsonHelper; + +import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; + +public class SelectableTheme extends WidgetTheme { + + private final WidgetTheme selected; + + public static SelectableTheme darkTextNoShadow(int defaultWidth, int defaultHeight, @Nullable IDrawable background, @Nullable IDrawable selectedBackground) { + return new SelectableTheme(defaultWidth, defaultHeight, background, Color.WHITE.main, Color.TEXT_COLOR_DARK, false, + Color.WHITE.main, selectedBackground, Color.WHITE.main, Color.TEXT_COLOR_DARK, false, Color.WHITE.main); + } + + public static SelectableTheme whiteTextShadow(int defaultWidth, int defaultHeight, @Nullable IDrawable background, @Nullable IDrawable selectedBackground) { + return new SelectableTheme(defaultWidth, defaultHeight, background, Color.WHITE.main, Color.WHITE.main, true, + Color.WHITE.main, selectedBackground, Color.WHITE.main, Color.WHITE.main, true, Color.WHITE.main); + } + + public SelectableTheme(int defaultWidth, int defaultHeight, @Nullable IDrawable background, + int color, int textColor, boolean textShadow, int iconColor, + @Nullable IDrawable selectedBackground, + int selectedColor, int selectedTextColor, boolean selectedTextShadow, int selectedIconColor) { + super(defaultWidth, defaultHeight, background, color, textColor, textShadow, iconColor); + this.selected = new WidgetTheme(defaultWidth, defaultHeight, selectedBackground, selectedColor, selectedTextColor, selectedTextShadow, selectedIconColor); + } + + public SelectableTheme(SelectableTheme parent, JsonObject json, JsonObject fallback) { + super(parent, json, fallback); + IDrawable selectedBackground = JsonHelper.deserializeWithFallback(json, fallback, IDrawable.class, parent.getSelected().getBackground(), IThemeApi.SELECTED_BACKGROUND); + int selectedColor = JsonHelper.getColorWithFallback(json, fallback, parent.getSelected().getColor(), IThemeApi.SELECTED_COLOR); + int selectedTextColor = JsonHelper.getColorWithFallback(json, fallback, parent.getSelected().getTextColor(), IThemeApi.SELECTED_TEXT_COLOR); + boolean selectedTextShadow = JsonHelper.getBoolWithFallback(json, fallback, parent.getSelected().getTextShadow(), IThemeApi.SELECTED_TEXT_SHADOW); + int selectedIconColor = JsonHelper.getColorWithFallback(json, fallback, parent.getSelected().getTextColor(), IThemeApi.SELECTED_ICON_COLOR); + this.selected = new WidgetTheme(getDefaultWidth(), getDefaultHeight(), selectedBackground, selectedColor, selectedTextColor, selectedTextShadow, selectedIconColor); + } + + public WidgetTheme getSelected() { + return selected; + } + + public static class Builder> extends WidgetThemeBuilder { + + public B selectedColor(int color) { + add(IThemeApi.SELECTED_COLOR, color); + return getThis(); + } + + public B selectedTextColor(int color) { + add(IThemeApi.SELECTED_TEXT_COLOR, color); + return getThis(); + } + + public B selectedTextShadow(int shadow) { + add(IThemeApi.SELECTED_TEXT_SHADOW, shadow); + return getThis(); + } + + public B selectedIconColor(int color) { + add(IThemeApi.SELECTED_ICON_COLOR, color); + return getThis(); + } + + public B selectedBackground(JsonBuilder builder) { + add(IThemeApi.SELECTED_BACKGROUND, builder); + return getThis(); + } + + public B selectedBackground(IDrawable drawable) { + add(IThemeApi.SELECTED_BACKGROUND, DrawableSerialization.serialize(drawable)); + return getThis(); + } + + public B selectedBackground(String textureId) { + return background(new JsonBuilder().add("type", "texture").add("id", textureId)); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/SlotTheme.java b/src/main/java/com/cleanroommc/modularui/theme/SlotTheme.java new file mode 100644 index 000000000..93959a746 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/SlotTheme.java @@ -0,0 +1,45 @@ +package com.cleanroommc.modularui.theme; + +import com.cleanroommc.modularui.api.IThemeApi; +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.JsonHelper; + +import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; + +public class SlotTheme extends WidgetTheme { + + private final int slotHoverColor; + + public SlotTheme(IDrawable background) { + this(background, Color.withAlpha(Color.WHITE.main, 0x60)); + } + + public SlotTheme(IDrawable background, int slotHoverColor) { + this(18, 18, background, Color.WHITE.main, 0xFF404040, false, Color.WHITE.main, slotHoverColor); + } + + public SlotTheme(int defaultWidth, int defaultHeight, @Nullable IDrawable background, + int color, int textColor, boolean textShadow, int iconColor, int slotHoverColor) { + super(defaultWidth, defaultHeight, background, color, textColor, textShadow, iconColor); + this.slotHoverColor = slotHoverColor; + } + + public SlotTheme(SlotTheme parent, JsonObject json, JsonObject fallback) { + super(parent, json, fallback); + this.slotHoverColor = JsonHelper.getColorWithFallback(json, fallback, parent.getSlotHoverColor(), IThemeApi.SLOT_HOVER_COLOR); + } + + public int getSlotHoverColor() { + return this.slotHoverColor; + } + + public static class Builder> extends WidgetThemeBuilder { + + public B hoverColor(int hoverColor) { + add(IThemeApi.SLOT_HOVER_COLOR, hoverColor); + return getThis(); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/TextFieldTheme.java b/src/main/java/com/cleanroommc/modularui/theme/TextFieldTheme.java new file mode 100644 index 000000000..41e91597d --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/TextFieldTheme.java @@ -0,0 +1,54 @@ +package com.cleanroommc.modularui.theme; + +import com.cleanroommc.modularui.api.IThemeApi; +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.drawable.GuiTextures; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.JsonHelper; + +import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; + +public class TextFieldTheme extends WidgetTheme { + + private final int markedColor; + private final int hintColor; + + public TextFieldTheme(int markedColor, int hintColor) { + this(56, 18, GuiTextures.DISPLAY_SMALL, Color.WHITE.main, Color.WHITE.main, false, Color.WHITE.main, markedColor, hintColor); + } + + public TextFieldTheme(int defaultWidth, int defaultHeight, @Nullable IDrawable background, + int color, int textColor, boolean textShadow, int iconColor, int markedColor, int hintColor) { + super(defaultWidth, defaultHeight, background, color, textColor, textShadow, iconColor); + this.markedColor = markedColor; + this.hintColor = hintColor; + } + + public TextFieldTheme(TextFieldTheme parent, JsonObject fallback, JsonObject json) { + super(parent, json, fallback); + this.markedColor = JsonHelper.getColorWithFallback(json, fallback, parent.getMarkedColor(), IThemeApi.MARKED_COLOR); + this.hintColor = JsonHelper.getColorWithFallback(json, fallback, parent.getHintColor(), IThemeApi.HINT_COLOR); + } + + public int getMarkedColor() { + return this.markedColor; + } + + public int getHintColor() { + return hintColor; + } + + public static class Builder> extends WidgetThemeBuilder { + + public B markedColor(int markedColor) { + add(IThemeApi.MARKED_COLOR, markedColor); + return getThis(); + } + + public B hintColor(int hintColor) { + add(IThemeApi.HINT_COLOR, hintColor); + return getThis(); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/Theme.java b/src/main/java/com/cleanroommc/modularui/theme/Theme.java index 91f1e135d..39f8a45a3 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/Theme.java +++ b/src/main/java/com/cleanroommc/modularui/theme/Theme.java @@ -1,140 +1,48 @@ package com.cleanroommc.modularui.theme; -import com.cleanroommc.modularui.ModularUIConfig; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.IThemeApi; -import com.cleanroommc.modularui.screen.RichTooltip; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.UnmodifiableView; -import java.util.Map; +import java.util.Collection; +import java.util.Collections; -public class Theme implements ITheme { +public class Theme extends AbstractTheme { - public static final String FALLBACK = IThemeApi.FALLBACK; - public static final String PANEL = IThemeApi.PANEL; - public static final String BUTTON = IThemeApi.BUTTON; - public static final String ITEM_SLOT = IThemeApi.ITEM_SLOT; - public static final String FLUID_SLOT = IThemeApi.FLUID_SLOT; - public static final String TEXT_FIELD = IThemeApi.TEXT_FIELD; - public static final String TOGGLE_BUTTON = IThemeApi.TOGGLE_BUTTON; + private final WidgetThemeMap widgetThemes = new WidgetThemeMap(); - private final Map widgetThemes = new Object2ObjectOpenHashMap<>(); - - private final String id; - private final ITheme parentTheme; - private final WidgetTheme fallback; - private final WidgetTheme panelTheme; - private final WidgetTheme buttonTheme; - private final WidgetSlotTheme itemSlotTheme; - private final WidgetSlotTheme fluidSlotTheme; - private final WidgetTextFieldTheme textFieldTheme; - private final WidgetThemeSelectable toggleButtonTheme; - - private int openCloseAnimationOverride = -1; - private Boolean smoothProgressBarOverride = null; - private RichTooltip.Pos tooltipPosOverride = null; - - Theme(String id, ITheme parent, Map widgetThemes) { - this.id = id; - this.parentTheme = parent; + Theme(String id, ITheme parent, WidgetThemeMap widgetThemes) { + super(id, parent); this.widgetThemes.putAll(widgetThemes); if (parent instanceof Theme theme) { - for (Map.Entry entry : theme.widgetThemes.entrySet()) { + for (WidgetThemeEntry entry : theme.widgetThemes.values()) { if (!this.widgetThemes.containsKey(entry.getKey())) { - this.widgetThemes.put(entry.getKey(), entry.getValue()); + this.widgetThemes.put(entry.getKey(), entry); } } - } else if (parent == IThemeApi.get().getDefaultTheme()) { - if (!this.widgetThemes.containsKey(FALLBACK)) { - this.widgetThemes.put(FALLBACK, ThemeManager.defaultdefaultWidgetTheme); + } else if (parent == DefaultTheme.INSTANCE) { + if (!this.widgetThemes.containsKey(IThemeApi.FALLBACK)) { + this.widgetThemes.putTheme(IThemeApi.FALLBACK, ThemeManager.defaultFallbackWidgetTheme); } - for (Map.Entry entry : ThemeAPI.INSTANCE.defaultWidgetThemes.entrySet()) { + for (WidgetThemeEntry entry : DefaultTheme.INSTANCE.getWidgetThemes()) { if (!this.widgetThemes.containsKey(entry.getKey())) { - this.widgetThemes.put(entry.getKey(), entry.getValue()); + this.widgetThemes.put(entry.getKey(), entry); } } } - this.panelTheme = this.widgetThemes.get(PANEL); - this.fallback = this.widgetThemes.get(FALLBACK); - this.buttonTheme = this.widgetThemes.get(BUTTON); - 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 = (WidgetThemeSelectable) this.widgetThemes.get(TOGGLE_BUTTON); - } - - void setOpenCloseAnimationOverride(int override) { - this.openCloseAnimationOverride = override; - } - - void setSmoothProgressBarOverride(boolean smooth) { - this.smoothProgressBarOverride = smooth; - } - - void setTooltipPosOverride(RichTooltip.Pos pos) { - this.tooltipPosOverride = pos; - } - - public String getId() { - return this.id; - } - - public ITheme getParentTheme() { - return this.parentTheme; - } - - public WidgetTheme getFallback() { - return this.fallback; - } - - public WidgetTheme getPanelTheme() { - return this.panelTheme; - } - - public WidgetTheme getButtonTheme() { - return this.buttonTheme; - } - - @Override - public WidgetSlotTheme getItemSlotTheme() { - return this.itemSlotTheme; - } - - @Override - public WidgetSlotTheme getFluidSlotTheme() { - return this.fluidSlotTheme; - } - - public WidgetTextFieldTheme getTextFieldTheme() { - return this.textFieldTheme; } @Override - public WidgetThemeSelectable getToggleButtonTheme() { - return this.toggleButtonTheme; + public @UnmodifiableView Collection> getWidgetThemes() { + return Collections.unmodifiableCollection(this.widgetThemes.values()); } - public WidgetTheme getWidgetTheme(String id) { - if (this.widgetThemes.containsKey(id)) { - return this.widgetThemes.get(id); - } - return getFallback(); - } - - @Override - public boolean getSmoothProgressBarOverride() { - if (this.smoothProgressBarOverride != null) { - return this.smoothProgressBarOverride; - } - return ModularUIConfig.smoothProgressBar; - } - - @Override - public RichTooltip.Pos getTooltipPosOverride() { - if (this.tooltipPosOverride != null) { - return this.tooltipPosOverride; + public WidgetThemeEntry getWidgetTheme(WidgetThemeKey key) { + WidgetThemeEntry widgetTheme = this.widgetThemes.getTheme(key); + while (widgetTheme == null && key.isSubWidgetTheme()) { + widgetTheme = this.widgetThemes.getTheme(key.getParent()); } - return ModularUIConfig.tooltipPos; + return widgetTheme; } } diff --git a/src/main/java/com/cleanroommc/modularui/theme/ThemeAPI.java b/src/main/java/com/cleanroommc/modularui/theme/ThemeAPI.java index a3d96b8da..4727817b9 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/ThemeAPI.java +++ b/src/main/java/com/cleanroommc/modularui/theme/ThemeAPI.java @@ -3,9 +3,6 @@ import com.cleanroommc.modularui.ModularUIConfig; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.IThemeApi; -import com.cleanroommc.modularui.api.drawable.IDrawable; -import com.cleanroommc.modularui.drawable.GuiTextures; -import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.JsonBuilder; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; @@ -14,35 +11,30 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.regex.Pattern; public class ThemeAPI implements IThemeApi { public static final ThemeAPI INSTANCE = new ThemeAPI(); - public static final String DEFAULT = "DEFAULT"; - public static final ITheme DEFAULT_DEFAULT = new DefaultTheme(); + public static final String DEFAULT_ID = "DEFAULT"; + public static final ITheme DEFAULT_THEME = DefaultTheme.INSTANCE; + + public static final Pattern widgetThemeNamePattern = Pattern.compile("[a-zA-Z0-9_-]+"); private final Object2ObjectMap themes = new Object2ObjectOpenHashMap<>(); protected final Object2ObjectMap> defaultThemes = new Object2ObjectOpenHashMap<>(); - protected final Object2ObjectMap defaultWidgetThemes = new Object2ObjectOpenHashMap<>(); - protected final Object2ObjectMap widgetThemeFunctions = new Object2ObjectOpenHashMap<>(); + private final List> keys = new ArrayList<>(); protected final Object2ObjectOpenHashMap jsonScreenThemes = new Object2ObjectOpenHashMap<>(); private final Object2ObjectMap screenThemes = new Object2ObjectOpenHashMap<>(); - private ThemeAPI() { - registerWidgetTheme(Theme.PANEL, new WidgetTheme(GuiTextures.MC_BACKGROUND, null, Color.WHITE.main, 0xFF404040, false), WidgetTheme::new); - registerWidgetTheme(Theme.BUTTON, new WidgetTheme(GuiTextures.MC_BUTTON, GuiTextures.MC_BUTTON_HOVERED, Color.WHITE.main, Color.WHITE.main, true), WidgetTheme::new); - 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, 0xFF5F5F5F), (parent, json, fallback) -> new WidgetTextFieldTheme(parent, fallback, json)); - 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); - } + private ThemeAPI() {} @Override public ITheme getDefaultTheme() { - return DEFAULT_DEFAULT; + return DEFAULT_THEME; } @Override @@ -55,11 +47,6 @@ public boolean hasTheme(String id) { return this.themes.containsKey(id); } - @Override - public boolean hasWidgetTheme(String id) { - return this.widgetThemeFunctions.containsKey(id); - } - @Override public void registerTheme(String id, JsonBuilder json) { List themes = getJavaDefaultThemes(id); @@ -98,13 +85,26 @@ public void registerThemeForScreen(String screen, String theme) { this.screenThemes.put(screen, theme); } + @SuppressWarnings("unchecked") @Override - public void registerWidgetTheme(String id, WidgetTheme defaultTheme, WidgetThemeParser parser) { - if (this.widgetThemeFunctions.containsKey(id)) { - throw new IllegalStateException(); + public WidgetThemeKey registerWidgetTheme(String id, T defaultTheme, T defaultHoverTheme, + WidgetThemeParser parser) { + Objects.requireNonNull(id, "Id for widget theme must not be null"); + Objects.requireNonNull(defaultTheme, "Default widget theme must not be null, but is null for id '" + id + "'."); + Objects.requireNonNull(parser, "Parser for widget theme must not be null, but is null for id '" + id + "'."); + if (WidgetThemeKey.getFromFullName(id) != null) { + throw new IllegalStateException("There already is a widget theme for id '" + id + "' registered."); + } + if (!widgetThemeNamePattern.matcher(id).matches()) { + throw new IllegalArgumentException("Widget theme id '" + id + "' is invalid. Id must only contain letters, numbers, underscores and minus."); } - this.widgetThemeFunctions.put(id, parser); - this.defaultWidgetThemes.put(id, defaultTheme); + Class type = (Class) defaultTheme.getClass(); + return new WidgetThemeKey<>(type, id, defaultTheme, defaultHoverTheme, parser); + } + + @Override + public List> getWidgetThemeKeys() { + return Collections.unmodifiableList(this.keys); } // Internals @@ -116,30 +116,13 @@ void registerTheme(ITheme theme) { this.themes.put(theme.getId(), theme); } + void registerWidgetThemeKey(WidgetThemeKey key) { + this.keys.add(key); + } + void onReload() { this.themes.clear(); this.jsonScreenThemes.clear(); - registerTheme(DEFAULT_DEFAULT); - } - - public static class DefaultTheme extends AbstractDefaultTheme { - - private DefaultTheme() { - } - - @Override - public String getId() { - return DEFAULT; - } - - @Override - public WidgetTheme getFallback() { - return ThemeManager.defaultdefaultWidgetTheme; - } - - @Override - public WidgetTheme getWidgetTheme(String id) { - return INSTANCE.defaultWidgetThemes.get(id); - } + registerTheme(DEFAULT_THEME); } } diff --git a/src/main/java/com/cleanroommc/modularui/theme/ThemeBuilder.java b/src/main/java/com/cleanroommc/modularui/theme/ThemeBuilder.java index 4dcb0f9aa..faf99d886 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/ThemeBuilder.java +++ b/src/main/java/com/cleanroommc/modularui/theme/ThemeBuilder.java @@ -5,139 +5,221 @@ import com.cleanroommc.modularui.drawable.DrawableSerialization; import com.cleanroommc.modularui.utils.JsonBuilder; +/** + * A json builder with helper methods to make building themes in java easier. + * This class is meant to be extended for custom helper methods. + * + * @param type of this builder class + */ public class ThemeBuilder> extends JsonBuilder { + private final String id; + private String parent; + + public ThemeBuilder(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public String getParent() { + return parent; + } + + @SuppressWarnings("unchecked") protected B getThis() { return (B) this; } public B parent(String v) { - add("parent", v); + add(IThemeApi.PARENT, v); + this.parent = v; return getThis(); } public B defaultBackground(IDrawable v) { - add("background", DrawableSerialization.serialize(v)); + add(IThemeApi.BACKGROUND, DrawableSerialization.serialize(v)); return getThis(); } public B defaultBackground(String textureId) { - add("background", new JsonBuilder().add("type", "texture").add("id", textureId)); + add(IThemeApi.BACKGROUND, new JsonBuilder().add("type", "texture").add("id", textureId)); return getThis(); } public B defaultHoverBackground(IDrawable v) { - add("hoverBackground", DrawableSerialization.serialize(v)); + mergeAdd(IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.BACKGROUND, DrawableSerialization.serialize(v))); return getThis(); } public B defaultHoverBackground(String textureId) { - add("hoverBackground", new JsonBuilder().add("type", "texture").add("id", textureId)); + mergeAdd(IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.BACKGROUND, new JsonBuilder().add("type", "texture").add("id", textureId))); return getThis(); } public B defaultColor(int v) { - add("color", v); + add(IThemeApi.COLOR, v); + return getThis(); + } + + public B defaultHoverColor(int v) { + mergeAdd(IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.COLOR, v)); return getThis(); } public B defaultTextColor(int v) { - add("textColor", v); + add(IThemeApi.TEXT_COLOR, v); + return getThis(); + } + + public B defaultTextHoverColor(int v) { + mergeAdd(IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.TEXT_COLOR, v)); return getThis(); } public B defaultTextShadow(boolean v) { - add("textShadow", v); + add(IThemeApi.TEXT_SHADOW, v); return getThis(); } - public B background(String widgetTheme, IDrawable v) { - mergeAdd(widgetTheme, new JsonBuilder().add("background", DrawableSerialization.serialize(v))); + public B defaultTextHoverShadow(boolean v) { + mergeAdd(IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.TEXT_SHADOW, v)); return getThis(); } - public B background(String widgetTheme, String textureId) { - mergeAdd(widgetTheme, new JsonBuilder().add("background", new JsonBuilder().add("type", "texture").add("id", textureId))); + public B defaultIconColor(int v) { + add(IThemeApi.ICON_COLOR, v); return getThis(); } - public B hoverBackground(String widgetTheme, IDrawable v) { - mergeAdd(widgetTheme, new JsonBuilder().add("hoverBackground", DrawableSerialization.serialize(v))); + public B defaultIconHoverColor(int v) { + mergeAdd(IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.ICON_COLOR, v)); return getThis(); } - public B hoverBackground(String widgetTheme, String textureId) { - mergeAdd(widgetTheme, new JsonBuilder().add("hoverBackground", new JsonBuilder().add("type", "texture").add("id", textureId))); + public B defaultWidth(WidgetThemeKey widgetTheme, int defaultWidth) { + mergeAdd(widgetTheme.getFullName(), new JsonBuilder().add(IThemeApi.DEFAULT_WIDTH, defaultWidth)); return getThis(); } - public B color(String widgetTheme, int v) { - mergeAdd(widgetTheme, new JsonBuilder().add("color", v)); + public B defaultHeight(WidgetThemeKey widgetTheme, int defaultHeight) { + mergeAdd(widgetTheme.getFullName(), new JsonBuilder().add(IThemeApi.DEFAULT_HEIGHT, defaultHeight)); return getThis(); } - public B textColor(String widgetTheme, int v) { - mergeAdd(widgetTheme, new JsonBuilder().add("textColor", v)); + public B background(WidgetThemeKey widgetTheme, IDrawable v) { + mergeAdd(widgetTheme.getFullName(), new JsonBuilder().add(IThemeApi.BACKGROUND, DrawableSerialization.serialize(v))); return getThis(); } - public B textShadow(String widgetTheme, boolean v) { - mergeAdd(widgetTheme, new JsonBuilder().add("textShadow", v)); + public B background(WidgetThemeKey widgetTheme, String textureId) { + return background(widgetTheme, new JsonBuilder().add("type", "texture").add("id", textureId)); + } + + public B background(WidgetThemeKey widgetTheme, JsonBuilder builder) { + mergeAdd(widgetTheme.getFullName(), new JsonBuilder().add(IThemeApi.BACKGROUND, builder)); return getThis(); } - public B itemSlotHoverColor(int v) { - mergeAdd(IThemeApi.ITEM_SLOT, new JsonBuilder().add("slotHoverColor", v)); + public B hoverBackground(WidgetThemeKey widgetTheme, IDrawable v) { + mergeAdd(widgetTheme.getFullName() + IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.BACKGROUND, DrawableSerialization.serialize(v))); return getThis(); } - public B fluidSlotHoverColor(int v) { - mergeAdd(IThemeApi.FLUID_SLOT, new JsonBuilder().add("slotHoverColor", v)); + public B hoverBackground(WidgetThemeKey widgetTheme, String textureId) { + return hoverBackground(widgetTheme, new JsonBuilder().add("type", "texture").add("id", textureId)); + } + + public B hoverBackground(WidgetThemeKey widgetTheme, JsonBuilder builder) { + mergeAdd(widgetTheme.getFullName() + IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.BACKGROUND, builder)); return getThis(); } - public B textFieldMarkedColor(int v) { - mergeAdd(IThemeApi.TEXT_FIELD, new JsonBuilder().add("markedColor", v)); + public B color(WidgetThemeKey widgetTheme, int v) { + mergeAdd(widgetTheme.getFullName(), new JsonBuilder().add(IThemeApi.COLOR, v)); return getThis(); } - public B textFieldHintColor(int v) { - mergeAdd(IThemeApi.TEXT_FIELD, new JsonBuilder().add("hintColor", v)); + public B hoverColor(WidgetThemeKey widgetTheme, int v) { + mergeAdd(widgetTheme.getFullName() + IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.COLOR, v)); + return getThis(); + } + + public B textColor(WidgetThemeKey widgetTheme, int v) { + mergeAdd(widgetTheme.getFullName(), new JsonBuilder().add(IThemeApi.TEXT_COLOR, v)); + return getThis(); + } + + public B textHoverColor(WidgetThemeKey widgetTheme, int v) { + mergeAdd(widgetTheme.getFullName() + IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.TEXT_COLOR, v)); + return getThis(); + } + + public B textShadow(WidgetThemeKey widgetTheme, boolean v) { + mergeAdd(widgetTheme.getFullName(), new JsonBuilder().add(IThemeApi.TEXT_SHADOW, v)); return getThis(); } - public B toggleButtonSelectedBackground(IDrawable v) { - mergeAdd(IThemeApi.TOGGLE_BUTTON, new JsonBuilder().add("selectedBackground", DrawableSerialization.serialize(v))); + public B textHoverShadow(WidgetThemeKey widgetTheme, boolean v) { + mergeAdd(widgetTheme.getFullName() + IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.TEXT_SHADOW, v)); return getThis(); } - public B toggleButtonSelectedBackground(String widgetTheme, String textureId) { - mergeAdd(IThemeApi.TOGGLE_BUTTON, new JsonBuilder().add("selectedBackground", new JsonBuilder().add("type", "texture").add("id", textureId))); + public B iconColor(WidgetThemeKey widgetTheme, int v) { + mergeAdd(widgetTheme.getFullName(), new JsonBuilder().add(IThemeApi.ICON_COLOR, v)); return getThis(); } - public B toggleButtonSelectedHoverBackground(IDrawable v) { - mergeAdd(IThemeApi.TOGGLE_BUTTON, new JsonBuilder().add("selectedHoverBackground", DrawableSerialization.serialize(v))); + public B iconHoverColor(WidgetThemeKey widgetTheme, int v) { + mergeAdd(widgetTheme.getFullName() + IThemeApi.HOVER_SUFFIX, new JsonBuilder().add(IThemeApi.ICON_COLOR, v)); return getThis(); } - public B toggleButtonSelectedHoverBackground(String widgetTheme, String textureId) { - mergeAdd(IThemeApi.TOGGLE_BUTTON, new JsonBuilder().add("selectedHoverBackground", new JsonBuilder().add("type", "texture").add("id", textureId))); + public B itemSlotHoverColor(int v) { + mergeAdd(IThemeApi.ITEM_SLOT.getName(), new JsonBuilder().add("slotHoverColor", v)); + return getThis(); + } + + public B fluidSlotHoverColor(int v) { + mergeAdd(IThemeApi.FLUID_SLOT.getName(), new JsonBuilder().add("slotHoverColor", v)); return getThis(); } - public B toggleButtonSelectedColor(int v) { - mergeAdd(IThemeApi.TOGGLE_BUTTON, new JsonBuilder().add("selectedColor", v)); + public B textFieldMarkedColor(int v) { + mergeAdd(IThemeApi.TEXT_FIELD.getName(), new JsonBuilder().add("markedColor", v)); + return getThis(); + } + + public B textFieldHintColor(int v) { + mergeAdd(IThemeApi.TEXT_FIELD.getName(), new JsonBuilder().add("hintColor", v)); return getThis(); } - public B toggleButtonSelectedTextColor(int v) { - mergeAdd(IThemeApi.TOGGLE_BUTTON, new JsonBuilder().add("selectedTextColor", v)); + /** + * Customizes widget themes in a more organized way than with the methods above. + * + * @param widgetThemeKey key of the widget theme to customize + * @param widgetThemeBuilder builder of the widget theme (take a look at its subclasses) + * @param type of the widget theme + * @return this + */ + public B widgetTheme(WidgetThemeKey widgetThemeKey, WidgetThemeBuilder widgetThemeBuilder) { + add(widgetThemeKey.getFullName(), widgetThemeBuilder); return getThis(); } - public B toggleButtonSelectedTextShadow(boolean v) { - mergeAdd(IThemeApi.TOGGLE_BUTTON, new JsonBuilder().add("selectedTextShadow", v)); + /** + * Customizes widget themes in a more organized way than with the methods above. + * + * @param widgetThemeKey key of the widget theme to customize + * @param widgetThemeBuilder builder of the widget theme (take a look at its subclasses) + * @param type of the widget theme + * @return this + */ + public B widgetThemeHover(WidgetThemeKey widgetThemeKey, WidgetThemeBuilder widgetThemeBuilder) { + add(widgetThemeKey.getFullName() + IThemeApi.HOVER_SUFFIX, widgetThemeBuilder); return getThis(); } } diff --git a/src/main/java/com/cleanroommc/modularui/theme/ThemeManager.java b/src/main/java/com/cleanroommc/modularui/theme/ThemeManager.java index b47803151..f23f13cd4 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/ThemeManager.java +++ b/src/main/java/com/cleanroommc/modularui/theme/ThemeManager.java @@ -3,9 +3,10 @@ import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.IThemeApi; -import com.cleanroommc.modularui.screen.RichTooltip; +import com.cleanroommc.modularui.utils.AssetHelper; +import com.cleanroommc.modularui.utils.JsonBuilder; +import com.cleanroommc.modularui.utils.JsonHelper; import com.cleanroommc.modularui.utils.ObjectList; -import com.cleanroommc.modularui.utils.*; import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResourceManager; @@ -20,12 +21,21 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import it.unimi.dsi.fastutil.objects.*; +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -33,7 +43,8 @@ @SideOnly(Side.CLIENT) public class ThemeManager implements ISelectiveResourceReloadListener { - protected static final WidgetTheme defaultdefaultWidgetTheme = new WidgetTheme(null, null, Color.WHITE.main, 0xFF404040, false); + protected static final WidgetThemeEntry defaultFallbackWidgetTheme = IThemeApi.get().getDefaultTheme().getWidgetTheme(IThemeApi.FALLBACK); + private static final JsonObject emptyJson = new JsonObject(); public static void reload() { ModularUI.LOGGER.info("Reloading Themes..."); @@ -113,7 +124,7 @@ public static void loadThemes(Map> themesPaths) { iterator = themeMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); - if (ThemeAPI.DEFAULT.equals(entry.getValue().parent) || sortedThemes.containsKey(entry.getValue().parent)) { + if (ThemeAPI.DEFAULT_ID.equals(entry.getValue().parent) || sortedThemes.containsKey(entry.getValue().parent)) { sortedThemes.put(entry.getKey(), entry.getValue()); iterator.remove(); changed = true; @@ -139,7 +150,7 @@ private static void validateAncestorTree(Map themeMap) { parents.add(theme); ThemeJson parent = theme; do { - if (ThemeAPI.DEFAULT.equals(parent.parent)) { + if (ThemeAPI.DEFAULT_ID.equals(parent.parent)) { break; } parent = themeMap.get(parent.parent); @@ -269,41 +280,89 @@ private Theme deserialize() { } // parse fallback theme for widget themes - Map widgetThemes = new Object2ObjectOpenHashMap<>(); - WidgetTheme parentWidgetTheme = parent.getFallback(); - WidgetTheme fallback = new WidgetTheme(parentWidgetTheme, jsonBuilder.getJson(), jsonBuilder.getJson()); - widgetThemes.put(Theme.FALLBACK, fallback); + WidgetThemeMap widgetThemes = new WidgetThemeMap(); + WidgetThemeEntry parentWidgetTheme = parent.getFallback(); // fallback theme of parent + WidgetTheme fallback = new WidgetTheme(parentWidgetTheme.getTheme(), jsonBuilder.getJson(), null); // fallback theme of new theme + WidgetTheme fallbackHover = fallback; + JsonObject hoverJson = getJson(jsonBuilder.getJson(), IThemeApi.HOVER_SUFFIX); + if (hoverJson == null) hoverJson = getJson(jsonBuilder.getJson(), IThemeApi.FALLBACK.getFullName() + IThemeApi.HOVER_SUFFIX); + if (hoverJson != null) { + fallbackHover = new WidgetTheme(fallback, hoverJson, null); + } + widgetThemes.putTheme(IThemeApi.FALLBACK, new WidgetThemeEntry<>(IThemeApi.FALLBACK, fallback, fallbackHover)); + + // parse all main widget themes + for (WidgetThemeKey key : ThemeAPI.INSTANCE.getWidgetThemeKeys()) { + if (key != IThemeApi.FALLBACK) { + parse(widgetThemes, parent, key, jsonBuilder); + } + } + return new Theme(this.id, parent, widgetThemes); + } - // parse all other widget themes - JsonObject emptyJson = new JsonObject(); - for (Map.Entry entry : ThemeAPI.INSTANCE.widgetThemeFunctions.entrySet()) { - JsonObject widgetThemeJson; - if (jsonBuilder.getJson().has(entry.getKey())) { - JsonElement element = jsonBuilder.getJson().get(entry.getKey()); - if (element.isJsonObject()) { - widgetThemeJson = element.getAsJsonObject(); - } else { - widgetThemeJson = emptyJson; + private void parse(WidgetThemeMap map, ITheme parent, WidgetThemeKey key, JsonBuilder json) { + WidgetThemeParser parser = key.getParser(); + boolean definedInTheme = true; + JsonObject widgetThemeJson = getJson(json.getJson(), key.getFullName()); + if (widgetThemeJson == null) { + definedInTheme = false; + } + + JsonObject widgetThemeHoverJson = getJson(json.getJson(), key.getFullName() + IThemeApi.HOVER_SUFFIX); + if (widgetThemeHoverJson == null) { + if (!definedInTheme) { + // widget theme undefined -> copy from parent + if (key.isSubWidgetTheme()) { + // if it is a sub widget theme, we use the parent widget theme from this theme + WidgetThemeEntry entry = map.getTheme(key.getParent()); + map.putTheme(key, new WidgetThemeEntry<>(key, entry.getTheme(), entry.getHoverTheme())); + return; } - } else { + // we still need to parse not inherited values (fallback) widgetThemeJson = emptyJson; } - parentWidgetTheme = parent.getWidgetTheme(entry.getKey()); - 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()); + + JsonObject fallback = key.isSubWidgetTheme() ? null : json.getJson(); + T widgetTheme = null; + if (widgetThemeJson != null) { + // widget theme defined + T parentWidgetTheme = key.isSubWidgetTheme() ? map.getTheme(key.getParent()).getTheme() : parent.getWidgetTheme(key).getTheme(); + // sub widget themes strictly only inherit from their parent widget theme and not the parent theme + widgetTheme = parser.parse(parentWidgetTheme, widgetThemeJson, fallback); } - if (jsonBuilder.getJson().has("smoothProgressBar")) { - theme.setSmoothProgressBarOverride(jsonBuilder.getJson().get("smoothProgressBar").getAsBoolean()); + + T widgetThemeHover = null; + if (widgetThemeHoverJson != null) { + // hover widget theme defined + T parentWidgetTheme = widgetTheme != null ? widgetTheme : parent.getWidgetTheme(key).getHoverTheme(); + widgetThemeHover = parser.parse(parentWidgetTheme, widgetThemeHoverJson, fallback); } - if (jsonBuilder.getJson().has("tooltipPos")) { - String posName = jsonBuilder.getJson().get("tooltipPos").getAsString(); - theme.setTooltipPosOverride(RichTooltip.Pos.valueOf(posName)); + + if (widgetTheme != null) { + if (widgetThemeHover != null) { + map.putTheme(key, new WidgetThemeEntry<>(key, widgetTheme, widgetThemeHover)); + } else { + map.putTheme(key, new WidgetThemeEntry<>(key, widgetTheme)); + } + } else { + map.putTheme(key, new WidgetThemeEntry<>(key, parent.getWidgetTheme(key).getTheme(), widgetThemeHover)); } - return theme; + } + + private JsonObject getJson(JsonObject json, String key) { + if (json.has(key)) { + // theme has widget theme defined + JsonElement element = json.get(key); + if (element.isJsonObject()) { + // widget theme is a json object + return element.getAsJsonObject(); + } + // incorrect data format + ModularUI.LOGGER.warn("WidgetTheme '{}' of theme '{}' with parent '{}' was found to have an incorrect data format.", key, this.id, this.parent); + } + // theme doesn't have widget theme defined + return null; } } } diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetSlotTheme.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetSlotTheme.java deleted file mode 100644 index 87f1da8e0..000000000 --- a/src/main/java/com/cleanroommc/modularui/theme/WidgetSlotTheme.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.cleanroommc.modularui.theme; - -import com.cleanroommc.modularui.api.IThemeApi; -import com.cleanroommc.modularui.api.drawable.IDrawable; -import com.cleanroommc.modularui.utils.Color; -import com.cleanroommc.modularui.utils.JsonHelper; - -import com.google.gson.JsonObject; - -public class WidgetSlotTheme extends WidgetTheme { - - private final int slotHoverColor; - - public WidgetSlotTheme(IDrawable background, int slotHoverColor) { - super(background, null, Color.WHITE.main, 0xFF404040, false); - this.slotHoverColor = slotHoverColor; - } - - public WidgetSlotTheme(WidgetTheme parent, JsonObject json, JsonObject fallback) { - super(parent, json, fallback); - this.slotHoverColor = JsonHelper.getColorWithFallback(json, fallback, ((WidgetSlotTheme) parent).getSlotHoverColor(), IThemeApi.SLOT_HOVER_COLOR); - } - - public int getSlotHoverColor() { - return this.slotHoverColor; - } -} diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetTextFieldTheme.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetTextFieldTheme.java deleted file mode 100644 index 890ffb474..000000000 --- a/src/main/java/com/cleanroommc/modularui/theme/WidgetTextFieldTheme.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.cleanroommc.modularui.theme; - -import com.cleanroommc.modularui.api.IThemeApi; -import com.cleanroommc.modularui.drawable.GuiTextures; -import com.cleanroommc.modularui.utils.Color; -import com.cleanroommc.modularui.utils.JsonHelper; - -import com.google.gson.JsonObject; - -public class WidgetTextFieldTheme extends WidgetTheme { - - private final int markedColor; - private final int hintColor; - - public WidgetTextFieldTheme(int markedColor, int hintColor) { - super(GuiTextures.DISPLAY_SMALL, null, Color.WHITE.main, Color.WHITE.main, false); - this.markedColor = markedColor; - this.hintColor = hintColor; - } - - public WidgetTextFieldTheme(WidgetTheme parent, JsonObject fallback, JsonObject json) { - super(parent, json, fallback); - this.markedColor = JsonHelper.getColorWithFallback(json, fallback, ((WidgetTextFieldTheme) parent).getMarkedColor(), IThemeApi.MARKED_COLOR); - this.hintColor = JsonHelper.getColorWithFallback(json, fallback, ((WidgetTextFieldTheme) parent).getHintColor(), IThemeApi.HINT_COLOR); - } - - public int getMarkedColor() { - return this.markedColor; - } - - public int getHintColor() { - return hintColor; - } -} diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetTheme.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetTheme.java index 3ca408fec..744cdb2fc 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/WidgetTheme.java +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetTheme.java @@ -2,48 +2,82 @@ import com.cleanroommc.modularui.api.IThemeApi; import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.JsonHelper; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import org.jetbrains.annotations.Nullable; public class WidgetTheme { - public static WidgetTheme getDefault() { - return ThemeAPI.DEFAULT_DEFAULT.getFallback(); + public static WidgetThemeEntry getDefault() { + return ThemeAPI.DEFAULT_THEME.getFallback(); } + private final int defaultWidth; + private final int defaultHeight; @Nullable private final IDrawable background; - @Nullable - private final IDrawable hoverBackground; private final int color; private final int textColor; private final boolean textShadow; + private final int iconColor; + + public static WidgetTheme whiteTextShadow(int defaultWidth, int defaultHeight, @Nullable IDrawable background) { + return new WidgetTheme(defaultWidth, defaultHeight, background, Color.WHITE.main, Color.WHITE.main, true, Color.WHITE.main); + } + + public static WidgetTheme darkTextNoShadow(int defaultWidth, int defaultHeight, @Nullable IDrawable background) { + return new WidgetTheme(defaultWidth, defaultHeight, background, Color.WHITE.main, Color.TEXT_COLOR_DARK, false, Color.WHITE.main); + } - public WidgetTheme(@Nullable IDrawable background, @Nullable IDrawable hoverBackground, - int color, int textColor, boolean textShadow) { + public WidgetTheme(int defaultWidth, int defaultHeight, @Nullable IDrawable background, + int color, int textColor, boolean textShadow, int iconColor) { + this.defaultWidth = defaultWidth; + this.defaultHeight = defaultHeight; this.background = background; - this.hoverBackground = hoverBackground; this.color = color; - this.textColor = textColor; + this.textColor = textColor == 0 ? color : textColor; this.textShadow = textShadow; + this.iconColor = iconColor == 0 ? color : iconColor; } public WidgetTheme(WidgetTheme parent, JsonObject json, JsonObject fallback) { - this.background = JsonHelper.deserializeWithFallback(json, fallback, IDrawable.class, parent.getBackground(), IThemeApi.BACKGROUND, "bg"); - this.hoverBackground = JsonHelper.deserializeWithFallback(json, fallback, IDrawable.class, parent.getHoverBackground(), IThemeApi.HOVER_BACKGROUND, "hbg"); - this.color = JsonHelper.getColorWithFallback(json, fallback, parent.getColor(), IThemeApi.COLOR); - this.textColor = JsonHelper.getColorWithFallback(json, fallback, parent.getTextColor(), IThemeApi.TEXT_COLOR); - this.textShadow = JsonHelper.getBoolWithFallback(json, fallback, parent.getTextShadow(), IThemeApi.TEXT_SHADOW); + this.defaultWidth = JsonHelper.getInt(json, parent.getDefaultWidth(), "w", "width"); + this.defaultHeight = JsonHelper.getInt(json, parent.getDefaultHeight(), "h", "height"); + this.background = JsonHelper.deserialize(json, IDrawable.class, parent.getBackground(), IThemeApi.BACKGROUND, "bg"); + // color, textColor, textShadow and iconColor inherit from fallback first and then from parent widget theme + this.color = JsonHelper.getColorWithFallback(json, inherits(json, IThemeApi.COLOR) ? null : fallback, parent.getColor(), IThemeApi.COLOR); + int textColor = JsonHelper.getColorWithFallback(json, inherits(json, IThemeApi.TEXT_COLOR) ? null : fallback, parent.getTextColor(), IThemeApi.TEXT_COLOR); + this.textColor = textColor == 0 ? color : textColor; + this.textShadow = JsonHelper.getBoolWithFallback(json, inherits(json, IThemeApi.TEXT_SHADOW) ? null : fallback, parent.getTextShadow(), IThemeApi.TEXT_SHADOW); + int iconColor = JsonHelper.getColorWithFallback(json, inherits(json, IThemeApi.ICON_COLOR) ? null : fallback, parent.getIconColor(), IThemeApi.ICON_COLOR); + this.iconColor = iconColor == 0 ? color : iconColor; } - public @Nullable IDrawable getBackground() { - return this.background; + protected static boolean inherits(JsonObject json, String property) { + if (!json.has("inherit")) return false; + JsonElement element = json.get("inherit"); + if (element.isJsonPrimitive()) return element.getAsString().equals(property); + if (element.isJsonArray()) { + for (JsonElement e : element.getAsJsonArray()) { + if (e.isJsonPrimitive() && e.getAsString().equals(property)) return true; + } + } + return false; } - public @Nullable IDrawable getHoverBackground() { - return this.hoverBackground; + public int getDefaultWidth() { + return defaultWidth; + } + + public int getDefaultHeight() { + return defaultHeight; + } + + public @Nullable IDrawable getBackground() { + return this.background; } public int getColor() { @@ -58,8 +92,11 @@ public boolean getTextShadow() { return this.textShadow; } + public int getIconColor() { + return iconColor; + } + public WidgetTheme withColor(int color) { - // TODO it is currently somewhat difficult to color drawable with a custom color. This is a dirty solution. - return new WidgetTheme(this.background, this.hoverBackground, color, this.textColor, this.textShadow); + return new WidgetTheme(this.defaultWidth, this.defaultHeight, this.background, color, this.textColor, this.textShadow, this.iconColor); } } diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeBuilder.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeBuilder.java new file mode 100644 index 000000000..ce73b9a24 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeBuilder.java @@ -0,0 +1,65 @@ +package com.cleanroommc.modularui.theme; + +import com.cleanroommc.modularui.api.IThemeApi; +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.drawable.DrawableSerialization; +import com.cleanroommc.modularui.utils.JsonBuilder; + +/** + * A json builder with helper methods to make building widget themes in java easier. + * This class is meant to be extended for custom widget themes. + * + * @param type of the widget theme this builder is for + * @param type of this builder class + */ +public class WidgetThemeBuilder> extends JsonBuilder { + + @SuppressWarnings("unchecked") + protected B getThis() { + return (B) this; + } + + public B defaultWidth(int defaultWidth) { + add(IThemeApi.DEFAULT_WIDTH, defaultWidth); + return getThis(); + } + + public B defaultHeight(int defaultHeight) { + add(IThemeApi.DEFAULT_HEIGHT, defaultHeight); + return getThis(); + } + + public B color(int color) { + add(IThemeApi.COLOR, color); + return getThis(); + } + + public B textColor(int color) { + add(IThemeApi.TEXT_COLOR, color); + return getThis(); + } + + public B textShadow(int shadow) { + add(IThemeApi.TEXT_SHADOW, shadow); + return getThis(); + } + + public B iconColor(int color) { + add(IThemeApi.ICON_COLOR, color); + return getThis(); + } + + public B background(JsonBuilder builder) { + add(IThemeApi.BACKGROUND, builder); + return getThis(); + } + + public B background(IDrawable drawable) { + add(IThemeApi.BACKGROUND, DrawableSerialization.serialize(drawable)); + return getThis(); + } + + public B background(String textureId) { + return background(new JsonBuilder().add("type", "texture").add("id", textureId)); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeEntry.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeEntry.java new file mode 100644 index 000000000..0b7ddd99f --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeEntry.java @@ -0,0 +1,44 @@ +package com.cleanroommc.modularui.theme; + +public class WidgetThemeEntry { + + private final WidgetThemeKey key; + private final T theme; + private final T hoverTheme; + + public WidgetThemeEntry(WidgetThemeKey key, T theme) { + this(key, theme, theme); + } + + public WidgetThemeEntry(WidgetThemeKey key, T theme, T hoverTheme) { + this.key = key; + this.theme = theme; + this.hoverTheme = hoverTheme; + } + + public WidgetThemeKey getKey() { + return key; + } + + public T getTheme() { + return theme; + } + + public T getHoverTheme() { + return hoverTheme; + } + + public T getTheme(boolean hover) { + return hover ? hoverTheme : theme; + } + + @SuppressWarnings("unchecked") + public WidgetThemeEntry expectType(Class expectedType) { + if (this.key.isOfType(expectedType)) { + return (WidgetThemeEntry) this; + } + throw new IllegalStateException( + String.format("Got widget theme with invalid type. Got type '%s', but expected type '%s'!", + this.key.getWidgetThemeType().getSimpleName(), expectedType.getSimpleName())); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeKey.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeKey.java new file mode 100644 index 000000000..1de4fd0e7 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeKey.java @@ -0,0 +1,160 @@ +package com.cleanroommc.modularui.theme; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Objects; + +/** + * A key used to identify widget themes. + * @param type of associated widget theme + */ +public class WidgetThemeKey implements Comparable> { + + private static final Map> KEYS = new Object2ObjectOpenHashMap<>(); + + public static @Nullable WidgetThemeKey getFromFullName(String key) { + return KEYS.get(key); + } + + @Nullable private final WidgetThemeKey parent; + private final Class type; + private final String name; + @Nullable private final String subName; + private final T defaultValue; + private final T defaultHoverValue; + private final WidgetThemeParser parser; + + WidgetThemeKey(Class type, String name, T defaultValue, WidgetThemeParser parser) { + this(type, name, defaultValue, defaultValue, parser); + } + + WidgetThemeKey(Class type, String name, T defaultValue, T defaultHoverValue, WidgetThemeParser parser) { + this(null, type, name, null, defaultValue, defaultHoverValue, parser); + } + + private WidgetThemeKey(@Nullable WidgetThemeKey parent, Class type, String name, @Nullable String subName, T defaultValue, T defaultHoverValue, WidgetThemeParser parser) { + this.parent = parent; + this.type = type; + this.name = name; + this.subName = subName; + this.defaultValue = defaultValue; + this.defaultHoverValue = defaultHoverValue; + this.parser = parser; + KEYS.put(getFullName(), this); + ThemeAPI.INSTANCE.registerWidgetThemeKey(this); + } + + public WidgetThemeKey createSubKey(String subName) { + return createSubKey(subName, null, null); + } + + public WidgetThemeKey createSubKey(String subName, @Nullable T defaultValue, @Nullable T defaultHoverValue) { + WidgetThemeKey existing = KEYS.get(getName() + ":" + subName); + if (existing != null) { + if (existing.type == type) { + return (WidgetThemeKey) existing; + } + throw new IllegalStateException("A widget theme key for id " + getName() + ":" + subName + + " already exists, but with a different types '" + existing.type.getSimpleName() + "' and '" + type.getSimpleName() + "'."); + } + return new WidgetThemeKey<>(this, type, name, subName, + defaultValue != null ? defaultValue : getDefaultValue(), + defaultHoverValue != null ? defaultHoverValue : getDefaultValue(), + this.parser); + } + + public @Nullable WidgetThemeKey getParent() { + return parent; + } + + public Class getWidgetThemeType() { + return type; + } + + public String getName() { + return name; + } + + public @Nullable String getSubName() { + return subName; + } + + public String getFullName() { + if (subName != null) { + return name + ":" + subName; + } + return name; + } + + public T getDefaultValue() { + return defaultValue; + } + + public T getDefaultHoverValue() { + return defaultHoverValue; + } + + public WidgetThemeParser getParser() { + return parser; + } + + public boolean isSubWidgetTheme() { + return parent != null; + } + + public boolean isCompatible(WidgetTheme theme) { + return type.isInstance(theme); + } + + public boolean isExactType(WidgetTheme theme) { + return theme != null && type == theme.getClass(); + } + + public boolean isOfType(Class type) { + return type.isAssignableFrom(this.type); + } + + public T cast(WidgetTheme theme) { + return type.cast(theme); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WidgetThemeKey that = (WidgetThemeKey) o; + return Objects.equals(name, that.name) && Objects.equals(subName, that.subName); + } + + @Override + public int hashCode() { + return Objects.hash(name, subName); + } + + @Override + public int compareTo(@NotNull WidgetThemeKey o) { + if (o == this) return 0; + int i = Boolean.compare(isSubWidgetTheme(), o.isSubWidgetTheme()); + if (i != 0) return i; + i = isAncestor(o); + if (i != 0) return i; + return name.compareTo(o.name); + } + + private int isAncestor(WidgetThemeKey other) { + WidgetThemeKey parent = getParent(); + while (parent != null) { + if (parent == other) return 1; + parent = parent.getParent(); + } + parent = other.getParent(); + while (parent != null) { + if (parent == this) return -1; + parent = parent.getParent(); + } + return 0; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeKeyBuilder.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeKeyBuilder.java new file mode 100644 index 000000000..bd6906a30 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeKeyBuilder.java @@ -0,0 +1,61 @@ +package com.cleanroommc.modularui.theme; + +import com.cleanroommc.modularui.api.IThemeApi; + +import com.google.gson.JsonObject; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Objects; + +public class WidgetThemeKeyBuilder { + + private final String id; + private T defaultTheme; + private T defaultHoverTheme; + private WidgetThemeParser parser; + + + public WidgetThemeKeyBuilder(String id) { + this.id = id; + } + + public WidgetThemeKeyBuilder defaultTheme(T defaultTheme) { + this.defaultTheme = defaultTheme; + return this; + } + + public WidgetThemeKeyBuilder defaultHoverTheme(T defaultHoverTheme) { + this.defaultHoverTheme = defaultHoverTheme; + return this; + } + + public WidgetThemeKeyBuilder parser(WidgetThemeParser parser) { + this.parser = parser; + return this; + } + + public WidgetThemeKey register() { + Objects.requireNonNull(this.id, "Id for widget theme must not be null"); + Objects.requireNonNull(this.defaultTheme, "Default widget theme must not be null, but is null for id '" + this.id + "'."); + if (this.parser == null) parser = createParserWithReflection(); + if (this.defaultHoverTheme == null) defaultHoverTheme = defaultTheme; + return IThemeApi.get().registerWidgetTheme(this.id, this.defaultTheme, this.defaultHoverTheme, this.parser); + } + + private WidgetThemeParser createParserWithReflection() { + Class type = (Class) this.defaultTheme.getClass(); + try { + Constructor ctor = type.getConstructor(type, JsonObject.class, JsonObject.class); + return (parent, json, fallback) -> { + try { + return ctor.newInstance(parent, json, fallback); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to instantiate widget theme of type " + type.getSimpleName(), e); + } + }; + } catch (NoSuchMethodException e) { + throw new RuntimeException(String.format("No constructor with signature '%s(%s parent, JsonObject json, JsonObject fallback)' found", type.getSimpleName(), type.getSimpleName())); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeMap.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeMap.java new file mode 100644 index 000000000..4e0a22744 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeMap.java @@ -0,0 +1,24 @@ +package com.cleanroommc.modularui.theme; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; + +public class WidgetThemeMap extends Object2ObjectOpenHashMap, WidgetThemeEntry> { + + @Override + public WidgetThemeEntry put(WidgetThemeKey widgetThemeKey, WidgetThemeEntry widgetTheme) { + if (widgetThemeKey != widgetTheme.getKey()) { + throw new IllegalArgumentException(widgetThemeKey.getFullName() + " is not compatible with " + widgetTheme.getKey().getFullName()); + } + return super.put(widgetThemeKey, widgetTheme); + } + + @SuppressWarnings("unchecked") + public WidgetThemeEntry putTheme(WidgetThemeKey widgetThemeKey, WidgetThemeEntry widgetTheme) { + return (WidgetThemeEntry) super.put(widgetThemeKey, widgetTheme); + } + + @SuppressWarnings("unchecked") + public WidgetThemeEntry getTheme(WidgetThemeKey widgetThemeKey) { + return (WidgetThemeEntry) super.get(widgetThemeKey); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeParser.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeParser.java index dfd9d041f..614cceb02 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeParser.java +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeParser.java @@ -6,7 +6,7 @@ * An interface used to parse json objects to widget themes. */ @FunctionalInterface -public interface WidgetThemeParser { +public interface WidgetThemeParser { /** * Parses a json object to a widget theme, @@ -16,5 +16,5 @@ public interface WidgetThemeParser { * @param fallback a fallback widget theme json data object * @return the parsed widget theme */ - WidgetTheme parse(WidgetTheme parent, JsonObject json, JsonObject fallback); + T parse(T parent, JsonObject json, JsonObject fallback); } diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeSelectable.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeSelectable.java deleted file mode 100644 index 5be4be9c6..000000000 --- a/src/main/java/com/cleanroommc/modularui/theme/WidgetThemeSelectable.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.cleanroommc.modularui.theme; - -import com.cleanroommc.modularui.api.IThemeApi; -import com.cleanroommc.modularui.api.drawable.IDrawable; -import com.cleanroommc.modularui.utils.JsonHelper; - -import com.google.gson.JsonObject; -import org.jetbrains.annotations.Nullable; - -public class WidgetThemeSelectable extends WidgetTheme { - - private final WidgetTheme selected; - - 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 WidgetThemeSelectable(WidgetTheme parent, JsonObject json, JsonObject fallback) { - super(parent, json, fallback); - WidgetThemeSelectable parentWTBT = (WidgetThemeSelectable) parent; - IDrawable selectedBackground = JsonHelper.deserializeWithFallback(json, fallback, IDrawable.class, parentWTBT.getSelected().getBackground(), IThemeApi.SELECTED_BACKGROUND); - IDrawable selectedHoverBackground = JsonHelper.deserializeWithFallback(json, fallback, IDrawable.class, parentWTBT.getSelected().getHoverBackground(), IThemeApi.SELECTED_HOVER_BACKGROUND); - int selectedColor = JsonHelper.getColorWithFallback(json, fallback, parentWTBT.getSelected().getColor(), IThemeApi.SELECTED_COLOR); - int selectedTextColor = JsonHelper.getColorWithFallback(json, fallback, parentWTBT.getSelected().getTextColor(), IThemeApi.SELECTED_TEXT_COLOR); - boolean selectedTextShadow = JsonHelper.getBoolWithFallback(json, fallback, parentWTBT.getSelected().getTextShadow(), IThemeApi.SELECTED_TEXT_SHADOW); - this.selected = new WidgetTheme(selectedBackground, selectedHoverBackground, selectedColor, selectedTextColor, selectedTextShadow); - } - - public WidgetTheme getSelected() { - return selected; - } -} diff --git a/src/main/java/com/cleanroommc/modularui/utils/Color.java b/src/main/java/com/cleanroommc/modularui/utils/Color.java index fe6698e00..7bc366ea9 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/Color.java +++ b/src/main/java/com/cleanroommc/modularui/utils/Color.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.utils; +import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.api.drawable.IInterpolation; import net.minecraft.client.renderer.GlStateManager; @@ -755,11 +756,37 @@ public static void resetGlColor() { */ public static int ofJson(JsonElement jsonElement) { if (jsonElement.isJsonPrimitive()) { - int color = (int) (long) Long.decode(jsonElement.getAsString()); // bruh - if (color != 0 && getAlpha(color) == 0) { - return withAlpha(color, 255); + String colorString = jsonElement.getAsString(); + if (colorString.isEmpty()) return WHITE.main; + if (colorString.charAt(0) == '0' || colorString.charAt(0) == '#') { + int color = (int) (long) Long.decode(colorString); // bruh + if (color != 0 && getAlpha(color) == 0) { + return withAlpha(color, 255); + } + return color; + } + + if ("invisible".equals(jsonElement.getAsString())) { + return withAlpha(WHITE.main, 0); } - return color; + int i = colorString.indexOf(':'); + int index = 0; + if (i > 0) { + try { + index = Integer.parseInt(colorString.substring(i + 1)); + } catch (NumberFormatException e) { + ModularUI.LOGGER.error("[THEME] If the color is a word, then after the : must come a negative or positive integer, but got '{}'", colorString.substring(i + 1)); + } + colorString = colorString.substring(0, i); + } + ColorShade colorShade = ColorShade.getFromName(colorString); + if (colorShade != null) { + if (index == 0) return colorShade.main; + if (index > 0) return colorShade.brighterSafe(index - 1); + else return colorShade.darkerSafe(-index - 1); + } + ModularUI.LOGGER.error("[THEME] No color shade for name '{}' was found", colorString); + return WHITE.main; } if (jsonElement.isJsonObject()) { JsonObject json = jsonElement.getAsJsonObject(); @@ -845,185 +872,187 @@ private static boolean hasCMYK(JsonObject json) { json.has("k") || json.has("black"); } - public static final ColorShade WHITE = ColorShade.builder(0xFFFFFFFF) + public static final int TEXT_COLOR_DARK = 0xFF404040; + + public static final ColorShade WHITE = ColorShade.builder("white", 0xFFFFFFFF) .addDarker(0xFFF7F7F7, 0xFFEFEFEF, 0xFFE7E7E7, 0xFFDFDFDF, 0xFFD7D7D7, 0xFFCFCFCF, 0xFFC7C7C7, 0xFFBFBFBF) .build(); - public static final ColorShade BLACK = ColorShade.builder(0xFF000000) + public static final ColorShade BLACK = ColorShade.builder("black", 0xFF000000) .addBrighter(0xFF080808, 0xFF101010, 0xFF181818, 0xFF202020, 0xFF282828, 0xFF303030, 0xFF383838, 0xFF404040) .build(); - public static final ColorShade RED = ColorShade.builder(0xFFF44336) + public static final ColorShade RED = ColorShade.builder("red", 0xFFF44336) .addBrighter(0xFFEF5350, 0xFFE57373, 0xFFEF9A9A, 0xFFFFCDD2, 0xFFFFEBEE) .addDarker(0xFFE53935, 0xFFD32F2F, 0xFFC62828, 0xFFB71C1C) .build(); - public static final ColorShade RED_ACCENT = ColorShade.builder(0xFFFF5252) + public static final ColorShade RED_ACCENT = ColorShade.builder("red_accent", 0xFFFF5252) .addBrighter(0xFFFF8A80) .addDarker(0xFFFF1744, 0xFFD50000) .build(); - public static final ColorShade PINK = ColorShade.builder(0xFFE91E63) + public static final ColorShade PINK = ColorShade.builder("pink", 0xFFE91E63) .addBrighter(0xFFEC407A, 0xFFF06292, 0xFFF48FB1, 0xFFF8BBD0, 0xFFFCE4EC) .addDarker(0xFFD81B60, 0xFFC2185B, 0xFFAD1457, 0xFF880E4F) .build(); - public static final ColorShade PINK_ACCENT = ColorShade.builder(0xFFFF4081) + public static final ColorShade PINK_ACCENT = ColorShade.builder("pink_accent", 0xFFFF4081) .addBrighter(0xFFFF80AB) .addDarker(0xFFF50057, 0xFFC51162) .build(); - public static final ColorShade PURPLE = ColorShade.builder(0xFF9C27B0) + public static final ColorShade PURPLE = ColorShade.builder("purple", 0xFF9C27B0) .addBrighter(0xFFAB47BC, 0xFFBA68C8, 0xFFCE93D8, 0xFFE1BEE7, 0xFFF3E5F5) .addDarker(0xFF8E24AA, 0xFF7B1FA2, 0xFF6A1B9A, 0xFF4A148C) .build(); - public static final ColorShade PURPLE_ACCENT = ColorShade.builder(0xFFE040FB) + public static final ColorShade PURPLE_ACCENT = ColorShade.builder("purple_accent", 0xFFE040FB) .addBrighter(0xFFEA80FC) .addDarker(0xFFD500F9, 0xFFAA00FF) .build(); - public static final ColorShade DEEP_PURPLE = ColorShade.builder(0xFF673AB7) + public static final ColorShade DEEP_PURPLE = ColorShade.builder("deep_purple", 0xFF673AB7) .addBrighter(0xFF7E57C2, 0xFF9575CD, 0xFFB39DDB, 0xFFD1C4E9, 0xFFEDE7F6) .addDarker(0xFF5E35B1, 0xFF512DA8, 0xFF4527A0, 0xFF311B92) .build(); - public static final ColorShade DEEP_PURPLE_ACCENT = ColorShade.builder(0xFF7C4DFF) + public static final ColorShade DEEP_PURPLE_ACCENT = ColorShade.builder("deep_purple_accent", 0xFF7C4DFF) .addBrighter(0xFFB388FF) .addDarker(0xFF651FFF, 0xFF651FFF) .build(); - public static final ColorShade INDIGO = ColorShade.builder(0xFF3F51B5) + public static final ColorShade INDIGO = ColorShade.builder("indigo", 0xFF3F51B5) .addBrighter(0xFF5C6BC0, 0xFF7986CB, 0xFF9FA8DA, 0xFFC5CAE9, 0xFFE8EAF6) .addDarker(0xFF3949AB, 0xFF303F9F, 0xFF283593, 0xFF1A237E) .build(); - public static final ColorShade INDIGO_ACCENT = ColorShade.builder(0xFF536DFE) + public static final ColorShade INDIGO_ACCENT = ColorShade.builder("indigo_accent", 0xFF536DFE) .addBrighter(0xFF8C9EFF) .addDarker(0xFF3D5AFE, 0xFF304FFE) .build(); - public static final ColorShade BLUE = ColorShade.builder(0xFF2196F3) + public static final ColorShade BLUE = ColorShade.builder("blue", 0xFF2196F3) .addBrighter(0xFF42A5F5, 0xFF64B5F6, 0xFF90CAF9, 0xFFBBDEFB, 0xFFE3F2FD) .addDarker(0xFF1E88E5, 0xFF1976D2, 0xFF1565C0, 0xFF0D47A1) .build(); - public static final ColorShade BLUE_ACCENT = ColorShade.builder(0xFF448AFF) + public static final ColorShade BLUE_ACCENT = ColorShade.builder("blue_accent", 0xFF448AFF) .addBrighter(0xFF82B1FF) .addDarker(0xFF2979FF, 0xFF2962FF) .build(); - public static final ColorShade LIGHT_BLUE = ColorShade.builder(0xFF03A9F4) + public static final ColorShade LIGHT_BLUE = ColorShade.builder("light_blue", 0xFF03A9F4) .addBrighter(0xFF29B6F6, 0xFF4FC3F7, 0xFF81D4FA, 0xFFB3E5FC, 0xFFE1F5FE) .addDarker(0xFF039BE5, 0xFF0288D1, 0xFF0277BD, 0xFF01579B) .build(); - public static final ColorShade LIGHT_BLUE_ACCENT = ColorShade.builder(0xFF40C4FF) + public static final ColorShade LIGHT_BLUE_ACCENT = ColorShade.builder("light_blue_accent", 0xFF40C4FF) .addBrighter(0xFF80D8FF) .addDarker(0xFF00B0FF, 0xFF0091EA) .build(); - public static final ColorShade CYAN = ColorShade.builder(0xFF00BCD4) + public static final ColorShade CYAN = ColorShade.builder("cyan", 0xFF00BCD4) .addBrighter(0xFF26C6DA, 0xFF4DD0E1, 0xFF80DEEA, 0xFFB2EBF2, 0xFFE0F7FA) .addDarker(0xFF00ACC1, 0xFF0097A7, 0xFF00838F, 0xFF006064) .build(); - public static final ColorShade CYAN_ACCENT = ColorShade.builder(0xFF18FFFF) + public static final ColorShade CYAN_ACCENT = ColorShade.builder("cyan_accent", 0xFF18FFFF) .addBrighter(0xFF84FFFF) .addDarker(0xFF00E5FF, 0xFF00B8D4) .build(); - public static final ColorShade TEAL = ColorShade.builder(0xFF009688) + public static final ColorShade TEAL = ColorShade.builder("teal", 0xFF009688) .addBrighter(0xFF26A69A, 0xFF4DB6AC, 0xFF80CBC4, 0xFFB2DFDB, 0xFFE0F2F1) .addDarker(0xFF00897B, 0xFF00796B, 0xFF00695C, 0xFF004D40) .build(); - public static final ColorShade TEAL_ACCENT = ColorShade.builder(0xFF64FFDA) + public static final ColorShade TEAL_ACCENT = ColorShade.builder("teal_accent", 0xFF64FFDA) .addBrighter(0xFFA7FFEB) .addDarker(0xFF1DE9B6, 0xFF00BFA5) .build(); - public static final ColorShade GREEN = ColorShade.builder(0xFF4CAF50) + public static final ColorShade GREEN = ColorShade.builder("green", 0xFF4CAF50) .addBrighter(0xFF66BB6A, 0xFF81C784, 0xFFA5D6A7, 0xFFC8E6C9, 0xFFE8F5E9) .addDarker(0xFF43A047, 0xFF388E3C, 0xFF2E7D32, 0xFF1B5E20) .build(); - public static final ColorShade GREEN_ACCENT = ColorShade.builder(0xFF69F0AE) + public static final ColorShade GREEN_ACCENT = ColorShade.builder("green_accent", 0xFF69F0AE) .addBrighter(0xFFB9F6CA) .addDarker(0xFF00E676, 0xFF00C853) .build(); - public static final ColorShade LIGHT_GREEN = ColorShade.builder(0xFF8BC34A) + public static final ColorShade LIGHT_GREEN = ColorShade.builder("light_green", 0xFF8BC34A) .addBrighter(0xFF9CCC65, 0xFFAED581, 0xFFC5E1A5, 0xFFDCEDC8, 0xFFF1F8E9) .addDarker(0xFF7CB342, 0xFF689F38, 0xFF558B2F, 0xFF33691E) .build(); - public static final ColorShade LIGHT_GREEN_ACCENT = ColorShade.builder(0xFFB2FF59) + public static final ColorShade LIGHT_GREEN_ACCENT = ColorShade.builder("light_green_accent", 0xFFB2FF59) .addBrighter(0xFFCCFF90) .addDarker(0xFF76FF03, 0xFF64DD17) .build(); - public static final ColorShade LIME = ColorShade.builder(0xFFCDDC39) + public static final ColorShade LIME = ColorShade.builder("lime", 0xFFCDDC39) .addBrighter(0xFFD4E157, 0xFFDCE775, 0xFFE6EE9C, 0xFFF0F4C3, 0xFFF9FBE7) .addDarker(0xFFC0CA33, 0xFFAFB42B, 0xFF9E9D24, 0xFF827717) .build(); - public static final ColorShade LIME_ACCENT = ColorShade.builder(0xFFEEFF41) + public static final ColorShade LIME_ACCENT = ColorShade.builder("lime_accent", 0xFFEEFF41) .addBrighter(0xFFF4FF81) .addDarker(0xFFC6FF00, 0xFFAEEA00) .build(); - public static final ColorShade YELLOW = ColorShade.builder(0xFFFFEB3B) + public static final ColorShade YELLOW = ColorShade.builder("yellow", 0xFFFFEB3B) .addBrighter(0xFFFFEE58, 0xFFFFF176, 0xFFFFF59D, 0xFFFFF9C4, 0xFFFFFDE7) .addDarker(0xFFFDD835, 0xFFFBC02D, 0xFFF9A825, 0xFFF57F17) .build(); - public static final ColorShade YELLOW_ACCENT = ColorShade.builder(0xFFFFFF00) + public static final ColorShade YELLOW_ACCENT = ColorShade.builder("yellow_accent", 0xFFFFFF00) .addBrighter(0xFFFFFF8D) .addDarker(0xFFFFEA00, 0xFFFFD600) .build(); - public static final ColorShade AMBER = ColorShade.builder(0xFFFFC107) + public static final ColorShade AMBER = ColorShade.builder("amber", 0xFFFFC107) .addBrighter(0xFFFFCA28, 0xFFFFD54F, 0xFFFFE082, 0xFFFFECB3, 0xFFFFF8E1) .addDarker(0xFFFFB300, 0xFFFFA000, 0xFFFF8F00, 0xFFFF6F00) .build(); - public static final ColorShade AMBER_ACCENT = ColorShade.builder(0xFFFFD740) + public static final ColorShade AMBER_ACCENT = ColorShade.builder("amber_accent", 0xFFFFD740) .addBrighter(0xFFFFE57F) .addDarker(0xFFFFC400, 0xFFFFAB00) .build(); - public static final ColorShade ORANGE = ColorShade.builder(0xFFFF9800) + public static final ColorShade ORANGE = ColorShade.builder("orange", 0xFFFF9800) .addBrighter(0xFFFFA726, 0xFFFFB74D, 0xFFFFCC80, 0xFFFFE0B2, 0xFFFFF3E0) .addDarker(0xFFFB8C00, 0xFFF57C00, 0xFFEF6C00, 0xFFE65100) .build(); - public static final ColorShade ORANGE_ACCENT = ColorShade.builder(0xFFFFAB40) + public static final ColorShade ORANGE_ACCENT = ColorShade.builder("orange_accent", 0xFFFFAB40) .addBrighter(0xFFFFD180) .addDarker(0xFFFF9100, 0xFFFF6D00) .build(); - public static final ColorShade DEEP_ORANGE = ColorShade.builder(0xFFFF5722) + public static final ColorShade DEEP_ORANGE = ColorShade.builder("deep_orange", 0xFFFF5722) .addBrighter(0xFFFF7043, 0xFFFF8A65, 0xFFFFAB91, 0xFFFFCCBC, 0xFFFBE9E7) .addDarker(0xFFF4511E, 0xFFE64A19, 0xFFD84315, 0xFFBF360C) .build(); - public static final ColorShade DEEP_ORANGE_ACCENT = ColorShade.builder(0xFFFF6E40) + public static final ColorShade DEEP_ORANGE_ACCENT = ColorShade.builder("deep_orange_accent", 0xFFFF6E40) .addBrighter(0xFFFF9E80) .addDarker(0xFFFF3D00, 0xFFDD2C00) .build(); - public static final ColorShade BROWN = ColorShade.builder(0xFF795548) + public static final ColorShade BROWN = ColorShade.builder("bown", 0xFF795548) .addBrighter(0xFF8D6E63, 0xFFA1887F, 0xFFBCAAA4, 0xFFD7CCC8, 0xFFEFEBE9) .addDarker(0xFF6D4C41, 0xFF5D4037, 0xFF4E342E, 0xFF3E2723) .build(); - public static final ColorShade GREY = ColorShade.builder(0xFF9E9E9E) + public static final ColorShade GREY = ColorShade.builder("grey", 0xFF9E9E9E) .addBrighter(0xFFBDBDBD, 0xFFE0E0E0, 0xFFEEEEEE, 0xFFF5F5F5, 0xFFFAFAFA) .addDarker(0xFF757575, 0xFF616161, 0xFF424242, 0xFF212121) .build(); - public static final ColorShade BLUE_GREY = ColorShade.builder(0xFF607D8B) + public static final ColorShade BLUE_GREY = ColorShade.builder("blue_grey", 0xFF607D8B) .addBrighter(0xFF78909C, 0xFF90A4AE, 0xFFB0BEC5, 0xFFCFD8DC, 0xFFECEFF1) .addDarker(0xFF546E7A, 0xFF455A64, 0xFF37474F, 0xFF263238) .build(); diff --git a/src/main/java/com/cleanroommc/modularui/utils/ColorShade.java b/src/main/java/com/cleanroommc/modularui/utils/ColorShade.java index 65767096a..fad877da2 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/ColorShade.java +++ b/src/main/java/com/cleanroommc/modularui/utils/ColorShade.java @@ -4,20 +4,32 @@ import it.unimi.dsi.fastutil.ints.IntIterable; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntIterators; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; public class ColorShade implements IntIterable { - public static Builder builder(int main) { - return new Builder(main); + public static Builder builder(String name, int main) { + return new Builder(name, main); + } + + private static final Map COLOR_SHADES = new Object2ObjectOpenHashMap<>(); + + public static @Nullable ColorShade getFromName(String name) { + return COLOR_SHADES.get(name); } + public final String name; public final int main; private final int[] brighter; private final int[] darker; private final int[] all; - private ColorShade(int main, int[] brighter, int[] darker) { + private ColorShade(String name, int main, int[] brighter, int[] darker) { + this.name = name; this.main = main; this.brighter = brighter; this.darker = darker; @@ -30,16 +42,25 @@ private ColorShade(int main, int[] brighter, int[] darker) { for (int j : darker) { this.all[k++] = j; } + COLOR_SHADES.put(name, this); } public int darker(int index) { return this.darker[index]; } + public int darkerSafe(int index) { + return this.darker[MathUtils.clamp(index, 0, this.darker.length - 1)]; + } + public int brighter(int index) { return this.brighter[index]; } + public int brighterSafe(int index) { + return this.brighter[MathUtils.clamp(index, 0, this.brighter.length - 1)]; + } + @NotNull @Override public IntIterator iterator() { @@ -48,11 +69,13 @@ public IntIterator iterator() { public static class Builder { + private final String name; private final int main; private final IntArrayList darker = new IntArrayList(); private final IntArrayList brighter = new IntArrayList(); - public Builder(int main) { + public Builder(String name, int main) { + this.name = name; this.main = main; } @@ -69,7 +92,7 @@ public Builder addBrighter(int... brighter) { public ColorShade build() { this.darker.trim(); this.brighter.trim(); - return new ColorShade(this.main, this.brighter.elements(), this.darker.elements()); + return new ColorShade(this.name, this.main, this.brighter.elements(), this.darker.elements()); } } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/JsonHelper.java b/src/main/java/com/cleanroommc/modularui/utils/JsonHelper.java index eb3f8c778..db0d5bb9d 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/JsonHelper.java +++ b/src/main/java/com/cleanroommc/modularui/utils/JsonHelper.java @@ -47,6 +47,7 @@ public static T deserializeWithFallback(JsonObject json, JsonObject fallback } public static float getFloat(JsonObject json, float defaultValue, String @NotNull ... keys) { + if (json == null) return defaultValue; for (String key : keys) { if (json.has(key)) { JsonElement jsonElement = json.get(key); @@ -60,6 +61,7 @@ public static float getFloat(JsonObject json, float defaultValue, String @NotNul } public static int getInt(JsonObject json, int defaultValue, String @NotNull ... keys) { + if (json == null) return defaultValue; for (String key : keys) { if (json.has(key)) { JsonElement jsonElement = json.get(key); @@ -78,6 +80,7 @@ public static int getIntWithFallback(JsonObject json, JsonObject fallback, int d } public static boolean getBoolean(JsonObject json, boolean defaultValue, String @NotNull ... keys) { + if (json == null) return defaultValue; for (String key : keys) { if (json.has(key)) { JsonElement jsonElement = json.get(key); @@ -96,6 +99,7 @@ public static boolean getBoolWithFallback(JsonObject json, JsonObject fallback, } public static String getString(JsonObject json, String defaultValue, String @NotNull ... keys) { + if (json == null) return defaultValue; for (String key : keys) { if (json.has(key)) { JsonElement jsonElement = json.get(key); @@ -106,6 +110,7 @@ public static String getString(JsonObject json, String defaultValue, String @Not } public static T getObject(JsonObject json, T defaultValue, Function factory, String @NotNull ... keys) { + if (json == null) return defaultValue; for (String key : keys) { if (json.has(key)) { JsonElement jsonElement = json.get(key); @@ -119,6 +124,7 @@ public static T getObject(JsonObject json, T defaultValue, Function T getElement(JsonObject json, T defaultValue, Function factory, String @NotNull ... keys) { + if (json == null) return defaultValue; for (String key : keys) { if (json.has(key)) { JsonElement jsonElement = json.get(key); @@ -129,6 +135,7 @@ public static T getElement(JsonObject json, T defaultValue, Function T getElement(JsonObject json, T defaultValue, Function widgetTheme = getWidgetTheme(getContext().getTheme()); + if (getBackground() == null && IDrawable.isVisible(widgetTheme.getTheme().getBackground())) return true; + return getHoverBackground() == null && IDrawable.isVisible(widgetTheme.getHoverTheme().getBackground()); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widget/EmptyWidget.java b/src/main/java/com/cleanroommc/modularui/widget/EmptyWidget.java index 02bf74348..ce67842e2 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/EmptyWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/EmptyWidget.java @@ -6,12 +6,11 @@ import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.widget.sizer.Area; import com.cleanroommc.modularui.widget.sizer.Flex; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class EmptyWidget implements IWidget { @@ -41,13 +40,13 @@ public boolean isValid() { } @Override - public void drawBackground(ModularGuiContext context, WidgetTheme widgetTheme) {} + public void drawBackground(ModularGuiContext context, WidgetThemeEntry widgetTheme) {} @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) {} + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) {} @Override - public void drawOverlay(ModularGuiContext context, WidgetTheme widgetTheme) {} + public void drawOverlay(ModularGuiContext context, WidgetThemeEntry widgetTheme) {} @Override public void drawForeground(ModularGuiContext context) {} diff --git a/src/main/java/com/cleanroommc/modularui/widget/Widget.java b/src/main/java/com/cleanroommc/modularui/widget/Widget.java index faa5e9c81..46aefb4b0 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/Widget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/Widget.java @@ -18,6 +18,8 @@ import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; +import com.cleanroommc.modularui.theme.WidgetThemeKey; import com.cleanroommc.modularui.value.sync.ModularSyncManager; import com.cleanroommc.modularui.value.sync.SyncHandler; import com.cleanroommc.modularui.value.sync.ValueSyncHandler; @@ -73,7 +75,7 @@ public class Widget> implements IWidget, IPositioned, ITo @Nullable private IDrawable hoverBackground = null; @Nullable private IDrawable hoverOverlay = null; @Nullable private RichTooltip tooltip; - @Nullable private String widgetThemeOverride = null; + @Nullable private WidgetThemeKey widgetThemeOverride = null; // listener @Nullable private List guiActionListeners; // TODO replace with proper event system @Nullable private Consumer onUpdateListener; @@ -189,24 +191,24 @@ public void dispose() { // ----------------- /** - * Called directly before {@link #draw(ModularGuiContext, WidgetTheme)}. Draws background textures. + * Called directly before {@link IWidget#draw(ModularGuiContext, WidgetThemeEntry)}. Draws background textures. * It is highly recommended to at least replicate this behaviour when overriding. - * Overriding {@link #draw(ModularGuiContext, WidgetTheme)} for custom visuals is preferred. + * Overriding {@link IWidget#draw(ModularGuiContext, WidgetThemeEntry)} for custom visuals is preferred. * If a parent of this widget is disabled, this widget will not be drawn. * * @param context gui context * @param widgetTheme widget theme of this widget */ @Override - public void drawBackground(ModularGuiContext context, WidgetTheme widgetTheme) { + public void drawBackground(ModularGuiContext context, WidgetThemeEntry widgetTheme) { IDrawable bg = getCurrentBackground(context.getTheme(), widgetTheme); if (bg != null) { - bg.drawAtZero(context, getArea().width, getArea().height, widgetTheme); + bg.drawAtZero(context, getArea().width, getArea().height, getActiveWidgetTheme(widgetTheme, isHovering())); } } /** - * Called between {@link #drawBackground(ModularGuiContext, WidgetTheme)} and {@link #drawOverlay(ModularGuiContext, WidgetTheme)}. + * Called between {@link IWidget#drawBackground(ModularGuiContext, WidgetThemeEntry)} and {@link IWidget#drawOverlay(ModularGuiContext, WidgetThemeEntry)}. * Custom visuals should be drawn here. For example the {@link com.cleanroommc.modularui.widgets.slot.ItemSlot ItemSlot} draws its item * here. If a parent of this widget is disabled, this widget will not be drawn. * @@ -214,22 +216,22 @@ public void drawBackground(ModularGuiContext context, WidgetTheme widgetTheme) { * @param widgetTheme widget theme */ @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) {} + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) {} /** - * Called directly after {@link #draw(ModularGuiContext, WidgetTheme)}. Draws overlay textures. + * Called directly after {@link IWidget#draw(ModularGuiContext, WidgetThemeEntry)}. Draws overlay textures. * It is highly recommended to at least replicate this behaviour when overriding. - * Overriding {@link #draw(ModularGuiContext, WidgetTheme)} for custom visuals is preferred. + * Overriding {@link IWidget#draw(ModularGuiContext, WidgetThemeEntry)} for custom visuals is preferred. * If a parent of this widget is disabled, this widget will not be drawn. * * @param context gui context * @param widgetTheme widget theme */ @Override - public void drawOverlay(ModularGuiContext context, WidgetTheme widgetTheme) { + public void drawOverlay(ModularGuiContext context, WidgetThemeEntry widgetTheme) { IDrawable bg = getCurrentOverlay(context.getTheme(), widgetTheme); if (bg != null) { - bg.drawAtZero(context, getArea(), widgetTheme); + bg.drawAtZero(context, getArea(), getActiveWidgetTheme(widgetTheme, isHovering())); } } @@ -250,7 +252,7 @@ public void drawForeground(ModularGuiContext context) { /** * The current set background. This is not an accurate representation of what is actually being displayed currently. * Usually background is handled by the theme, which is when this is null. - * Backgrounds are drawn in {@link #drawBackground(ModularGuiContext, WidgetTheme)}. + * Backgrounds are drawn in {@link IWidget#drawBackground(ModularGuiContext, WidgetThemeEntry)}. * * @return background of this widget */ @@ -260,7 +262,7 @@ public void drawForeground(ModularGuiContext context) { /** * The current set overlay. This is used when the widget is not hovered or no hovered overlay is set. - * Overlays are drawn in {@link #drawOverlay(ModularGuiContext, WidgetTheme)}. + * Overlays are drawn in {@link IWidget#drawOverlay(ModularGuiContext, WidgetThemeEntry)}. * * @return overlay of this widget */ @@ -293,14 +295,14 @@ public void drawForeground(ModularGuiContext context) { * @param widgetTheme widget theme which is used by this widget * @return currently displayed background */ - public @Nullable IDrawable getCurrentBackground(ITheme theme, WidgetTheme widgetTheme) { + public @Nullable IDrawable getCurrentBackground(ITheme theme, WidgetThemeEntry widgetTheme) { if (isHovering()) { IDrawable hoverBackground = getHoverBackground(); - if (hoverBackground == null) hoverBackground = widgetTheme.getHoverBackground(); + if (hoverBackground == null) hoverBackground = getActiveWidgetTheme(widgetTheme, true).getBackground(); if (hoverBackground != null && hoverBackground != IDrawable.NONE) return hoverBackground; } IDrawable background = getBackground(); - return background == null ? widgetTheme.getBackground() : background; + return background == null ? getActiveWidgetTheme(widgetTheme, false).getBackground() : background; } /** @@ -310,7 +312,7 @@ public void drawForeground(ModularGuiContext context) { * @param widgetTheme widget theme which is used by this widget * @return currently displayed background */ - public @Nullable IDrawable getCurrentOverlay(ITheme theme, WidgetTheme widgetTheme) { + public @Nullable IDrawable getCurrentOverlay(ITheme theme, WidgetThemeEntry widgetTheme) { IDrawable hoverBackground = getHoverOverlay(); return hoverBackground != null && hoverBackground != IDrawable.NONE && isHovering() ? hoverBackground : getOverlay(); } @@ -365,24 +367,43 @@ public void markTooltipDirty() { * @return widget theme this widget wishes to use */ @ApiStatus.OverrideOnly - protected WidgetTheme getWidgetThemeInternal(ITheme theme) { + protected WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { return theme.getFallback(); } + @ApiStatus.OverrideOnly + protected WidgetTheme getActiveWidgetTheme(WidgetThemeEntry widgetTheme, boolean hover) { + return widgetTheme.getTheme(hover); + } + + @ApiStatus.NonExtendable + @Override + public final WidgetThemeEntry getWidgetTheme(ITheme theme) { + if (this.widgetThemeOverride != null) { + return theme.getWidgetTheme(this.widgetThemeOverride); + } + return getWidgetThemeInternal(theme); + } + /** * Returns the actual used widget theme. Uses {@link #widgetTheme(String)} if it has been set, otherwise calls * {@link #getWidgetThemeInternal(ITheme)} * - * @param theme theme to get widget theme from + * @param theme theme to get widget theme from + * @param expectedType type of the widget theme to expect used for validation * @return widget theme this widget will use + * @throws IllegalStateException if the received widget theme type doesn't match the expected type */ + @SuppressWarnings("unchecked") @ApiStatus.NonExtendable - @Override - public final WidgetTheme getWidgetTheme(ITheme theme) { - if (this.widgetThemeOverride != null) { - return theme.getWidgetTheme(this.widgetThemeOverride); + public final WidgetThemeEntry getWidgetTheme(ITheme theme, Class expectedType) { + WidgetThemeEntry entry = getWidgetTheme(theme); + if (entry.getKey().isOfType(expectedType)) { + return (WidgetThemeEntry) entry; } - return getWidgetThemeInternal(theme); + throw new IllegalStateException( + String.format("Got widget theme with invalid type in widget '%s'. Got type '%s', but expected type '%s'!", + this, entry.getKey().getWidgetThemeType().getSimpleName(), expectedType.getSimpleName())); } /** @@ -416,7 +437,7 @@ public W overlay(IDrawable... overlay) { *

    * Following argument special cases should be considered: *

      - *
    • {@code null} will fallback to {@link WidgetTheme#getHoverBackground()}
    • + *
    • {@code null} will fallback to {@link WidgetThemeEntry#getHoverTheme()}
    • *
    • {@link IDrawable#EMPTY} will make the hover background invisible
    • *
    • {@link IDrawable#NONE} will use the normal background instead (which is also achieved using {@link #disableHoverBackground()})
    • *
    • multiple drawables, will result in them being drawn on top of each other in the order they are passed to the method
    • @@ -475,9 +496,20 @@ public W disableHoverOverlay() { * @return this */ public W widgetTheme(String s) { - if (!IThemeApi.get().hasWidgetTheme(s)) { + WidgetThemeKey widgetThemeKey = WidgetThemeKey.getFromFullName(s); + if (widgetThemeKey == null) { throw new IllegalArgumentException("No widget theme for id '" + s + "' exists."); } + return widgetTheme(widgetThemeKey); + } + + /** + * Sets an override widget theme. This will change of the appearance of this widget according to the widget theme. + * + * @param s id of the widget theme (see constants in {@link IThemeApi}) + * @return this + */ + public W widgetTheme(WidgetThemeKey s) { this.widgetThemeOverride = s; return getThis(); } @@ -576,6 +608,16 @@ public W setEnabledIf(Predicate condition) { // === Resizing === // ---------------- + @Override + public int getDefaultWidth() { + return isValid() ? getWidgetTheme(getContext().getTheme()).getTheme().getDefaultWidth() : 18; + } + + @Override + public int getDefaultHeight() { + return isValid() ? getWidgetTheme(getContext().getTheme()).getTheme().getDefaultHeight() : 18; + } + @Override public void scheduleResize() { this.requiresResize = true; diff --git a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java index 1a336fd52..cc2fc7dbf 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java +++ b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java @@ -10,7 +10,7 @@ import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.ObjectList; import com.cleanroommc.modularui.value.sync.ModularSyncManager; import com.cleanroommc.modularui.value.sync.PanelSyncManager; @@ -144,7 +144,7 @@ public static void drawTree(IWidget parent, ModularGuiContext context, boolean i GlStateManager.colorMask(true, true, true, true); GlStateManager.color(1f, 1f, 1f, alpha); GlStateManager.enableBlend(); - WidgetTheme widgetTheme = parent.getWidgetTheme(context.getTheme()); + WidgetThemeEntry widgetTheme = parent.getWidgetTheme(context.getTheme()); if (drawBackground) parent.drawBackground(context, widgetTheme); parent.draw(context, widgetTheme); parent.drawOverlay(context, widgetTheme); @@ -235,7 +235,7 @@ public static void drawBackground(IWidget parent, ModularGuiContext context, boo GlStateManager.colorMask(true, true, true, true); GlStateManager.color(1f, 1f, 1f, alpha); GlStateManager.enableBlend(); - WidgetTheme widgetTheme = parent.getWidgetTheme(context.getTheme()); + WidgetThemeEntry widgetTheme = parent.getWidgetTheme(context.getTheme()); parent.drawBackground(context, widgetTheme); GlStateManager.popMatrix(); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java index 80bf7bcb0..22bd46e43 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java @@ -10,7 +10,7 @@ import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.UITexture; import com.cleanroommc.modularui.screen.RichTooltip; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.value.IntValue; import com.cleanroommc.modularui.value.sync.SyncHandler; @@ -97,12 +97,12 @@ public void setState(int state, boolean setSource) { } @Override - public WidgetTheme getWidgetThemeInternal(ITheme theme) { + public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { return theme.getButtonTheme(); } @Override - public IDrawable getCurrentBackground(ITheme theme, WidgetTheme widgetTheme) { + public IDrawable getCurrentBackground(ITheme theme, WidgetThemeEntry widgetTheme) { // make sure texture is up-to-date int state = getState(); if (isHovering() && this.hoverBackground != null && this.hoverBackground[state] != null && this.hoverBackground[state] != IDrawable.NONE) { @@ -112,7 +112,7 @@ public IDrawable getCurrentBackground(ITheme theme, WidgetTheme widgetTheme) { } @Override - public IDrawable getCurrentOverlay(ITheme theme, WidgetTheme widgetTheme) { + public IDrawable getCurrentOverlay(ITheme theme, WidgetThemeEntry widgetTheme) { int state = getState(); if (isHovering() && this.hoverOverlay != null && this.hoverOverlay[state] != null && this.hoverBackground[state] != IDrawable.NONE) { return this.hoverOverlay[state]; diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java index 266d596ed..9a6475fe9 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java @@ -5,7 +5,7 @@ import com.cleanroommc.modularui.api.widget.IGuiAction; import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.GuiTextures; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.value.sync.InteractionSyncHandler; import com.cleanroommc.modularui.value.sync.SyncHandler; import com.cleanroommc.modularui.widget.SingleChildWidget; @@ -46,7 +46,7 @@ public boolean isValidSyncHandler(SyncHandler syncHandler) { } @Override - public WidgetTheme getWidgetThemeInternal(ITheme theme) { + public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { return theme.getButtonTheme(); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java b/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java index 906e94285..84c296b16 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java @@ -6,7 +6,7 @@ import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.GuiTextures; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.widget.AbstractParentWidget; import com.cleanroommc.modularui.widget.WidgetTree; @@ -25,12 +25,12 @@ public class CategoryList extends AbstractParentWidget im private IDrawable collapsedOverlay; @Override - public void drawOverlay(ModularGuiContext context, WidgetTheme widgetTheme) { + public void drawOverlay(ModularGuiContext context, WidgetThemeEntry widgetTheme) { super.drawOverlay(context, widgetTheme); if (this.expanded) { - this.expandedOverlay.drawAtZero(context, getArea(), widgetTheme); + this.expandedOverlay.drawAtZero(context, getArea(), getActiveWidgetTheme(widgetTheme, isHovering())); } else { - this.collapsedOverlay.drawAtZero(context, getArea(), widgetTheme); + this.collapsedOverlay.drawAtZero(context, getArea(), getActiveWidgetTheme(widgetTheme, isHovering())); } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ItemDisplayWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ItemDisplayWidget.java index e7d731287..a780b698d 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ItemDisplayWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ItemDisplayWidget.java @@ -4,7 +4,7 @@ import com.cleanroommc.modularui.api.value.IValue; import com.cleanroommc.modularui.drawable.GuiDraw; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Platform; import com.cleanroommc.modularui.value.ObjectValue; import com.cleanroommc.modularui.value.sync.GenericSyncValue; @@ -37,12 +37,12 @@ public boolean isValidSyncHandler(SyncHandler syncHandler) { } @Override - protected WidgetTheme getWidgetThemeInternal(ITheme theme) { + protected WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { return theme.getItemSlotTheme(); } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { ItemStack item = value.getValue(); if (!Platform.isStackEmpty(item)) { GuiDraw.drawItem(item, 1, 1, 16, 16, context.getCurrentDrawingZ()); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java index 5f51b40f0..1aa22ee0c 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java @@ -6,7 +6,7 @@ import com.cleanroommc.modularui.api.widget.IParentWidget; import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.widget.AbstractScrollWidget; import com.cleanroommc.modularui.widget.scroll.ScrollData; import com.cleanroommc.modularui.widget.scroll.VerticalScrollData; @@ -41,7 +41,7 @@ public void onInit() { } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { if (this.childSeparator == null || this.separatorPositions.isEmpty()) return; GuiAxis axis = this.scrollData.getAxis(); int x = getArea().getPadding().getLeft(), y = getArea().getPadding().getTop(), w, h; @@ -58,7 +58,7 @@ public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { } else { y = p; } - this.childSeparator.draw(context, x, y, w, h, widgetTheme); + this.childSeparator.draw(context, x, y, w, h, getActiveWidgetTheme(widgetTheme, isHovering())); } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java b/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java index cca9e7db6..78d3c3798 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java @@ -5,8 +5,9 @@ import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.DrawableStack; import com.cleanroommc.modularui.drawable.TabTexture; +import com.cleanroommc.modularui.theme.SelectableTheme; import com.cleanroommc.modularui.theme.WidgetTheme; -import com.cleanroommc.modularui.theme.WidgetThemeSelectable; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.widget.Widget; import org.jetbrains.annotations.NotNull; @@ -25,9 +26,14 @@ public PageButton(int index, PagedWidget.Controller controller) { } @Override - public WidgetTheme getWidgetThemeInternal(ITheme theme) { - WidgetThemeSelectable widgetTheme = theme.getToggleButtonTheme(); - return isActive() ^ invertSelected() ? widgetTheme : widgetTheme.getSelected(); + public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { + return theme.getToggleButtonTheme(); + } + + @Override + protected WidgetTheme getActiveWidgetTheme(WidgetThemeEntry widgetTheme, boolean hover) { + SelectableTheme selectableTheme = widgetTheme.expectType(SelectableTheme.class).getTheme(hover); + return isActive() ^ invertSelected() ? selectableTheme.getSelected() : selectableTheme; } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java index 800098d5a..0a7171db2 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java @@ -1,9 +1,11 @@ package com.cleanroommc.modularui.widgets; +import com.cleanroommc.modularui.ModularUIConfig; import com.cleanroommc.modularui.api.value.IDoubleValue; import com.cleanroommc.modularui.drawable.UITexture; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.MathUtils; import com.cleanroommc.modularui.value.DoubleValue; @@ -54,7 +56,8 @@ public float getCurrentProgress() { } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry entry) { + WidgetTheme widgetTheme = getActiveWidgetTheme(entry, isHovering()); if (this.emptyTexture != null) { this.emptyTexture.draw(context, 0, 0, getArea().w(), getArea().h(), widgetTheme); Color.setGlColorOpaque(Color.WHITE.main); @@ -97,7 +100,7 @@ public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { } public float getProgressUV(float uv) { - if (getScreen().getCurrentTheme().getSmoothProgressBarOverride()) { + if (ModularUIConfig.smoothProgressBar) { return uv; } return (float) (Math.floor(uv * this.imageSize) / this.imageSize); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/RichTextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/RichTextWidget.java index 347fecd8d..f2b9f2176 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/RichTextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/RichTextWidget.java @@ -7,7 +7,7 @@ import com.cleanroommc.modularui.drawable.text.RichText; import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.widget.Widget; import org.jetbrains.annotations.NotNull; @@ -27,7 +27,7 @@ public void markDirty() { } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { super.draw(context, widgetTheme); if (this.autoUpdate || this.dirty) { if (this.builder != null) { @@ -36,7 +36,7 @@ public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { } this.dirty = false; } - this.text.drawAtZero(context, getArea(), widgetTheme); + this.text.drawAtZero(context, getArea(), getActiveWidgetTheme(widgetTheme, isHovering())); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java index e4d31f0e9..e796b05d6 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java @@ -8,6 +8,7 @@ import com.cleanroommc.modularui.drawable.text.TextRenderer; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Interpolation; public class ScrollingTextWidget extends TextWidget { @@ -51,7 +52,7 @@ public void onMouseEndHover() { } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { if (this.animator == null) { animator(new Animator().curve(Interpolation.SINE_INOUT)); } @@ -59,10 +60,11 @@ public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { updateLine(getKey().getFormatted()); } checkString(); + WidgetTheme theme = getActiveWidgetTheme(widgetTheme, isHovering()); TextRenderer renderer = TextRenderer.SHARED; - renderer.setColor(getColor() != null ? getColor().getAsInt() : widgetTheme.getTextColor()); + renderer.setColor(getColor() != null ? getColor().getAsInt() : theme.getTextColor()); renderer.setAlignment(getAlignment(), getArea().w(), getArea().h()); - renderer.setShadow(isShadow() != null ? isShadow() : widgetTheme.getTextShadow()); + renderer.setShadow(isShadow() != null ? isShadow() : theme.getTextShadow()); renderer.setPos(getArea().getPadding().getLeft(), getArea().getPadding().getTop()); renderer.setScale(getScale()); renderer.setSimulate(false); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java index 6208dacc6..d9e12b73e 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java @@ -9,6 +9,7 @@ import com.cleanroommc.modularui.drawable.Rectangle; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.MathUtils; import com.cleanroommc.modularui.value.DoubleValue; @@ -67,7 +68,7 @@ public boolean isValidSyncHandler(SyncHandler syncHandler) { } @Override - public void drawBackground(ModularGuiContext context, WidgetTheme widgetTheme) { + public void drawBackground(ModularGuiContext context, WidgetThemeEntry widgetTheme) { super.drawBackground(context, widgetTheme); if (this.stopper != null && this.stopperDrawable != null && this.stopperWidth > 0 && this.stopperHeight > 0) { for (double stop : this.stopper) { @@ -75,20 +76,20 @@ public void drawBackground(ModularGuiContext context, WidgetTheme widgetTheme) { if (this.axis.isHorizontal()) { pos -= this.stopperWidth / 2; int crossAxisPos = (int) (getArea().height / 2D - this.stopperHeight / 2D); - this.stopperDrawable.draw(context, pos, crossAxisPos, this.stopperWidth, this.stopperHeight, WidgetTheme.getDefault()); + this.stopperDrawable.draw(context, pos, crossAxisPos, this.stopperWidth, this.stopperHeight, WidgetTheme.getDefault().getTheme()); } else { pos -= this.stopperHeight / 2; int crossAxisPos = (int) (getArea().width / 2D - this.stopperWidth / 2D); - this.stopperDrawable.draw(context, crossAxisPos, pos, this.stopperWidth, this.stopperHeight, WidgetTheme.getDefault()); + this.stopperDrawable.draw(context, crossAxisPos, pos, this.stopperWidth, this.stopperHeight, WidgetTheme.getDefault().getTheme()); } } } } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { if (this.handleDrawable != null) { - this.handleDrawable.draw(context, this.sliderArea, context.getTheme().getButtonTheme()); + this.handleDrawable.draw(context, this.sliderArea, context.getTheme().getButtonTheme().getTheme()); } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java index 3af0290a3..073ca22c7 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java @@ -4,6 +4,7 @@ import com.cleanroommc.modularui.drawable.text.TextRenderer; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.widget.Widget; import com.cleanroommc.modularui.widget.WidgetTree; @@ -35,12 +36,13 @@ public TextWidget(String key) { } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { TextRenderer renderer = TextRenderer.SHARED; this.lastText = checkString(); - renderer.setColor(this.color != null ? this.color.getAsInt() : widgetTheme.getTextColor()); + WidgetTheme theme = getActiveWidgetTheme(widgetTheme, isHovering()); + renderer.setColor(this.color != null ? this.color.getAsInt() : theme.getTextColor()); renderer.setAlignment(this.alignment, getArea().paddedWidth() + this.scale, getArea().paddedHeight()); - renderer.setShadow(this.shadow != null ? this.shadow : widgetTheme.getTextShadow()); + renderer.setShadow(this.shadow != null ? this.shadow : theme.getTextShadow()); renderer.setPos(getArea().getPadding().getLeft(), getArea().getPadding().getTop()); renderer.setScale(this.scale); renderer.setSimulate(false); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java b/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java index 7aedc9a88..945884578 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java @@ -4,13 +4,15 @@ import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.value.IBoolValue; import com.cleanroommc.modularui.screen.RichTooltip; +import com.cleanroommc.modularui.theme.SelectableTheme; import com.cleanroommc.modularui.theme.WidgetTheme; -import com.cleanroommc.modularui.theme.WidgetThemeSelectable; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import java.util.function.Consumer; /** * A button which cycles between 2 states by clicking on it. Background, overlay and tooltip can be supplied per state. + * * @see CycleButtonWidget */ public class ToggleButton extends AbstractCycleButtonWidget { @@ -22,9 +24,14 @@ public ToggleButton() { } @Override - public WidgetTheme getWidgetThemeInternal(ITheme theme) { - WidgetThemeSelectable widgetTheme = theme.getToggleButtonTheme(); - return isValueSelected() ^ invertSelected() ? widgetTheme.getSelected() : widgetTheme; + public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { + return theme.getToggleButtonTheme(); + } + + @Override + protected WidgetTheme getActiveWidgetTheme(WidgetThemeEntry widgetTheme, boolean hover) { + SelectableTheme selectableTheme = widgetTheme.expectType(SelectableTheme.class).getTheme(hover); + return isValueSelected() ^ invertSelected() ? selectableTheme.getSelected() : selectableTheme; } public boolean isValueSelected() { diff --git a/src/main/java/com/cleanroommc/modularui/widgets/slot/FluidSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/slot/FluidSlot.java index d42664c46..38662378d 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/slot/FluidSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/slot/FluidSlot.java @@ -13,8 +13,8 @@ import com.cleanroommc.modularui.integration.recipeviewer.RecipeViewerIngredientProvider; import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetSlotTheme; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.SlotTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.MouseData; @@ -139,7 +139,7 @@ public boolean isValidSyncHandler(SyncHandler syncHandler) { } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { IFluidTank fluidTank = getFluidTank(); FluidStack content = this.syncHandler.getValue(); if (content != null) { @@ -153,7 +153,7 @@ public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { GuiDraw.drawFluidTexture(content, this.contentOffsetX, y, getArea().width - this.contentOffsetX * 2, height, 0); } if (this.overlayTexture != null) { - this.overlayTexture.drawAtZero(context, getArea(), widgetTheme); + this.overlayTexture.drawAtZero(context, getArea(), getActiveWidgetTheme(widgetTheme, isHovering())); } if (content != null && this.syncHandler.controlsAmount()) { String s = NumberFormat.format(getBaseUnitAmount(content.amount), NumberFormat.AMOUNT_TEXT) + getBaseUnit(); @@ -164,7 +164,7 @@ public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { } @Override - public void drawOverlay(ModularGuiContext context, WidgetTheme widgetTheme) { + public void drawOverlay(ModularGuiContext context, WidgetThemeEntry widgetTheme) { super.drawOverlay(context, widgetTheme); if (ModularUI.Mods.JEI.isLoaded() && (ModularUIJeiPlugin.draggingValidIngredient(this) || ModularUIJeiPlugin.hoveringOverIngredient(this))) { GlStateManager.colorMask(true, true, true, false); @@ -178,16 +178,13 @@ public void drawOverlay(ModularGuiContext context, WidgetTheme widgetTheme) { } @Override - public WidgetSlotTheme getWidgetThemeInternal(ITheme theme) { + public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { return theme.getFluidSlotTheme(); } public int getSlotHoverColor() { - WidgetTheme theme = getWidgetTheme(getContext().getTheme()); - if (theme instanceof WidgetSlotTheme slotTheme) { - return slotTheme.getSlotHoverColor(); - } - return ITheme.getDefault().getFluidSlotTheme().getSlotHoverColor(); + WidgetThemeEntry theme = getWidgetTheme(getContext().getTheme(), SlotTheme.class); + return theme.getTheme().getSlotHoverColor(); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widgets/slot/ItemSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/slot/ItemSlot.java index 398ef7920..5546fe72b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/slot/ItemSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/slot/ItemSlot.java @@ -2,6 +2,7 @@ import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.IThemeApi; import com.cleanroommc.modularui.api.widget.IVanillaSlot; import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.core.mixins.early.minecraft.GuiAccessor; @@ -13,8 +14,8 @@ import com.cleanroommc.modularui.screen.NEAAnimationHandler; import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetSlotTheme; -import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.theme.SlotTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Platform; import com.cleanroommc.modularui.value.sync.ItemSlotSH; import com.cleanroommc.modularui.value.sync.SyncHandler; @@ -80,7 +81,7 @@ public void onUpdate() { } @Override - public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { if (this.syncHandler == null) return; RenderHelper.enableGUIStandardItemLighting(); drawSlot(getSlot()); @@ -111,16 +112,20 @@ public void buildTooltip(ItemStack stack, RichTooltip tooltip) { } @Override - public WidgetSlotTheme getWidgetThemeInternal(ITheme theme) { - return theme.getItemSlotTheme(); + public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { + PlayerSlotType playerSlotType = this.syncHandler != null ? this.syncHandler.getPlayerSlotType() : null; + if (playerSlotType == null) return theme.getWidgetTheme(IThemeApi.ITEM_SLOT); + return switch (playerSlotType) { + case HOTBAR -> theme.getWidgetTheme(IThemeApi.ITEM_SLOT_PLAYER_HOTBAR); + case MAIN_INVENTORY -> theme.getWidgetTheme(IThemeApi.ITEM_SLOT_PLAYER_MAIN_INV); + case OFFHAND -> theme.getWidgetTheme(IThemeApi.ITEM_SLOT_PLAYER_OFFHAND); + case ARMOR -> theme.getWidgetTheme(IThemeApi.ITEM_SLOT_PLAYER_ARMOR); + }; } public int getSlotHoverColor() { - WidgetTheme theme = getWidgetTheme(getContext().getTheme()); - if (theme instanceof WidgetSlotTheme slotTheme) { - return slotTheme.getSlotHoverColor(); - } - return ITheme.getDefault().getItemSlotTheme().getSlotHoverColor(); + WidgetThemeEntry theme = getWidgetTheme(getContext().getTheme(), SlotTheme.class); + return theme.getTheme().getSlotHoverColor(); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java index 12aea270c..e6985b24a 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java @@ -4,12 +4,18 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.SlotItemHandler; +import net.minecraftforge.items.wrapper.PlayerInvWrapper; + +import net.minecraftforge.items.wrapper.PlayerMainInvWrapper; + import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -226,4 +232,12 @@ public ModularSlot singletonSlotGroup(int shiftClickPriority) { public ModularSlot singletonSlotGroup() { return singletonSlotGroup(SlotGroup.STORAGE_SLOT_PRIO); } + + public static boolean isPlayerSlot(Slot slot) { + return slot.inventory instanceof InventoryPlayer; + } + + public static boolean isPlayerSlot(SlotItemHandler slot) { + return slot.getItemHandler() instanceof PlayerInvWrapper || slot.getItemHandler() instanceof PlayerMainInvWrapper; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/slot/PlayerSlotType.java b/src/main/java/com/cleanroommc/modularui/widgets/slot/PlayerSlotType.java new file mode 100644 index 000000000..0e9fa648c --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/widgets/slot/PlayerSlotType.java @@ -0,0 +1,40 @@ +package com.cleanroommc.modularui.widgets.slot; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; +import net.minecraftforge.items.SlotItemHandler; +import net.minecraftforge.items.wrapper.InvWrapper; +import net.minecraftforge.items.wrapper.PlayerArmorInvWrapper; +import net.minecraftforge.items.wrapper.PlayerInvWrapper; +import net.minecraftforge.items.wrapper.PlayerMainInvWrapper; +import net.minecraftforge.items.wrapper.PlayerOffhandInvWrapper; + +public enum PlayerSlotType { + HOTBAR, MAIN_INVENTORY, OFFHAND, ARMOR; + + public static PlayerSlotType getPlayerSlotType(Slot slot) { + int index = slot.getSlotIndex(); + if (index < 0 || index > 40) return null; + if (slot instanceof SlotItemHandler slotitemhandler) { + if (slotitemhandler.getItemHandler() instanceof PlayerMainInvWrapper) { + return index < 9 ? HOTBAR : MAIN_INVENTORY; + } + if (slotitemhandler.getItemHandler() instanceof PlayerArmorInvWrapper) { + return ARMOR; + } + if (slotitemhandler.getItemHandler() instanceof PlayerOffhandInvWrapper) { + return OFFHAND; + } + if (!(slotitemhandler.getItemHandler() instanceof PlayerInvWrapper) && + !(slotitemhandler.getItemHandler() instanceof InvWrapper invWrapper && invWrapper.getInv() instanceof InventoryPlayer)) { + return null; + } + } else if (!(slot.inventory instanceof InventoryPlayer)) { + return null; + } + if (index < 9) return HOTBAR; + if (index < 36) return MAIN_INVENTORY; + if (index < 40) return ARMOR; + return OFFHAND; + } +} 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 91a3e4759..4411a8f02 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java @@ -7,7 +7,8 @@ import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.Stencil; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTextFieldTheme; +import com.cleanroommc.modularui.theme.TextFieldTheme; +import com.cleanroommc.modularui.theme.WidgetThemeEntry; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.widget.AbstractScrollWidget; import com.cleanroommc.modularui.widget.scroll.HorizontalScrollData; @@ -104,7 +105,8 @@ public void onUpdate() { @Override public void preDraw(ModularGuiContext context, boolean transformed) { if (transformed) { - WidgetTextFieldTheme widgetTheme = (WidgetTextFieldTheme) getWidgetTheme(context.getTheme()); + WidgetThemeEntry entry = getWidgetTheme(context.getTheme(), TextFieldTheme.class); + TextFieldTheme widgetTheme = entry.getTheme(); this.renderer.setColor(this.textColor != null ? this.textColor : widgetTheme.getTextColor()); this.renderer.setCursorColor(this.textColor != null ? this.textColor : widgetTheme.getTextColor()); this.renderer.setMarkedColor(this.markedColor != null ? this.markedColor : widgetTheme.getMarkedColor()); @@ -115,14 +117,14 @@ public void preDraw(ModularGuiContext context, boolean transformed) { } } - protected void setupDrawText(ModularGuiContext context, WidgetTextFieldTheme widgetTheme) { + protected void setupDrawText(ModularGuiContext context, TextFieldTheme widgetTheme) { this.renderer.setSimulate(false); this.renderer.setPos(getArea().getPadding().getLeft(), getArea().getPadding().getTop()); this.renderer.setScale(this.scale); this.renderer.setAlignment(this.textAlignment, getArea().paddedWidth(), getArea().paddedHeight()); } - protected void drawText(ModularGuiContext context, WidgetTextFieldTheme widgetTheme) { + protected void drawText(ModularGuiContext context, TextFieldTheme widgetTheme) { if (this.handler.isTextEmpty() && this.hintText != null) { int c = this.renderer.getColor(); int hintColor = this.hintTextColor != null ? this.hintTextColor : widgetTheme.getHintColor(); @@ -136,7 +138,7 @@ protected void drawText(ModularGuiContext context, WidgetTextFieldTheme widgetTh } @Override - public WidgetTextFieldTheme getWidgetThemeInternal(ITheme theme) { + public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { return theme.getTextFieldTheme(); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldRenderer.java b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldRenderer.java index 392ece13e..23e7f45c5 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldRenderer.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldRenderer.java @@ -16,7 +16,7 @@ public class TextFieldRenderer extends TextRenderer { protected final TextFieldHandler handler; - protected int markedColor = 0x2F72A8; + protected int markedColor = 0xFF2F72A8; protected int cursorColor = 0xFFFFFFFF; protected boolean renderCursor = false; diff --git a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java index d8ff4597a..d72b05519 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java @@ -1,12 +1,9 @@ package com.cleanroommc.modularui.widgets.textfield; import com.cleanroommc.modularui.ModularUI; -import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.api.value.IStringValue; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; -import com.cleanroommc.modularui.theme.WidgetTextFieldTheme; -import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.MathUtils; import com.cleanroommc.modularui.utils.ParseResult; import com.cleanroommc.modularui.value.StringValue; @@ -15,7 +12,6 @@ import org.jetbrains.annotations.NotNull; -import java.awt.*; import java.text.ParsePosition; import java.util.function.Function; import java.util.function.Supplier; @@ -58,14 +54,6 @@ public void onInit() { } } - public int getMarkedColor() { - WidgetTheme theme = getWidgetTheme(getContext().getTheme()); - if (theme instanceof WidgetTextFieldTheme textFieldTheme) { - return textFieldTheme.getMarkedColor(); - } - return ITheme.getDefault().getTextFieldTheme().getMarkedColor(); - } - @Override public boolean isValidSyncHandler(SyncHandler syncHandler) { if (syncHandler instanceof IStringValue iStringValue && syncHandler instanceof ValueSyncHandler valueSyncHandler) { diff --git a/src/main/resources/assets/modularui/themes/vanilla.json b/src/main/resources/assets/modularui/themes/vanilla.json index ecda96781..73db0c610 100644 --- a/src/main/resources/assets/modularui/themes/vanilla.json +++ b/src/main/resources/assets/modularui/themes/vanilla.json @@ -1,3 +1,9 @@ { - "parent": "DEFAULT" + "parent": "DEFAULT", + "itemSlot:player": { + "color": "#FF914354" + }, + "itemSlot:player_hotbar": { + "color": "#FF60917c" + } } \ No newline at end of file