diff options
7 files changed, 174 insertions, 24 deletions
diff --git a/gradle.properties b/gradle.properties index b25dc91..973acc0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ org.gradle.jvmargs=-Xmx1G loader_version=0.11.6 # Mod Properties - mod_version = 4.1.6 + mod_version = 4.1.7 maven_group = io.github.cottonmc archives_base_name = LibGui diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java index 8fed347..331f68b 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonClientScreen.java @@ -38,6 +38,8 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { @Nullable protected WWidget lastResponder = null; + + private final MouseInputHandler<CottonClientScreen> mouseInputHandler = new MouseInputHandler<>(this); public CottonClientScreen(GuiDescription description) { this(new LiteralText(""), description); @@ -48,7 +50,8 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { this.description = description; description.getRootPanel().validate(description); } - + + @Override public GuiDescription getDescription() { return description; } @@ -178,7 +181,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; if (containerX<0 || containerY<0 || containerX>=width || containerY>=height) return true; - MouseInputHandler.onMouseDown(description, this, containerX, containerY, mouseButton); + mouseInputHandler.onMouseDown(containerX, containerY, mouseButton); return true; } @@ -189,7 +192,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { super.mouseReleased(mouseX, mouseY, mouseButton); int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; - MouseInputHandler.onMouseUp(description, this, containerX, containerY, mouseButton); + mouseInputHandler.onMouseUp(containerX, containerY, mouseButton); return true; } @@ -201,7 +204,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; - MouseInputHandler.onMouseDrag(description, this, containerX, containerY, mouseButton, deltaX, deltaY); + mouseInputHandler.onMouseDrag(containerX, containerY, mouseButton, deltaX, deltaY); return true; } @@ -212,7 +215,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; - MouseInputHandler.onMouseScroll(description, containerX, containerY, amount); + mouseInputHandler.onMouseScroll(containerX, containerY, amount); return true; } @@ -223,7 +226,7 @@ public class CottonClientScreen extends Screen implements CottonScreenImpl { int containerX = (int)mouseX-left; int containerY = (int)mouseY-top; - MouseInputHandler.onMouseMove(description, containerX, containerY); + mouseInputHandler.onMouseMove(containerX, containerY); } @Override diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java index 0a1a3f4..953563c 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/CottonInventoryScreen.java @@ -9,6 +9,7 @@ import net.minecraft.text.LiteralText; import net.minecraft.text.Style; import net.minecraft.text.Text; +import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.SyncedGuiDescription; import io.github.cottonmc.cotton.gui.impl.VisualLogger; import io.github.cottonmc.cotton.gui.impl.client.CottonScreenImpl; @@ -16,6 +17,7 @@ import io.github.cottonmc.cotton.gui.impl.client.MouseInputHandler; import io.github.cottonmc.cotton.gui.impl.client.NarrationHelper; import io.github.cottonmc.cotton.gui.widget.WPanel; import io.github.cottonmc.cotton.gui.widget.WWidget; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; @@ -28,6 +30,7 @@ import org.lwjgl.opengl.GL11; public class CottonInventoryScreen<T extends SyncedGuiDescription> extends HandledScreen<T> implements CottonScreenImpl { protected SyncedGuiDescription description; @Nullable protected WWidget lastResponder = null; + private final MouseInputHandler<CottonInventoryScreen<T>> mouseInputHandler = new MouseInputHandler<>(this); /** * Constructs a new screen without a title. @@ -85,6 +88,12 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl VisualLogger.reset(); } + @ApiStatus.Internal + @Override + public GuiDescription getDescription() { + return description; + } + @Nullable @Override public WWidget getLastResponder() { @@ -189,7 +198,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; if (containerX<0 || containerY<0 || containerX>=width || containerY>=height) return result; - MouseInputHandler.onMouseDown(description, this, containerX, containerY, mouseButton); + mouseInputHandler.onMouseDown(containerX, containerY, mouseButton); return true; } @@ -199,7 +208,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl super.mouseReleased(mouseX, mouseY, mouseButton); int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; - MouseInputHandler.onMouseUp(description, this, containerX, containerY, mouseButton); + mouseInputHandler.onMouseUp(containerX, containerY, mouseButton); return true; } @@ -210,7 +219,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; - MouseInputHandler.onMouseDrag(description, this, containerX, containerY, mouseButton, deltaX, deltaY); + mouseInputHandler.onMouseDrag(containerX, containerY, mouseButton, deltaX, deltaY); return true; } @@ -221,7 +230,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; - MouseInputHandler.onMouseScroll(description, containerX, containerY, amount); + mouseInputHandler.onMouseScroll(containerX, containerY, amount); return true; } @@ -232,7 +241,7 @@ public class CottonInventoryScreen<T extends SyncedGuiDescription> extends Handl int containerX = (int)mouseX-x; int containerY = (int)mouseY-y; - MouseInputHandler.onMouseMove(description, containerX, containerY); + mouseInputHandler.onMouseMove(containerX, containerY); } @Override diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/CottonScreenImpl.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/CottonScreenImpl.java index 2ef9632..cd215b3 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/CottonScreenImpl.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/CottonScreenImpl.java @@ -5,11 +5,14 @@ import net.fabricmc.api.Environment; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Style; +import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.widget.WWidget; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public interface CottonScreenImpl { + GuiDescription getDescription(); + @Nullable WWidget getLastResponder(); diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/MouseInputHandler.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/MouseInputHandler.java index 45fc17f..4791c39 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/MouseInputHandler.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/MouseInputHandler.java @@ -2,7 +2,6 @@ package io.github.cottonmc.cotton.gui.impl.client; import net.minecraft.client.gui.screen.Screen; -import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.widget.WWidget; import io.github.cottonmc.cotton.gui.widget.data.InputResult; import org.jetbrains.annotations.Nullable; @@ -12,10 +11,17 @@ import java.util.function.Function; /** * The implementation for mouse inputs. */ -public final class MouseInputHandler { - public static void onMouseDown(GuiDescription description, CottonScreenImpl screen, int containerX, int containerY, int mouseButton) { +public final class MouseInputHandler<S extends Screen & CottonScreenImpl> { + private final S screen; + private @Nullable WWidget hovered; + + public MouseInputHandler(S screen) { + this.screen = screen; + } + + public void onMouseDown(int containerX, int containerY, int mouseButton) { if (screen.getLastResponder() == null) { - WWidget lastResponder = description.getRootPanel().hit(containerX, containerY); + WWidget lastResponder = screen.getDescription().getRootPanel().hit(containerX, containerY); screen.setLastResponder(lastResponder); if (lastResponder != null) { runTree( @@ -28,7 +34,7 @@ public final class MouseInputHandler { } } - public static <S extends Screen & CottonScreenImpl> void onMouseUp(GuiDescription description, S screen, int containerX, int containerY, int mouseButton) { + public void onMouseUp(int containerX, int containerY, int mouseButton) { WWidget lastResponder = screen.getLastResponder(); if (lastResponder != null) { @@ -48,7 +54,7 @@ public final class MouseInputHandler { } } else { runTree( - description.getRootPanel().hit(containerX, containerY), + screen.getDescription().getRootPanel().hit(containerX, containerY), widget -> widget.onMouseUp(containerX - widget.getAbsoluteX(), containerY - widget.getAbsoluteY(), mouseButton) ); } @@ -56,7 +62,7 @@ public final class MouseInputHandler { screen.setLastResponder(null); } - public static <S extends Screen & CottonScreenImpl> void onMouseDrag(GuiDescription description, S screen, int containerX, int containerY, int mouseButton, double deltaX, double deltaY) { + public void onMouseDrag(int containerX, int containerY, int mouseButton, double deltaX, double deltaY) { WWidget lastResponder = screen.getLastResponder(); if (lastResponder != null) { @@ -68,22 +74,34 @@ public final class MouseInputHandler { if (containerX < 0 || containerY < 0 || containerX >= width || containerY >= height) return; runTree( - description.getRootPanel().hit(containerX, containerY), + screen.getDescription().getRootPanel().hit(containerX, containerY), widget -> widget.onMouseDrag(containerX - widget.getAbsoluteX(), containerY - widget.getAbsoluteY(), mouseButton, deltaX, deltaY) ); } } - public static void onMouseScroll(GuiDescription description, int containerX, int containerY, double amount) { + public void onMouseScroll(int containerX, int containerY, double amount) { runTree( - description.getRootPanel().hit(containerX, containerY), + screen.getDescription().getRootPanel().hit(containerX, containerY), widget -> widget.onMouseScroll(containerX - widget.getAbsoluteX(), containerY - widget.getAbsoluteY(), amount) ); } - public static void onMouseMove(GuiDescription description, int containerX, int containerY) { + public void onMouseMove(int containerX, int containerY) { + WWidget hit = screen.getDescription().getRootPanel().hit(containerX, containerY); + + // TODO: Some sort of canHover? + if (hit != hovered) { + if (hovered != null) { + hovered.getHovered().set(false); + } + + hovered = hit; + hit.getHovered().set(true); + } + runTree( - description.getRootPanel().hit(containerX, containerY), + hit, widget -> widget.onMouseMove(containerX - widget.getAbsoluteX(), containerY - widget.getAbsoluteY()) ); } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java index e6f314f..3beacee 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java @@ -1,5 +1,7 @@ package io.github.cottonmc.cotton.gui.widget; +import io.github.cottonmc.cotton.gui.widget.data.ObservableProperty; + import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; @@ -42,6 +44,8 @@ public class WWidget { @Nullable protected GuiDescription host; + private final ObservableProperty<Boolean> hovered = ObservableProperty.of(false).nonnullValues(); + /** * Sets the location of this widget relative to its parent. * @@ -457,6 +461,10 @@ public class WWidget { public void addPainters() { } + public ObservableProperty<Boolean> getHovered() { + return hovered; + } + /** * {@return whether this widget can be narrated} * diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/data/ObservableProperty.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/data/ObservableProperty.java new file mode 100644 index 0000000..723cf60 --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/data/ObservableProperty.java @@ -0,0 +1,109 @@ +package io.github.cottonmc.cotton.gui.widget.data; + +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +public final class ObservableProperty<T> { + private Supplier<? extends T> value; + private final List<ChangeListener<? super T>> listeners = new ArrayList<>(); + private boolean allowNull = true; + + private ObservableProperty() { + } + + private ObservableProperty(Supplier<? extends T> value) { + this.value = value; + } + + public static <T> ObservableProperty<T> lateinit() { + return new ObservableProperty<>(); + } + + public static <T> ObservableProperty<T> of(T initialValue) { + return new ObservableProperty<>(() -> initialValue); + } + + public static <T> ObservableProperty<T> bound(Supplier<? extends T> initialValue) { + return new ObservableProperty<>(initialValue); + } + + /** + * {@return the value of this property} + * @throws IllegalStateException if not initialized + * @throws NullPointerException if the value is null and null values aren't allowed + */ + public T get() { + if (value == null) { + throw new IllegalStateException("Property not initialized!"); + } + + T ret = value.get(); + if (ret == null && !allowNull) throw new NullPointerException("Null value for nonnull property!"); + return ret; + } + + /** + * Sets this property to a constant value. + * + * @param value the new value + * @throws NullPointerException if the value is null and nulls aren't allowed + */ + public void set(T value) { + if (value == null && !allowNull) throw new NullPointerException("value"); + bind(() -> value); + } + + /** + * Binds this property to a supplier. + * + * @param value the new value supplier + */ + public void bind(Supplier<? extends T> value) { + Objects.requireNonNull(value, "value"); + T oldValue = this.value != null ? this.value.get() : null; + this.value = value; + + for (ChangeListener<? super T> listener : listeners) { + listener.onPropertyChange(this, oldValue, value.get()); + } + } + + /** + * Clears the current value, if any, from this property. + */ + public void clear() { + T oldValue = this.value != null ? this.value.get() : null; + value = null; + + for (ChangeListener<? super T> listener : listeners) { + listener.onPropertyChange(this, oldValue, null); + } + } + + /** + * Prevents this property from accepting null values. + * + * @return this property + */ + public ObservableProperty<T> nonnullValues() { + allowNull = false; + return this; + } + + public void addListener(ChangeListener<? super T> listener) { + listeners.add(listener); + } + + public void removeListener(ChangeListener<? super T> listener) { + listeners.remove(listener); + } + + @FunctionalInterface + public interface ChangeListener<T> { + void onPropertyChange(ObservableProperty<? extends T> property, @Nullable T from, @Nullable T to); + } +} |