diff options
3 files changed, 75 insertions, 46 deletions
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 055df75..156f967 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 @@ -14,7 +14,7 @@ import java.util.function.Function; */ public final class MouseInputHandler<S extends Screen & CottonScreenImpl> { private final S screen; - private final ObservableProperty<WWidget> hovered = ObservableProperty.of(null); + private final ObservableProperty<@Nullable WWidget> hovered = ObservableProperty.<WWidget>of(null).build(); public MouseInputHandler(S screen) { this.screen = screen; 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 27e62f8..417c01d 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 @@ -46,7 +46,7 @@ public class WWidget { @Nullable protected GuiDescription host; - private final ObservableProperty<Boolean> hovered = ObservableProperty.of(false).nonnullValues().setName("WWidget.hovered"); + private final ObservableProperty<Boolean> hovered = ObservableProperty.of(false).nonnull().name("WWidget.hovered").build(); /** * Sets the location of this widget relative to its parent. 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 eb92387..5f375c2 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 @@ -21,23 +21,44 @@ import java.util.Objects; */ @ApiStatus.Experimental public final class ObservableProperty<T> implements ObservableView<T> { + private static final String DEFAULT_NAME = "<unnamed>"; private boolean hasValue; private T value; private final List<ChangeListener<? super T>> listeners = new ArrayList<>(); - private boolean allowNull = true; - private String name = "<unnamed>"; + private final boolean allowNull; + private final String name; - private ObservableProperty(@Nullable T value, boolean hasValue) { + private ObservableProperty(@Nullable T value, boolean hasValue, boolean allowNull, String name) { this.value = value; this.hasValue = hasValue; + this.allowNull = allowNull; + this.name = name; + + if (hasValue && value == null && !allowNull) { + throw new NullPointerException("Cannot initialise nonnull property " + name + " with null value"); + } } - public static <T> ObservableProperty<T> lateinit() { - return new ObservableProperty<>(null, false); + /** + * Creates a "late init" property without an initial value. + * The created property will throw an exception if it has not been initialised yet. + * + * @param <T> the contained value type + * @return the created empty property builder + */ + public static <T> Builder<T> empty() { + return new Builder<>(null, false); } - public static <T> ObservableProperty<T> of(T initialValue) { - return new ObservableProperty<>(initialValue, true); + /** + * Creates a property with an initial value. + * + * @param initialValue the initial value + * @param <T> the contained value type + * @return the created property + */ + public static <T> Builder<T> of(T initialValue) { + return new Builder<>(initialValue, true); } @Override @@ -48,7 +69,7 @@ public final class ObservableProperty<T> implements ObservableView<T> { @Override public T get() { if (!hasValue) { - throw new IllegalStateException("Property " + name + " not initialized!"); + throw new IllegalStateException("Property " + name + " not initialised!"); } return value; @@ -74,31 +95,6 @@ public final class ObservableProperty<T> implements ObservableView<T> { } /** - * Clears the current value, if any, from this property. - */ - public void clear() { - T oldValue = value; - value = null; - hasValue = false; - - if (oldValue != 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; - } - - /** * Returns a read-only view of this property. * The result is not an instance of {@link ObservableProperty}, * and thus can't be mutated. @@ -137,17 +133,6 @@ public final class ObservableProperty<T> implements ObservableView<T> { return name; } - /** - * Sets the name of this property, which is used in debug messages. - * - * @param name the new name - * @return this property - */ - public ObservableProperty<T> setName(String name) { - this.name = Objects.requireNonNull(name, "name"); - return this; - } - @Override public void addListener(ChangeListener<? super T> listener) { Objects.requireNonNull(listener); @@ -159,4 +144,48 @@ public final class ObservableProperty<T> implements ObservableView<T> { Objects.requireNonNull(listener); listeners.remove(listener); } + + /** + * A builder for properties. + * + * @param <T> the contained value type + */ + public static final class Builder<T> { + private final T initialValue; + private final boolean hasValue; + private String name = DEFAULT_NAME; + private boolean allowNull = true; + + Builder(@Nullable T initialValue, boolean hasValue) { + this.initialValue = initialValue; + this.hasValue = hasValue; + } + + /** + * Disallows null values. + * + * @return this builder + */ + public Builder<T> nonnull() { + allowNull = false; + return this; + } + + /** + * Sets the name of this property, which is used in debug messages. + */ + public Builder<T> name(String name) { + this.name = Objects.requireNonNull(name, "name"); + return this; + } + + /** + * Builds the observable property. + * + * @return the created property + */ + public ObservableProperty<T> build() { + return new ObservableProperty<>(initialValue, hasValue, allowNull, name); + } + } } |