From 24697f732d64aba10a164794a4a0bee32bf5cbb6 Mon Sep 17 00:00:00 2001 From: Juuz <6596629+Juuxel@users.noreply.github.com> Date: Fri, 10 Sep 2021 11:33:30 +0300 Subject: More work on hovering and observables --- .../cotton/gui/impl/client/MouseInputHandler.java | 18 +++----- .../cotton/gui/impl/client/NarrationHelper.java | 4 +- .../github/cottonmc/cotton/gui/widget/WButton.java | 3 +- .../cottonmc/cotton/gui/widget/WItemSlot.java | 2 + .../github/cottonmc/cotton/gui/widget/WLabel.java | 8 ++++ .../github/cottonmc/cotton/gui/widget/WText.java | 8 ++++ .../cottonmc/cotton/gui/widget/WToggleButton.java | 3 +- .../github/cottonmc/cotton/gui/widget/WWidget.java | 32 +++++++++++++- .../cotton/gui/widget/data/ObservableProperty.java | 50 +++++++++++++++++++--- 9 files changed, 102 insertions(+), 26 deletions(-) (limited to 'src/main/java/io') 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 4791c39..055df75 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 @@ -4,6 +4,7 @@ import net.minecraft.client.gui.screen.Screen; import io.github.cottonmc.cotton.gui.widget.WWidget; import io.github.cottonmc.cotton.gui.widget.data.InputResult; +import io.github.cottonmc.cotton.gui.widget.data.ObservableProperty; import org.jetbrains.annotations.Nullable; import java.util.function.Function; @@ -13,10 +14,14 @@ import java.util.function.Function; */ public final class MouseInputHandler { private final S screen; - private @Nullable WWidget hovered; + private final ObservableProperty hovered = ObservableProperty.of(null); public MouseInputHandler(S screen) { this.screen = screen; + hovered.addListener((property, from, to) -> { + if (from != null) from.setHovered(false); + if (to != null) to.setHovered(true); + }); } public void onMouseDown(int containerX, int containerY, int mouseButton) { @@ -89,16 +94,7 @@ public final class MouseInputHandler { 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); - } + hovered.set(hit); runTree( hit, diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationHelper.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationHelper.java index 9b44210..55095a3 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationHelper.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NarrationHelper.java @@ -18,7 +18,7 @@ public final class NarrationHelper { public static void addNarrations(WPanel rootPanel, NarrationMessageBuilder builder) { List narratableWidgets = getAllWidgets(rootPanel) .filter(WWidget::isNarratable) - .filter(WWidget::isFocused) // TODO: || isHovered + .filter(widget -> widget.isFocused() || widget.isHovered()) .collect(Collectors.toList()); for (int i = 0, childCount = narratableWidgets.size(); i < childCount; i++) { @@ -28,7 +28,7 @@ public final class NarrationHelper { if (narratableWidgets.size() > 1) { builder.put(NarrationPart.POSITION, new TranslatableText(NarrationMessages.Vanilla.SCREEN_POSITION_KEY, i + 1, childCount)); - if (child.isFocused()) { // TODO: this check is currently useless but will be used with hovering + if (child.isFocused()) { builder.put(NarrationPart.USAGE, NarrationMessages.Vanilla.COMPONENT_LIST_USAGE); } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java index c6fdfc2..533e436 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java @@ -230,8 +230,7 @@ public class WButton extends WWidget { if (isEnabled()) { if (isFocused()) { builder.put(NarrationPart.USAGE, NarrationMessages.Vanilla.BUTTON_USAGE_FOCUSED); - } else { - // TODO: hovering, again + } else if (isHovered()) { builder.put(NarrationPart.USAGE, NarrationMessages.Vanilla.BUTTON_USAGE_HOVERED); } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java index 18c812d..a4788a7 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WItemSlot.java @@ -450,6 +450,8 @@ public class WItemSlot extends WWidget { parts.add(new TranslatableText(NarrationMessages.ITEM_SLOT_TITLE_KEY, focusedSlot + 1, slotsWide * slotsHigh)); builder.put(NarrationPart.TITLE, parts.toArray(new Text[0])); } + + // TODO: hovered slot } @Nullable diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabel.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabel.java index 5c6b2da..83f0700 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabel.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabel.java @@ -5,6 +5,8 @@ import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.screen.narration.NarrationPart; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.LiteralText; import net.minecraft.text.Style; @@ -261,4 +263,10 @@ public class WLabel extends WWidget { this.verticalAlignment = align; return this; } + + @Environment(EnvType.CLIENT) + @Override + public void addNarrations(NarrationMessageBuilder builder) { + builder.put(NarrationPart.TITLE, text); + } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WText.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WText.java index 96f78ed..8a4f03e 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WText.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WText.java @@ -4,6 +4,8 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.screen.narration.NarrationPart; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.OrderedText; import net.minecraft.text.Style; @@ -253,4 +255,10 @@ public class WText extends WWidget { this.verticalAlignment = verticalAlignment; return this; } + + @Environment(EnvType.CLIENT) + @Override + public void addNarrations(NarrationMessageBuilder builder) { + builder.put(NarrationPart.TITLE, text); + } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WToggleButton.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WToggleButton.java index d4fb728..ca0285f 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WToggleButton.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WToggleButton.java @@ -223,8 +223,7 @@ public class WToggleButton extends WWidget { if (isFocused()) { builder.put(NarrationPart.USAGE, NarrationMessages.Vanilla.BUTTON_USAGE_FOCUSED); - } else { - // TODO: hovering, again + } else if (isHovered()) { builder.put(NarrationPart.USAGE, NarrationMessages.Vanilla.BUTTON_USAGE_HOVERED); } } 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 3beacee..ac08ae6 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 @@ -44,7 +44,7 @@ public class WWidget { @Nullable protected GuiDescription host; - private final ObservableProperty hovered = ObservableProperty.of(false).nonnullValues(); + private final ObservableProperty hovered = ObservableProperty.of(false).nonnullValues().setName("WWidget.hovered"); /** * Sets the location of this widget relative to its parent. @@ -461,10 +461,38 @@ public class WWidget { public void addPainters() { } - public ObservableProperty getHovered() { + /** + * Returns whether the user is hovering over this widget. + * The result is an observable property that can be modified and listened to. + * + * @return the {@code hovered} property + * @since 4.2.0 + */ + public ObservableProperty hoveredProperty() { return hovered; } + /** + * Returns whether the user is hovering over this widget. + * This is equivalent to calling {@link #hoveredProperty()}.get(). + * + * @return true if this widget is hovered, false otherwise + * @since 4.2.0 + */ + public final boolean isHovered() { + return hoveredProperty().get(); + } + + /** + * Sets the {@link ##hoveredProperty()} hovered} property. + * + * @param hovered the new value; true if hovered, false otherwise + * @since 4.2.0 + */ + public final void setHovered(boolean hovered) { + hoveredProperty().set(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 index 723cf60..3b0205e 100644 --- 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 @@ -7,10 +7,22 @@ import java.util.List; import java.util.Objects; import java.util.function.Supplier; -public final class ObservableProperty { +/** + * An observable mutable property. Observable properties are containers for values + * that can be modified, listened to and bound to other suppliers. + * + *

The naming convention for {@code ObservableProperty} getters follows the convention + * {@code Property}. For example, the {@code WWidget.hovered} property can be retrieved with + * {@link io.github.cottonmc.cotton.gui.widget.WWidget#hoveredProperty() hoveredProperty()}. + * + * @param the contained value type + * @since 4.2.0 + */ +public final class ObservableProperty implements Supplier { private Supplier value; private final List> listeners = new ArrayList<>(); private boolean allowNull = true; + private String name = ""; private ObservableProperty() { } @@ -36,13 +48,14 @@ public final class ObservableProperty { * @throws IllegalStateException if not initialized * @throws NullPointerException if the value is null and null values aren't allowed */ + @Override public T get() { if (value == null) { - throw new IllegalStateException("Property not initialized!"); + throw new IllegalStateException("Property " + name + " not initialized!"); } T ret = value.get(); - if (ret == null && !allowNull) throw new NullPointerException("Null value for nonnull property!"); + if (ret == null && !allowNull) throw new NullPointerException("Null value for nonnull property " + name + "!"); return ret; } @@ -66,9 +79,12 @@ public final class ObservableProperty { Objects.requireNonNull(value, "value"); T oldValue = this.value != null ? this.value.get() : null; this.value = value; + T newValue = value.get(); - for (ChangeListener listener : listeners) { - listener.onPropertyChange(this, oldValue, value.get()); + if (oldValue != newValue) { + for (ChangeListener listener : listeners) { + listener.onPropertyChange(this, oldValue, newValue); + } } } @@ -79,8 +95,10 @@ public final class ObservableProperty { T oldValue = this.value != null ? this.value.get() : null; value = null; - for (ChangeListener listener : listeners) { - listener.onPropertyChange(this, oldValue, null); + if (oldValue != null) { + for (ChangeListener listener : listeners) { + listener.onPropertyChange(this, oldValue, null); + } } } @@ -94,6 +112,24 @@ public final class ObservableProperty { return this; } + /** + * {@return the name of this property} + */ + public String getName() { + return name; + } + + /** + * Sets the name of this property, which is used in debug messages. + * + * @param name the new name + * @return this property + */ + public ObservableProperty setName(String name) { + this.name = Objects.requireNonNull(name, "name"); + return this; + } + public void addListener(ChangeListener listener) { listeners.add(listener); } -- cgit