diff --git a/build.gradle b/build.gradle index 1e340af..0e1089b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' version '1.11-SNAPSHOT' apply false + id 'dev.architectury.loom' version '1.13.467' apply false id 'architectury-plugin' version '3.4-SNAPSHOT' id 'com.gradleup.shadow' version '8.3.6' apply false } diff --git a/common/src/main/java/net/weyne1/easegui/client/config/ScreenGroup.java b/common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenGroup.java similarity index 77% rename from common/src/main/java/net/weyne1/easegui/client/config/ScreenGroup.java rename to common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenGroup.java index 26963b7..b32afbd 100644 --- a/common/src/main/java/net/weyne1/easegui/client/config/ScreenGroup.java +++ b/common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenGroup.java @@ -1,6 +1,6 @@ -package net.weyne1.easegui.client.config; +package net.weyne1.easegui.api; -public enum ScreenGroup { +public enum EaseGUIScreenGroup { BASIC("easegui.screen_group.basic"), EDITORS("easegui.screen_group.editors"), WORLDS("easegui.screen_group.worlds"), @@ -9,7 +9,7 @@ public enum ScreenGroup { private final String translationKey; - ScreenGroup(String translationKey) { + EaseGUIScreenGroup(String translationKey) { this.translationKey = translationKey; } diff --git a/common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenRegistry.java b/common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenRegistry.java new file mode 100644 index 0000000..86d07f5 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenRegistry.java @@ -0,0 +1,243 @@ +package net.weyne1.easegui.api; + +import com.mojang.realmsclient.RealmsMainScreen; +import net.minecraft.client.gui.screens.*; +import net.minecraft.client.gui.screens.achievement.StatsScreen; +import net.minecraft.client.gui.screens.advancements.AdvancementsScreen; +import net.minecraft.client.gui.screens.inventory.*; +import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; +import net.minecraft.client.gui.screens.multiplayer.ServerReconfigScreen; +import net.minecraft.client.gui.screens.multiplayer.WarningScreen; +import net.minecraft.client.gui.screens.options.OptionsScreen; +import net.minecraft.client.gui.screens.options.OptionsSubScreen; +import net.minecraft.client.gui.screens.packs.PackSelectionScreen; +import net.minecraft.client.gui.screens.social.SocialInteractionsScreen; +import net.minecraft.client.gui.screens.worldselection.*; +import net.weyne1.easegui.api.animation.AnimationProfile; +import net.weyne1.easegui.client.config.EaseGUIConfig; +import net.weyne1.easegui.client.gui.screens.EaseGUIAbstractSplitScreen; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +/** + * Registry that maps Minecraft Screen classes to ScreenType definitions. + * + *

This is used by EaseGUI to decide how different screens should be categorized + * and animated. Matching works in two steps: + *

+ * + *

Mod developers can register custom screens and default animation parameters + * to integrate them into the EaseGUI animation system. + */ +public final class EaseGUIScreenRegistry { + + private static final Map, EaseGUIScreenType> EXACT_MATCH_CACHE = new ConcurrentHashMap<>(); + private static final Map> DEFAULT_CONFIGURATORS = new ConcurrentHashMap<>(); + private static final List HIERARCHY_LIST = new ArrayList<>(); + + public static final EaseGUIScreenType OTHER = new EaseGUIScreenType("other", Screen.class, Integer.MIN_VALUE, EaseGUIScreenGroup.OTHER, false); + + static { + // Vanilla UI screens + register("title", TitleScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("options", OptionsScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("options_sub", OptionsSubScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("pack_selection", PackSelectionScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("advancements", AdvancementsScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("statistics", StatsScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("warning", WarningScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("pause", PauseScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("share_to_lan", ShareToLanScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("death", DeathScreen.class, 1000, EaseGUIScreenGroup.BASIC); + register("social_interactions", SocialInteractionsScreen.class, 1000, EaseGUIScreenGroup.BASIC); + + // Editors + register("sign_edit", AbstractSignEditScreen.class, 1000, EaseGUIScreenGroup.EDITORS); + register("book_edit", BookEditScreen.class, 1000, EaseGUIScreenGroup.EDITORS); + register("book_view", BookViewScreen.class, 1000, EaseGUIScreenGroup.EDITORS); + register("command_block_edit", AbstractCommandBlockEditScreen.class, 1000, EaseGUIScreenGroup.EDITORS); + register("structure_block_edit", StructureBlockEditScreen.class, 1000, EaseGUIScreenGroup.EDITORS); + register("jigsaw_block_edit", JigsawBlockEditScreen.class, 1000, EaseGUIScreenGroup.EDITORS); + + // World / multiplayer menus + register("world_selection", SelectWorldScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("server_selection", JoinMultiplayerScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("realms_main", RealmsMainScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("create_world", CreateWorldScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("create_flat_world", CreateFlatWorldScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("direct_join_server", DirectJoinServerScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("edit_world", EditWorldScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("edit_server", ManageServerScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("edit_game_rules", EditGameRulesScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("experiments", ExperimentsScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("connecting", ConnectScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("disconnected", DisconnectedScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("server_reconfig", ServerReconfigScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + register("credits", CreditsAndAttributionScreen.class, 1000, EaseGUIScreenGroup.WORLDS); + + // Containers / inventories + register("creative_inventory", CreativeModeInventoryScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("survival_inventory", InventoryScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("anvil", AnvilScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("enchanting_table", EnchantmentScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("container", ContainerScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("smithing", SmithingScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("dispenser", DispenserScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("beacon", BeaconScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("crafter", CrafterScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("crafting", CraftingScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("brewing_stand", BrewingStandScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("cartography_table", CartographyTableScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("furnace", AbstractFurnaceScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("grindstone", GrindstoneScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("hopper", HopperScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("horse_inventory", HorseInventoryScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("lectern", LecternScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("loom", LoomScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("shulker_box", ShulkerBoxScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("stonecutter", StonecutterScreen.class, 500, EaseGUIScreenGroup.CONTAINERS); + register("other_containers", AbstractContainerScreen.class, 100, EaseGUIScreenGroup.CONTAINERS); + + // Internal config UI + register("ease_gui_config", EaseGUIAbstractSplitScreen.class, 100, EaseGUIScreenGroup.OTHER); + + // Optional mod integration (Mod Menu) + try { + Class modMenuScreenClass = Class.forName("com.terraformersmc.modmenu.gui.ModsScreen"); + register("modmenu", modMenuScreenClass.asSubclass(Screen.class), 1000, EaseGUIScreenGroup.BASIC, false); + } catch (ClassNotFoundException ignored) { + // Mod Menu is not installed — safely skip registration + } + } + + /** + * Registers a custom configurator callback for a specific screen ID. + * This is intended for third-party developers to populate custom default configurations. + * + * @param id the unique screen ID (e.g. "mymod:grinder") + * @param configurator the consumer that configures the default {@link EaseGUIConfig.ScreenSettings} + */ + @SuppressWarnings("unused") + public static void registerConfigurator(String id, Consumer configurator) { + DEFAULT_CONFIGURATORS.put(id, configurator); + } + + /** + * Configures the screen settings with registered developer defaults. + * Primarily called by the config factory during clean setup initialization. + * + * @param id the screen identifier + * @param settings the target settings to configure + */ + public static void configureDefaults(String id, EaseGUIConfig.ScreenSettings settings) { + Consumer configurator = DEFAULT_CONFIGURATORS.get(id); + if (configurator != null) { + configurator.accept(settings); + } + } + + /** + * Patches the existing screen settings with newly introduced developer defaults. + * Ensures that new categories or default entries are added safely without + * overwriting any manual modifications made by the user in the config file. + * + * @param id the screen identifier + * @param settings the user's existing settings + * @return true if the settings were modified and need to be saved + */ + public static boolean patchDefaults(String id, EaseGUIConfig.ScreenSettings settings) { + Consumer configurator = DEFAULT_CONFIGURATORS.get(id); + if (configurator == null) return false; + + EaseGUIConfig.ScreenSettings defaultSettings = new EaseGUIConfig.ScreenSettings(); + configurator.accept(defaultSettings); + + boolean changed = false; + + for (Map.Entry entry : defaultSettings.customProfiles.entrySet()) { + if (!settings.customProfiles.containsKey(entry.getKey())) { + settings.customProfiles.put(entry.getKey(), entry.getValue()); + changed = true; + } + } + + return changed; + } + + /** + * Registers a screen type with default enabled state (true). + */ + public static synchronized void register( + String id, + Class screenClass, + int priority, + EaseGUIScreenGroup category + ) { + register(id, screenClass, priority, category, true); + } + + /** + * Registers a screen type for animation classification. + * + * @param id unique identifier (e.g. "inventory" or "modid:screen") + * @param screenClass target screen class + * @param priority matching priority (higher = checked earlier) + * @param category logical grouping of screen type + * @param enabledByDefault whether this screen type is enabled on first config creation + */ + public static synchronized void register( + String id, + Class screenClass, + int priority, + EaseGUIScreenGroup category, + boolean enabledByDefault + ) { + EaseGUIScreenType type = new EaseGUIScreenType(id, screenClass, priority, category, enabledByDefault); + + EXACT_MATCH_CACHE.put(screenClass, type); + + HIERARCHY_LIST.removeIf(t -> t.getId().equals(id)); + HIERARCHY_LIST.add(type); + HIERARCHY_LIST.sort(Comparator.comparingInt(EaseGUIScreenType::getPriority).reversed()); + } + + /** + * Finds a ScreenType for a runtime screen instance. + * + *

Resolution order: + *

    + *
  1. Exact class match (fast path)
  2. + *
  3. Assignable-from hierarchy scan
  4. + *
  5. Fallback to OTHER
  6. + *
+ */ + public static EaseGUIScreenType from(Screen screen) { + if (screen == null) return OTHER; + + Class screenClass = screen.getClass(); + + EaseGUIScreenType exact = EXACT_MATCH_CACHE.get(screenClass); + if (exact != null) return exact; + + for (EaseGUIScreenType type : HIERARCHY_LIST) { + if (type.getScreenClass().isAssignableFrom(screenClass)) { + EXACT_MATCH_CACHE.put(screenClass, type); + return type; + } + } + + return OTHER; + } + + /** + * Returns all registered screen types (read-only view). + */ + public static Collection getRegisteredTypes() { + return Collections.unmodifiableList(HIERARCHY_LIST); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/config/ScreenType.java b/common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenType.java similarity index 87% rename from common/src/main/java/net/weyne1/easegui/client/config/ScreenType.java rename to common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenType.java index 4c12e7d..346bf9b 100644 --- a/common/src/main/java/net/weyne1/easegui/client/config/ScreenType.java +++ b/common/src/main/java/net/weyne1/easegui/api/EaseGUIScreenType.java @@ -1,4 +1,4 @@ -package net.weyne1.easegui.client.config; +package net.weyne1.easegui.api; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; @@ -17,13 +17,13 @@ *
  • UI grouping
  • * */ -public final class ScreenType { +public final class EaseGUIScreenType { private final String id; private final Class screenClass; private final String translationKey; private final int priority; - private final ScreenGroup group; + private final EaseGUIScreenGroup group; private final boolean enabledByDefault; /** @@ -35,11 +35,11 @@ public final class ScreenType { *
  • "modid:screen" → "modid.screen_type.screen"
  • * */ - public ScreenType( + public EaseGUIScreenType( String id, Class screenClass, int priority, - ScreenGroup group, + EaseGUIScreenGroup group, boolean enabledByDefault ) { this.id = id; @@ -59,11 +59,12 @@ public ScreenType( /** * Creates a screen type enabled by default. */ - public ScreenType( + @SuppressWarnings("unused") + public EaseGUIScreenType( String id, Class screenClass, int priority, - ScreenGroup group + EaseGUIScreenGroup group ) { this(id, screenClass, priority, group, true); } @@ -80,7 +81,7 @@ public int getPriority() { return priority; } - public ScreenGroup getGroup() { + public EaseGUIScreenGroup getGroup() { return group; } diff --git a/common/src/main/java/net/weyne1/easegui/api/WidgetCategory.java b/common/src/main/java/net/weyne1/easegui/api/WidgetCategory.java new file mode 100644 index 0000000..d1f4309 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/api/WidgetCategory.java @@ -0,0 +1,82 @@ +package net.weyne1.easegui.api; + +import net.minecraft.client.gui.components.*; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.weyne1.easegui.client.config.ProfileFeature; +import org.jetbrains.annotations.NotNull; + +import java.util.EnumSet; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Represents the logical category of a UI element, defining which animation features + * are permitted and how classes map to these categories. + */ +public enum WidgetCategory { + BUTTON_LIKE(EnumSet.allOf(ProfileFeature.class)), + TEXT(EnumSet.of(ProfileFeature.OFFSET, ProfileFeature.SCALE, ProfileFeature.ALPHA, ProfileFeature.PIVOT)), + SCROLLABLE(EnumSet.of(ProfileFeature.OFFSET, ProfileFeature.SCALE, ProfileFeature.ALPHA)), + LIST_ENTRY(EnumSet.allOf(ProfileFeature.class)), + CONTAINERS(EnumSet.of(ProfileFeature.OFFSET, ProfileFeature.SCALE, ProfileFeature.ALPHA, ProfileFeature.PIVOT)), + UNKNOWN(EnumSet.noneOf(ProfileFeature.class)); + + private static final Map, WidgetCategory> CUSTOM_MAPPINGS = new ConcurrentHashMap<>(); + + private final EnumSet allowedFeatures; + + WidgetCategory(EnumSet allowedFeatures) { + this.allowedFeatures = allowedFeatures; + } + + public EnumSet getAllowedFeatures() { + return EnumSet.copyOf(this.allowedFeatures); + } + + /** + * Registers a custom class mapping to a specific UI element category. + * This allows third-party mods with custom UI frameworks to support EaseGUI animations. + * + * @param clazz the target widget class to map + * @param category the category to assign to this class and its descendants + */ + @SuppressWarnings("unused") + public static void registerMapping(@NotNull Class clazz, @NotNull WidgetCategory category) { + CUSTOM_MAPPINGS.put(clazz, category); + } + + /** + * Resolves a UIElementCategory for a given class type. + * Evaluates custom developer mappings first, then falls back to standard vanilla rules. + * + * @param clazz the class of the active UI element + * @return the matched category, or {@link #UNKNOWN} if no match is found + */ + @NotNull + public static WidgetCategory fromClass(Class clazz) { + if (clazz == null) return UNKNOWN; + + for (Map.Entry, WidgetCategory> entry : CUSTOM_MAPPINGS.entrySet()) { + if (entry.getKey().isAssignableFrom(clazz)) { + return entry.getValue(); + } + } + + if (AbstractButton.class.isAssignableFrom(clazz) || + AbstractSliderButton.class.isAssignableFrom(clazz) || + EditBox.class.isAssignableFrom(clazz)) { + return BUTTON_LIKE; + } + if (AbstractStringWidget.class.isAssignableFrom(clazz)) { + return TEXT; + } + if (AbstractSelectionList.class.isAssignableFrom(clazz)) { + return SCROLLABLE; + } + if (AbstractContainerScreen.class.isAssignableFrom(clazz)) { + return CONTAINERS; + } + + return UNKNOWN; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/api/animation/AnimationProfile.java b/common/src/main/java/net/weyne1/easegui/api/animation/AnimationProfile.java new file mode 100644 index 0000000..a0da75d --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/api/animation/AnimationProfile.java @@ -0,0 +1,177 @@ +package net.weyne1.easegui.api.animation; + +import org.joml.Vector2f; + +/** + * Configuration profile holding animation properties and fluent builder methods. + */ +@SuppressWarnings({"unused", "UnusedReturnValue"}) +public class AnimationProfile { + + private boolean enabled = true; + private long duration = 400; + private final Vector2f offset = new Vector2f(); + private final Vector2f startScale = new Vector2f(1f, 1f); + private float startAlpha = 0.0f; + private long cascadeDelay = 0L; + + private PivotPoint pivot = PivotPoint.CENTER; + private EasingType easing = EasingType.EASE_OUT_QUAD; + private CascadeDirection cascadeDirection = CascadeDirection.TOP_TO_BOTTOM; + + public boolean isEnabled() { + return enabled; + } + + public long getDuration() { + return duration; + } + + public float getOffsetX() { + return offset.x; + } + + public float getOffsetY() { + return offset.y; + } + + public float getStartScaleX() { + return startScale.x; + } + + public float getStartScaleY() { + return startScale.y; + } + + public float getStartAlpha() { + return startAlpha; + } + + public long getCascadeDelay() { + return cascadeDelay; + } + + public PivotPoint getPivot() { + return pivot; + } + + public EasingType getEasing() { + return easing; + } + + public CascadeDirection getCascadeDirection() { + return cascadeDirection; + } + + public AnimationProfile enabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public AnimationProfile duration(long duration) { + if (duration < 0) { + throw new IllegalArgumentException("Duration must be non-negative."); + } + + this.duration = duration; + return this; + } + + public AnimationProfile offset(float x, float y) { + this.offset.set(x, y); + return this; + } + + public AnimationProfile offsetX(float x) { + this.offset.x = x; + return this; + } + + public AnimationProfile offsetY(float y) { + this.offset.y = y; + return this; + } + + public AnimationProfile startAlpha(float startAlpha) { + if (startAlpha < 0.0f || startAlpha > 1.0f) { + throw new IllegalArgumentException("Start alpha must be between 0.0 and 1.0."); + } + + this.startAlpha = startAlpha; + return this; + } + + public AnimationProfile startScale(float x, float y) { + this.startScale.set(x, y); + return this; + } + + public AnimationProfile startScale(float scale) { + return startScale(scale, scale); + } + + public AnimationProfile startScaleX(float x) { + this.startScale.x = x; + return this; + } + + public AnimationProfile startScaleY(float y) { + this.startScale.y = y; + return this; + } + + public AnimationProfile cascadeDelay(long cascadeDelay) { + if (cascadeDelay < 0) { + throw new IllegalArgumentException("Cascade delay must be non-negative."); + } + + this.cascadeDelay = cascadeDelay; + return this; + } + + public AnimationProfile pivot(PivotPoint pivot) { + if (pivot == null) { + throw new NullPointerException("Pivot point cannot be null."); + } + + this.pivot = pivot; + return this; + } + + public AnimationProfile easing(EasingType easing) { + if (easing == null) { + throw new NullPointerException("Easing type cannot be null."); + } + + this.easing = easing; + return this; + } + + public AnimationProfile cascadeDirection(CascadeDirection cascadeDirection) { + if (cascadeDirection == null) { + throw new NullPointerException("Cascade direction cannot be null."); + } + + this.cascadeDirection = cascadeDirection; + return this; + } + + /** + * Creates a deep copy of this animation profile. + */ + public AnimationProfile copy() { + AnimationProfile copy = new AnimationProfile(); + + copy.enabled = this.enabled; + copy.duration = this.duration; + copy.offset.set(this.offset); + copy.startScale.set(this.startScale); + copy.startAlpha = this.startAlpha; + copy.cascadeDelay = this.cascadeDelay; + copy.pivot = this.pivot; + copy.easing = this.easing; + copy.cascadeDirection = this.cascadeDirection; + + return copy; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/api/animation/CascadeDirection.java b/common/src/main/java/net/weyne1/easegui/api/animation/CascadeDirection.java new file mode 100644 index 0000000..ba8a36c --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/api/animation/CascadeDirection.java @@ -0,0 +1,8 @@ +package net.weyne1.easegui.api.animation; + +public enum CascadeDirection { + TOP_TO_BOTTOM, + BOTTOM_TO_TOP, + LEFT_TO_RIGHT, + RIGHT_TO_LEFT +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/api/animation/EasingType.java b/common/src/main/java/net/weyne1/easegui/api/animation/EasingType.java new file mode 100644 index 0000000..8c465f1 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/api/animation/EasingType.java @@ -0,0 +1,65 @@ +package net.weyne1.easegui.api.animation; + +import net.weyne1.easegui.client.animation.EasingFunction; + +public enum EasingType { + LINEAR(t -> t), + + EASE_IN_QUAD(t -> t * t), + EASE_OUT_QUAD(t -> 1f - (1f - t) * (1f - t)), + EASE_IN_OUT_QUAD(t -> t < 0.5f ? 2f * t * t : 1f - (float) Math.pow(-2f * t + 2f, 2) / 2f), + + EASE_IN_CUBIC(t -> t * t * t), + EASE_OUT_CUBIC(t -> 1f - (float) Math.pow(1f - t, 3)), + EASE_IN_OUT_CUBIC(t -> t < 0.5f ? 4f * t * t * t : 1f - (float) Math.pow(-2f * t + 2f, 3) / 2f), + + EASE_OUT_QUINT(t -> 1f - (float) Math.pow(1f - t, 5)), + EASE_OUT_EXPO(t -> t == 1f ? 1f : 1f - (float) Math.pow(2, -10f * t)), + + EASE_IN_BACK(t -> { + float c1 = 1.70158f; + float c3 = c1 + 1f; + return c3 * t * t * t - c1 * t * t; + }), + + EASE_OUT_BACK(t -> { + float c1 = 1.70158f; + float c3 = c1 + 1f; + return 1f + c3 * (float) Math.pow(t - 1f, 3) + c1 * (float) Math.pow(t - 1f, 2); + }), + + EASE_OUT_ELASTIC(t -> { + if (t == 0f) return 0f; + if (t == 1f) return 1f; + float c4 = (2f * (float) Math.PI) / 3f; + return (float) Math.pow(2, -10f * t) * (float) Math.sin((t * 10f - 0.75f) * c4) + 1f; + }), + + EASE_OUT_BOUNCE(t -> { + float n1 = 7.5625f; + float d1 = 2.75f; + + if (t < 1f / d1) { + return n1 * t * t; + } else if (t < 2f / d1) { + float t2 = t - 1.5f / d1; + return n1 * t2 * t2 + 0.75f; + } else if (t < 2.5f / d1) { + float t2 = t - 2.25f / d1; + return n1 * t2 * t2 + 0.9375f; + } else { + float t2 = t - 2.625f / d1; + return n1 * t2 * t2 + 0.984375f; + } + }); + + private final EasingFunction function; + + EasingType(EasingFunction function) { + this.function = function; + } + + public float ease(float t) { + return function.apply(t); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/api/animation/PivotPoint.java b/common/src/main/java/net/weyne1/easegui/api/animation/PivotPoint.java new file mode 100644 index 0000000..7958764 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/api/animation/PivotPoint.java @@ -0,0 +1,29 @@ +package net.weyne1.easegui.api.animation; + +public enum PivotPoint { + TOP_LEFT(0.0f, 0.0f), + TOP_CENTER(0.5f, 0.0f), + TOP_RIGHT(1.0f, 0.0f), + CENTER_LEFT(0.0f, 0.5f), + CENTER(0.5f, 0.5f), + CENTER_RIGHT(1.0f, 0.5f), + BOTTOM_LEFT(0.0f, 1.0f), + BOTTOM_CENTER(0.5f, 1.0f), + BOTTOM_RIGHT(1.0f, 1.0f); + + private final float xFactor; + private final float yFactor; + + PivotPoint(float xFactor, float yFactor) { + this.xFactor = xFactor; + this.yFactor = yFactor; + } + + public float getX(float x, float width) { + return x + (width * this.xFactor); + } + + public float getY(float y, float height) { + return y + (height * this.yFactor); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/EaseGUIWidget.java b/common/src/main/java/net/weyne1/easegui/client/EaseGUIWidget.java deleted file mode 100644 index e600718..0000000 --- a/common/src/main/java/net/weyne1/easegui/client/EaseGUIWidget.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.weyne1.easegui.client; - -import net.weyne1.easegui.client.config.UIElementCategory; - -public interface EaseGUIWidget { - UIElementCategory easeGUI$getCategory(); - - float easeGUI$getAlpha(); -} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/accessor/RecipeBookAccessor.java b/common/src/main/java/net/weyne1/easegui/client/accessor/RecipeBookAccessor.java deleted file mode 100644 index 36939bd..0000000 --- a/common/src/main/java/net/weyne1/easegui/client/accessor/RecipeBookAccessor.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.weyne1.easegui.client.accessor; - -public interface RecipeBookAccessor { - boolean easeGUI$isVisible(); - int easeGUI$getXOffset(); - int easeGUI$getBookWidth(); - int easeGUI$getBookHeight(); -} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/accessor/ScreenAnimationAccessor.java b/common/src/main/java/net/weyne1/easegui/client/accessor/ScreenAnimationAccessor.java deleted file mode 100644 index e469b26..0000000 --- a/common/src/main/java/net/weyne1/easegui/client/accessor/ScreenAnimationAccessor.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.weyne1.easegui.client.accessor; - -import net.weyne1.easegui.client.animation.AnimationScope; - -public interface ScreenAnimationAccessor { - AnimationScope easeGUI$getContainerScope(); -} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationContext.java b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationContext.java index a3809f8..1018ce1 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationContext.java +++ b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationContext.java @@ -6,39 +6,54 @@ import java.util.Deque; public final class AnimationContext { - private static final Deque ALPHA_STACK = new ArrayDeque<>(); - private static int suspensionDepth = 0; + private static final Deque SCOPE_STACK = new ArrayDeque<>(); private static int parentAnimationDepth = 0; - public static void pushAnimation(float alpha) { - ALPHA_STACK.push(alpha); + public static void pushScope(AnimationScope scope) { + SCOPE_STACK.push(scope); } - public static void popAnimation() { - if (!ALPHA_STACK.isEmpty()) { - ALPHA_STACK.pop(); + public static void popScope(AnimationScope scope) { + if (SCOPE_STACK.isEmpty()) { + EaseGUIDebug.reportError("scope_underflow", () -> "Attempt to close a scope when the stack is empty!"); + return; } - } - public static void suspend() { - suspensionDepth++; + if (SCOPE_STACK.peek() == scope) { + SCOPE_STACK.pop(); + } else { + EaseGUIDebug.reportError("scope_mismatch", () -> "Animation closing order violated! Attempted to close the wrong scope."); + } } - public static void resume() { - if (suspensionDepth > 0) { - suspensionDepth--; - } + public static AnimationScope getCurrentScope() { + return SCOPE_STACK.peek(); } - public static boolean isAnimating() { - return suspensionDepth == 0 && !ALPHA_STACK.isEmpty(); + public static boolean isActive() { + if (SCOPE_STACK.isEmpty()) return false; + for (AnimationScope scope : SCOPE_STACK) { + if (!scope.isSuspended()) return true; + } + return false; } public static float getCurrentAlpha() { - if (suspensionDepth > 0 || ALPHA_STACK.isEmpty()) { + if (SCOPE_STACK.isEmpty()) { return 1.0f; } - return ALPHA_STACK.peek(); + + float totalAlpha = 1.0f; + boolean hasActiveScope = false; + + for (AnimationScope scope : SCOPE_STACK) { + if (!scope.isSuspended()) { + totalAlpha *= scope.getAlpha(); + hasActiveScope = true; + } + } + + return hasActiveScope ? totalAlpha : 1.0f; } public static void pushParentAnimation() { @@ -59,8 +74,7 @@ public static boolean hasParentAnimation() { } public static void resetFrameState() { - ALPHA_STACK.clear(); - suspensionDepth = 0; + SCOPE_STACK.clear(); parentAnimationDepth = 0; } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationMath.java b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationMath.java index 21a38ea..6ad3b46 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationMath.java +++ b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationMath.java @@ -1,5 +1,7 @@ package net.weyne1.easegui.client.animation; +import net.weyne1.easegui.api.animation.EasingType; + public final class AnimationMath { private AnimationMath() { } @@ -20,7 +22,7 @@ public static float calculateCurrentOffset(float baseOffset, float progress) { public static float calculateProgress( long elapsed, long duration, - AnimationProfile.EasingType easing + EasingType easing ) { if (elapsed <= 0) return 0f; if (elapsed >= duration) return 1f; diff --git a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationProfile.java b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationProfile.java deleted file mode 100644 index 71b7585..0000000 --- a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationProfile.java +++ /dev/null @@ -1,128 +0,0 @@ -package net.weyne1.easegui.client.animation; - -import org.joml.Vector2f; - -@SuppressWarnings("UnusedReturnValue") -public class AnimationProfile { - public boolean enabled = true; - public long duration = 400; - public final Vector2f offset = new Vector2f(0f, 0f); - public final Vector2f startScale = new Vector2f(1f, 1f); - public float startAlpha = 0.0f; - public long cascadeDelay = 0L; - public PivotPoint pivot = PivotPoint.CENTER; - public EasingType easing = EasingType.EASE_OUT_QUAD; - public CascadeDirection cascadeDirection = CascadeDirection.TOP_TO_BOTTOM; - - public AnimationProfile enabled(boolean enabled) { this.enabled = enabled; return this; } - public AnimationProfile duration(long duration) { this.duration = duration; return this; } - public AnimationProfile offset(float x, float y) { this.offset.set(x, y); return this; } - public AnimationProfile offsetX(float x) { this.offset.x = x; return this; } - public AnimationProfile offsetY(float y) { this.offset.y = y; return this; } - public AnimationProfile startAlpha(float startAlpha) { this.startAlpha = startAlpha; return this; } - public AnimationProfile startScale(float x, float y) { this.startScale.set(x, y); return this; } - public AnimationProfile startScale(float scale) { this.startScale.set(scale, scale); return this; } - public AnimationProfile startScaleX(float x) { this.startScale.x = x; return this; } - public AnimationProfile startScaleY(float y) { this.startScale.y = y; return this; } - public AnimationProfile cascadeDelay(long cascadeDelay) { this.cascadeDelay = cascadeDelay; return this; } - public AnimationProfile pivot(PivotPoint pivot) { this.pivot = pivot; return this; } - public AnimationProfile easing(EasingType easing) { this.easing = easing; return this; } - public AnimationProfile cascadeDirection(CascadeDirection cascadeDirection) { this.cascadeDirection = cascadeDirection; return this; } - - @FunctionalInterface - public interface EasingFunction { - float apply(float t); - } - - public enum PivotPoint { - TOP_LEFT(0.0f, 0.0f), TOP_CENTER(0.5f, 0.0f), TOP_RIGHT(1.0f, 0.0f), - CENTER_LEFT(0.0f, 0.5f), CENTER(0.5f, 0.5f), CENTER_RIGHT(1.0f, 0.5f), - BOTTOM_LEFT(0.0f, 1.0f), BOTTOM_CENTER(0.5f, 1.0f), BOTTOM_RIGHT(1.0f, 1.0f); - - private final float xFactor; - private final float yFactor; - - PivotPoint(float xFactor, float yFactor) { - this.xFactor = xFactor; - this.yFactor = yFactor; - } - - public float getX(float x, float width) { - return x + (width * this.xFactor); - } - - public float getY(float y, float height) { - return y + (height * this.yFactor); - } - } - - public enum EasingType { - LINEAR(t -> t), - - EASE_IN_QUAD(t -> t * t), - EASE_OUT_QUAD(t -> 1f - (1f - t) * (1f - t)), - EASE_IN_OUT_QUAD(t -> t < 0.5f ? 2f * t * t : 1f - (float) Math.pow(-2f * t + 2f, 2) / 2f), - - EASE_IN_CUBIC(t -> t * t * t), - EASE_OUT_CUBIC(t -> 1f - (float) Math.pow(1f - t, 3)), - EASE_IN_OUT_CUBIC(t -> t < 0.5f ? 4f * t * t * t : 1f - (float) Math.pow(-2f * t + 2f, 3) / 2f), - - EASE_OUT_QUINT(t -> 1f - (float) Math.pow(1f - t, 5)), - - EASE_OUT_EXPO(t -> t == 1f ? 1f : 1f - (float) Math.pow(2, -10f * t)), - - EASE_IN_BACK(t -> { - float c1 = 1.70158f; - float c3 = c1 + 1f; - return c3 * t * t * t - c1 * t * t; - }), - - EASE_OUT_BACK(t -> { - float c1 = 1.70158f; - float c3 = c1 + 1f; - return 1f + c3 * (float) Math.pow(t - 1f, 3) + c1 * (float) Math.pow(t - 1f, 2); - }), - - EASE_OUT_ELASTIC(t -> { - if (t == 0f) return 0f; - if (t == 1f) return 1f; - float c4 = (2f * (float) Math.PI) / 3f; - return (float) Math.pow(2, -10f * t) * (float) Math.sin((t * 10f - 0.75f) * c4) + 1f; - }), - - EASE_OUT_BOUNCE(t -> { - float n1 = 7.5625f; - float d1 = 2.75f; - - if (t < 1f / d1) { - return n1 * t * t; - } else if (t < 2f / d1) { - float t2 = t - 1.5f / d1; - return n1 * t2 * t2 + 0.75f; - } else if (t < 2.5f / d1) { - float t2 = t - 2.25f / d1; - return n1 * t2 * t2 + 0.9375f; - } else { - float t2 = t - 2.625f / d1; - return n1 * t2 * t2 + 0.984375f; - } - }); - - private final EasingFunction function; - - EasingType(EasingFunction function) { - this.function = function; - } - - public float ease(float t) { - return function.apply(t); - } - } - - public enum CascadeDirection { - TOP_TO_BOTTOM, - BOTTOM_TO_TOP, - LEFT_TO_RIGHT, - RIGHT_TO_LEFT - } -} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationScope.java b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationScope.java index 72ceb7c..a0d5c29 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationScope.java +++ b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationScope.java @@ -2,11 +2,13 @@ import net.minecraft.client.gui.GuiGraphics; import net.weyne1.easegui.client.EaseGUIDebug; +import org.joml.Matrix3x2fStack; public final class AnimationScope implements AutoCloseable { private static final float MIN_SCALE = 0.001f; private final GuiGraphics guiGraphics; + private final float alpha; private boolean isClosed = false; private boolean isSuspended = false; @@ -15,11 +17,32 @@ public final class AnimationScope implements AutoCloseable { private float scaleX = 1.0f, scaleY = 1.0f; private float pivotX, pivotY; + public float getOffsetX() { return offsetX; } + public float getOffsetY() { return offsetY; } + + public float getScaleX() { return scaleX; } + public float getScaleY() { return scaleY; } + + public float getPivotX() { return pivotX; } + public float getPivotY() { return pivotY; } + + public float getAlpha() { + return alpha; + } + + public boolean isClosed() { + return isClosed; + } + public boolean isSuspended() { + return isSuspended; + } + public AnimationScope(GuiGraphics guiGraphics, float alpha) { this.guiGraphics = guiGraphics; - this.guiGraphics.flush(); - AnimationContext.pushAnimation(alpha); - this.guiGraphics.pose().pushPose(); + this.alpha = alpha; + + AnimationContext.pushScope(this); + this.guiGraphics.pose().pushMatrix(); } public void setTransformParams(float offsetX, float offsetY, float scaleX, float scaleY, float pivotX, float pivotY) { @@ -30,29 +53,29 @@ public void setTransformParams(float offsetX, float offsetY, float scaleX, float this.pivotX = pivotX; this.pivotY = pivotY; + Matrix3x2fStack poseStack = guiGraphics.pose(); + if (this.scaleX != 1.0f || this.scaleY != 1.0f) { - guiGraphics.pose().translate(offsetX + pivotX, offsetY + pivotY, 0.0f); - guiGraphics.pose().scale(this.scaleX, this.scaleY, 1.0f); - guiGraphics.pose().translate(-pivotX, -pivotY, 0.0f); + poseStack.translate(offsetX + pivotX, offsetY + pivotY); + poseStack.scale(this.scaleX, this.scaleY); + poseStack.translate(-pivotX, -pivotY); } else { - guiGraphics.pose().translate(offsetX, offsetY, 0.0f); + poseStack.translate(offsetX, offsetY); } } public void suspend() { if (isClosed || isSuspended) return; - this.guiGraphics.flush(); - AnimationContext.suspend(); - - guiGraphics.pose().pushPose(); + Matrix3x2fStack poseStack = guiGraphics.pose(); + poseStack.pushMatrix(); if (scaleX != 1.0f || scaleY != 1.0f) { - guiGraphics.pose().translate(pivotX, pivotY, 0.0f); - guiGraphics.pose().scale(1.0f / scaleX, 1.0f / scaleY, 1.0f); - guiGraphics.pose().translate(-(offsetX + pivotX), -(offsetY + pivotY), 0.0f); + poseStack.translate(pivotX, pivotY); + poseStack.scale(1.0f / scaleX, 1.0f / scaleY); + poseStack.translate(-(offsetX + pivotX), -(offsetY + pivotY)); } else { - guiGraphics.pose().translate(-offsetX, -offsetY, 0.0f); + poseStack.translate(-offsetX, -offsetY); } isSuspended = true; @@ -61,9 +84,7 @@ public void suspend() { public void resume() { if (isClosed || !isSuspended) return; - this.guiGraphics.flush(); - guiGraphics.pose().popPose(); - AnimationContext.resume(); + guiGraphics.pose().popMatrix(); isSuspended = false; } @@ -73,25 +94,18 @@ public void close() { if (isClosed) return; isClosed = true; - this.guiGraphics.flush(); - if (isSuspended) { - guiGraphics.pose().popPose(); - AnimationContext.resume(); + guiGraphics.pose().popMatrix(); isSuspended = false; } try { - guiGraphics.pose().popPose(); + guiGraphics.pose().popMatrix(); } catch (IllegalStateException e) { EaseGUIDebug.reportError("pose_stack_underflow", () -> "PoseStack underflow inside AnimationScope close!"); } - AnimationContext.popAnimation(); - } - - public boolean isClosed() { - return isClosed; + AnimationContext.popScope(this); } private static float clampScale(float scale) { diff --git a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationState.java b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationState.java index 378b817..4be2f5a 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationState.java +++ b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationState.java @@ -1,11 +1,8 @@ package net.weyne1.easegui.client.animation; -public class AnimationState { - - public static final class AnimationData { - public long startTime = -1; - public long delay = 0; - public boolean init = false; - public int lastRenderFrame = -1; - } +public final class AnimationState { + public long startTime = -1; + public long delay = 0; + public boolean init = false; + public int lastRenderFrame = -1; } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationSystem.java b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationSystem.java index a6ed5e0..317da5b 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animation/AnimationSystem.java +++ b/common/src/main/java/net/weyne1/easegui/client/animation/AnimationSystem.java @@ -1,9 +1,33 @@ package net.weyne1.easegui.client.animation; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.util.Util; +import net.weyne1.easegui.api.animation.AnimationProfile; public final class AnimationSystem { + /** + * An entry point for animations with automatic timing and progress calculation. + */ + public static AnimationScope begin( + GuiGraphics gg, + int x, int y, int width, int height, + AnimationProfile profile, + long startTime, + long delay, + float baseAlpha + ) { + long elapsed = Util.getMillis() - startTime - delay; + + if (elapsed >= profile.getDuration()) { + return null; + } + + float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.getDuration(), profile.getEasing()); + + return begin(gg, x, y, width, height, profile, progress, baseAlpha); + } + public static AnimationScope begin( GuiGraphics gg, int x, int y, int width, int height, @@ -12,17 +36,17 @@ public static AnimationScope begin( float baseAlpha ) { float alphaProgress = AnimationMath.clamp(progress, 0.0f, 1.0f); - float lerpedAlpha = AnimationMath.lerp(profile.startAlpha, 1.0f, alphaProgress); + float lerpedAlpha = AnimationMath.lerp(profile.getStartAlpha(), 1.0f, alphaProgress); float finalAlpha = AnimationMath.clamp(baseAlpha * lerpedAlpha, 0.0f, 1.0f); AnimationScope scope = new AnimationScope(gg, finalAlpha); scope.setTransformParams( - AnimationMath.calculateCurrentOffset(profile.offset.x, progress), - AnimationMath.calculateCurrentOffset(profile.offset.y, progress), - AnimationMath.lerp(profile.startScale.x, 1.0f, progress), - AnimationMath.lerp(profile.startScale.y, 1.0f, progress), - profile.pivot.getX(x, width), - profile.pivot.getY(y, height) + AnimationMath.calculateCurrentOffset(profile.getOffsetX(), progress), + AnimationMath.calculateCurrentOffset(profile.getOffsetY(), progress), + AnimationMath.lerp(profile.getStartScaleX(), 1.0f, progress), + AnimationMath.lerp(profile.getStartScaleY(), 1.0f, progress), + profile.getPivot().getX(x, width), + profile.getPivot().getY(y, height) ); return scope; } diff --git a/common/src/main/java/net/weyne1/easegui/client/animation/EasingFunction.java b/common/src/main/java/net/weyne1/easegui/client/animation/EasingFunction.java new file mode 100644 index 0000000..0f4af49 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/animation/EasingFunction.java @@ -0,0 +1,6 @@ +package net.weyne1.easegui.client.animation; + +@FunctionalInterface +public interface EasingFunction { + float apply(float t); +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animation/PipTransform.java b/common/src/main/java/net/weyne1/easegui/client/animation/PipTransform.java new file mode 100644 index 0000000..9e83dd2 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/animation/PipTransform.java @@ -0,0 +1,52 @@ +package net.weyne1.easegui.client.animation; + +/** + * Utility for applying UI animation transformations (scale, offset) + * to Minecraft's Picture-in-Picture (PiP) render states. + *

    + * Used to smoothly animate 3D models (skins, books, banners, etc.) + * relative to the current {@link AnimationScope} pivot. + */ +public final class PipTransform { + private PipTransform() {} + + private static final int MIN_SIZE = 1; + + public static int[] transformRangeX(int x0, int x1) { + return transformRange(x0, x1, true); + } + + public static int[] transformRangeY(int y0, int y1) { + return transformRange(y0, y1, false); + } + + private static int[] transformRange(int start, int end, boolean horizontal) { + AnimationScope scope = AnimationContext.getCurrentScope(); + if (scope == null || scope.isSuspended()) { + return new int[]{start, end}; + } + + float pivot = horizontal ? scope.getPivotX() : scope.getPivotY(); + float offset = horizontal ? scope.getOffsetX() : scope.getOffsetY(); + float scale = horizontal ? scope.getScaleX() : scope.getScaleY(); + + float newStart = pivot + offset + (start - pivot) * scale; + float newEnd = pivot + offset + (end - pivot) * scale; + + int roundedStart = Math.round(newStart); + int roundedEnd = Math.round(newEnd); + + if (Math.abs(roundedEnd - roundedStart) < MIN_SIZE) { + int sign = (newEnd >= newStart) ? 1 : -1; + roundedEnd = roundedStart + sign * MIN_SIZE; + } + + return new int[]{roundedStart, roundedEnd}; + } + + public static float scale(float scale) { + AnimationScope scope = AnimationContext.getCurrentScope(); + if (scope == null || scope.isSuspended()) return scale; + return scale * (scope.getScaleX() + scope.getScaleY()) * 0.5f; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animator/AdvancementsAnimator.java b/common/src/main/java/net/weyne1/easegui/client/animator/AdvancementsAnimator.java index c540cfb..e745b49 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animator/AdvancementsAnimator.java +++ b/common/src/main/java/net/weyne1/easegui/client/animator/AdvancementsAnimator.java @@ -1,6 +1,6 @@ package net.weyne1.easegui.client.animator; -import net.minecraft.Util; +import net.minecraft.util.Util; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.advancements.AdvancementsScreen; @@ -9,22 +9,17 @@ import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animation.AnimationSystem; import net.weyne1.easegui.client.config.ConfigManager; -import net.weyne1.easegui.client.config.EaseGUIScreenRegistry; -import net.weyne1.easegui.client.config.ScreenType; +import net.weyne1.easegui.api.EaseGUIScreenRegistry; +import net.weyne1.easegui.api.EaseGUIScreenType; import net.weyne1.easegui.client.state.ScreenStateTracker; public class AdvancementsAnimator { - /** - * Starts the animation. - * - * @return an {@link AnimationScope} that must be closed, or {@code null} if no animation is needed - */ public static AnimationScope beginRenderWindow(AdvancementsScreen screen, GuiGraphics gg) { - ScreenType type = EaseGUIScreenRegistry.from(screen); + EaseGUIScreenType type = EaseGUIScreenRegistry.from(screen); var titleSettings = ConfigManager.getConfig().screens.get(type.getId()); - if (titleSettings == null || !titleSettings.enabled || titleSettings.advancements == null || !titleSettings.advancements.windowProfile.enabled) { + if (titleSettings == null || !titleSettings.enabled || titleSettings.advancements == null || !titleSettings.advancements.windowProfile.isEnabled()) { return null; } @@ -32,23 +27,18 @@ public static AnimationScope beginRenderWindow(AdvancementsScreen screen, GuiGra long startTime = ScreenStateTracker.getScreenOpenTime(); long elapsed = Util.getMillis() - startTime; - if (elapsed >= profile.duration) return null; + if (elapsed >= profile.getDuration()) return null; - float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.duration, profile.easing); + float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.getDuration(), profile.getEasing()); return AnimationSystem.begin(gg, 0, 0, screen.width, screen.height, profile, progress, 1.0f); } - /** - * Starts the animation. - * - * @return an {@link AnimationScope} that must be closed, or {@code null} if no animation is needed - */ public static AnimationScope beginRenderTab(Screen screen, GuiGraphics gg, int tabIndex) { - ScreenType type = EaseGUIScreenRegistry.from(screen); + EaseGUIScreenType type = EaseGUIScreenRegistry.from(screen); var titleSettings = ConfigManager.getConfig().screens.get(type.getId()); - if (titleSettings == null || !titleSettings.enabled || titleSettings.advancements == null || !titleSettings.advancements.tabsProfile.enabled) { + if (titleSettings == null || !titleSettings.enabled || titleSettings.advancements == null || !titleSettings.advancements.tabsProfile.isEnabled()) { return null; } @@ -60,15 +50,15 @@ public static AnimationScope beginRenderTab(Screen screen, GuiGraphics gg, int t var profile = titleSettings.advancements.tabsProfile; long startTime = ScreenStateTracker.getScreenOpenTime(); - long tabDelay = tabIndex * profile.cascadeDelay; + long tabDelay = tabIndex * profile.getCascadeDelay(); long elapsed = Util.getMillis() - startTime - tabDelay; - if (elapsed >= profile.duration) return null; + if (elapsed >= profile.getDuration()) return null; float progress = 0.0f; if (elapsed > 0) { - progress = AnimationMath.calculateProgress(elapsed, profile.duration, profile.easing); + progress = AnimationMath.calculateProgress(elapsed, profile.getDuration(), profile.getEasing()); } return AnimationSystem.begin(gg, 0, 0, 28, 32, profile, progress, parentAlpha); diff --git a/common/src/main/java/net/weyne1/easegui/client/animator/BackgroundAnimator.java b/common/src/main/java/net/weyne1/easegui/client/animator/BackgroundAnimator.java index 5e41d12..3db1d5e 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animator/BackgroundAnimator.java +++ b/common/src/main/java/net/weyne1/easegui/client/animator/BackgroundAnimator.java @@ -1,36 +1,40 @@ package net.weyne1.easegui.client.animator; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.*; import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animation.AnimationSystem; import net.weyne1.easegui.client.config.ConfigManager; import net.weyne1.easegui.client.config.EaseGUIConfig; -import net.weyne1.easegui.client.config.EaseGUIScreenRegistry; -import net.weyne1.easegui.client.config.ScreenType; +import net.weyne1.easegui.api.EaseGUIScreenRegistry; +import net.weyne1.easegui.api.EaseGUIScreenType; import net.weyne1.easegui.client.state.ScreenAnimationTracker; import net.weyne1.easegui.client.state.ScreenStateTracker; -/** - * Handles background fade animations. - */ +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + public class BackgroundAnimator { public static boolean skipBackgroundFade = false; - public static boolean isLoadingScreen(Screen screen) { - return screen instanceof LevelLoadingScreen - || screen instanceof ProgressScreen - || screen instanceof ConnectScreen - || screen instanceof ReceivingLevelScreen - || screen instanceof GenericWaitingScreen - || screen instanceof BackupConfirmScreen; + private static final Set> IGNORED_SCREEN_CLASSES = ConcurrentHashMap.newKeySet(); + + static { + IGNORED_SCREEN_CLASSES.add(TitleScreen.class); + IGNORED_SCREEN_CLASSES.add(LevelLoadingScreen.class); + IGNORED_SCREEN_CLASSES.add(ProgressScreen.class); + IGNORED_SCREEN_CLASSES.add(ConnectScreen.class); + IGNORED_SCREEN_CLASSES.add(GenericWaitingScreen.class); + IGNORED_SCREEN_CLASSES.add(BackupConfirmScreen.class); + } + + @SuppressWarnings("unused") + public static void registerIgnoredScreen(Class screenClass) { + IGNORED_SCREEN_CLASSES.add(screenClass); } - public static boolean isScreenBlurred(Screen screen) { - if (screen == null - || screen instanceof TitleScreen - || isLoadingScreen(screen)) { + public static boolean shouldAnimateBackground(Screen screen) { + if (screen == null || isIgnoredScreen(screen)) { return false; } @@ -38,7 +42,7 @@ public static boolean isScreenBlurred(Screen screen) { if (!config.global.enableSmoothBlur) return false; try { - ScreenType screenType = EaseGUIScreenRegistry.from(screen); + EaseGUIScreenType screenType = EaseGUIScreenRegistry.from(screen); if (screenType == null) return true; EaseGUIConfig.ScreenSettings screenSettings = config.screens.get(screenType.getId()); @@ -48,15 +52,8 @@ public static boolean isScreenBlurred(Screen screen) { } } - public static boolean shouldAnimate() { - return isScreenBlurred(Minecraft.getInstance().screen); - } - - /** - * Returns whether background animations are enabled. - */ - public static int getAnimatedColor(int originalColor) { - if (!shouldAnimate() || skipBackgroundFade) { + public static int getAnimatedColor(Screen screen, int originalColor) { + if (!shouldAnimateBackground(screen) || skipBackgroundFade) { return originalColor; } @@ -73,13 +70,10 @@ public static int getAnimatedColor(int originalColor) { return (originalColor & 0x00FFFFFF) | (finalAlpha << 24); } - /** - * Starts the animation. - * - * @return an {@link AnimationScope} that must be closed, or {@code null} if no animation is needed - */ - public static AnimationScope beginRenderMenu(GuiGraphics gg) { - if (!shouldAnimate() || skipBackgroundFade) return null; + public static AnimationScope beginRenderMenu(Screen screen, GuiGraphics gg) { + if (!shouldAnimateBackground(screen) || skipBackgroundFade) { + return null; + } long elapsed = ScreenStateTracker.getScreenElapsed(); long duration = ConfigManager.getConfig().global.blurDuration; @@ -90,4 +84,14 @@ public static AnimationScope beginRenderMenu(GuiGraphics gg) { float progress = Math.max(0.0f, Math.min(1.0f, ScreenAnimationTracker.getProgress())); return AnimationSystem.beginAlphaOnly(gg, progress); } + + private static boolean isIgnoredScreen(Screen screen) { + Class screenClass = screen.getClass(); + for (Class ignored : IGNORED_SCREEN_CLASSES) { + if (ignored.isAssignableFrom(screenClass)) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animator/ContainerAnimator.java b/common/src/main/java/net/weyne1/easegui/client/animator/ContainerAnimator.java index 0fae467..7ce80a1 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animator/ContainerAnimator.java +++ b/common/src/main/java/net/weyne1/easegui/client/animator/ContainerAnimator.java @@ -1,29 +1,21 @@ package net.weyne1.easegui.client.animator; -import net.minecraft.Util; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; -import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener; -import net.weyne1.easegui.client.accessor.ContainerScreenAccessor; -import net.weyne1.easegui.client.accessor.RecipeBookAccessor; -import net.weyne1.easegui.client.animation.AnimationContext; -import net.weyne1.easegui.client.animation.AnimationMath; +import net.weyne1.easegui.client.extension.ContainerScreenExtension; +import net.weyne1.easegui.client.extension.RecipeBookComponentExtension; +import net.weyne1.easegui.client.extension.RecipeBookScreenExtension; import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animation.AnimationSystem; import net.weyne1.easegui.client.config.ConfigManager; -import net.weyne1.easegui.client.config.UIElementCategory; +import net.weyne1.easegui.api.WidgetCategory; import net.weyne1.easegui.client.state.ScreenStateTracker; public class ContainerAnimator { - /** - * Evaluates container bounds (integrating the Recipe Book if open) and instantiates - * a localized context-aware AnimationScope. - * - * @return a new {@link AnimationScope} mapped to the screen matrix, or {@code null} if transitions are disabled - */ - public static AnimationScope beginScreenAnimation(Screen screen, GuiGraphics gg) { - if (!(screen instanceof ContainerScreenAccessor container)) { + + public static AnimationScope beginAnimation(Screen screen, GuiGraphics gg) { + if (!(screen instanceof ContainerScreenExtension container)) { return null; } @@ -32,67 +24,21 @@ public static AnimationScope beginScreenAnimation(Screen screen, GuiGraphics gg) int maxX = minX + container.easeGUI$getImageWidth(); int maxY = minY + container.easeGUI$getImageHeight(); - if (screen instanceof RecipeUpdateListener listener) { - RecipeBookComponent book = listener.getRecipeBookComponent(); - - if (((RecipeBookAccessor) book).easeGUI$isVisible()) { - RecipeBookAccessor accessor = (RecipeBookAccessor) book; - - int bookWidth = accessor.easeGUI$getBookWidth(); - int bookHeight = accessor.easeGUI$getBookHeight(); - - int bookX = (screen.width - bookWidth) / 2 - accessor.easeGUI$getXOffset(); - int bookY = (screen.height - bookHeight) / 2; - - minX = Math.min(minX, bookX); - minY = Math.min(minY, bookY); - maxX = Math.max(maxX, bookX + bookWidth); - maxY = Math.max(maxY, bookY + bookHeight); + if (screen instanceof RecipeBookScreenExtension recipeScreen) { + RecipeBookComponent book = recipeScreen.easeGUI$getRecipeBookComponent(); + if (book != null && ((RecipeBookComponentExtension) book).easeGUI$isVisible()) { + RecipeBookComponentExtension accessor = (RecipeBookComponentExtension) book; + minX = Math.min(minX, accessor.easeGUI$getXOrigin()); + minY = Math.min(minY, accessor.easeGUI$getYOrigin()); + maxX = Math.max(maxX, accessor.easeGUI$getXOrigin() + RecipeBookComponent.IMAGE_WIDTH); + maxY = Math.max(maxY, accessor.easeGUI$getYOrigin() + RecipeBookComponent.IMAGE_HEIGHT); } } - AnimationScope scope = beginAnimation(gg, minX, minY, maxX - minX, maxY - minY); - if (scope != null) { - AnimationContext.pushParentAnimation(); - } - return scope; - } - - /** - * Safely closes the provided animation scope and cleanly balances the global animation context depth. - * - * @param scope Target scope to dissolve - */ - public static void closeScope(AnimationScope scope) { - if (scope != null) { - try { - if (!scope.isClosed()) { - scope.close(); - } - } finally { - if (AnimationContext.hasParentAnimation()) { - AnimationContext.popParentAnimation(); - } - } - } - } - - /** - * Looks up user configurations and computes tween progress mapping to generate the core AnimationScope. - * - * @return an active {@link AnimationScope} or {@code null} if config conditions are unmet or duration expired - */ - public static AnimationScope beginAnimation(GuiGraphics gg, int x, int y, int width, int height) { - var profile = ConfigManager.getProfileForCurrentContext(UIElementCategory.CONTAINERS); - if (profile == null || !profile.enabled) return null; + var profile = ConfigManager.getProfileForCurrentContext(WidgetCategory.CONTAINERS); + if (profile == null || !profile.isEnabled()) return null; long startTime = ScreenStateTracker.getScreenOpenTime(); - long elapsed = Util.getMillis() - startTime; - - if (elapsed >= profile.duration) return null; - - float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.duration, profile.easing); - - return AnimationSystem.begin(gg, x, y, width, height, profile, progress, 1.0f); + return AnimationSystem.begin(gg, minX, minY, maxX - minX, maxY - minY, profile, startTime, 0L, 1.0f); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animator/ListItemAnimator.java b/common/src/main/java/net/weyne1/easegui/client/animator/ListItemAnimator.java index 093e3e4..c4c10be 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animator/ListItemAnimator.java +++ b/common/src/main/java/net/weyne1/easegui/client/animator/ListItemAnimator.java @@ -1,43 +1,24 @@ package net.weyne1.easegui.client.animator; -import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; -import net.weyne1.easegui.client.animation.AnimationMath; -import net.weyne1.easegui.client.animation.AnimationProfile; +import net.weyne1.easegui.api.animation.AnimationProfile; import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animation.AnimationSystem; import net.weyne1.easegui.client.config.ConfigManager; -import net.weyne1.easegui.client.config.UIElementCategory; +import net.weyne1.easegui.api.WidgetCategory; import net.weyne1.easegui.client.state.ScreenStateTracker; -/** - * Animates list entries using cascade timing. - */ public class ListItemAnimator { - /** - * Starts the animation. - * - * @return an {@link AnimationScope} that must be closed, or {@code null} if no animation is needed - */ public static AnimationScope beginRender(GuiGraphics gg, int top, int left, int width, int height) { - var profile = ConfigManager.getProfileForCurrentContext(UIElementCategory.LIST_ENTRY); - if (profile == null || !profile.enabled) return null; + var profile = ConfigManager.getProfileForCurrentContext(WidgetCategory.LIST_ENTRY); + if (profile == null || !profile.isEnabled()) return null; long delay = getDelay(top, left, profile); long startTime = ScreenStateTracker.getScreenOpenTime(); - long elapsed = Util.getMillis() - startTime - delay; - - if (elapsed >= profile.duration) return null; - - float progress = 0.0f; - if (elapsed > 0) { - progress = AnimationMath.calculateProgress(elapsed, profile.duration, profile.easing); - } - - return AnimationSystem.begin(gg, left, top, width, height, profile, progress, 1.0f); + return AnimationSystem.begin(gg, left, top, width, height, profile, startTime, delay, 1.0f); } private static long getDelay(int top, int left, AnimationProfile profile) { @@ -45,13 +26,13 @@ private static long getDelay(int top, int left, AnimationProfile profile) { int screenHeight = window.getGuiScaledHeight(); int screenWidth = window.getGuiScaledWidth(); - float distance = switch (profile.cascadeDirection) { + float distance = switch (profile.getCascadeDirection()) { case TOP_TO_BOTTOM -> top; case BOTTOM_TO_TOP -> Math.max(0f, screenHeight - top); case LEFT_TO_RIGHT -> left; case RIGHT_TO_LEFT -> Math.max(0f, screenWidth - left); }; - return (long) (distance * (profile.cascadeDelay / 100.0f)); + return (long) (distance * (profile.getCascadeDelay() / 100.0f)); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animator/LogoAnimator.java b/common/src/main/java/net/weyne1/easegui/client/animator/LogoAnimator.java index cffb2bd..8125fa5 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animator/LogoAnimator.java +++ b/common/src/main/java/net/weyne1/easegui/client/animator/LogoAnimator.java @@ -1,11 +1,12 @@ package net.weyne1.easegui.client.animator; -import net.minecraft.Util; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.util.Util; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.LogoRenderer; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.weyne1.easegui.client.animation.AnimationMath; -import net.weyne1.easegui.client.animation.AnimationProfile; +import net.weyne1.easegui.api.animation.AnimationProfile; import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animation.AnimationSystem; import net.weyne1.easegui.client.config.ConfigManager; @@ -18,16 +19,16 @@ */ public class LogoAnimator { - private static final ResourceLocation[] LETTER_TEXTURES = new ResourceLocation[] { - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/m.png"), - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/i.png"), - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/n.png"), - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/e.png"), - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/t.png"), - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/f.png"), - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/a.png"), - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/r.png"), - ResourceLocation.fromNamespaceAndPath("easegui", "textures/gui/title/letters/c.png") + private static final Identifier[] LETTER_TEXTURES = new Identifier[] { + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/m.png"), + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/i.png"), + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/n.png"), + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/e.png"), + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/t.png"), + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/f.png"), + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/a.png"), + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/r.png"), + Identifier.fromNamespaceAndPath("easegui", "textures/gui/title/letters/c.png") }; /** @@ -36,22 +37,17 @@ public class LogoAnimator { */ private static final int[] LOGICAL_INDICES = new int[] { 0, 1, 2, 3, 8, 7, 6, 5, 4 }; - private static long lastTrackedSessionTime = -1L; - private static long actualStartTime = -1L; - public static boolean render(GuiGraphics gg, int screenWidth, float transparency, int height, boolean showEasterEgg, boolean keepLogoThroughFade) { var titleSettings = ConfigManager.getConfig().screens.get("title"); if (titleSettings == null || !titleSettings.enabled || titleSettings.logo == null) { return false; } - trackSessionTime(); - var logoConfig = titleSettings.logo; float finalAlpha = keepLogoThroughFade ? 1.0f : transparency; int startX = screenWidth / 2 - (LogoRendererAccessor.easeGUI$getLogoWidth() / 2); - ResourceLocation logoTexture = showEasterEgg ? LogoRenderer.EASTER_EGG_LOGO : LogoRenderer.MINECRAFT_LOGO; + Identifier logoTexture = showEasterEgg ? LogoRenderer.EASTER_EGG_LOGO : LogoRenderer.MINECRAFT_LOGO; if (logoConfig.animateWholeText) { renderWholeLogo(gg, logoTexture, logoConfig.logoProfile, startX, height, finalAlpha); } else { @@ -63,53 +59,51 @@ public static boolean render(GuiGraphics gg, int screenWidth, float transparency return true; } - private static void renderWholeLogo(GuiGraphics gg, ResourceLocation texture, AnimationProfile profile, int startX, int height, float finalAlpha) { + private static void renderWholeLogo(GuiGraphics gg, Identifier texture, AnimationProfile profile, int startX, int height, float finalAlpha) { + long actualStartTime = ScreenStateTracker.getTitleActualStartTime(); long elapsed = Util.getMillis() - actualStartTime; int logoWidth = LogoRendererAccessor.easeGUI$getLogoWidth(); int logoHeight = LogoRendererAccessor.easeGUI$getLogoHeight(); - if (elapsed >= profile.duration) { + if (elapsed >= profile.getDuration()) { try (AnimationScope ignored = AnimationSystem.beginAlphaOnly(gg, finalAlpha)) { drawLogoTexture(gg, texture, startX, height); - gg.flush(); } return; } - float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.duration, profile.easing); + float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.getDuration(), profile.getEasing()); try (AnimationScope ignored = AnimationSystem.begin(gg, startX, height, logoWidth, logoHeight, profile, progress, finalAlpha)) { drawLogoTexture(gg, texture, startX, height); - gg.flush(); } } private static void renderCascadedLetters(GuiGraphics gg, AnimationProfile profile, int startX, int height, float finalAlpha) { long now = Util.getMillis(); - long maxLogoDelay = (LETTER_TEXTURES.length - 1) * profile.cascadeDelay; + long actualStartTime = ScreenStateTracker.getTitleActualStartTime(); + long maxLogoDelay = (LETTER_TEXTURES.length - 1) * profile.getCascadeDelay(); int logoWidth = LogoRendererAccessor.easeGUI$getLogoWidth(); int logoHeight = LogoRendererAccessor.easeGUI$getLogoHeight(); - if (now - actualStartTime >= maxLogoDelay + profile.duration) { + if (now - actualStartTime >= maxLogoDelay + profile.getDuration()) { try (AnimationScope ignored = AnimationSystem.beginAlphaOnly(gg, finalAlpha)) { - for (ResourceLocation texture : LETTER_TEXTURES) { + for (Identifier texture : LETTER_TEXTURES) { drawLogoTexture(gg, texture, startX, height); } - gg.flush(); } return; } for (int i = 0; i < LETTER_TEXTURES.length; i++) { int logicalIndex = LOGICAL_INDICES[i]; - ResourceLocation texture = LETTER_TEXTURES[i]; + Identifier texture = LETTER_TEXTURES[i]; long cascadeDelay = calculateCascadeDelay(profile, logicalIndex); long elapsed = now - actualStartTime - cascadeDelay; - float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.duration, profile.easing); + float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.getDuration(), profile.getEasing()); try (AnimationScope ignored = AnimationSystem.begin(gg, startX, height, logoWidth, logoHeight, profile, progress, finalAlpha)) { drawLogoTexture(gg, texture, startX, height); - gg.flush(); } } } @@ -122,58 +116,49 @@ private static void renderEditionText(GuiGraphics gg, EaseGUIConfig.LogoSettings int y = height + logoHeight - 7; var profile = config.editionProfile; - if (profile == null || !profile.enabled) { + if (profile == null || !profile.isEnabled()) { drawStaticEdition(gg, x, y, finalAlpha); return; } long elapsed = getEditionElapsed(config); - if (elapsed >= profile.duration) { + if (elapsed >= profile.getDuration()) { drawStaticEdition(gg, x, y, finalAlpha); return; } - float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.duration, profile.easing); + float progress = elapsed <= 0 ? 0.0f : AnimationMath.calculateProgress(elapsed, profile.getDuration(), profile.getEasing()); try (AnimationScope ignored = AnimationSystem.begin(gg, x, y, editionWidth, editionHeight, profile, progress, finalAlpha)) { drawEditionTexture(gg, x, y); - gg.flush(); } } private static void drawStaticEdition(GuiGraphics gg, int x, int y, float finalAlpha) { try (AnimationScope ignored = AnimationSystem.beginAlphaOnly(gg, finalAlpha)) { drawEditionTexture(gg, x, y); - gg.flush(); } } private static long calculateCascadeDelay(AnimationProfile profile, int logicalIndex) { - return switch (profile.cascadeDirection) { - case LEFT_TO_RIGHT -> logicalIndex * profile.cascadeDelay; - case RIGHT_TO_LEFT -> (LETTER_TEXTURES.length - 1 - logicalIndex) * profile.cascadeDelay; + return switch (profile.getCascadeDirection()) { + case LEFT_TO_RIGHT -> logicalIndex * profile.getCascadeDelay(); + case RIGHT_TO_LEFT -> (LETTER_TEXTURES.length - 1 - logicalIndex) * profile.getCascadeDelay(); case TOP_TO_BOTTOM, BOTTOM_TO_TOP -> 0L; }; } private static long getEditionElapsed(EaseGUIConfig.LogoSettings config) { - long maxLogoDelay = config.animateWholeText ? 0L : switch (config.logoProfile.cascadeDirection) { - case LEFT_TO_RIGHT, RIGHT_TO_LEFT -> (LETTER_TEXTURES.length - 1) * config.logoProfile.cascadeDelay; + long actualStartTime = ScreenStateTracker.getTitleActualStartTime(); + long maxLogoDelay = config.animateWholeText ? 0L : switch (config.logoProfile.getCascadeDirection()) { + case LEFT_TO_RIGHT, RIGHT_TO_LEFT -> (LETTER_TEXTURES.length - 1) * config.logoProfile.getCascadeDelay(); case TOP_TO_BOTTOM, BOTTOM_TO_TOP -> 0L; }; return Util.getMillis() - actualStartTime - maxLogoDelay; } - private static void trackSessionTime() { - long currentSessionTime = ScreenStateTracker.getScreenOpenTime(); - if (lastTrackedSessionTime != currentSessionTime) { - lastTrackedSessionTime = currentSessionTime; - actualStartTime = Util.getMillis(); - } - } - - private static void drawLogoTexture(GuiGraphics gg, ResourceLocation texture, int x, int y) { - gg.blit(texture, x, y, 0.0f, 0.0f, + private static void drawLogoTexture(GuiGraphics gg, Identifier texture, int x, int y) { + gg.blit(RenderPipelines.GUI_TEXTURED, texture, x, y, 0.0f, 0.0f, LogoRendererAccessor.easeGUI$getLogoWidth(), LogoRendererAccessor.easeGUI$getLogoHeight(), LogoRendererAccessor.easeGUI$getLogoWidth(), @@ -181,7 +166,7 @@ private static void drawLogoTexture(GuiGraphics gg, ResourceLocation texture, in } private static void drawEditionTexture(GuiGraphics gg, int x, int y) { - gg.blit(LogoRenderer.MINECRAFT_EDITION, x, y, 0.0f, 0.0f, + gg.blit(RenderPipelines.GUI_TEXTURED, LogoRenderer.MINECRAFT_EDITION, x, y, 0.0f, 0.0f, LogoRendererAccessor.easeGUI$getEditionWidth(), LogoRendererAccessor.easeGUI$getEditionHeight(), LogoRendererAccessor.easeGUI$getEditionWidth(), diff --git a/common/src/main/java/net/weyne1/easegui/client/animator/SplashAnimator.java b/common/src/main/java/net/weyne1/easegui/client/animator/SplashAnimator.java index 94806db..4f5c43d 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animator/SplashAnimator.java +++ b/common/src/main/java/net/weyne1/easegui/client/animator/SplashAnimator.java @@ -1,44 +1,39 @@ package net.weyne1.easegui.client.animator; -import net.minecraft.Util; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.ActiveTextCollector; +import net.minecraft.util.Util; +import net.weyne1.easegui.client.animation.AnimationContext; import net.weyne1.easegui.client.animation.AnimationMath; -import net.weyne1.easegui.client.animation.AnimationScope; -import net.weyne1.easegui.client.animation.AnimationSystem; import net.weyne1.easegui.client.config.ConfigManager; import net.weyne1.easegui.client.state.ScreenStateTracker; -/** - * Animates the title screen splash text. - */ public class SplashAnimator { - /** - * Starts the animation. - * - * @return an {@link AnimationScope} that must be closed, or {@code null} if no animation is needed - */ - public static AnimationScope beginRender(GuiGraphics gg, int color) { + + public static ActiveTextCollector.Parameters getAnimatedParameters(ActiveTextCollector.Parameters parameters) { var screenConfig = ConfigManager.getConfig().screens.get("title"); + if (screenConfig == null || !screenConfig.enabled || screenConfig.splash == null || !screenConfig.splash.enabled) { - return null; + return parameters.withOpacity(AnimationContext.getCurrentAlpha()); } var splashConfig = screenConfig.splash; - long startTime = ScreenStateTracker.getScreenOpenTime(); - long elapsed = Util.getMillis() - startTime - splashConfig.splashDelay; + long actualStartTime = ScreenStateTracker.getTitleActualStartTime(); + long elapsed = Util.getMillis() - actualStartTime - splashConfig.splashDelay; + float parentAlpha = AnimationContext.getCurrentAlpha(); - if (elapsed >= splashConfig.splashDuration) { + if (elapsed <= 0) { return null; } - float progress = AnimationMath.calculateProgress(elapsed, splashConfig.splashDuration, splashConfig.splashEasing); - - float baseAlpha = ((color >> 24) & 255) / 255.0f; - float finalAlpha = baseAlpha * progress; - AnimationScope scope = AnimationSystem.beginAlphaOnly(gg, finalAlpha); + if (elapsed >= splashConfig.splashDuration) { + return parameters.withOpacity(parentAlpha); + } - gg.pose().scale(progress, progress, 1.0f); + float progress = AnimationMath.calculateProgress(elapsed, splashConfig.splashDuration, splashConfig.splashEasing); + float finalAlpha = AnimationMath.clamp(progress * parentAlpha, 0f, 1f); - return scope; + return parameters + .withOpacity(finalAlpha) + .withScale(progress); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/animator/WidgetAnimator.java b/common/src/main/java/net/weyne1/easegui/client/animator/WidgetAnimator.java index 7629c4c..e2054dc 100644 --- a/common/src/main/java/net/weyne1/easegui/client/animator/WidgetAnimator.java +++ b/common/src/main/java/net/weyne1/easegui/client/animator/WidgetAnimator.java @@ -1,14 +1,15 @@ package net.weyne1.easegui.client.animator; -import net.minecraft.Util; +import net.minecraft.util.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.weyne1.easegui.client.EaseGUIWidget; +import net.weyne1.easegui.api.animation.AnimationProfile; +import net.weyne1.easegui.client.extension.WidgetExtension; import net.weyne1.easegui.client.animation.*; import net.weyne1.easegui.client.config.ConfigManager; -import net.weyne1.easegui.client.config.UIElementCategory; +import net.weyne1.easegui.api.WidgetCategory; import net.weyne1.easegui.client.state.ScreenStateTracker; /** @@ -16,54 +17,32 @@ */ public class WidgetAnimator { - /** - * Starts the animation. - * - * @return an {@link AnimationScope} that must be closed, or {@code null} if no animation is needed - */ - public static AnimationScope beginRender(AbstractWidget widget, GuiGraphics gg, UIElementCategory category, AnimationState.AnimationData state) { + public static AnimationScope beginRender(AbstractWidget widget, GuiGraphics gg, WidgetCategory category, AnimationState state) { if (Minecraft.getInstance().screen instanceof AbstractContainerScreen) { return null; } var profile = ConfigManager.getProfileForCurrentContext(category); - if (profile == null || !profile.enabled) return null; + if (profile == null || !profile.isEnabled()) return null; long now = Util.getMillis(); updateAnimationState(widget, state, now, profile); if (ScreenStateTracker.isResizeFrame() || AnimationContext.hasParentAnimation()) { - state.startTime = now - profile.duration - state.delay; + state.startTime = now - profile.getDuration() - state.delay; return null; } - long elapsed = now - state.startTime - state.delay; - - if (elapsed >= profile.duration) return null; - - float progress = 0.0f; - if (elapsed > 0) { - progress = AnimationMath.calculateProgress(elapsed, profile.duration, profile.easing); - } - - return AnimationSystem.begin( - gg, - widget.getX(), - widget.getY(), - widget.getWidth(), - widget.getHeight(), - profile, - progress, - ((EaseGUIWidget) widget).easeGUI$getAlpha() - ); + return AnimationSystem.begin(gg, widget.getX(), widget.getY(), widget.getWidth(), widget.getHeight(), profile, + state.startTime, state.delay, ((WidgetExtension) widget).easeGUI$getAlpha()); } /** * Initializes animation state when a widget appears and * recalculates cascade timing if needed. */ - private static void updateAnimationState(AbstractWidget widget, AnimationState.AnimationData state, long now, AnimationProfile profile) { + private static void updateAnimationState(AbstractWidget widget, AnimationState state, long now, AnimationProfile profile) { int currentFrame = ScreenStateTracker.getCurrentFrameId(); if (state.init && currentFrame > state.lastRenderFrame + 1) { @@ -76,7 +55,7 @@ private static void updateAnimationState(AbstractWidget widget, AnimationState.A float distance = getDistance(widget, profile); - float delayMultiplier = profile.cascadeDelay / 100.0f; + float delayMultiplier = profile.getCascadeDelay() / 100.0f; state.delay = (long) (distance * delayMultiplier); } @@ -84,8 +63,8 @@ private static void updateAnimationState(AbstractWidget widget, AnimationState.A } /** - * Вычисляет расстояние до виджета в виртуальной шкале координат, - * делая скорость каскада независимой от GUI Scale и разрешения монитора. + * Calculates the distance to the widget in a virtual coordinate scale, + * making the cascade speed independent of the GUI scale and monitor resolution. */ private static float getDistance(AbstractWidget widget, AnimationProfile profile) { var window = Minecraft.getInstance().getWindow(); @@ -98,7 +77,7 @@ private static float getDistance(AbstractWidget widget, AnimationProfile profile final float BASELINE_WIDTH = 960.0f; final float BASELINE_HEIGHT = 540.0f; - return switch (profile.cascadeDirection) { + return switch (profile.getCascadeDirection()) { case TOP_TO_BOTTOM -> (Math.max(0, y) / (float) screenHeight) * BASELINE_HEIGHT; case BOTTOM_TO_TOP -> (Math.max(0f, screenHeight - y) / (float) screenHeight) * BASELINE_HEIGHT; case LEFT_TO_RIGHT -> (Math.max(0, x) / (float) screenWidth) * BASELINE_WIDTH; diff --git a/common/src/main/java/net/weyne1/easegui/client/config/ConfigManager.java b/common/src/main/java/net/weyne1/easegui/client/config/ConfigManager.java index b01618e..c35fdd6 100644 --- a/common/src/main/java/net/weyne1/easegui/client/config/ConfigManager.java +++ b/common/src/main/java/net/weyne1/easegui/client/config/ConfigManager.java @@ -5,7 +5,10 @@ import com.google.gson.JsonObject; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; -import net.weyne1.easegui.client.animation.AnimationProfile; +import net.weyne1.easegui.api.WidgetCategory; +import net.weyne1.easegui.api.EaseGUIScreenRegistry; +import net.weyne1.easegui.api.EaseGUIScreenType; +import net.weyne1.easegui.api.animation.AnimationProfile; import java.io.File; import java.io.FileReader; @@ -19,11 +22,11 @@ public class ConfigManager { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - private static EaseGUIConfig currentConfig = new EaseGUIConfig(); + private static EaseGUIConfig currentConfig = EaseGUIConfigFactory.createDefaultConfig(); private static boolean isLoaded = false; private static Screen cachedScreenInstance = null; - private static ScreenType cachedScreenType = EaseGUIScreenRegistry.OTHER; + private static EaseGUIScreenType cachedScreenType = EaseGUIScreenRegistry.OTHER; public static void load() { if (isLoaded) return; @@ -54,12 +57,12 @@ public static void load() { currentConfig = GSON.fromJson(jsonConfig, EaseGUIConfig.class); if (currentConfig == null) { - currentConfig = new EaseGUIConfig(); + currentConfig = EaseGUIConfigFactory.createDefaultConfig(); } currentConfig.schemaVersion = EaseGUIConfig.CURRENT_SCHEMA_VERSION; - if (currentConfig.mergeDefaults() || migrated) { + if (EaseGUIConfigFactory.mergeDefaults(currentConfig) || migrated) { LOGGER.info("[EaseGUI] Config schema updated from version {} to {}.", version, EaseGUIConfig.CURRENT_SCHEMA_VERSION); save(); } @@ -67,7 +70,7 @@ public static void load() { LOGGER.info("[EaseGUI] Config successfully loaded from disk."); } catch (Exception e) { LOGGER.error("[EaseGUI] Failed to read config, creating default... Error: {}", e.getMessage()); - currentConfig = new EaseGUIConfig(); + currentConfig = EaseGUIConfigFactory.createDefaultConfig(); save(); } } else { @@ -91,8 +94,8 @@ public static EaseGUIConfig getConfig() { return currentConfig; } - public static AnimationProfile getProfileForCurrentContext(UIElementCategory category) { - if (category == null || category == UIElementCategory.UNKNOWN) return null; + public static AnimationProfile getProfileForCurrentContext(WidgetCategory category) { + if (category == null || category == WidgetCategory.UNKNOWN) return null; if (!isLoaded) load(); Screen currentScreen = Minecraft.getInstance().screen; @@ -109,7 +112,7 @@ public static AnimationProfile getProfileForCurrentContext(UIElementCategory cat AnimationProfile customProfile = screenSettings.customProfiles.get(category); if (customProfile != null) { - return customProfile.enabled ? customProfile : null; + return customProfile.isEnabled() ? customProfile : null; } } diff --git a/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIConfig.java b/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIConfig.java index bb667ba..d1a740c 100644 --- a/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIConfig.java +++ b/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIConfig.java @@ -1,134 +1,33 @@ package net.weyne1.easegui.client.config; -import net.weyne1.easegui.client.animation.AnimationProfile; -import net.weyne1.easegui.client.animation.AnimationProfile.EasingType; +import net.weyne1.easegui.api.WidgetCategory; +import net.weyne1.easegui.api.animation.AnimationProfile; +import net.weyne1.easegui.api.animation.EasingType; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; -import static net.weyne1.easegui.client.animation.AnimationProfile.CascadeDirection.BOTTOM_TO_TOP; -import static net.weyne1.easegui.client.animation.AnimationProfile.CascadeDirection.LEFT_TO_RIGHT; -import static net.weyne1.easegui.client.animation.AnimationProfile.EasingType.*; +import static net.weyne1.easegui.api.animation.EasingType.EASE_OUT_CUBIC; public class EaseGUIConfig { public static final int CURRENT_SCHEMA_VERSION = 2; - public int schemaVersion = CURRENT_SCHEMA_VERSION; - public final GlobalSettings global = new GlobalSettings(); + public int schemaVersion = CURRENT_SCHEMA_VERSION; + public GlobalSettings global = new GlobalSettings(); public Map screens = new HashMap<>(); - public EaseGUIConfig() { - global.elementProfiles.put(UIElementCategory.BUTTON_LIKE, createButtonProfile()); - global.elementProfiles.put(UIElementCategory.TEXT, createTextProfile()); - global.elementProfiles.put(UIElementCategory.SCROLLABLE, createScrollableProfile()); - global.elementProfiles.put(UIElementCategory.LIST_ENTRY, createListEntryProfile()); - global.elementProfiles.put(UIElementCategory.CONTAINERS, createContainerProfile()); - - for (ScreenType type : EaseGUIScreenRegistry.getRegisteredTypes()) { - screens.put(type.getId(), createDefaultSettingsFor(type)); - } - screens.put(EaseGUIScreenRegistry.OTHER.getId(), createDefaultSettingsFor(EaseGUIScreenRegistry.OTHER)); - } - - /** - * Patches missing structure and runs schema migrations. - * - * @return true if config was modified and should be saved back to disk - */ - public boolean mergeDefaults() { - boolean changed = false; - - if (this.schemaVersion < CURRENT_SCHEMA_VERSION) { - this.schemaVersion = CURRENT_SCHEMA_VERSION; - changed = true; - } - - if (this.screens == null) { - this.screens = new HashMap<>(); - changed = true; - } - - for (ScreenType type : EaseGUIScreenRegistry.getRegisteredTypes()) { - if (!this.screens.containsKey(type.getId())) { - this.screens.put(type.getId(), createDefaultSettingsFor(type)); - changed = true; - } else { - changed |= patchScreenSettings(type, this.screens.get(type.getId())); - } - } - - if (!this.screens.containsKey(EaseGUIScreenRegistry.OTHER.getId())) { - this.screens.put(EaseGUIScreenRegistry.OTHER.getId(), createDefaultSettingsFor(EaseGUIScreenRegistry.OTHER)); - changed = true; - } - - return changed; - } - - /** - * Applies structural defaults for an existing screen entry. - * This only fills missing nested sections, not arbitrary primitive fields. - */ - private boolean patchScreenSettings(ScreenType type, ScreenSettings settings) { - boolean changed = false; - - if (settings == null) { - this.screens.put(type.getId(), createDefaultSettingsFor(type)); - return true; - } - - if ("title".equals(type.getId())) { - if (settings.logo == null) { - settings.logo = createLogoSettings(); - changed = true; - } - if (settings.splash == null) { - settings.splash = new SplashSettings(); - changed = true; - } - } else if ("advancements".equals(type.getId())) { - if (settings.advancements == null) { - settings.advancements = createAdvancementsSettings(); - changed = true; - } - } - - if (settings.customProfiles == null) { - settings.customProfiles = new EnumMap<>(UIElementCategory.class); - changed = true; - } - - return changed; - } - - private ScreenSettings createDefaultSettingsFor(ScreenType type) { - ScreenSettings settings = new ScreenSettings(); - settings.enabled = type.isEnabledByDefault(); - - switch (type.getId()) { - case "title" -> { - settings.logo = createLogoSettings(); - settings.splash = new SplashSettings(); - } - case "advancements" -> settings.advancements = createAdvancementsSettings(); - } - - return settings; - } - public static class GlobalSettings { public boolean enableSmoothBlur = true; public long blurDuration = 300L; public boolean blurContainers = true; public EasingType dimmingEasing = EASE_OUT_CUBIC; - public final Map elementProfiles = new EnumMap<>(UIElementCategory.class); + public final Map elementProfiles = new EnumMap<>(WidgetCategory.class); } public static class ScreenSettings { public boolean enabled = true; - public Map customProfiles = - new EnumMap<>(UIElementCategory.class); + public Map customProfiles = new EnumMap<>(WidgetCategory.class); public LogoSettings logo = null; public SplashSettings splash = null; @@ -137,95 +36,19 @@ public static class ScreenSettings { public static class LogoSettings { public boolean animateWholeText = false; - - public AnimationProfile logoProfile = new AnimationProfile() - .duration(400L) - .offsetY(10f) - .startScale(0.8f) - .startAlpha(0.0f) - .cascadeDelay(60L) - .cascadeDirection(LEFT_TO_RIGHT) - .easing(EASE_OUT_BACK) - .pivot(AnimationProfile.PivotPoint.CENTER); - - public AnimationProfile editionProfile = new AnimationProfile() - .duration(400L) - .offsetY(5f) - .startScale(0.9f) - .startAlpha(0.0f) - .easing(EASE_OUT_QUAD) - .pivot(AnimationProfile.PivotPoint.CENTER); + public AnimationProfile logoProfile; + public AnimationProfile editionProfile; } public static class SplashSettings { public boolean enabled = true; public long splashDelay = 500L; public long splashDuration = 500L; - public EasingType splashEasing = EASE_OUT_BACK; + public EasingType splashEasing; } public static class AdvancementsSettings { - public AnimationProfile windowProfile = new AnimationProfile() - .duration(250) - .startAlpha(0.0f) - .startScale(0.8f) - .easing(EASE_OUT_CUBIC); - - public AnimationProfile tabsProfile = new AnimationProfile() - .duration(400L) - .offsetX(-40f) - .startAlpha(0.0f) - .cascadeDelay(45L) - .cascadeDirection(LEFT_TO_RIGHT) - .easing(EASE_OUT_BACK); - } - - private static AnimationProfile createButtonProfile() { - return new AnimationProfile() - .duration(400) - .offsetY(15f) - .startAlpha(0.0f) - .cascadeDelay(45L) - .cascadeDirection(BOTTOM_TO_TOP) - .easing(EASE_OUT_BACK); - } - - private static AnimationProfile createTextProfile() { - return new AnimationProfile() - .duration(300) - .startAlpha(0.0f) - .easing(LINEAR); - } - - private static AnimationProfile createScrollableProfile() { - return new AnimationProfile() - .duration(300) - .startAlpha(0.0f) - .easing(EASE_OUT_BACK); - } - - private static AnimationProfile createListEntryProfile() { - return new AnimationProfile() - .duration(350) - .offsetY(15f) - .startAlpha(0.0f) - .cascadeDelay(45L) - .easing(EASE_OUT_CUBIC); - } - - private static AnimationProfile createContainerProfile() { - return new AnimationProfile() - .duration(250) - .offsetY(20f) - .startAlpha(0.0f) - .easing(EASE_OUT_CUBIC); - } - - private static LogoSettings createLogoSettings() { - return new LogoSettings(); - } - - private static AdvancementsSettings createAdvancementsSettings() { - return new AdvancementsSettings(); + public AnimationProfile windowProfile; + public AnimationProfile tabsProfile; } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIConfigFactory.java b/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIConfigFactory.java new file mode 100644 index 0000000..26cd38e --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIConfigFactory.java @@ -0,0 +1,217 @@ +package net.weyne1.easegui.client.config; + +import net.weyne1.easegui.api.WidgetCategory; +import net.weyne1.easegui.api.EaseGUIScreenRegistry; +import net.weyne1.easegui.api.EaseGUIScreenType; +import net.weyne1.easegui.api.animation.AnimationProfile; +import net.weyne1.easegui.api.animation.PivotPoint; +import java.util.EnumMap; +import java.util.HashMap; + +import static net.weyne1.easegui.api.animation.CascadeDirection.BOTTOM_TO_TOP; +import static net.weyne1.easegui.api.animation.CascadeDirection.LEFT_TO_RIGHT; +import static net.weyne1.easegui.api.animation.EasingType.*; + +/** + * Factory responsible for creating default configurations, applying structural patches, + * and handling schema migrations. + */ +public final class EaseGUIConfigFactory { + + public static EaseGUIConfig createDefaultConfig() { + EaseGUIConfig config = new EaseGUIConfig(); + + config.global.elementProfiles.put(WidgetCategory.BUTTON_LIKE, createButtonProfile()); + config.global.elementProfiles.put(WidgetCategory.TEXT, createTextProfile()); + config.global.elementProfiles.put(WidgetCategory.SCROLLABLE, createScrollableProfile()); + config.global.elementProfiles.put(WidgetCategory.LIST_ENTRY, createListEntryProfile()); + config.global.elementProfiles.put(WidgetCategory.CONTAINERS, createContainerProfile()); + + for (EaseGUIScreenType type : EaseGUIScreenRegistry.getRegisteredTypes()) { + config.screens.put(type.getId(), createDefaultSettingsFor(type)); + } + config.screens.put(EaseGUIScreenRegistry.OTHER.getId(), createDefaultSettingsFor(EaseGUIScreenRegistry.OTHER)); + + return config; + } + + /** + * Patches missing structures in an existing config and bumps the schema version if needed. + * Replaces the old non-SRP mergeDefaults() inside the config class. + * + * @return true if config was modified and should be saved back to disk + */ + public static boolean mergeDefaults(EaseGUIConfig config) { + boolean changed = false; + + if (config.schemaVersion < EaseGUIConfig.CURRENT_SCHEMA_VERSION) { + config.schemaVersion = EaseGUIConfig.CURRENT_SCHEMA_VERSION; + changed = true; + } + + if (config.screens == null) { + config.screens = new HashMap<>(); + changed = true; + } + + for (EaseGUIScreenType type : EaseGUIScreenRegistry.getRegisteredTypes()) { + if (!config.screens.containsKey(type.getId())) { + config.screens.put(type.getId(), createDefaultSettingsFor(type)); + changed = true; + } else { + changed |= patchScreenSettings(type, config.screens.get(type.getId()), config); + } + } + + if (!config.screens.containsKey(EaseGUIScreenRegistry.OTHER.getId())) { + config.screens.put(EaseGUIScreenRegistry.OTHER.getId(), createDefaultSettingsFor(EaseGUIScreenRegistry.OTHER)); + changed = true; + } + + return changed; + } + + private static boolean patchScreenSettings(EaseGUIScreenType type, EaseGUIConfig.ScreenSettings settings, EaseGUIConfig config) { + boolean changed = false; + + if (settings == null) { + config.screens.put(type.getId(), createDefaultSettingsFor(type)); + return true; + } + + if ("title".equals(type.getId())) { + if (settings.logo == null) { + settings.logo = createLogoSettings(); + changed = true; + } + if (settings.splash == null) { + settings.splash = createSplashSettings(); + changed = true; + } + } else if ("advancements".equals(type.getId())) { + if (settings.advancements == null) { + settings.advancements = createAdvancementsSettings(); + changed = true; + } + } + + if (settings.customProfiles == null) { + settings.customProfiles = new EnumMap<>(WidgetCategory.class); + changed = true; + } + + // Apply external developer animation defaults over user settings + changed |= EaseGUIScreenRegistry.patchDefaults(type.getId(), settings); + + return changed; + } + + private static EaseGUIConfig.ScreenSettings createDefaultSettingsFor(EaseGUIScreenType type) { + EaseGUIConfig.ScreenSettings settings = new EaseGUIConfig.ScreenSettings(); + settings.enabled = type.isEnabledByDefault(); + + switch (type.getId()) { + case "title" -> { + settings.logo = createLogoSettings(); + settings.splash = createSplashSettings(); + } + case "advancements" -> settings.advancements = createAdvancementsSettings(); + } + + // Allow external mods to apply their default profile configurations on screen creation + EaseGUIScreenRegistry.configureDefaults(type.getId(), settings); + + return settings; + } + + private static EaseGUIConfig.LogoSettings createLogoSettings() { + EaseGUIConfig.LogoSettings settings = new EaseGUIConfig.LogoSettings(); + settings.animateWholeText = false; + settings.logoProfile = new AnimationProfile() + .duration(400L) + .offsetY(10f) + .startScale(0.8f) + .startAlpha(0.0f) + .cascadeDelay(60L) + .cascadeDirection(LEFT_TO_RIGHT) + .easing(EASE_OUT_BACK) + .pivot(PivotPoint.CENTER); + + settings.editionProfile = new AnimationProfile() + .duration(400L) + .offsetY(5f) + .startScale(0.9f) + .startAlpha(0.0f) + .easing(EASE_OUT_QUAD) + .pivot(PivotPoint.CENTER); + return settings; + } + + private static EaseGUIConfig.SplashSettings createSplashSettings() { + EaseGUIConfig.SplashSettings settings = new EaseGUIConfig.SplashSettings(); + settings.enabled = true; + settings.splashDelay = 500L; + settings.splashDuration = 500L; + settings.splashEasing = EASE_OUT_BACK; + return settings; + } + + private static EaseGUIConfig.AdvancementsSettings createAdvancementsSettings() { + EaseGUIConfig.AdvancementsSettings settings = new EaseGUIConfig.AdvancementsSettings(); + settings.windowProfile = new AnimationProfile() + .duration(250) + .startAlpha(0.0f) + .startScale(0.8f) + .easing(EASE_OUT_CUBIC); + + settings.tabsProfile = new AnimationProfile() + .duration(400L) + .offsetX(-40f) + .startAlpha(0.0f) + .cascadeDelay(45L) + .cascadeDirection(LEFT_TO_RIGHT) + .easing(EASE_OUT_BACK); + return settings; + } + + private static AnimationProfile createButtonProfile() { + return new AnimationProfile() + .duration(400) + .offsetY(15f) + .startAlpha(0.0f) + .cascadeDelay(45L) + .cascadeDirection(BOTTOM_TO_TOP) + .easing(EASE_OUT_BACK); + } + + private static AnimationProfile createTextProfile() { + return new AnimationProfile() + .duration(300) + .startAlpha(0.0f) + .easing(LINEAR); + } + + private static AnimationProfile createScrollableProfile() { + return new AnimationProfile() + .duration(300) + .startAlpha(0.0f) + .easing(EASE_OUT_BACK); + } + + private static AnimationProfile createListEntryProfile() { + return new AnimationProfile() + .duration(350) + .offsetY(15f) + .startAlpha(0.0f) + .cascadeDelay(45L) + .easing(EASE_OUT_CUBIC); + } + + private static AnimationProfile createContainerProfile() { + return new AnimationProfile() + .duration(250) + .offsetY(20f) + .startAlpha(0.0f) + .easing(EASE_OUT_CUBIC); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIScreenRegistry.java b/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIScreenRegistry.java deleted file mode 100644 index 2e34667..0000000 --- a/common/src/main/java/net/weyne1/easegui/client/config/EaseGUIScreenRegistry.java +++ /dev/null @@ -1,188 +0,0 @@ -package net.weyne1.easegui.client.config; - -import com.mojang.realmsclient.RealmsMainScreen; -import net.minecraft.client.gui.screens.*; -import net.minecraft.client.gui.screens.achievement.StatsScreen; -import net.minecraft.client.gui.screens.advancements.AdvancementsScreen; -import net.minecraft.client.gui.screens.inventory.*; -import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; -import net.minecraft.client.gui.screens.multiplayer.ServerReconfigScreen; -import net.minecraft.client.gui.screens.multiplayer.WarningScreen; -import net.minecraft.client.gui.screens.options.OptionsScreen; -import net.minecraft.client.gui.screens.options.OptionsSubScreen; -import net.minecraft.client.gui.screens.packs.PackSelectionScreen; -import net.minecraft.client.gui.screens.social.SocialInteractionsScreen; -import net.minecraft.client.gui.screens.worldselection.*; -import net.weyne1.easegui.client.gui.screens.EaseGUIAbstractSplitScreen; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Registry that maps Minecraft Screen classes to ScreenType definitions. - * - *

    This is used by EaseGUI to decide how different screens should be categorized - * and animated. Matching works in two steps: - *

      - *
    • Fast exact class lookup (cache)
    • - *
    • Fallback hierarchy scan using isAssignableFrom
    • - *
    - * - *

    Mod developers can register custom screens to integrate them into animation system. - */ -public final class EaseGUIScreenRegistry { - - /** Cache for exact class → ScreenType mapping to avoid hierarchy checks */ - private static final Map, ScreenType> EXACT_MATCH_CACHE = new ConcurrentHashMap<>(); - - /** Sorted list of screen types used for inheritance-based matching */ - private static final List HIERARCHY_LIST = new ArrayList<>(); - - /** Fallback type used when no match is found */ - public static final ScreenType OTHER = new ScreenType("other", Screen.class, Integer.MIN_VALUE, ScreenGroup.OTHER, false); - - static { - // Vanilla UI screens - register("title", TitleScreen.class, 1000, ScreenGroup.BASIC); - register("options", OptionsScreen.class, 1000, ScreenGroup.BASIC); - register("options_sub", OptionsSubScreen.class, 1000, ScreenGroup.BASIC); - register("pack_selection", PackSelectionScreen.class, 1000, ScreenGroup.BASIC); - register("advancements", AdvancementsScreen.class, 1000, ScreenGroup.BASIC); - register("statistics", StatsScreen.class, 1000, ScreenGroup.BASIC); - register("warning", WarningScreen.class, 1000, ScreenGroup.BASIC); - register("pause", PauseScreen.class, 1000, ScreenGroup.BASIC); - register("share_to_lan", ShareToLanScreen.class, 1000, ScreenGroup.BASIC); - register("death", DeathScreen.class, 1000, ScreenGroup.BASIC); - register("social_interactions", SocialInteractionsScreen.class, 1000, ScreenGroup.BASIC); - - // Editors - register("sign_edit", AbstractSignEditScreen.class, 1000, ScreenGroup.EDITORS); - register("book_edit", BookEditScreen.class, 1000, ScreenGroup.EDITORS); - register("book_view", BookViewScreen.class, 1000, ScreenGroup.EDITORS); - register("command_block_edit", AbstractCommandBlockEditScreen.class, 1000, ScreenGroup.EDITORS); - register("structure_block_edit", StructureBlockEditScreen.class, 1000, ScreenGroup.EDITORS); - register("jigsaw_block_edit", JigsawBlockEditScreen.class, 1000, ScreenGroup.EDITORS); - - // World / multiplayer menus - register("world_selection", SelectWorldScreen.class, 1000, ScreenGroup.WORLDS); - register("server_selection", JoinMultiplayerScreen.class, 1000, ScreenGroup.WORLDS); - register("realms_main", RealmsMainScreen.class, 1000, ScreenGroup.WORLDS); - register("create_world", CreateWorldScreen.class, 1000, ScreenGroup.WORLDS); - register("create_flat_world", CreateFlatWorldScreen.class, 1000, ScreenGroup.WORLDS); - register("direct_join_server", DirectJoinServerScreen.class, 1000, ScreenGroup.WORLDS); - register("edit_world", EditWorldScreen.class, 1000, ScreenGroup.WORLDS); - register("edit_server", EditServerScreen.class, 1000, ScreenGroup.WORLDS); - register("edit_game_rules", EditGameRulesScreen.class, 1000, ScreenGroup.WORLDS); - register("experiments", ExperimentsScreen.class, 1000, ScreenGroup.WORLDS); - register("connecting", ConnectScreen.class, 1000, ScreenGroup.WORLDS); - register("disconnected", DisconnectedScreen.class, 1000, ScreenGroup.WORLDS); - register("server_reconfig", ServerReconfigScreen.class, 1000, ScreenGroup.WORLDS); - register("credits", CreditsAndAttributionScreen.class, 1000, ScreenGroup.WORLDS); - - // Containers / inventories - register("creative_inventory", CreativeModeInventoryScreen.class, 500, ScreenGroup.CONTAINERS); - register("survival_inventory", InventoryScreen.class, 500, ScreenGroup.CONTAINERS); - register("anvil", AnvilScreen.class, 500, ScreenGroup.CONTAINERS); - register("enchanting_table", EnchantmentScreen.class, 500, ScreenGroup.CONTAINERS); - register("container", ContainerScreen.class, 500, ScreenGroup.CONTAINERS); - register("smithing", SmithingScreen.class, 500, ScreenGroup.CONTAINERS); - register("dispenser", DispenserScreen.class, 500, ScreenGroup.CONTAINERS); - register("beacon", BeaconScreen.class, 500, ScreenGroup.CONTAINERS); - register("crafter", CrafterScreen.class, 500, ScreenGroup.CONTAINERS); - register("crafting", CraftingScreen.class, 500, ScreenGroup.CONTAINERS); - register("brewing_stand", BrewingStandScreen.class, 500, ScreenGroup.CONTAINERS); - register("cartography_table", CartographyTableScreen.class, 500, ScreenGroup.CONTAINERS); - register("furnace", AbstractFurnaceScreen.class, 500, ScreenGroup.CONTAINERS); - register("grindstone", GrindstoneScreen.class, 500, ScreenGroup.CONTAINERS); - register("hopper", HopperScreen.class, 500, ScreenGroup.CONTAINERS); - register("horse_inventory", HorseInventoryScreen.class, 500, ScreenGroup.CONTAINERS); - register("lectern", LecternScreen.class, 500, ScreenGroup.CONTAINERS); - register("loom", LoomScreen.class, 500, ScreenGroup.CONTAINERS); - register("shulker_box", ShulkerBoxScreen.class, 500, ScreenGroup.CONTAINERS); - register("stonecutter", StonecutterScreen.class, 500, ScreenGroup.CONTAINERS); - register("other_containers", AbstractContainerScreen.class, 100, ScreenGroup.CONTAINERS); - - // Internal config UI - register("ease_gui_config", EaseGUIAbstractSplitScreen.class, 100, ScreenGroup.OTHER); - - // Optional mod integration (Mod Menu) - try { - Class modMenuScreenClass = Class.forName("com.terraformersmc.modmenu.gui.ModsScreen"); - register("modmenu", modMenuScreenClass.asSubclass(Screen.class), 1000, ScreenGroup.BASIC, false); - } catch (ClassNotFoundException ignored) { - // Mod Menu is not installed — safely skip registration - } - } - - /** - * Registers a screen type with default enabled state (true). - */ - public static synchronized void register( - String id, - Class screenClass, - int priority, - ScreenGroup category - ) { - register(id, screenClass, priority, category, true); - } - - /** - * Registers a screen type for animation classification. - * - * @param id unique identifier (e.g. "inventory" or "modid:screen") - * @param screenClass target screen class - * @param priority matching priority (higher = checked earlier) - * @param category logical grouping of screen type - * @param enabledByDefault whether this screen type is enabled on first config creation - */ - public static synchronized void register( - String id, - Class screenClass, - int priority, - ScreenGroup category, - boolean enabledByDefault - ) { - ScreenType type = new ScreenType(id, screenClass, priority, category, enabledByDefault); - - EXACT_MATCH_CACHE.put(screenClass, type); - - HIERARCHY_LIST.removeIf(t -> t.getId().equals(id)); - HIERARCHY_LIST.add(type); - HIERARCHY_LIST.sort(Comparator.comparingInt(ScreenType::getPriority).reversed()); - } - - /** - * Finds a ScreenType for a runtime screen instance. - * - *

    Resolution order: - *

      - *
    1. Exact class match (fast path)
    2. - *
    3. Assignable-from hierarchy scan
    4. - *
    5. Fallback to OTHER
    6. - *
    - */ - public static ScreenType from(Screen screen) { - if (screen == null) return OTHER; - - Class screenClass = screen.getClass(); - - ScreenType exact = EXACT_MATCH_CACHE.get(screenClass); - if (exact != null) return exact; - - for (ScreenType type : HIERARCHY_LIST) { - if (type.getScreenClass().isAssignableFrom(screenClass)) { - EXACT_MATCH_CACHE.put(screenClass, type); - return type; - } - } - - return OTHER; - } - - /** - * Returns all registered screen types (read-only view). - */ - public static Collection getRegisteredTypes() { - return Collections.unmodifiableList(HIERARCHY_LIST); - } -} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/config/UIElementCategory.java b/common/src/main/java/net/weyne1/easegui/client/config/UIElementCategory.java deleted file mode 100644 index 996dc55..0000000 --- a/common/src/main/java/net/weyne1/easegui/client/config/UIElementCategory.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.weyne1.easegui.client.config; - -import java.util.EnumSet; - -public enum UIElementCategory { - BUTTON_LIKE(EnumSet.allOf(ProfileFeature.class)), - TEXT(EnumSet.of(ProfileFeature.OFFSET, ProfileFeature.SCALE, ProfileFeature.ALPHA, ProfileFeature.PIVOT)), - SCROLLABLE(EnumSet.of(ProfileFeature.OFFSET, ProfileFeature.SCALE, ProfileFeature.ALPHA)), - LIST_ENTRY(EnumSet.allOf(ProfileFeature.class)), - CONTAINERS(EnumSet.of(ProfileFeature.OFFSET, ProfileFeature.SCALE, ProfileFeature.ALPHA, ProfileFeature.PIVOT)), - UNKNOWN(EnumSet.noneOf(ProfileFeature.class)); - - private final EnumSet allowedFeatures; - - UIElementCategory(EnumSet allowedFeatures) { - this.allowedFeatures = allowedFeatures; - } - - public EnumSet getAllowedFeatures() { - return EnumSet.copyOf(this.allowedFeatures); - } -} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/accessor/ContainerScreenAccessor.java b/common/src/main/java/net/weyne1/easegui/client/extension/ContainerScreenExtension.java similarity index 58% rename from common/src/main/java/net/weyne1/easegui/client/accessor/ContainerScreenAccessor.java rename to common/src/main/java/net/weyne1/easegui/client/extension/ContainerScreenExtension.java index a2bc48e..3eb953f 100644 --- a/common/src/main/java/net/weyne1/easegui/client/accessor/ContainerScreenAccessor.java +++ b/common/src/main/java/net/weyne1/easegui/client/extension/ContainerScreenExtension.java @@ -1,6 +1,6 @@ -package net.weyne1.easegui.client.accessor; +package net.weyne1.easegui.client.extension; -public interface ContainerScreenAccessor { +public interface ContainerScreenExtension { int easeGUI$getLeftPos(); int easeGUI$getTopPos(); int easeGUI$getImageWidth(); diff --git a/common/src/main/java/net/weyne1/easegui/client/extension/ItemExtension.java b/common/src/main/java/net/weyne1/easegui/client/extension/ItemExtension.java new file mode 100644 index 0000000..ff4084b --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/extension/ItemExtension.java @@ -0,0 +1,5 @@ +package net.weyne1.easegui.client.extension; + +public interface ItemExtension { + float easegui$getAlpha(); +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/extension/RecipeBookComponentExtension.java b/common/src/main/java/net/weyne1/easegui/client/extension/RecipeBookComponentExtension.java new file mode 100644 index 0000000..eb57916 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/extension/RecipeBookComponentExtension.java @@ -0,0 +1,7 @@ +package net.weyne1.easegui.client.extension; + +public interface RecipeBookComponentExtension { + boolean easeGUI$isVisible(); + int easeGUI$getXOrigin(); + int easeGUI$getYOrigin(); +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/extension/RecipeBookScreenExtension.java b/common/src/main/java/net/weyne1/easegui/client/extension/RecipeBookScreenExtension.java new file mode 100644 index 0000000..9e5e1dd --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/extension/RecipeBookScreenExtension.java @@ -0,0 +1,7 @@ +package net.weyne1.easegui.client.extension; + +import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; + +public interface RecipeBookScreenExtension { + RecipeBookComponent easeGUI$getRecipeBookComponent(); +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/extension/WidgetExtension.java b/common/src/main/java/net/weyne1/easegui/client/extension/WidgetExtension.java new file mode 100644 index 0000000..3e0cc9c --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/extension/WidgetExtension.java @@ -0,0 +1,9 @@ +package net.weyne1.easegui.client.extension; + +import net.weyne1.easegui.api.WidgetCategory; + +public interface WidgetExtension { + WidgetCategory easeGUI$getCategory(); + + float easeGUI$getAlpha(); +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/FieldValidator.java b/common/src/main/java/net/weyne1/easegui/client/gui/components/FieldValidator.java similarity index 92% rename from common/src/main/java/net/weyne1/easegui/client/gui/FieldValidator.java rename to common/src/main/java/net/weyne1/easegui/client/gui/components/FieldValidator.java index de2f9be..d5b0449 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/FieldValidator.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/components/FieldValidator.java @@ -1,4 +1,4 @@ -package net.weyne1.easegui.client.gui; +package net.weyne1.easegui.client.gui.components; import net.minecraft.client.gui.components.EditBox; @@ -9,8 +9,8 @@ public class FieldValidator { public static final String REGEX_INT = "\\d*"; public static final String REGEX_FLOAT = "-?\\d*\\.?\\d*"; - private static final int COLOR_VALID = 0xE0E0E0; - private static final int COLOR_INVALID = 0xFF5555; + private static final int COLOR_VALID = 0xFFE0E0E0; + private static final int COLOR_INVALID = 0xFFFF5555; public static void registerLongValidator(EditBox editBox, long min, long max, Consumer onSuccess) { editBox.setFilter(s -> s.matches(REGEX_INT)); diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/components/SettingsScrollList.java b/common/src/main/java/net/weyne1/easegui/client/gui/components/SettingsScrollList.java index 817fe16..7d336a5 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/components/SettingsScrollList.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/components/SettingsScrollList.java @@ -1,6 +1,5 @@ package net.weyne1.easegui.client.gui.components; -import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -11,6 +10,7 @@ import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import java.util.List; @@ -32,21 +32,13 @@ public int getRowWidth() { return Math.min(this.width - 40, MAX_ROW_WIDTH); } - @Override - protected int getScrollbarPosition() { - return this.getX() + this.width - 6; - } - public void addHeader(String text) { this.addEntry(new HeaderEntry(text)); } - - public void addButton(Button btn) { this.addEntry(new ButtonEntry(btn)); } - public void addTwoButtons(Button btn1, Button btn2) {this.addTwoButtons(btn1, btn2, 0.60f); } + public void addButton(Button btn) { this.addEntry(new ButtonEntry(this.getRowWidth(), btn)); } + public void addTwoButtons(Button btn1, Button btn2) { this.addTwoButtons(btn1, btn2, 0.60f); } public void addTwoButtons(Button btn1, Button btn2, float firstButtonRatio) { this.addEntry(new TwoButtonsEntry(this.getRowWidth(), btn1, btn2, firstButtonRatio)); } - public void addField(String label, EditBox box) { this.addEntry(new FieldEntry(this.getRowWidth(), label, box)); } public void addTwoFields(String label, EditBox box1, EditBox box2) { this.addEntry(new TwoFieldsEntry(this.getRowWidth(), label, box1, box2)); } - public abstract static class Entry extends ContainerObjectSelectionList.Entry { } public static class HeaderEntry extends Entry { @@ -57,10 +49,9 @@ public HeaderEntry(String text) { } @Override - public void render(GuiGraphics gg, int index, int top, int left, int width, int height, - int mouseX, int mouseY, boolean isHovered, float partialTick) { + public void renderContent(GuiGraphics gg, int mouseX, int mouseY, boolean isHovered, float partialTick) { Font font = Minecraft.getInstance().font; - gg.drawCenteredString(font, this.text, left + width / 2, top + (height - 9) / 2, COLOR_HEADER); + gg.drawCenteredString(font, this.text, this.getContentXMiddle(), this.getContentYMiddle() - 4, COLOR_HEADER); } @Override @@ -72,16 +63,16 @@ public void render(GuiGraphics gg, int index, int top, int left, int width, int public static class ButtonEntry extends Entry { private final Button button; - public ButtonEntry(Button button) { + public ButtonEntry(int listWidth, Button button) { this.button = button; + this.button.setWidth(listWidth - SCROLLBAR_WIDTH_GAP); + this.button.setHeight(WIDGET_HEIGHT); } @Override - public void render(GuiGraphics gg, int index, int top, int left, int width, int height, - int mouseX, int mouseY, boolean isHovered, float partialTick) { - button.setWidth(width - SCROLLBAR_WIDTH_GAP); - button.setX(left + (SCROLLBAR_WIDTH_GAP / 2)); - button.setY(top); + public void renderContent(@NonNull GuiGraphics gg, int mouseX, int mouseY, boolean isHovered, float partialTick) { + button.setX(this.getContentX() + SCROLLBAR_WIDTH_GAP / 2); + button.setY(this.getContentY()); button.render(gg, mouseX, mouseY, partialTick); } @@ -112,13 +103,12 @@ public TwoButtonsEntry(int listWidth, Button button1, Button button2, float rati } @Override - public void render(GuiGraphics gg, int index, int top, int left, int width, int height, - int mouseX, int mouseY, boolean isHovered, float partialTick) { - button1.setX(left + (SCROLLBAR_WIDTH_GAP / 2)); - button1.setY(top); + public void renderContent(@NonNull GuiGraphics gg, int mouseX, int mouseY, boolean isHovered, float partialTick) { + button1.setX(this.getContentX() + SCROLLBAR_WIDTH_GAP / 2); + button1.setY(this.getContentY()); button2.setX(button1.getX() + button1.getWidth() + ELEMENT_SPACING); - button2.setY(top); + button2.setY(this.getContentY()); button1.render(gg, mouseX, mouseY, partialTick); button2.render(gg, mouseX, mouseY, partialTick); @@ -151,15 +141,13 @@ public FieldEntry(int listWidth, String labelText, EditBox field) { } @Override - public void render(GuiGraphics gg, int index, int top, int left, int width, int height, - int mouseX, int mouseY, boolean isHovered, float partialTick) { - label.setX(left + (SCROLLBAR_WIDTH_GAP / 2)); - label.setY(top); + public void renderContent(@NonNull GuiGraphics gg, int mouseX, int mouseY, boolean isHovered, float partialTick) { + label.setX(this.getContentX() + SCROLLBAR_WIDTH_GAP / 2); + label.setY(this.getContentY()); field.setX(label.getX() + label.getWidth() + ELEMENT_SPACING); - field.setY(top); + field.setY(this.getContentY()); label.render(gg, mouseX, mouseY, partialTick); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); field.render(gg, mouseX, mouseY, partialTick); } @@ -197,19 +185,17 @@ public TwoFieldsEntry(int listWidth, String labelText, EditBox field1, EditBox f } @Override - public void render(GuiGraphics gg, int index, int top, int left, int width, int height, - int mouseX, int mouseY, boolean isHovered, float partialTick) { - label.setX(left + (SCROLLBAR_WIDTH_GAP / 2)); - label.setY(top); + public void renderContent(@NonNull GuiGraphics gg, int mouseX, int mouseY, boolean isHovered, float partialTick) { + label.setX(this.getContentX() + SCROLLBAR_WIDTH_GAP / 2); + label.setY(this.getContentY()); field1.setX(label.getX() + label.getWidth() + ELEMENT_SPACING); - field1.setY(top); + field1.setY(this.getContentY()); field2.setX(field1.getX() + field1.getWidth() + ELEMENT_SPACING); - field2.setY(top); + field2.setY(this.getContentY()); label.render(gg, mouseX, mouseY, partialTick); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); field1.render(gg, mouseX, mouseY, partialTick); field2.render(gg, mouseX, mouseY, partialTick); } diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/configurator/AdvancementsScreenConfigurator.java b/common/src/main/java/net/weyne1/easegui/client/gui/configurator/AdvancementsScreenConfigurator.java index 914ad72..b550167 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/configurator/AdvancementsScreenConfigurator.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/configurator/AdvancementsScreenConfigurator.java @@ -4,7 +4,7 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; -import net.weyne1.easegui.client.animation.AnimationProfile; +import net.weyne1.easegui.api.animation.AnimationProfile; import net.weyne1.easegui.client.config.ConfigManager; import net.weyne1.easegui.client.config.EaseGUIConfig; import net.weyne1.easegui.client.config.ProfileFeature; diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/configurator/IScreenConfigurator.java b/common/src/main/java/net/weyne1/easegui/client/gui/configurator/IScreenConfigurator.java index be66d9d..8aae04b 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/configurator/IScreenConfigurator.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/configurator/IScreenConfigurator.java @@ -5,7 +5,7 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.weyne1.easegui.client.config.EaseGUIConfig; -import net.weyne1.easegui.client.gui.FieldValidator; +import net.weyne1.easegui.client.gui.components.FieldValidator; import net.weyne1.easegui.client.gui.components.SettingsScrollList; import java.util.HashMap; diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/configurator/TitleScreenConfigurator.java b/common/src/main/java/net/weyne1/easegui/client/gui/configurator/TitleScreenConfigurator.java index 433304b..9e9351b 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/configurator/TitleScreenConfigurator.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/configurator/TitleScreenConfigurator.java @@ -5,8 +5,9 @@ import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; -import net.weyne1.easegui.client.StringUtils; -import net.weyne1.easegui.client.animation.AnimationProfile; +import net.weyne1.easegui.api.animation.EasingType; +import net.weyne1.easegui.client.util.StringUtils; +import net.weyne1.easegui.api.animation.AnimationProfile; import net.weyne1.easegui.client.config.ConfigManager; import net.weyne1.easegui.client.config.EaseGUIConfig; import net.weyne1.easegui.client.config.ProfileFeature; @@ -78,7 +79,7 @@ public void populate(SettingsScrollList list, EaseGUIConfig.ScreenSettings setti // Интерполяция сплеша Component easingComp = Component.literal(StringUtils.toTitleCase(splash.splashEasing)); list.addButton(Button.builder(Component.translatable("easegui.config.title.splash.easing", easingComp), btn -> { - AnimationProfile.EasingType[] values = AnimationProfile.EasingType.values(); + EasingType[] values = EasingType.values(); splash.splashEasing = values[(splash.splashEasing.ordinal() + 1) % values.length]; Component updatedEasing = Component.literal(StringUtils.toTitleCase(splash.splashEasing)); btn.setMessage(Component.translatable("easegui.config.title.splash.easing", updatedEasing)); diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/preview/ProfilePreviewRenderer.java b/common/src/main/java/net/weyne1/easegui/client/gui/preview/ProfilePreviewRenderer.java index 17d4e99..fb34573 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/preview/ProfilePreviewRenderer.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/preview/ProfilePreviewRenderer.java @@ -3,10 +3,9 @@ import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; -import net.weyne1.easegui.client.animation.AnimationMath; -import net.weyne1.easegui.client.animation.AnimationProfile; -import net.weyne1.easegui.client.animation.AnimationScope; -import net.weyne1.easegui.client.animation.AnimationSystem; +import net.weyne1.easegui.api.animation.AnimationProfile; +import net.weyne1.easegui.api.animation.CascadeDirection; +import net.weyne1.easegui.client.animation.*; import net.weyne1.easegui.client.config.ProfileFeature; import java.util.EnumSet; @@ -39,10 +38,10 @@ public static void render(GuiGraphics gg, Font font, int screenWidth, int screen boolean isCascadeActive = activeFeatures.contains(ProfileFeature.CASCADE_DELAY); int itemCount = isCascadeActive ? 3 : 1; - boolean isEnabled = profile.enabled; + boolean isEnabled = profile.isEnabled(); - boolean isHorizontal = profile.cascadeDirection == AnimationProfile.CascadeDirection.LEFT_TO_RIGHT || - profile.cascadeDirection == AnimationProfile.CascadeDirection.RIGHT_TO_LEFT; + boolean isHorizontal = profile.getCascadeDirection() == CascadeDirection.LEFT_TO_RIGHT || + profile.getCascadeDirection() == CascadeDirection.RIGHT_TO_LEFT; int boxWidth = (isHorizontal && isCascadeActive) ? 40 : 120; @@ -64,9 +63,9 @@ private static void renderStaticBounds(GuiGraphics gg, int centerX, int centerY, } private static void renderAnimatedElements(GuiGraphics gg, Font font, int centerX, int centerY, AnimationProfile profile, boolean isCascade, int count, boolean isHorizontal, int boxWidth) { - boolean isEnabled = profile.enabled; - long duration = Math.max(profile.duration, 50L); - long totalLoopTime = duration + (isCascade ? (2 * profile.cascadeDelay) : 0L) + LOOP_PADDING_MS; + boolean isEnabled = profile.isEnabled(); + long duration = Math.max(profile.getDuration(), 50L); + long totalLoopTime = duration + (isCascade ? (2 * profile.getCascadeDelay()) : 0L) + LOOP_PADDING_MS; long currentTime = isEnabled ? (System.currentTimeMillis() % totalLoopTime) : 0L; int halfW = boxWidth / 2; @@ -81,7 +80,7 @@ private static void renderAnimatedElements(GuiGraphics gg, Font font, int center long itemDelay = isCascade ? calculateCascadeDelay(profile, i) : 0L; long itemTime = currentTime - itemDelay; float progress = itemTime >= duration ? 1.0f : (itemTime > 0 ? (float) itemTime / duration : 0.0f); - easedProgress = profile.easing != null ? profile.easing.ease(progress) : progress; + easedProgress = profile.getEasing() != null ? profile.getEasing().ease(progress) : progress; } int targetX = getTargetX(centerX, isCascade, isHorizontal, i); @@ -91,7 +90,7 @@ private static void renderAnimatedElements(GuiGraphics gg, Font font, int center int y = targetY - halfH; try (AnimationScope ignored = AnimationSystem.begin(gg, x, y, boxWidth, BOX_HEIGHT, profile, easedProgress, 1.0f)) { - int bgAlpha = calcAlphaColor(profile.startAlpha, easedProgress); + int bgAlpha = calcAlphaColor(profile.getStartAlpha(), easedProgress); int boxColor = isEnabled ? 0x353535 : 0x222222; gg.fill(x, y, x + boxWidth, y + BOX_HEIGHT, (bgAlpha << 24) | boxColor); @@ -99,7 +98,7 @@ private static void renderAnimatedElements(GuiGraphics gg, Font font, int center ? (isHorizontal ? CASCADE_SHORT_LABELS[i] : CASCADE_LABELS[i]) : STATIC_LABEL; - int fontAlpha = calcAlphaColor(profile.startAlpha, easedProgress); + int fontAlpha = calcAlphaColor(profile.getStartAlpha(), easedProgress); int textColor = isEnabled ? 0xE0E0E0 : 0x888888; gg.drawCenteredString(font, label, targetX, targetY - 4, (fontAlpha << 24) | textColor); @@ -172,10 +171,10 @@ private static int calcAlphaColor(float startAlpha, float progress) { } private static long calculateCascadeDelay(AnimationProfile profile, int i) { - boolean reverse = profile.cascadeDirection == AnimationProfile.CascadeDirection.BOTTOM_TO_TOP || - profile.cascadeDirection == AnimationProfile.CascadeDirection.RIGHT_TO_LEFT; + boolean reverse = profile.getCascadeDirection() == CascadeDirection.BOTTOM_TO_TOP || + profile.getCascadeDirection() == CascadeDirection.RIGHT_TO_LEFT; int factor = reverse ? (2 - i) : i; - return factor * profile.cascadeDelay; + return factor * profile.getCascadeDelay(); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/screens/EaseGUIAbstractSplitScreen.java b/common/src/main/java/net/weyne1/easegui/client/gui/screens/EaseGUIAbstractSplitScreen.java index 0cc43bb..464b457 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/screens/EaseGUIAbstractSplitScreen.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/screens/EaseGUIAbstractSplitScreen.java @@ -1,10 +1,12 @@ package net.weyne1.easegui.client.gui.screens; +import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; +import org.jspecify.annotations.NonNull; public abstract class EaseGUIAbstractSplitScreen extends Screen { protected final Screen parent; @@ -13,7 +15,6 @@ public abstract class EaseGUIAbstractSplitScreen extends Screen { protected int listHeight; protected int leftX; protected int rightX; - protected int stringColor; private static final int LINE_COLOR = 0x33FFFFFF; @@ -32,34 +33,35 @@ protected void init() { this.leftX = 0; this.rightX = halfWidth; - this.stringColor = 0xAAAAAA; - initScreen(); // Главный заголовок экрана (в самом верху по центру) - StringWidget titleWidget = new StringWidget(this.title, this.font); + Component titleColored = this.title.copy().withStyle(ChatFormatting.WHITE); + StringWidget titleWidget = new StringWidget(titleColored, this.font); + titleWidget.setX(this.halfWidth - titleWidget.getWidth() / 2); titleWidget.setY(15); - titleWidget.setColor(0xFFFFFF); this.addRenderableWidget(titleWidget); // Левый подзаголовок Component leftSub = getLeftSubtitle(); if (leftSub != null) { - StringWidget leftSubWidget = new StringWidget(leftSub, this.font); + Component leftSubColored = leftSub.copy().withStyle(ChatFormatting.GRAY); + StringWidget leftSubWidget = new StringWidget(leftSubColored, this.font); + leftSubWidget.setX((this.halfWidth / 2) - (leftSubWidget.getWidth() / 2)); leftSubWidget.setY(35); - leftSubWidget.setColor(this.stringColor); this.addRenderableWidget(leftSubWidget); } // Правый подзаголовок Component rightSub = getRightSubtitle(); if (rightSub != null) { - StringWidget rightSubWidget = new StringWidget(rightSub, this.font); + Component rightSubColored = rightSub.copy().withStyle(ChatFormatting.GRAY); + StringWidget rightSubWidget = new StringWidget(rightSubColored, this.font); + rightSubWidget.setX((this.halfWidth + (this.halfWidth / 2)) - (rightSubWidget.getWidth() / 2)); rightSubWidget.setY(35); - rightSubWidget.setColor(this.stringColor); this.addRenderableWidget(rightSubWidget); } } @@ -73,7 +75,7 @@ protected void init() { protected void renderOverlay(GuiGraphics gg, int mouseX, int mouseY, float partialTick) {} @Override - public void render(GuiGraphics gg, int mouseX, int mouseY, float partialTick) { + public void render(@NonNull GuiGraphics gg, int mouseX, int mouseY, float partialTick) { super.render(gg, mouseX, mouseY, partialTick); // Вертикальный разделитель по центру экрана diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/screens/MainConfigScreen.java b/common/src/main/java/net/weyne1/easegui/client/gui/screens/MainConfigScreen.java index 7ae6bf3..5debbc7 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/screens/MainConfigScreen.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/screens/MainConfigScreen.java @@ -5,9 +5,13 @@ import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; -import net.weyne1.easegui.client.animation.AnimationProfile; +import net.weyne1.easegui.api.WidgetCategory; +import net.weyne1.easegui.api.EaseGUIScreenRegistry; +import net.weyne1.easegui.api.EaseGUIScreenGroup; +import net.weyne1.easegui.api.EaseGUIScreenType; +import net.weyne1.easegui.api.animation.AnimationProfile; import net.weyne1.easegui.client.config.*; -import net.weyne1.easegui.client.gui.FieldValidator; +import net.weyne1.easegui.client.gui.components.FieldValidator; import net.weyne1.easegui.client.gui.components.SettingsScrollList; import java.util.Comparator; @@ -77,11 +81,11 @@ protected void initScreen() { leftList.addHeader(Component.translatable("easegui.config.title.elements").getString()); - addGlobalProfileButton(leftList, config, mc, UIElementCategory.BUTTON_LIKE, "easegui.main.button.button_like"); - addGlobalProfileButton(leftList, config, mc, UIElementCategory.TEXT, "easegui.main.button.text"); - addGlobalProfileButton(leftList, config, mc, UIElementCategory.SCROLLABLE, "easegui.main.button.scrollable"); - addGlobalProfileButton(leftList, config, mc, UIElementCategory.LIST_ENTRY, "easegui.main.button.list_entry"); - addGlobalProfileButton(leftList, config, mc, UIElementCategory.CONTAINERS, "easegui.main.button.containers"); + addGlobalProfileButton(leftList, config, mc, WidgetCategory.BUTTON_LIKE, "easegui.main.button.button_like"); + addGlobalProfileButton(leftList, config, mc, WidgetCategory.TEXT, "easegui.main.button.text"); + addGlobalProfileButton(leftList, config, mc, WidgetCategory.SCROLLABLE, "easegui.main.button.scrollable"); + addGlobalProfileButton(leftList, config, mc, WidgetCategory.LIST_ENTRY, "easegui.main.button.list_entry"); + addGlobalProfileButton(leftList, config, mc, WidgetCategory.CONTAINERS, "easegui.main.button.containers"); this.addRenderableWidget(leftList); @@ -91,8 +95,8 @@ protected void initScreen() { rightList.setX(rightX); rightList.setY(50); - for (ScreenGroup category : ScreenGroup.values()) { - List categoryScreens = EaseGUIScreenRegistry.getRegisteredTypes().stream() + for (EaseGUIScreenGroup category : EaseGUIScreenGroup.values()) { + List categoryScreens = EaseGUIScreenRegistry.getRegisteredTypes().stream() .filter(type -> type.getGroup() == category) .sorted(Comparator.comparing(type -> type.getDisplayName().getString())) .toList(); @@ -100,7 +104,7 @@ protected void initScreen() { if (!categoryScreens.isEmpty()) { rightList.addHeader(Component.translatable(category.getTranslationKey()).getString()); - for (ScreenType type : categoryScreens) { + for (EaseGUIScreenType type : categoryScreens) { rightList.addButton(Button.builder( type.getDisplayName(), b -> mc.setScreen(new ScreenSpecificConfigScreen(this, type)) @@ -124,7 +128,7 @@ protected void initScreen() { ).bounds(halfWidth - 100, this.height - 30, 200, 20).build()); } - private void addGlobalProfileButton(SettingsScrollList list, EaseGUIConfig config, Minecraft mc, UIElementCategory category, String translationKey) { + private void addGlobalProfileButton(SettingsScrollList list, EaseGUIConfig config, Minecraft mc, WidgetCategory category, String translationKey) { AnimationProfile cleanDefault = new EaseGUIConfig().global.elementProfiles.get(category); if (cleanDefault == null) cleanDefault = new AnimationProfile(); AnimationProfile finalCleanDefault = cleanDefault; @@ -145,7 +149,6 @@ private void addGlobalProfileButton(SettingsScrollList list, EaseGUIConfig confi } private EditBox createTextField(String value) { - assert this.minecraft != null; EditBox editBox = new EditBox(this.minecraft.font, 0, 0, 60, 16, Component.empty()); editBox.setValue(value); return editBox; diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/screens/ProfileEditorScreen.java b/common/src/main/java/net/weyne1/easegui/client/gui/screens/ProfileEditorScreen.java index a4129ac..89896fb 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/screens/ProfileEditorScreen.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/screens/ProfileEditorScreen.java @@ -5,10 +5,13 @@ import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; -import net.weyne1.easegui.client.StringUtils; -import net.weyne1.easegui.client.animation.AnimationProfile; +import net.weyne1.easegui.api.animation.CascadeDirection; +import net.weyne1.easegui.api.animation.EasingType; +import net.weyne1.easegui.api.animation.PivotPoint; +import net.weyne1.easegui.client.util.StringUtils; +import net.weyne1.easegui.api.animation.AnimationProfile; import net.weyne1.easegui.client.config.ProfileFeature; -import net.weyne1.easegui.client.gui.FieldValidator; +import net.weyne1.easegui.client.gui.components.FieldValidator; import net.weyne1.easegui.client.gui.components.SettingsScrollList; import net.weyne1.easegui.client.gui.preview.ProfilePreviewRenderer; @@ -42,12 +45,12 @@ protected void initScreen() { leftScrollList.setY(50); // --- 0. Переключатель "Включено / Выключено" + кнопка "Reset" --- - Component statusComp = workingCopy.enabled ? Component.translatable("easegui.generic.on") : Component.translatable("easegui.generic.off"); + Component statusComp = workingCopy.isEnabled() ? Component.translatable("easegui.generic.on") : Component.translatable("easegui.generic.off"); Button toggleBtn = Button.builder( Component.translatable("easegui.editor.button.enabled", statusComp), button -> { - workingCopy.enabled(!workingCopy.enabled); - Component newStatus = workingCopy.enabled ? Component.translatable("easegui.generic.on") : Component.translatable("easegui.generic.off"); + workingCopy.enabled(!workingCopy.isEnabled()); + Component newStatus = workingCopy.isEnabled() ? Component.translatable("easegui.generic.on") : Component.translatable("easegui.generic.off"); button.setMessage(Component.translatable("easegui.editor.button.enabled", newStatus)); } ).build(); @@ -56,25 +59,23 @@ protected void initScreen() { Component.translatable("easegui.generic.reset"), button -> { applyProfileValues(this.workingCopy, this.defaultProfile); - if (this.minecraft != null) { - this.init(this.minecraft, this.width, this.height); - } + this.init(this.width, this.height); } ).build(); leftScrollList.addTwoButtons(toggleBtn, resetBtn, 0.70f); // --- 1. Длительность (Лимит: от 0 до 5000 мс) --- - EditBox durationField = createTextField(String.valueOf(workingCopy.duration)); + EditBox durationField = createTextField(String.valueOf(workingCopy.getDuration())); FieldValidator.registerLongValidator(durationField, 0L, 5000L, workingCopy::duration); leftScrollList.addField(Component.translatable("easegui.editor.field.duration").getString(), durationField); // --- 2. Смещение (Лимит: от -1000 до 1000 пикселей) --- if (activeFeatures.contains(ProfileFeature.OFFSET)) { - EditBox ox = createTextField(String.valueOf(workingCopy.offset.x)); + EditBox ox = createTextField(String.valueOf(workingCopy.getOffsetX())); FieldValidator.registerFloatValidator(ox, -1000f, 1000f, workingCopy::offsetX); - EditBox oy = createTextField(String.valueOf(workingCopy.offset.y)); + EditBox oy = createTextField(String.valueOf(workingCopy.getOffsetY())); FieldValidator.registerFloatValidator(oy, -1000f, 1000f, workingCopy::offsetY); leftScrollList.addTwoFields(Component.translatable("easegui.editor.field.offset").getString(), ox, oy); @@ -82,10 +83,10 @@ protected void initScreen() { // --- 3. Масштаб (Лимит: от 0.0 до 10.0 крат) --- if (activeFeatures.contains(ProfileFeature.SCALE)) { - EditBox sx = createTextField(String.valueOf(workingCopy.startScale.x)); + EditBox sx = createTextField(String.valueOf(workingCopy.getStartScaleX())); FieldValidator.registerFloatValidator(sx, 0.0f, 10.0f, workingCopy::startScaleX); - EditBox sy = createTextField(String.valueOf(workingCopy.startScale.y)); + EditBox sy = createTextField(String.valueOf(workingCopy.getStartScaleY())); FieldValidator.registerFloatValidator(sy, 0.0f, 10.0f, workingCopy::startScaleY); leftScrollList.addTwoFields(Component.translatable("easegui.editor.field.scale").getString(), sx, sy); @@ -93,47 +94,47 @@ protected void initScreen() { // --- 4. Прозрачность (Лимит: от 0.0 до 1.0) --- if (activeFeatures.contains(ProfileFeature.ALPHA)) { - EditBox alphaField = createTextField(String.valueOf(workingCopy.startAlpha)); + EditBox alphaField = createTextField(String.valueOf(workingCopy.getStartAlpha())); FieldValidator.registerFloatValidator(alphaField, 0.0f, 1.0f, workingCopy::startAlpha); leftScrollList.addField(Component.translatable("easegui.editor.field.alpha").getString(), alphaField); } // --- 5. Каскадность (Лимит: от 0 до 1000 мс) --- if (activeFeatures.contains(ProfileFeature.CASCADE_DELAY)) { - EditBox cascadeField = createTextField(String.valueOf(workingCopy.cascadeDelay)); + EditBox cascadeField = createTextField(String.valueOf(workingCopy.getCascadeDelay())); FieldValidator.registerLongValidator(cascadeField, 0L, 1000L, workingCopy::cascadeDelay); leftScrollList.addField(Component.translatable("easegui.editor.field.cascade_delay").getString(), cascadeField); } // --- 5.1. Направление каскада if (activeFeatures.contains(ProfileFeature.CASCADE_DIRECTION)) { - Component dirComp = getCascadeDirectionComponent(workingCopy.cascadeDirection); + Component dirComp = getCascadeDirectionComponent(workingCopy.getCascadeDirection()); leftScrollList.addButton(Button.builder(Component.translatable("easegui.editor.button.cascade_dir", dirComp), b -> { - AnimationProfile.CascadeDirection[] v = AnimationProfile.CascadeDirection.values(); - workingCopy.cascadeDirection(v[(workingCopy.cascadeDirection.ordinal() + 1) % v.length]); - b.setMessage(Component.translatable("easegui.editor.button.cascade_dir", getCascadeDirectionComponent(workingCopy.cascadeDirection))); + CascadeDirection[] v = CascadeDirection.values(); + workingCopy.cascadeDirection(v[(workingCopy.getCascadeDirection().ordinal() + 1) % v.length]); + b.setMessage(Component.translatable("easegui.editor.button.cascade_dir", getCascadeDirectionComponent(workingCopy.getCascadeDirection()))); }).build()); } // --- 6. Точка опоры (Pivot) --- if (activeFeatures.contains(ProfileFeature.PIVOT)) { - Component pivotComp = Component.translatable("easegui.pivot." + workingCopy.pivot.name().toLowerCase()); + Component pivotComp = Component.translatable("easegui.pivot." + workingCopy.getPivot().name().toLowerCase()); leftScrollList.addButton(Button.builder(Component.translatable("easegui.editor.button.pivot", pivotComp), b -> { - AnimationProfile.PivotPoint[] v = AnimationProfile.PivotPoint.values(); - workingCopy.pivot(v[(workingCopy.pivot.ordinal() + 1) % v.length]); - Component updatedPivot = Component.translatable("easegui.pivot." + workingCopy.pivot.name().toLowerCase()); + PivotPoint[] v = PivotPoint.values(); + workingCopy.pivot(v[(workingCopy.getPivot().ordinal() + 1) % v.length]); + Component updatedPivot = Component.translatable("easegui.pivot." + workingCopy.getPivot().name().toLowerCase()); b.setMessage(Component.translatable("easegui.editor.button.pivot", updatedPivot)); }).build()); } // --- 7. Интерполяция (Easing) --- - Component easingComp = Component.literal(StringUtils.toTitleCase(workingCopy.easing)); + Component easingComp = Component.literal(StringUtils.toTitleCase(workingCopy.getEasing())); leftScrollList.addButton(Button.builder( Component.translatable("easegui.editor.button.easing", easingComp), button -> { - AnimationProfile.EasingType[] values = AnimationProfile.EasingType.values(); - workingCopy.easing(values[(workingCopy.easing.ordinal() + 1) % values.length]); - Component updatedEasing = Component.literal(StringUtils.toTitleCase(workingCopy.easing)); + EasingType[] values = EasingType.values(); + workingCopy.easing(values[(workingCopy.getEasing().ordinal() + 1) % values.length]); + Component updatedEasing = Component.literal(StringUtils.toTitleCase(workingCopy.getEasing())); button.setMessage(Component.translatable("easegui.editor.button.easing", updatedEasing)); } ).build()); @@ -146,13 +147,12 @@ protected void initScreen() { } private EditBox createTextField(String value) { - assert this.minecraft != null; EditBox editBox = new EditBox(this.minecraft.font, 0, 0, 60, 16, Component.empty()); editBox.setValue(value); return editBox; } - private Component getCascadeDirectionComponent(AnimationProfile.CascadeDirection dir) { + private Component getCascadeDirectionComponent(CascadeDirection dir) { return Component.translatable(switch (dir) { case TOP_TO_BOTTOM -> "easegui.cascade.top_to_bottom"; case BOTTOM_TO_TOP -> "easegui.cascade.bottom_to_top"; @@ -180,14 +180,14 @@ private AnimationProfile cloneProfile(AnimationProfile s) { private AnimationProfile applyProfileValues(AnimationProfile target, AnimationProfile source) { return target - .enabled(source.enabled) - .duration(source.duration) - .offset(source.offset.x, source.offset.y) - .startScale(source.startScale.x, source.startScale.y) - .startAlpha(source.startAlpha) - .cascadeDelay(source.cascadeDelay) - .easing(source.easing) - .pivot(source.pivot) - .cascadeDirection(source.cascadeDirection); + .enabled(source.isEnabled()) + .duration(source.getDuration()) + .offset(source.getOffsetX(), source.getOffsetY()) + .startScale(source.getStartScaleX(), source.getStartScaleY()) + .startAlpha(source.getStartAlpha()) + .cascadeDelay(source.getCascadeDelay()) + .easing(source.getEasing()) + .pivot(source.getPivot()) + .cascadeDirection(source.getCascadeDirection()); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/gui/screens/ScreenSpecificConfigScreen.java b/common/src/main/java/net/weyne1/easegui/client/gui/screens/ScreenSpecificConfigScreen.java index 49b8be5..82912a5 100644 --- a/common/src/main/java/net/weyne1/easegui/client/gui/screens/ScreenSpecificConfigScreen.java +++ b/common/src/main/java/net/weyne1/easegui/client/gui/screens/ScreenSpecificConfigScreen.java @@ -6,7 +6,9 @@ import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; -import net.weyne1.easegui.client.animation.AnimationProfile; +import net.weyne1.easegui.api.WidgetCategory; +import net.weyne1.easegui.api.EaseGUIScreenType; +import net.weyne1.easegui.api.animation.AnimationProfile; import net.weyne1.easegui.client.config.*; import net.weyne1.easegui.client.gui.components.SettingsScrollList; import net.weyne1.easegui.client.gui.configurator.IScreenConfigurator; @@ -15,9 +17,9 @@ import java.util.EnumSet; public class ScreenSpecificConfigScreen extends EaseGUIAbstractSplitScreen { - private final ScreenType screenType; + private final EaseGUIScreenType screenType; - public ScreenSpecificConfigScreen(Screen parent, ScreenType type) { + public ScreenSpecificConfigScreen(Screen parent, EaseGUIScreenType type) { super(type.getDisplayName(), parent); this.screenType = type; } @@ -63,11 +65,12 @@ protected void initScreen() { this.addRenderableWidget(warningWidget); } else { - StringWidget noOptionsWidget = new StringWidget(Component.translatable("easegui.gui.no_unique_options"), this.font); + Component noOptionsText = Component.translatable("easegui.gui.no_unique_options") + .withStyle(style -> style.withColor(0x55FFFFFF)); + StringWidget noOptionsWidget = new StringWidget(noOptionsText, this.font); int leftBlockCenterX = leftX + (listWidth / 2); noOptionsWidget.setX(leftBlockCenterX - (noOptionsWidget.getWidth() / 2)); noOptionsWidget.setY(this.height / 2 - 4); - noOptionsWidget.setColor(0x55FFFFFF); this.addRenderableWidget(noOptionsWidget); } } @@ -106,9 +109,9 @@ protected void initScreen() { } private void setupCategoryButtons(SettingsScrollList rightScrollList, EaseGUIConfig.ScreenSettings settings, EaseGUIConfig config) { - EnumSet overridableCategories = EnumSet.complementOf(EnumSet.of(UIElementCategory.UNKNOWN)); + EnumSet overridableCategories = EnumSet.complementOf(EnumSet.of(WidgetCategory.UNKNOWN)); - for (UIElementCategory category : overridableCategories) { + for (WidgetCategory category : overridableCategories) { boolean hasCustom = settings.customProfiles.containsKey(category); Component categoryLabel = Component.translatable("easegui.category." + category.name().toLowerCase()); @@ -156,14 +159,14 @@ private AnimationProfile cloneProfile(AnimationProfile src) { AnimationProfile dest = new AnimationProfile(); if (src == null) return dest; return dest - .enabled(src.enabled) - .duration(src.duration) - .offset(src.offset.x, src.offset.y) - .startScale(src.startScale.x, src.startScale.y) - .startAlpha(src.startAlpha) - .cascadeDelay(src.cascadeDelay) - .easing(src.easing) - .pivot(src.pivot) - .cascadeDirection(src.cascadeDirection); + .enabled(src.isEnabled()) + .duration(src.getDuration()) + .offset(src.getOffsetX(), src.getOffsetY()) + .startScale(src.getStartScaleX(), src.getStartScaleY()) + .startAlpha(src.getStartAlpha()) + .cascadeDelay(src.getCascadeDelay()) + .easing(src.getEasing()) + .pivot(src.getPivot()) + .cascadeDirection(src.getCascadeDirection()); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/GameRendererMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/GameRendererMixin.java index bee4a7d..672f0d5 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/GameRendererMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/GameRendererMixin.java @@ -3,34 +3,15 @@ import net.minecraft.client.DeltaTracker; import net.minecraft.client.renderer.GameRenderer; import net.weyne1.easegui.client.animation.AnimationContext; -import net.weyne1.easegui.client.animator.BackgroundAnimator; -import net.weyne1.easegui.client.config.ConfigManager; -import net.weyne1.easegui.client.state.ScreenAnimationTracker; import net.weyne1.easegui.client.state.ScreenStateTracker; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(GameRenderer.class) public class GameRendererMixin { - @ModifyArg( - method = "processBlurEffect", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/PostChain;setUniform(Ljava/lang/String;F)V"), - index = 1 - ) - private float easeGUI$animateBlurRadiusArg(float originalRadius) { - boolean enableSmoothBlur = ConfigManager.getConfig().global.enableSmoothBlur; - - if (!enableSmoothBlur || BackgroundAnimator.skipBackgroundFade) { - return originalRadius; - } - - return originalRadius * ScreenAnimationTracker.getProgress(); - } - @Inject(method = "render", at = @At("HEAD")) private void easeGUI$onFrameStart(DeltaTracker deltaTracker, boolean renderLevel, CallbackInfo ci) { ScreenStateTracker.incrementFrame(); diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/MinecraftMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/MinecraftMixin.java index 75b5968..d53b874 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/MinecraftMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/MinecraftMixin.java @@ -18,13 +18,13 @@ public class MinecraftMixin { private void easeGUI$onScreenTransition(Screen guiScreen, CallbackInfo ci) { Screen oldScreen = this.screen; - if (BackgroundAnimator.isLoadingScreen(guiScreen)) { + if (!BackgroundAnimator.shouldAnimateBackground(guiScreen)) { BackgroundAnimator.skipBackgroundFade = true; return; } - boolean wasBlurred = BackgroundAnimator.isScreenBlurred(oldScreen); - boolean willBeBlurred = BackgroundAnimator.isScreenBlurred(guiScreen); + boolean wasBlurred = BackgroundAnimator.shouldAnimateBackground(oldScreen); + boolean willBeBlurred = BackgroundAnimator.shouldAnimateBackground(guiScreen); BackgroundAnimator.skipBackgroundFade = wasBlurred && willBeBlurred; } diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/OptionsMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/OptionsMixin.java new file mode 100644 index 0000000..0dd3c6e --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/OptionsMixin.java @@ -0,0 +1,28 @@ +package net.weyne1.easegui.client.mixin; + +import net.minecraft.client.Options; +import net.weyne1.easegui.client.animator.BackgroundAnimator; +import net.weyne1.easegui.client.config.ConfigManager; +import net.weyne1.easegui.client.state.ScreenAnimationTracker; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Options.class) +public class OptionsMixin { + + @Inject(method = "getMenuBackgroundBlurriness", at = @At("RETURN"), cancellable = true) + private void easeGUI$animateMenuBlurriness(CallbackInfoReturnable cir) { + boolean enableSmoothBlur = ConfigManager.getConfig().global.enableSmoothBlur; + + if (!enableSmoothBlur || BackgroundAnimator.skipBackgroundFade) { + return; + } + + int originalValue = cir.getReturnValue(); + int animatedValue = Math.round(originalValue * ScreenAnimationTracker.getProgress()); + + cir.setReturnValue(animatedValue); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/blaze3d/VertexBufferMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/blaze3d/VertexBufferMixin.java deleted file mode 100644 index b415748..0000000 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/blaze3d/VertexBufferMixin.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.weyne1.easegui.client.mixin.blaze3d; - -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.VertexBuffer; -import net.minecraft.client.renderer.ShaderInstance; -import net.weyne1.easegui.client.animation.AnimationContext; -import org.joml.Matrix4f; -import org.lwjgl.opengl.GL11; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(VertexBuffer.class) -public class VertexBufferMixin { - @Unique private float easeGUI$savedR; - @Unique private float easeGUI$savedG; - @Unique private float easeGUI$savedB; - @Unique private float easeGUI$savedA; - @Unique private boolean easeGUI$hasSavedColor = false; - @Unique private boolean easeGUI$wasBlendEnabled; - - @Inject(method = "_drawWithShader", at = @At("HEAD")) - private void easeGUI$injectAlphaBeforeShader(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, ShaderInstance shader, CallbackInfo ci) { - if (AnimationContext.isAnimating()) { - float[] currentColor = RenderSystem.getShaderColor(); - - this.easeGUI$savedR = currentColor[0]; - this.easeGUI$savedG = currentColor[1]; - this.easeGUI$savedB = currentColor[2]; - this.easeGUI$savedA = currentColor[3]; - this.easeGUI$hasSavedColor = true; - - this.easeGUI$wasBlendEnabled = GL11.glIsEnabled(GL11.GL_BLEND); - - float animationAlpha = AnimationContext.getCurrentAlpha(); - float finalAlpha = this.easeGUI$savedA * animationAlpha; - - if (!this.easeGUI$wasBlendEnabled && finalAlpha < 1.0f) { - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - } - - RenderSystem.setShaderColor(this.easeGUI$savedR, this.easeGUI$savedG, this.easeGUI$savedB, finalAlpha); - } - } - - @Inject(method = "_drawWithShader", at = @At("TAIL")) - private void easeGUI$restoreAlphaAfterShader(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, ShaderInstance shader, CallbackInfo ci) { - if (this.easeGUI$hasSavedColor) { - RenderSystem.setShaderColor(this.easeGUI$savedR, this.easeGUI$savedG, this.easeGUI$savedB, this.easeGUI$savedA); - - if (!this.easeGUI$wasBlendEnabled) { - RenderSystem.disableBlend(); - } - - this.easeGUI$hasSavedColor = false; - } - } -} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/compat/EmiCompatMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/compat/EmiCompatMixin.java index 36e3039..974b73d 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/compat/EmiCompatMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/compat/EmiCompatMixin.java @@ -1,12 +1,10 @@ package net.weyne1.easegui.client.mixin.compat; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.Screen; -import net.weyne1.easegui.client.accessor.ScreenAnimationAccessor; +import net.weyne1.easegui.client.animation.AnimationContext; import net.weyne1.easegui.client.animation.AnimationScope; +import org.spongepowered.asm.mixin.Dynamic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; -import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -19,26 +17,17 @@ @Mixin(targets = "dev.emi.emi.screen.EmiScreenManager", remap = false) public class EmiCompatMixin { - @Unique - private static AnimationScope easeGUI$getCurrentScope() { - Screen currentScreen = Minecraft.getInstance().screen; - if (currentScreen instanceof ScreenAnimationAccessor accessor) { - return accessor.easeGUI$getContainerScope(); - } - return null; - } - + @Dynamic @Inject(method = {"render", "drawBackground", "drawForeground"}, at = @At("HEAD"), require = 0) private static void easeGUI$onEmiRenderStart(CallbackInfo ci) { - AnimationScope scope = easeGUI$getCurrentScope(); - if (scope != null) { - scope.suspend(); - } + AnimationScope scope = AnimationContext.getCurrentScope(); + if (scope != null) scope.suspend(); } + @Dynamic @Inject(method = {"render", "drawBackground", "drawForeground"}, at = @At("TAIL"), require = 0) private static void easeGUI$onEmiRenderEnd(CallbackInfo ci) { - AnimationScope scope = easeGUI$getCurrentScope(); + AnimationScope scope = AnimationContext.getCurrentScope(); if (scope != null) scope.resume(); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AbstractSelectionListMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AbstractSelectionListMixin.java index b54c174..0bfac78 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AbstractSelectionListMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AbstractSelectionListMixin.java @@ -1,42 +1,44 @@ package net.weyne1.easegui.client.mixin.gui; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractSelectionList; +import net.minecraft.client.gui.layouts.LayoutElement; import net.weyne1.easegui.client.animation.AnimationContext; import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animator.ListItemAnimator; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.Coerce; @Mixin(AbstractSelectionList.class) -public class AbstractSelectionListMixin { +public abstract class AbstractSelectionListMixin { + + @Shadow public abstract int getRowLeft(); + @Shadow public abstract int getRowWidth(); + + @WrapOperation( + method = "renderListItems", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/components/AbstractSelectionList;renderItem(Lnet/minecraft/client/gui/GuiGraphics;IIFLnet/minecraft/client/gui/components/AbstractSelectionList$Entry;)V" + ) + ) + private void easeGUI$wrapRenderItem(AbstractSelectionList instance, GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, @Coerce Object item, Operation original) { + LayoutElement element = (LayoutElement) item; + int left = this.getRowLeft(); + int width = this.getRowWidth(); + int top = element.getY(); + int height = element.getHeight(); - @Unique - private AnimationScope easeGUI$itemScope = null; - - @Inject(method = "renderItem", at = @At("HEAD")) - private void easeGUI$onPreRenderItem(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, - int index, int left, int top, int width, int height, CallbackInfo ci) { AnimationContext.pushParentAnimation(); - if (this.easeGUI$itemScope != null) { - this.easeGUI$itemScope.close(); - } - - this.easeGUI$itemScope = ListItemAnimator.beginRender(guiGraphics, top, left, width, height); - } - - @Inject(method = "renderItem", at = @At("RETURN")) - private void easeGUI$onPostRenderItem(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, - int index, int left, int top, int width, int height, CallbackInfo ci) { - if (this.easeGUI$itemScope != null) { - this.easeGUI$itemScope.close(); - this.easeGUI$itemScope = null; + try (AnimationScope ignored = ListItemAnimator.beginRender(guiGraphics, top, left, width, height)) { + original.call(instance, guiGraphics, mouseX, mouseY, partialTick, item); + } finally { + AnimationContext.popParentAnimation(); } - - AnimationContext.popParentAnimation(); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AbstractWidgetMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AbstractWidgetMixin.java index 2e18aa1..32422e5 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AbstractWidgetMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AbstractWidgetMixin.java @@ -1,30 +1,28 @@ package net.weyne1.easegui.client.mixin.gui; -import net.minecraft.Util; +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import net.minecraft.util.Util; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.*; -import net.weyne1.easegui.client.EaseGUIWidget; +import net.weyne1.easegui.client.extension.WidgetExtension; import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animation.AnimationState; import net.weyne1.easegui.client.animator.WidgetAnimator; import net.weyne1.easegui.client.config.ConfigManager; -import net.weyne1.easegui.client.config.UIElementCategory; +import net.weyne1.easegui.api.WidgetCategory; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(AbstractWidget.class) -public abstract class AbstractWidgetMixin implements EaseGUIWidget { +public abstract class AbstractWidgetMixin implements WidgetExtension { @Shadow protected float alpha; @Shadow protected boolean isHovered; - @Unique private final AnimationState.AnimationData easeGUI$animationData = new AnimationState.AnimationData(); - @Unique private UIElementCategory easeGUI$cachedCategory = null; - @Unique private AnimationScope easeGUI$widgetScope = null; + @Unique private final AnimationState easeGUI$animationState = new AnimationState(); + @Unique private WidgetCategory easeGUI$cachedCategory = null; @Override public float easeGUI$getAlpha() { @@ -32,57 +30,50 @@ public abstract class AbstractWidgetMixin implements EaseGUIWidget { } @Override - public UIElementCategory easeGUI$getCategory() { + public WidgetCategory easeGUI$getCategory() { if (this.easeGUI$cachedCategory == null) { - Class clazz = this.getClass(); - if (AbstractButton.class.isAssignableFrom(clazz) || AbstractSliderButton.class.isAssignableFrom(clazz) || EditBox.class.isAssignableFrom(clazz)) { - this.easeGUI$cachedCategory = UIElementCategory.BUTTON_LIKE; - } - else if (AbstractSelectionList.class.isAssignableFrom(clazz)) { - this.easeGUI$cachedCategory = UIElementCategory.SCROLLABLE; - } - else if (StringWidget.class.isAssignableFrom(clazz) || MultiLineTextWidget.class.isAssignableFrom(clazz)) { - this.easeGUI$cachedCategory = UIElementCategory.TEXT; - } - else { - this.easeGUI$cachedCategory = UIElementCategory.UNKNOWN; - } + this.easeGUI$cachedCategory = WidgetCategory.fromClass(this.getClass()); } return this.easeGUI$cachedCategory; } - @Inject(method = "render", at = @At("HEAD")) - private void easeGUI$onPreRender(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, CallbackInfo ci) { - if (this.easeGUI$widgetScope != null) { - this.easeGUI$widgetScope.close(); - this.easeGUI$widgetScope = null; - } - + @WrapMethod(method = "render") + private void easeGUI$wrapWidgetRender(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, Operation original) { AbstractWidget widget = (AbstractWidget) (Object) this; if (!widget.visible) { + original.call(guiGraphics, mouseX, mouseY, partialTick); return; } var category = this.easeGUI$getCategory(); - if (category == null || category == UIElementCategory.UNKNOWN) return; + if (category == null || category == WidgetCategory.UNKNOWN) { + original.call(guiGraphics, mouseX, mouseY, partialTick); + return; + } - this.easeGUI$widgetScope = WidgetAnimator.beginRender(widget, guiGraphics, category, this.easeGUI$animationData); + try (AnimationScope ignored = WidgetAnimator.beginRender(widget, guiGraphics, category, this.easeGUI$animationState)) { - var profile = ConfigManager.getProfileForCurrentContext(category); - if (profile != null && profile.enabled && this.easeGUI$animationData.init) { - long elapsed = Util.getMillis() - this.easeGUI$animationData.startTime - this.easeGUI$animationData.delay; - if (elapsed < profile.duration) { - this.isHovered = false; + var profile = ConfigManager.getProfileForCurrentContext(category); + boolean shouldBypassHover = false; + + if (profile != null && profile.isEnabled() && this.easeGUI$animationState.init) { + long elapsed = Util.getMillis() - this.easeGUI$animationState.startTime - this.easeGUI$animationState.delay; + if (elapsed < profile.getDuration()) { + shouldBypassHover = true; + } } - } - } - @Inject(method = "render", at = @At("RETURN")) - private void easeGUI$onPostRender(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, CallbackInfo ci) { - if (this.easeGUI$widgetScope != null) { - this.easeGUI$widgetScope.close(); - this.easeGUI$widgetScope = null; + if (shouldBypassHover) { + boolean savedHover = this.isHovered; + this.isHovered = false; + + original.call(guiGraphics, mouseX, mouseY, partialTick); + + this.isHovered = savedHover; + } else { + original.call(guiGraphics, mouseX, mouseY, partialTick); + } } } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AdvancementsScreenMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AdvancementsScreenMixin.java index 3020069..d37df27 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AdvancementsScreenMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/AdvancementsScreenMixin.java @@ -5,6 +5,7 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.advancements.AdvancementTab; import net.minecraft.client.gui.screens.advancements.AdvancementsScreen; +import net.weyne1.easegui.client.animation.AnimationContext; import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animator.AdvancementsAnimator; import net.weyne1.easegui.client.mixin.accessor.AdvancementTabAccessor; @@ -17,20 +18,24 @@ @Mixin(AdvancementsScreen.class) public class AdvancementsScreenMixin { - @Unique - private AnimationScope easeGUI$windowScope = null; + @Unique private AnimationScope easeGUI$windowScope = null; - // === АНИМАЦИЯ ГЛАВНОГО ОКНА === + // Main window animation @Inject( method = "render", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementsScreen;renderInside(Lnet/minecraft/client/gui/GuiGraphics;IIII)V") + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementsScreen;renderInside(Lnet/minecraft/client/gui/GuiGraphics;II)V") ) private void easeGUI$preRenderWindow(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, CallbackInfo ci) { if (this.easeGUI$windowScope != null) { this.easeGUI$windowScope.close(); + AnimationContext.popParentAnimation(); } this.easeGUI$windowScope = AdvancementsAnimator.beginRenderWindow((AdvancementsScreen) (Object) this, guiGraphics); + + if (this.easeGUI$windowScope != null) { + AnimationContext.pushParentAnimation(); + } } @Inject(method = "render", at = @At("RETURN")) @@ -38,29 +43,25 @@ public class AdvancementsScreenMixin { if (this.easeGUI$windowScope != null) { this.easeGUI$windowScope.close(); this.easeGUI$windowScope = null; + AnimationContext.popParentAnimation(); } } - // === АНИМАЦИЯ ВКЛАДОК (ФОН) === + // Tab animation (bg) @WrapOperation( method = "renderWindow", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementTab;drawTab(Lnet/minecraft/client/gui/GuiGraphics;IIZ)V") + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementTab;drawTab(Lnet/minecraft/client/gui/GuiGraphics;IIIIZ)V") ) - private void easeGUI$wrapDrawTab(AdvancementTab tab, GuiGraphics guiGraphics, int offsetX, int offsetY, boolean isSelected, Operation original) { + private void easeGUI$wrapDrawTab(AdvancementTab tab, GuiGraphics guiGraphics, int x, int y, int mouseX, int mouseY, boolean selected, Operation original) { AdvancementsScreen screen = (AdvancementsScreen) (Object) this; int index = ((AdvancementTabAccessor) tab).easeGUI$getIndex(); - AnimationScope scope = AdvancementsAnimator.beginRenderTab(screen, guiGraphics, index); - if (scope != null) { - try (scope) { - original.call(tab, guiGraphics, offsetX, offsetY, isSelected); - } - } else { - original.call(tab, guiGraphics, offsetX, offsetY, isSelected); + try (AnimationScope ignored = AdvancementsAnimator.beginRenderTab(screen, guiGraphics, index)) { + original.call(tab, guiGraphics, x, y, mouseX, mouseY, selected); } } - // === АНИМАЦИЯ ВКЛАДОК (ИКОНКИ) === + // Tab animation (fg/icons) @WrapOperation( method = "renderWindow", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementTab;drawIcon(Lnet/minecraft/client/gui/GuiGraphics;II)V") @@ -69,12 +70,7 @@ public class AdvancementsScreenMixin { AdvancementsScreen screen = (AdvancementsScreen) (Object) this; int index = ((AdvancementTabAccessor) tab).easeGUI$getIndex(); - AnimationScope scope = AdvancementsAnimator.beginRenderTab(screen, guiGraphics, index); - if (scope != null) { - try (scope) { - original.call(tab, guiGraphics, offsetX, offsetY); - } - } else { + try (AnimationScope ignored = AdvancementsAnimator.beginRenderTab(screen, guiGraphics, index)) { original.call(tab, guiGraphics, offsetX, offsetY); } } diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiGraphicsMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiGraphicsMixin.java index e579264..1780077 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiGraphicsMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiGraphicsMixin.java @@ -1,53 +1,138 @@ package net.weyne1.easegui.client.mixin.gui; -import com.mojang.blaze3d.vertex.PoseStack; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.gui.render.state.pip.*; +import net.minecraft.client.model.Model; +import net.minecraft.client.model.object.banner.BannerFlagModel; +import net.minecraft.client.model.object.book.BookModel; +import net.minecraft.client.model.player.PlayerModel; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.resources.Identifier; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.block.entity.BannerPatternLayers; +import net.minecraft.world.level.block.state.properties.WoodType; import net.weyne1.easegui.client.animation.AnimationContext; -import org.joml.Matrix4f; +import net.weyne1.easegui.client.animation.PipTransform; +import net.weyne1.easegui.client.util.ColorUtils; +import org.joml.Quaternionf; +import org.joml.Vector3f; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArgs; -import org.spongepowered.asm.mixin.injection.invoke.arg.Args; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +@SuppressWarnings("ModifyVariableMayUseName") @Mixin(GuiGraphics.class) -public abstract class GuiGraphicsMixin { +public class GuiGraphicsMixin { - @Shadow public abstract PoseStack pose(); + @ModifyVariable(method = "submitColoredRectangle", at = @At("HEAD"), argsOnly = true, ordinal = 4) + private int easeGUI$modifyColorFrom(int colorFrom) { + if (!AnimationContext.isActive()) return colorFrom; + return ColorUtils.getAnimatedColor(colorFrom); + } + + @ModifyVariable(method = "submitColoredRectangle", at = @At("HEAD"), argsOnly = true, ordinal = 0) + private Integer easeGUI$modifyColorTo(Integer colorTo) { + if (!AnimationContext.isActive() || colorTo == null) return colorTo; + return ColorUtils.getAnimatedColor(colorTo); + } + + @ModifyVariable(method = "submitBlit", at = @At("HEAD"), argsOnly = true, ordinal = 4) + private int easeGUI$modifyBlitColor(int color) { + if (!AnimationContext.isActive()) return color; + return ColorUtils.getAnimatedColor(color); + } + + @ModifyVariable(method = "submitTiledBlit", at = @At("HEAD"), argsOnly = true, ordinal = 6) + private int easeGUI$modifyTiledBlitColor(int color) { + if (!AnimationContext.isActive()) return color; + return ColorUtils.getAnimatedColor(color); + } - @ModifyArgs( - method = "enableScissor", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/navigation/ScreenRectangle;(IIII)V") + @WrapOperation( + method = "submitEntityRenderState", + at = @At(value = "NEW", target = "Lnet/minecraft/client/gui/render/state/pip/GuiEntityRenderState;") ) - private void easeGUI$transformScissorBounds(Args args) { - if (!AnimationContext.isAnimating()) { - return; - } - - int minX = args.get(0); - int minY = args.get(1); - int width = args.get(2); - int height = args.get(3); - int maxX = minX + width; - int maxY = minY + height; - - Matrix4f matrix = this.pose().last().pose(); - float sx = matrix.m00(); - float sy = matrix.m11(); - float tx = matrix.m30(); - float ty = matrix.m31(); - - int newMinX = Math.round(minX * sx + tx); - int newMinY = Math.round(minY * sy + ty); - int newMaxX = Math.round(maxX * sx + tx); - int newMaxY = Math.round(maxY * sy + ty); - - int newWidth = newMaxX - newMinX; - int newHeight = newMaxY - newMinY; - - args.set(0, newMinX); - args.set(1, newMinY); - args.set(2, Math.max(0, newWidth)); - args.set(3, Math.max(0, newHeight)); + private GuiEntityRenderState easeGUI$transformEntity( + EntityRenderState renderState, Vector3f translation, Quaternionf rotation, Quaternionf overrideCameraAngle, + int x0, int y0, int x1, int y1, float scale, ScreenRectangle scissorArea, + Operation original) { + + int[] xs = PipTransform.transformRangeX(x0, x1); + int[] ys = PipTransform.transformRangeY(y0, y1); + + return original.call( + renderState, translation, rotation, overrideCameraAngle, + xs[0], ys[0], xs[1], ys[1], PipTransform.scale(scale), scissorArea + ); + } + + @WrapOperation( + method = "submitBookModelRenderState", + at = @At(value = "NEW", target = "Lnet/minecraft/client/gui/render/state/pip/GuiBookModelRenderState;") + ) + private GuiBookModelRenderState easeGUI$transformBook( + BookModel bookModel, Identifier texture, float _open, float flip, + int x0, int y0, int x1, int y1, float scale, ScreenRectangle scissorArea, Operation original) { + + int[] xs = PipTransform.transformRangeX(x0, x1); + int[] ys = PipTransform.transformRangeY(y0, y1); + + return original.call( + bookModel, texture, _open, flip, + xs[0], ys[0], xs[1], ys[1], PipTransform.scale(scale), scissorArea + ); + } + + @WrapOperation( + method = "submitSkinRenderState", + at = @At(value = "NEW", target = "Lnet/minecraft/client/gui/render/state/pip/GuiSkinRenderState;") + ) + private GuiSkinRenderState easeGUI$transformSkin( + PlayerModel playerModel, Identifier texture, float rotationX, float rotationY, float pivotY, + int x0, int y0, int x1, int y1, float scale, ScreenRectangle scissorArea, Operation original) { + + int[] xs = PipTransform.transformRangeX(x0, x1); + int[] ys = PipTransform.transformRangeY(y0, y1); + + return original.call( + playerModel, texture, rotationY, pivotY, + (float) xs[0], ys[0], xs[1], ys[1], Math.round(PipTransform.scale(scale)), rotationX, scissorArea + ); + } + + @WrapOperation( + method = "submitBannerPatternRenderState", + at = @At(value = "NEW", target = "Lnet/minecraft/client/gui/render/state/pip/GuiBannerResultRenderState;") + ) + private GuiBannerResultRenderState easeGUI$transformBanner( + BannerFlagModel flag, DyeColor baseColor, BannerPatternLayers resultBannerPatterns, + int x0, int y0, int x1, int y1, ScreenRectangle scissorArea, + Operation original) { + + int[] xs = PipTransform.transformRangeX(x0, x1); + int[] ys = PipTransform.transformRangeY(y0, y1); + + return original.call(flag, baseColor, resultBannerPatterns, xs[0], ys[0], xs[1], ys[1], scissorArea); + } + + @WrapOperation( + method = "submitSignRenderState", + at = @At(value = "NEW", target = "Lnet/minecraft/client/gui/render/state/pip/GuiSignRenderState;") + ) + private GuiSignRenderState easeGUI$transformSign( + Model.Simple signModel, WoodType woodType, int x0, int y0, int x1, int y1, float scale, ScreenRectangle scissorArea, + Operation original) { + + int[] xs = PipTransform.transformRangeX(x0, x1); + int[] ys = PipTransform.transformRangeY(y0, y1); + + return original.call( + signModel, woodType, + xs[0], ys[0], xs[1], ys[1], + PipTransform.scale(scale), scissorArea + ); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiItemRenderStateMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiItemRenderStateMixin.java new file mode 100644 index 0000000..3447d8f --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiItemRenderStateMixin.java @@ -0,0 +1,28 @@ +package net.weyne1.easegui.client.mixin.gui; + +import net.minecraft.client.gui.render.state.GuiItemRenderState; +import net.weyne1.easegui.client.animation.AnimationContext; +import net.weyne1.easegui.client.extension.ItemExtension; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GuiItemRenderState.class) +public class GuiItemRenderStateMixin implements ItemExtension { + @Unique + private float easegui$alpha = 1.0f; + + @Inject(method = "", at = @At("RETURN")) + private void easeGUI$captureAlphaOnCreation(CallbackInfo ci) { + if (AnimationContext.isActive()) { + this.easegui$alpha = AnimationContext.getCurrentAlpha(); + } + } + + @Override + public float easegui$getAlpha() { + return this.easegui$alpha; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiRendererMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiRendererMixin.java new file mode 100644 index 0000000..abe81df --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiRendererMixin.java @@ -0,0 +1,42 @@ +package net.weyne1.easegui.client.mixin.gui; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.gui.render.state.BlitRenderState; +import net.minecraft.client.gui.render.state.GuiItemRenderState; +import net.minecraft.client.gui.render.GuiRenderer; +import net.weyne1.easegui.client.extension.ItemExtension; +import org.joml.Matrix3x2f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(GuiRenderer.class) +public class GuiRendererMixin { + + @SuppressWarnings("NameDoesntMatchTargetClass") + @WrapOperation( + method = "submitBlitFromItemAtlas", + at = @At(value = "NEW", target = "Lnet/minecraft/client/gui/render/state/BlitRenderState;") + ) + private BlitRenderState easeGUI$applyStoredAlphaToItems( + RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, + int x0, int y0, int x1, int y1, float u0, float u1, float v0, float v1, int color, + ScreenRectangle scissorArea, ScreenRectangle bounds, + Operation original, + GuiItemRenderState renderState, float x, float y, int itemSize, int atlasSize) { + float alpha = ((ItemExtension) (Object) renderState).easegui$getAlpha(); + + if (alpha >= 1.0f) { + return original.call(pipeline, textureSetup, pose, x0, y0, x1, y1, u0, u1, v0, v1, color, scissorArea, bounds); + } + + int a = (int) (alpha * 255) & 0xFF; + + int premultipliedColor = (a << 24) | (a << 16) | (a << 8) | a; + + return original.call(pipeline, textureSetup, pose, x0, y0, x1, y1, u0, u1, v0, v1, premultipliedColor, scissorArea, bounds); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiTextRenderStateMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiTextRenderStateMixin.java new file mode 100644 index 0000000..00a091a --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/GuiTextRenderStateMixin.java @@ -0,0 +1,19 @@ +package net.weyne1.easegui.client.mixin.gui; + +import net.minecraft.client.gui.render.state.GuiTextRenderState; +import net.weyne1.easegui.client.animation.AnimationContext; +import net.weyne1.easegui.client.util.ColorUtils; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@SuppressWarnings("ModifyVariableMayUseName") +@Mixin(GuiTextRenderState.class) +public class GuiTextRenderStateMixin { + + @ModifyVariable(method = "", at = @At("HEAD"), argsOnly = true, ordinal = 2) + private static int easeGUI$modifyTextStateColor(int color) { + if (!AnimationContext.isActive()) return color; + return ColorUtils.getAnimatedColor(color); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/ScreenLifecycleMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/ScreenLifecycleMixin.java index d86f766..59b7dbf 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/ScreenLifecycleMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/ScreenLifecycleMixin.java @@ -1,6 +1,5 @@ package net.weyne1.easegui.client.mixin.gui; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.weyne1.easegui.client.state.ScreenStateTracker; import org.spongepowered.asm.mixin.Mixin; @@ -9,10 +8,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(Screen.class) - public class ScreenLifecycleMixin { - @Inject(method = "init(Lnet/minecraft/client/Minecraft;II)V", at = @At("HEAD")) - private void easeGUI$onScreenInit(Minecraft minecraft, int width, int height, CallbackInfo ci) { + + @Inject(method = "added()V", at = @At("HEAD")) + private void easeGUI$onScreenAdded(CallbackInfo ci) { Screen currentScreen = (Screen) (Object) this; if (ScreenStateTracker.checkAndTrackNewScreen(currentScreen)) { ScreenStateTracker.markScreenOpened(); diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/ScreenMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/ScreenMixin.java index d287e0a..db9d04e 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/ScreenMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/ScreenMixin.java @@ -1,173 +1,98 @@ package net.weyne1.easegui.client.mixin.gui; +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; -import net.weyne1.easegui.client.accessor.ContainerScreenAccessor; -import net.weyne1.easegui.client.accessor.ScreenAnimationAccessor; +import net.weyne1.easegui.client.extension.ContainerScreenExtension; +import net.weyne1.easegui.client.animation.AnimationContext; import net.weyne1.easegui.client.animation.AnimationScope; import net.weyne1.easegui.client.animator.BackgroundAnimator; import net.weyne1.easegui.client.animator.ContainerAnimator; import net.weyne1.easegui.client.config.ConfigManager; -import net.weyne1.easegui.client.config.EaseGUIConfig; -import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -/** - * Injects custom animation behavior into the global Screen rendering pipeline. - * Manages container transition lifecycles, menu textures, and background dimming. - */ +import org.spongepowered.asm.mixin.injection.ModifyArgs; +import org.spongepowered.asm.mixin.injection.invoke.arg.Args; + @Mixin(Screen.class) -public abstract class ScreenMixin implements ScreenAnimationAccessor { +@SuppressWarnings("ConstantConditions") +public abstract class ScreenMixin { - @Unique private AnimationScope easeGUI$containerScreenScope = null; - @Unique private AnimationScope easeGUI$menuBackgroundScope = null; + @Final @Shadow protected Minecraft minecraft; + @Shadow protected abstract void renderBlurredBackground(GuiGraphics guiGraphics); - @Shadow @Nullable protected Minecraft minecraft; - @Shadow protected abstract void renderBlurredBackground(float partialTick); + // Container lifecycle - @Override - @Unique - public AnimationScope easeGUI$getContainerScope() { - return this.easeGUI$containerScreenScope; - } + @WrapMethod(method = "renderWithTooltipAndSubtitles") + private void easeGUI$wrapScreenRender(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, Operation original) { + if (RenderSystem.isOnRenderThread() && this instanceof ContainerScreenExtension) { - @Unique - private boolean easeGUI$isWorldLoadingScreen() { - return BackgroundAnimator.isLoadingScreen((Screen) (Object) this); - } + try (AnimationScope ignored = ContainerAnimator.beginAnimation((Screen) (Object) this, guiGraphics)) { + AnimationContext.pushParentAnimation(); - // CONTAINER SCREEN ANIMATION LIFECYCLE - - /** - * Initializes the container animation scope at the absolute beginning of the screen - * rendering pipeline, ensuring both widgets and late-rendered tooltips are captured. - */ - @Inject(method = "renderWithTooltip", at = @At("HEAD")) - private void easeGUI$beforeScreenRenderWithTooltip(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, CallbackInfo ci) { - if (RenderSystem.isOnRenderThread() && this instanceof ContainerScreenAccessor) { - if (this.easeGUI$containerScreenScope != null) { - ContainerAnimator.closeScope(this.easeGUI$containerScreenScope); - } + original.call(guiGraphics, mouseX, mouseY, partialTick); - this.easeGUI$containerScreenScope = ContainerAnimator.beginScreenAnimation((Screen) (Object) this, guiGraphics); - } - } + AnimationContext.popParentAnimation(); + } - /** - * Safely collapses and closes the container animation scope at the end of the rendering - * pipeline to prevent matrix stack underflows. - */ - @Inject(method = "renderWithTooltip", at = @At("RETURN")) - private void easeGUI$afterScreenRenderWithTooltip(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick, CallbackInfo ci) { - if (RenderSystem.isOnRenderThread() && this.easeGUI$containerScreenScope != null) { - ContainerAnimator.closeScope(this.easeGUI$containerScreenScope); - this.easeGUI$containerScreenScope = null; + } else { + original.call(guiGraphics, mouseX, mouseY, partialTick); } } - // BACKGROUND RENDERING ISOLATION (SUSPEND / RESUME) + // Transparent background (blur / dim) - /** - * Suspends active container scaling/translation right before the transparent background - * gradient is drawn, keeping the background tint absolute and static. - */ - @Inject(method = "renderTransparentBackground", at = @At("HEAD")) - private void easeGUI$suspendBeforeTransparentBackground(GuiGraphics guiGraphics, CallbackInfo ci) { - if (this.easeGUI$containerScreenScope != null) { - this.easeGUI$containerScreenScope.suspend(); - } + @WrapMethod(method = "renderTransparentBackground") + private void easeGUI$wrapTransparentBackground(GuiGraphics guiGraphics, Operation original) { + AnimationScope currentScope = AnimationContext.getCurrentScope(); + if (currentScope != null) currentScope.suspend(); - boolean blurContainers = ConfigManager.getConfig().global.blurContainers; + try { + Screen currentScreen = (Screen) (Object) this; + boolean blurContainers = ConfigManager.getConfig().global.blurContainers; - if (this.minecraft != null && this.minecraft.level != null && blurContainers) { - float partialTick = this.minecraft.getTimer().getGameTimeDeltaTicks(); - this.renderBlurredBackground(partialTick); - } - } + if (this.minecraft != null && this.minecraft.level != null && blurContainers && BackgroundAnimator.shouldAnimateBackground(currentScreen)) { + this.renderBlurredBackground(guiGraphics); + } - /** - * Resumes the container animation transformations immediately after the transparent - * background gradient has finished rendering. - */ - @Inject(method = "renderTransparentBackground", at = @At("RETURN")) - private void easeGUI$resumeAfterTransparentBackground(GuiGraphics guiGraphics, CallbackInfo ci) { - if (this.easeGUI$containerScreenScope != null) { - this.easeGUI$containerScreenScope.resume(); + original.call(guiGraphics); + } finally { + if (currentScope != null) currentScope.resume(); } } - /** - * Isolates the main menu background rendering from container transformations and - * handles standalone main-menu background animations. - */ - @Inject(method = "renderMenuBackground(Lnet/minecraft/client/gui/GuiGraphics;)V", at = @At("HEAD")) - private void easeGUI$preRenderMenuBackground(GuiGraphics guiGraphics, CallbackInfo ci) { - if (this.easeGUI$containerScreenScope != null) { - this.easeGUI$containerScreenScope.suspend(); - } + // Menu background - if (this.easeGUI$menuBackgroundScope != null) { - this.easeGUI$menuBackgroundScope.close(); - } + @WrapMethod(method = "renderMenuBackground(Lnet/minecraft/client/gui/GuiGraphics;)V") + private void easeGUI$wrapMenuBackground(GuiGraphics guiGraphics, Operation original) { + AnimationScope currentScope = AnimationContext.getCurrentScope(); + if (currentScope != null) currentScope.suspend(); - if (!easeGUI$isWorldLoadingScreen() && BackgroundAnimator.shouldAnimate()) { - this.easeGUI$menuBackgroundScope = BackgroundAnimator.beginRenderMenu(guiGraphics); + try (AnimationScope ignored = BackgroundAnimator.beginRenderMenu((Screen) (Object) this, guiGraphics)) { + original.call(guiGraphics); + } finally { + if (currentScope != null) currentScope.resume(); } } - /** - * Cleans up menu background scope and restores the container animation transforms - * for subsequent interface elements. - */ - @Inject(method = "renderMenuBackground(Lnet/minecraft/client/gui/GuiGraphics;)V", at = @At("RETURN")) - private void easeGUI$postRenderMenuBackground(GuiGraphics guiGraphics, CallbackInfo ci) { - if (this.easeGUI$menuBackgroundScope != null) { - this.easeGUI$menuBackgroundScope.close(); - this.easeGUI$menuBackgroundScope = null; - } - - if (this.easeGUI$containerScreenScope != null) { - this.easeGUI$containerScreenScope.resume(); - } - } + // Gradient color - // BACKGROUND GRADIENT COLOR MODIFICATIONS - - /** - * Applies animated alpha to the top color of the Screen background gradient. - */ - @ModifyArg( + @ModifyArgs( method = "renderTransparentBackground(Lnet/minecraft/client/gui/GuiGraphics;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;fillGradient(IIIIII)V"), - index = 4 + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;fillGradient(IIIIII)V") ) - private int easeGUI$modifyTransparentBgTopColor(int originalColor) { - if (easeGUI$isWorldLoadingScreen()) { - return originalColor; + private void easeGUI$modifyTransparentBgColors(Args args) { + Screen currentScreen = (Screen) (Object) this; + if (!BackgroundAnimator.shouldAnimateBackground(currentScreen)) { + return; } - return BackgroundAnimator.getAnimatedColor(originalColor); - } - /** - * Applies animated alpha to the bottom color of the Screen background gradient. - */ - @ModifyArg( - method = "renderTransparentBackground(Lnet/minecraft/client/gui/GuiGraphics;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;fillGradient(IIIIII)V"), - index = 5 - ) - private int easeGUI$modifyTransparentBgBottomColor(int originalColor) { - if (easeGUI$isWorldLoadingScreen()) { - return originalColor; - } - return BackgroundAnimator.getAnimatedColor(originalColor); + args.set(4, BackgroundAnimator.getAnimatedColor(currentScreen, args.get(4))); + args.set(5, BackgroundAnimator.getAnimatedColor(currentScreen, args.get(5))); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/AbstractContainerScreenMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/AbstractContainerScreenMixin.java index 2844c11..74ecd22 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/AbstractContainerScreenMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/AbstractContainerScreenMixin.java @@ -3,12 +3,12 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.network.chat.Component; -import net.weyne1.easegui.client.accessor.ContainerScreenAccessor; +import net.weyne1.easegui.client.extension.ContainerScreenExtension; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @Mixin(AbstractContainerScreen.class) -public abstract class AbstractContainerScreenMixin extends Screen implements ContainerScreenAccessor { +public abstract class AbstractContainerScreenMixin extends Screen implements ContainerScreenExtension { @Shadow protected int leftPos; @Shadow protected int topPos; diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/AbstractRecipeBookScreenMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/AbstractRecipeBookScreenMixin.java new file mode 100644 index 0000000..b58fcc0 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/AbstractRecipeBookScreenMixin.java @@ -0,0 +1,20 @@ +package net.weyne1.easegui.client.mixin.gui.container; + +import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen; +import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; +import net.weyne1.easegui.client.extension.RecipeBookScreenExtension; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(AbstractRecipeBookScreen.class) +public abstract class AbstractRecipeBookScreenMixin implements RecipeBookScreenExtension { + + @Final + @Shadow private RecipeBookComponent recipeBookComponent; + + @Override + public RecipeBookComponent easeGUI$getRecipeBookComponent() { + return this.recipeBookComponent; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/RecipeBookComponentMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/RecipeBookComponentMixin.java index 66fe930..3090bfe 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/RecipeBookComponentMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/container/RecipeBookComponentMixin.java @@ -1,29 +1,20 @@ package net.weyne1.easegui.client.mixin.gui.container; import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; -import net.weyne1.easegui.client.accessor.RecipeBookAccessor; +import net.weyne1.easegui.client.extension.RecipeBookComponentExtension; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(RecipeBookComponent.class) -public abstract class RecipeBookComponentMixin implements RecipeBookAccessor { - @Shadow private boolean visible; - @Shadow private int xOffset; +public interface RecipeBookComponentMixin extends RecipeBookComponentExtension { - @Accessor("IMAGE_WIDTH") - public static int easeGUI$getVanillaWidth() { - throw new AssertionError(); - } + @Accessor("visible") + boolean easeGUI$isVisible(); - @Accessor("IMAGE_HEIGHT") - public static int easeGUI$getVanillaHeight() { - throw new AssertionError(); - } + @Invoker("getXOrigin") + int easeGUI$getXOrigin(); - @Override public boolean easeGUI$isVisible() { return this.visible; } - @Override public int easeGUI$getXOffset() { return this.xOffset; } - - @Override public int easeGUI$getBookWidth() { return easeGUI$getVanillaWidth(); } - @Override public int easeGUI$getBookHeight() { return easeGUI$getVanillaHeight(); } + @Invoker("getYOrigin") + int easeGUI$getYOrigin(); } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/title/SplashRendererMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/title/SplashRendererMixin.java index 935cc09..a91ee6e 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/title/SplashRendererMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/title/SplashRendererMixin.java @@ -2,10 +2,10 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.ActiveTextCollector; +import net.minecraft.client.gui.TextAlignment; import net.minecraft.client.gui.components.SplashRenderer; -import net.weyne1.easegui.client.animation.AnimationScope; +import net.minecraft.network.chat.Component; import net.weyne1.easegui.client.animator.SplashAnimator; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -15,17 +15,24 @@ public class SplashRendererMixin { @WrapOperation( method = "render", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;drawCenteredString(Lnet/minecraft/client/gui/Font;Ljava/lang/String;III)V") + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/ActiveTextCollector;accept(Lnet/minecraft/client/gui/TextAlignment;IILnet/minecraft/client/gui/ActiveTextCollector$Parameters;Lnet/minecraft/network/chat/Component;)V" + ) ) - private void easeGUI$wrapSplash(GuiGraphics gg, Font font, String text, int x, int y, int color, Operation original) { - AnimationScope scope = SplashAnimator.beginRender(gg, color); + private void easeGUI$animateSplash( + ActiveTextCollector collector, + TextAlignment textAlignment, + int x, + int y, + ActiveTextCollector.Parameters parameters, + Component text, + Operation original + ) { + ActiveTextCollector.Parameters animatedParams = SplashAnimator.getAnimatedParameters(parameters); - if (scope != null) { - try (scope) { - original.call(gg, font, text, x, y, color); - } - } else { - original.call(gg, font, text, x, y, color); + if (animatedParams != null) { + original.call(collector, textAlignment, x, y, animatedParams, text); } } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/title/TitleScreenMixin.java b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/title/TitleScreenMixin.java index 15ab170..0e88ea2 100644 --- a/common/src/main/java/net/weyne1/easegui/client/mixin/gui/title/TitleScreenMixin.java +++ b/common/src/main/java/net/weyne1/easegui/client/mixin/gui/title/TitleScreenMixin.java @@ -1,11 +1,13 @@ package net.weyne1.easegui.client.mixin.gui.title; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.LogoRenderer; import net.minecraft.client.gui.screens.TitleScreen; import net.weyne1.easegui.client.config.ConfigManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(TitleScreen.class) public class TitleScreenMixin { @@ -15,14 +17,38 @@ public class TitleScreenMixin { * This allows EaseGUI to fully control widget fade-in animation, * while preserving vanilla transitions if the screen animation is disabled. */ - @Inject(method = "fadeWidgets", at = @At("HEAD"), cancellable = true) - private void easeGUI$cancelFade(float alpha, CallbackInfo ci) { + @WrapOperation( + method = "render", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/screens/TitleScreen;fadeWidgets(F)V" + ) + ) + private void easeGUI$conditionallyFadeWidgets(TitleScreen instance, float v, Operation original) { var titleSettings = ConfigManager.getConfig().screens.get("title"); - if (titleSettings == null || !titleSettings.enabled) { + if (titleSettings != null && titleSettings.enabled) { return; } - ci.cancel(); + original.call(instance, v); + } + + @WrapOperation( + method = "render", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/components/LogoRenderer;renderLogo(Lnet/minecraft/client/gui/GuiGraphics;IF)V" + ) + ) + private void easeGUI$conditionallyFadeLogo(LogoRenderer instance, GuiGraphics guiGraphics, int screenWidth, float transparency, Operation original) { + var titleSettings = ConfigManager.getConfig().screens.get("title"); + + if (titleSettings != null && titleSettings.enabled) { + original.call(instance, guiGraphics, screenWidth, 1.0F); + return; + } + + original.call(instance, guiGraphics, screenWidth, transparency); } } \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/state/ScreenStateTracker.java b/common/src/main/java/net/weyne1/easegui/client/state/ScreenStateTracker.java index 5e6f310..cf3c553 100644 --- a/common/src/main/java/net/weyne1/easegui/client/state/ScreenStateTracker.java +++ b/common/src/main/java/net/weyne1/easegui/client/state/ScreenStateTracker.java @@ -1,12 +1,14 @@ package net.weyne1.easegui.client.state; -import net.minecraft.Util; +import net.minecraft.util.Util; import net.minecraft.client.gui.screens.Screen; import java.lang.ref.WeakReference; public class ScreenStateTracker { private static long screenOpenTime = -1; + private static long lastTrackedSessionTime = -1L; + private static long titleActualStartTime = -1L; private static int currentFrameId = 0; private static int resizeGraceFrames = 0; private static int lastWidth = -1; @@ -51,6 +53,17 @@ public static void incrementFrame() { } } + public static long getTitleActualStartTime() { + long openTime = getScreenOpenTime(); + + if (lastTrackedSessionTime != openTime) { + lastTrackedSessionTime = openTime; + titleActualStartTime = Util.getMillis(); + } + + return titleActualStartTime; + } + public static long getScreenOpenTime() { if (screenOpenTime == -1) { screenOpenTime = Util.getMillis(); diff --git a/common/src/main/java/net/weyne1/easegui/client/util/ColorUtils.java b/common/src/main/java/net/weyne1/easegui/client/util/ColorUtils.java new file mode 100644 index 0000000..7ebd546 --- /dev/null +++ b/common/src/main/java/net/weyne1/easegui/client/util/ColorUtils.java @@ -0,0 +1,21 @@ +package net.weyne1.easegui.client.util; + +import net.weyne1.easegui.client.animation.AnimationContext; + +public final class ColorUtils { + + private ColorUtils() {} + + public static int getAnimatedColor(int originalColor) { + int originalAlpha = (originalColor >> 24) & 0xFF; + + if (originalAlpha == 0 && (originalColor & 0x00FFFFFF) != 0) { + originalAlpha = 255; + } + + float animationAlpha = AnimationContext.getCurrentAlpha(); + int finalAlpha = Math.round(originalAlpha * animationAlpha); + + return (originalColor & 0x00FFFFFF) | (finalAlpha << 24); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/weyne1/easegui/client/StringUtils.java b/common/src/main/java/net/weyne1/easegui/client/util/StringUtils.java similarity index 94% rename from common/src/main/java/net/weyne1/easegui/client/StringUtils.java rename to common/src/main/java/net/weyne1/easegui/client/util/StringUtils.java index 56d4c2b..70c46ef 100644 --- a/common/src/main/java/net/weyne1/easegui/client/StringUtils.java +++ b/common/src/main/java/net/weyne1/easegui/client/util/StringUtils.java @@ -1,4 +1,4 @@ -package net.weyne1.easegui.client; +package net.weyne1.easegui.client.util; public class StringUtils { public static String toTitleCase(Enum e) { diff --git a/common/src/main/resources/easegui.mixins.json b/common/src/main/resources/easegui.mixins.json index c0c4949..92ed8db 100644 --- a/common/src/main/resources/easegui.mixins.json +++ b/common/src/main/resources/easegui.mixins.json @@ -6,16 +6,20 @@ "client": [ "GameRendererMixin", "MinecraftMixin", + "OptionsMixin", "accessor.AdvancementTabAccessor", "accessor.LogoRendererAccessor", - "blaze3d.VertexBufferMixin", "gui.AbstractSelectionListMixin", "gui.AbstractWidgetMixin", "gui.AdvancementsScreenMixin", "gui.GuiGraphicsMixin", + "gui.GuiItemRenderStateMixin", + "gui.GuiRendererMixin", + "gui.GuiTextRenderStateMixin", "gui.ScreenLifecycleMixin", "gui.ScreenMixin", "gui.container.AbstractContainerScreenMixin", + "gui.container.AbstractRecipeBookScreenMixin", "gui.container.RecipeBookComponentMixin", "gui.title.LogoRendererMixin", "gui.title.SplashRendererMixin", diff --git a/common/src/test/java/net/weyne1/easegui/client/animation/AnimationMathUtilsTest.java b/common/src/test/java/net/weyne1/easegui/client/animation/AnimationMathUtilsTest.java index 755c262..4743827 100644 --- a/common/src/test/java/net/weyne1/easegui/client/animation/AnimationMathUtilsTest.java +++ b/common/src/test/java/net/weyne1/easegui/client/animation/AnimationMathUtilsTest.java @@ -1,6 +1,6 @@ package net.weyne1.easegui.client.animation; -import net.weyne1.easegui.client.animation.AnimationProfile.PivotPoint; +import net.weyne1.easegui.api.animation.EasingType; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -43,19 +43,19 @@ void testCalculateCurrentOffset() { "500, 1000, 0.5f" // В середине }) void testAnimationProgress(long timePassed, long duration, float expectedProgress) { - float result = AnimationMath.calculateProgress(timePassed, duration, AnimationProfile.EasingType.LINEAR); + float result = AnimationMath.calculateProgress(timePassed, duration, EasingType.LINEAR); assertEquals(expectedProgress, result, 0.0001f); } @ParameterizedTest - @EnumSource(AnimationProfile.EasingType.class) - void allEasingsReturnZeroAtStart(AnimationProfile.EasingType easing) { + @EnumSource(EasingType.class) + void allEasingsReturnZeroAtStart(EasingType easing) { assertEquals(0.0f, easing.ease(0.0f), 0.0001f); } @ParameterizedTest - @EnumSource(AnimationProfile.EasingType.class) - void allEasingsReturnOneAtEnd(AnimationProfile.EasingType easing) { + @EnumSource(EasingType.class) + void allEasingsReturnOneAtEnd(EasingType easing) { assertEquals(1.0f, easing.ease(1.0f), 0.0001f); } } \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle index 06ad8cc..2fea682 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -29,8 +29,6 @@ dependencies { modCompileOnly "com.terraformersmc:modmenu:${rootProject.mod_menu_version}" modLocalRuntime "com.terraformersmc:modmenu:${rootProject.mod_menu_version}" - modLocalRuntime "dev.emi:emi-fabric:${rootProject.emi_version}" - common(project(path: ':common', configuration: 'namedElements')) { transitive = false } shadowBundle project(path: ':common', configuration: 'transformProductionFabric') } diff --git a/fabric/src/main/java/net/weyne1/easegui/neoforge/client/EaseGUIFabricClient.java b/fabric/src/main/java/net/weyne1/easegui/fabric/client/EaseGUIFabricClient.java similarity index 84% rename from fabric/src/main/java/net/weyne1/easegui/neoforge/client/EaseGUIFabricClient.java rename to fabric/src/main/java/net/weyne1/easegui/fabric/client/EaseGUIFabricClient.java index 66cd58b..5ba5b4a 100644 --- a/fabric/src/main/java/net/weyne1/easegui/neoforge/client/EaseGUIFabricClient.java +++ b/fabric/src/main/java/net/weyne1/easegui/fabric/client/EaseGUIFabricClient.java @@ -1,4 +1,4 @@ -package net.weyne1.easegui.neoforge.client; +package net.weyne1.easegui.fabric.client; import net.fabricmc.api.ClientModInitializer; import net.weyne1.easegui.client.EaseGUIClient; diff --git a/fabric/src/main/java/net/weyne1/easegui/neoforge/client/ModMenuIntegration.java b/fabric/src/main/java/net/weyne1/easegui/fabric/client/ModMenuIntegration.java similarity index 88% rename from fabric/src/main/java/net/weyne1/easegui/neoforge/client/ModMenuIntegration.java rename to fabric/src/main/java/net/weyne1/easegui/fabric/client/ModMenuIntegration.java index 1efaf69..93664e0 100644 --- a/fabric/src/main/java/net/weyne1/easegui/neoforge/client/ModMenuIntegration.java +++ b/fabric/src/main/java/net/weyne1/easegui/fabric/client/ModMenuIntegration.java @@ -1,4 +1,4 @@ -package net.weyne1.easegui.neoforge.client; +package net.weyne1.easegui.fabric.client; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 85427c1..09fb815 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -17,10 +17,10 @@ "environment": "client", "entrypoints": { "client": [ - "net.weyne1.easegui.neoforge.client.EaseGUIFabricClient" + "net.weyne1.easegui.fabric.client.EaseGUIFabricClient" ], "modmenu": [ - "net.weyne1.easegui.neoforge.client.ModMenuIntegration" + "net.weyne1.easegui.fabric.client.ModMenuIntegration" ] }, "mixins": [ diff --git a/gradle.properties b/gradle.properties index 257d044..b64e679 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,21 +6,20 @@ org.gradle.parallel=true org.gradle.configuration-cache=false # Minecraft properties -minecraft_version=1.21.1 +minecraft_version=1.21.11 # Mod Properties -mod_version=0.3.0 +mod_version=0.4.0 maven_group=net.weyne1.easegui archives_name=easegui enabled_platforms=fabric,neoforge # Mappings version. Find versions here: https://parchmentmc.org/docs/getting-started -parchment_version=2024.11.17 +parchment_version=2025.12.20 # Dependencies -neoforge_version=21.1.65 +neoforge_version=21.11.0-beta fabric_loader_version=0.18.4 -fabric_api_version=0.105.0+1.21.1 -mod_menu_version=11.0.4 -emi_version=1.1.12+1.21 +fabric_api_version=0.139.4+1.21.11 +mod_menu_version=17.0.0-alpha.1 junit_version=5.13.4 \ No newline at end of file diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 3430b6b..4f90a20 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -25,8 +25,6 @@ configurations { dependencies { neoForge "net.neoforged:neoforge:$rootProject.neoforge_version" - localRuntime "dev.emi:emi-neoforge:${rootProject.emi_version}" - common(project(path: ':common', configuration: 'namedElements')) { transitive = false } shadowBundle project(path: ':common', configuration: 'transformProductionNeoForge') }