diff --git a/src/main/java/de/fullrisk/citrusDev/MyScreen.java b/src/main/java/de/fullrisk/citrusDev/MyScreen.java deleted file mode 100644 index f607689..0000000 --- a/src/main/java/de/fullrisk/citrusDev/MyScreen.java +++ /dev/null @@ -1,82 +0,0 @@ -package de.fullrisk.citrusDev; - -import io.wispforest.owo.ui.base.BaseOwoScreen; -import io.wispforest.owo.ui.component.UIComponents; -import io.wispforest.owo.ui.container.FlowLayout; -import io.wispforest.owo.ui.container.UIContainers; -import io.wispforest.owo.ui.core.*; -import net.minecraft.client.Minecraft; -import net.minecraft.client.resources.sounds.SimpleSoundInstance; -import net.minecraft.network.chat.Component; -import net.minecraft.sounds.SoundEvents; -import de.fullrisk.citrusDev.ScalableLabel; -import de.fullrisk.citrusDev.ScalableButton; -import de.fullrisk.citrusDev.ScaleValue; - -public class MyScreen extends BaseOwoScreen { - - public MyScreen() { - super(Component.literal("Citrus Dev")); - Minecraft.getInstance().getSoundManager().play( - SimpleSoundInstance.forUI(SoundEvents.UI_TOAST_IN, 1.5F, 2) - ); - } - - @Override - protected OwoUIAdapter createAdapter() { - return OwoUIAdapter.create(this, UIContainers::verticalFlow); - } - - @Override - protected void build(FlowLayout rootComponent) { - rootComponent - .horizontalAlignment(HorizontalAlignment.CENTER) - .verticalAlignment(VerticalAlignment.CENTER); - - FlowLayout panel = UIContainers.verticalFlow(Sizing.content(), Sizing.content()); - panel.gap(6); - panel.padding(Insets.of(12)); - - panel.horizontalAlignment(HorizontalAlignment.CENTER); - panel.verticalAlignment(VerticalAlignment.CENTER); - panel.horizontalSizing(Sizing.fixed(0)); - panel.verticalSizing(Sizing.fixed(0)); - panel.horizontalSizing().animate(300, Easing.CUBIC, Sizing.fixed(250)).forwards(); - panel.verticalSizing().animate(300, Easing.CUBIC, Sizing.fixed(150)).forwards(); - - var header = new ScalableLabel(Component.literal("Citrus Dev")); - panel.child(header); - - var header_button = new ScalableButton(Component.literal("Click me"), btn -> { - btn.setMessage(Component.literal("Clicked!")); - Minecraft.getInstance().getSoundManager().play( - SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 2.0F, 2) - ); - }); - - header_button.scale().animate(300, Easing.CUBIC, new ScaleValue(1f)).forwards(); - panel.child(header_button); - header.scale().animate(300, Easing.CUBIC, new ScaleValue(2f)).forwards(); - - - Surface glassPanel = Surface.blur(5f, 15f) - .and(Surface.flat(0x40FFFFFF)) - .and(Surface.outline(0xFFFFFFFF)); - - panel.surface(glassPanel); - - var version = new ScalableLabel(Component.literal("Citrus-Dev v2.0")) - .horizontalTextAlignment(HorizontalAlignment.RIGHT) - .verticalTextAlignment(VerticalAlignment.BOTTOM); - - panel.child(version); - rootComponent.child(panel); - } - - - - @Override - public boolean isPauseScreen() { - return false; - } -} \ No newline at end of file diff --git a/src/main/java/de/fullrisk/citrusDev/ScalableButton.java b/src/main/java/de/fullrisk/citrusDev/ScalableButton.java deleted file mode 100644 index 04730de..0000000 --- a/src/main/java/de/fullrisk/citrusDev/ScalableButton.java +++ /dev/null @@ -1,44 +0,0 @@ -package de.fullrisk.citrusDev; - -import io.wispforest.owo.ui.component.ButtonComponent; -import io.wispforest.owo.ui.core.AnimatableProperty; -import io.wispforest.owo.ui.core.OwoUIGraphics; -import net.minecraft.network.chat.Component; - -import java.util.function.Consumer; - -public class ScalableButton extends ButtonComponent { - - protected final AnimatableProperty scale = AnimatableProperty.of(new ScaleValue(0f)); - - public ScalableButton(Component message, Consumer onPress) { - super(message, onPress); - } - - public AnimatableProperty scale() { - return this.scale; - } - - @Override - public void update(float delta, int mouseX, int mouseY) { - super.update(delta, mouseX, mouseY); - this.scale.update(delta); - } - - @Override - public void draw(OwoUIGraphics graphics, int mouseX, int mouseY, float partialTicks, float delta) { - float s = this.scale.get().value(); - - float centerX = this.x() + this.width() / 2f; - float centerY = this.y() + this.height() / 2f; - - graphics.pose().pushMatrix(); - graphics.pose().translate(centerX, centerY); - graphics.pose().scale(s, s); - graphics.pose().translate(-centerX, -centerY); - - super.draw(graphics, mouseX, mouseY, partialTicks, delta); - - graphics.pose().popMatrix(); - } -} \ No newline at end of file diff --git a/src/main/java/de/fullrisk/citrusDev/UI/MyScreen.java b/src/main/java/de/fullrisk/citrusDev/UI/MyScreen.java new file mode 100644 index 0000000..7495c97 --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/UI/MyScreen.java @@ -0,0 +1,138 @@ +package de.fullrisk.citrusDev.UI; + +import io.wispforest.owo.ui.base.BaseOwoScreen; +import io.wispforest.owo.ui.component.LabelComponent; +import io.wispforest.owo.ui.component.UIComponents; +import io.wispforest.owo.ui.container.FlowLayout; +import io.wispforest.owo.ui.container.UIContainers; +import io.wispforest.owo.ui.core.*; +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; +import de.fullrisk.citrusDev.UIHelper.ScalableLabel; +import de.fullrisk.citrusDev.UIHelper.ScalableButton; +import de.fullrisk.citrusDev.UIHelper.ScaleValue; + +public class MyScreen extends BaseOwoScreen { + + public MyScreen() { + super(Component.literal("Citrus Dev")); + Minecraft.getInstance().getSoundManager().play( + SimpleSoundInstance.forUI(SoundEvents.UI_TOAST_IN, 1.5F, 2) + ); + } + + @Override + protected OwoUIAdapter createAdapter() { + return OwoUIAdapter.create(this, UIContainers::verticalFlow); + } + + @Override + protected void build(FlowLayout rootComponent) { + rootComponent.gap(0); + rootComponent + .horizontalAlignment(HorizontalAlignment.CENTER) + .verticalAlignment(VerticalAlignment.CENTER); + + // ----- Main panel (now a horizontal container: sidebar | divider | content) ----- + FlowLayout panel = UIContainers.horizontalFlow(Sizing.content(), Sizing.content()); + panel.gap(0); + panel.padding(Insets.of(2)); + + panel.horizontalAlignment(HorizontalAlignment.CENTER); + panel.verticalAlignment(VerticalAlignment.CENTER); + panel.horizontalSizing(Sizing.fixed(0)); + panel.verticalSizing(Sizing.fixed(0)); + panel.horizontalSizing().animate(300, Easing.CUBIC, Sizing.fixed(250)).forwards(); + panel.verticalSizing().animate(300, Easing.CUBIC, Sizing.fixed(150)).forwards(); + + // inner content area, accounting for panel's 2px padding on each side + int innerHeight = 150 - 4; // 146 + int sidebarWidth = 34; + int dividerWidth = 1; + int contentWidth = 250 - 4 - sidebarWidth - dividerWidth; // 211 + + // ----- Sidebar ----- + FlowLayout sidebar = UIContainers.verticalFlow(Sizing.fixed(sidebarWidth), Sizing.fixed(innerHeight)); + sidebar.horizontalAlignment(HorizontalAlignment.CENTER); + sidebar.verticalAlignment(VerticalAlignment.CENTER); + sidebar.padding(Insets.of(4)); + sidebar.gap(2); + + var sidebarButton = new ScalableButton(Component.literal("+"), btn -> { + Minecraft.getInstance().getSoundManager().play( + SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 2.0F, 2) + ); + }, + 0x80FFFFFF, // base fill + 0xA0FFFFFF, // hovered fill + 0xFFFFFFFF, // base outline + 0xFFFFFFFF, // hovered outline + 1 // outline width + ); + sidebarButton.horizontalSizing(Sizing.fixed(20)); + sidebarButton.verticalSizing(Sizing.fixed(20)); + sidebarButton.scale().animate(300, Easing.CUBIC, new ScaleValue(1f)).forwards(); + + sidebar.child(sidebarButton); + panel.child(sidebar); + + // ----- Divider ----- + FlowLayout divider = UIContainers.verticalFlow(Sizing.fixed(dividerWidth), Sizing.fixed(innerHeight)); + divider.surface(Surface.flat(0x80FFFFFF)); // subtle line, matches glass aesthetic + panel.child(divider); + + // ----- Content column (header, button, version) ----- + FlowLayout content = UIContainers.verticalFlow(Sizing.fixed(contentWidth), Sizing.fixed(innerHeight)); + content.gap(6); + content.horizontalAlignment(HorizontalAlignment.CENTER); + content.verticalAlignment(VerticalAlignment.CENTER); + panel.child(content); + + var header = new ScalableLabel(Component.literal("Citrus Dev")); + content.child(header); + + var header_button = new ScalableButton(Component.literal("Click me"), btn -> { + btn.setMessage(Component.literal("Clicked!")); + Minecraft.getInstance().getSoundManager().play( + SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 2.0F, 2) + ); + }, + 0x80FFFFFF, // base fill + 0xA0FFFFFF, // hovered fill + 0xFFFFFFFF, // base outline + 0xFFFFFFFF, // hovered outline + 1 // outline width + ); + + header_button.scale().animate(300, Easing.CUBIC, new ScaleValue(1f)).forwards(); + header_button.margins(Insets.top(6)); + content.child(header_button); + header.scale().animate(300, Easing.CUBIC, new ScaleValue(2f)).forwards(); + + Surface glassPanel = Surface.blur(5f, 15f) + .and(Surface.flat(0x40FFFFFF)) + .and(Surface.outline(0xFFFFFFFF)); + + panel.surface(glassPanel); + + var version = new ScalableLabel(Component.literal("Citrus-Dev v2.0")); + version.horizontalTextAlignment(HorizontalAlignment.RIGHT); + version.verticalTextAlignment(VerticalAlignment.BOTTOM); + version.horizontalSizing(Sizing.fixed(202)); + version.verticalSizing(Sizing.fixed(12)); + version.positioning(Positioning.relative(115, 100)); // bottom-right corner of `panel` + + version.scale().set(new ScaleValue(0.7f)); + + panel.child(version); + + rootComponent.child(panel); + } + + @Override + public boolean isPauseScreen() { + return false; + } +} \ No newline at end of file diff --git a/src/main/java/de/fullrisk/citrusDev/UIHelper/ColorValue.java b/src/main/java/de/fullrisk/citrusDev/UIHelper/ColorValue.java new file mode 100644 index 0000000..68840b3 --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/UIHelper/ColorValue.java @@ -0,0 +1,19 @@ +package de.fullrisk.citrusDev.UIHelper; + +import io.wispforest.owo.ui.core.Animatable; +import net.minecraft.util.Mth; + +public record ColorValue(int argb) implements Animatable { + @Override + public ColorValue interpolate(ColorValue next, float delta) { + int a1 = (this.argb >> 24) & 0xFF, r1 = (this.argb >> 16) & 0xFF, g1 = (this.argb >> 8) & 0xFF, b1 = this.argb & 0xFF; + int a2 = (next.argb >> 24) & 0xFF, r2 = (next.argb >> 16) & 0xFF, g2 = (next.argb >> 8) & 0xFF, b2 = next.argb & 0xFF; + + int a = Mth.floor(Mth.lerp(delta, a1, a2)); + int r = Mth.floor(Mth.lerp(delta, r1, r2)); + int g = Mth.floor(Mth.lerp(delta, g1, g2)); + int b = Mth.floor(Mth.lerp(delta, b1, b2)); + + return new ColorValue((a << 24) | (r << 16) | (g << 8) | b); + } +} \ No newline at end of file diff --git a/src/main/java/de/fullrisk/citrusDev/UIHelper/ScalableButton.java b/src/main/java/de/fullrisk/citrusDev/UIHelper/ScalableButton.java new file mode 100644 index 0000000..d7548bc --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/UIHelper/ScalableButton.java @@ -0,0 +1,91 @@ +package de.fullrisk.citrusDev.UIHelper; + +import io.wispforest.owo.ui.component.ButtonComponent; +import io.wispforest.owo.ui.core.AnimatableProperty; +import io.wispforest.owo.ui.core.Easing; +import io.wispforest.owo.ui.core.OwoUIGraphics; +import net.minecraft.network.chat.Component; + +import java.util.function.Consumer; + +public class ScalableButton extends ButtonComponent { + + protected final AnimatableProperty scale = AnimatableProperty.of(new ScaleValue(0f)); + protected final AnimatableProperty fillColor; + protected final AnimatableProperty outlineColor; + + private final int baseFill; + private final int hoveredFill; + private final int baseOutline; + private final int hoveredOutline; + private final int outlineWidth; + + private boolean wasHovered = false; + + public ScalableButton(Component message, Consumer onPress, + int baseFill, int hoveredFill, + int baseOutline, int hoveredOutline, + int outlineWidth) { + super(message, onPress); + this.baseFill = baseFill; + this.hoveredFill = hoveredFill; + this.baseOutline = baseOutline; + this.hoveredOutline = hoveredOutline; + this.outlineWidth = outlineWidth; + + this.fillColor = AnimatableProperty.of(new ColorValue(baseFill)); + this.outlineColor = AnimatableProperty.of(new ColorValue(baseOutline)); + + this.renderer((context, button, delta) -> { + int x = button.getX(); + int y = button.getY(); + int w = button.getWidth(); + int h = button.getHeight(); + + int fill = this.fillColor.get().argb(); + int outline = this.outlineColor.get().argb(); + + context.fill(x, y, x + w, y + h, fill); + context.fill(x, y, x + w, y + this.outlineWidth, outline); // top + context.fill(x, y + h - this.outlineWidth, x + w, y + h, outline); // bottom + context.fill(x, y, x + this.outlineWidth, y + h, outline); // left + context.fill(x + w - this.outlineWidth, y, x + w, y + h, outline); // right + }); + } + + public AnimatableProperty scale() { + return this.scale; + } + + @Override + public void update(float delta, int mouseX, int mouseY) { + super.update(delta, mouseX, mouseY); + this.scale.update(delta); + this.fillColor.update(delta); + this.outlineColor.update(delta); + + boolean hovered = this.isHovered(); + if (hovered != this.wasHovered) { + this.fillColor.animate(150, Easing.CUBIC, new ColorValue(hovered ? hoveredFill : baseFill)).forwards(); + this.outlineColor.animate(150, Easing.CUBIC, new ColorValue(hovered ? hoveredOutline : baseOutline)).forwards(); + this.wasHovered = hovered; + } + } + + @Override + public void draw(OwoUIGraphics graphics, int mouseX, int mouseY, float partialTicks, float delta) { + float s = this.scale.get().value(); + + float centerX = this.x() + this.width() / 2f; + float centerY = this.y() + this.height() / 2f; + + graphics.pose().pushMatrix(); + graphics.pose().translate(centerX, centerY); + graphics.pose().scale(s, s); + graphics.pose().translate(-centerX, -centerY); + + super.draw(graphics, mouseX, mouseY, partialTicks, delta); + + graphics.pose().popMatrix(); + } +} \ No newline at end of file diff --git a/src/main/java/de/fullrisk/citrusDev/ScalableLabel.java b/src/main/java/de/fullrisk/citrusDev/UIHelper/ScalableLabel.java similarity index 96% rename from src/main/java/de/fullrisk/citrusDev/ScalableLabel.java rename to src/main/java/de/fullrisk/citrusDev/UIHelper/ScalableLabel.java index 9d158c0..8b17a0a 100644 --- a/src/main/java/de/fullrisk/citrusDev/ScalableLabel.java +++ b/src/main/java/de/fullrisk/citrusDev/UIHelper/ScalableLabel.java @@ -1,4 +1,4 @@ -package de.fullrisk.citrusDev; +package de.fullrisk.citrusDev.UIHelper; import io.wispforest.owo.ui.component.LabelComponent; import io.wispforest.owo.ui.core.AnimatableProperty; diff --git a/src/main/java/de/fullrisk/citrusDev/ScaleValue.java b/src/main/java/de/fullrisk/citrusDev/UIHelper/ScaleValue.java similarity index 88% rename from src/main/java/de/fullrisk/citrusDev/ScaleValue.java rename to src/main/java/de/fullrisk/citrusDev/UIHelper/ScaleValue.java index e500914..3a1f408 100644 --- a/src/main/java/de/fullrisk/citrusDev/ScaleValue.java +++ b/src/main/java/de/fullrisk/citrusDev/UIHelper/ScaleValue.java @@ -1,4 +1,4 @@ -package de.fullrisk.citrusDev; +package de.fullrisk.citrusDev.UIHelper; import io.wispforest.owo.ui.core.Animatable; import net.minecraft.util.Mth; diff --git a/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java b/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java index a0f1b06..6c63801 100644 --- a/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java +++ b/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java @@ -1,6 +1,6 @@ package de.fullrisk.citrusDev.client; -import de.fullrisk.citrusDev.MyScreen; +import de.fullrisk.citrusDev.UI.MyScreen; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper;