Skip to content

Commit 71b4fba

Browse files
Weakly observe properties
1 parent f971bdc commit 71b4fba

File tree

7 files changed

+66
-43
lines changed

7 files changed

+66
-43
lines changed

gradle/libs.versions.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ kotlin = "2.1.0"
33
paper = "1.21.5-R0.1-SNAPSHOT"
44

55
[libraries]
6-
commons-provider = { module = "xyz.xenondevs.commons:commons-provider", version = "1.25" }
6+
commons-provider = { module = "xyz.xenondevs.commons:commons-provider", version = "1.29" }
77
jetbrains-annotations = { module = "org.jetbrains:annotations", version = "26.0.1" }
88
jspecify = { module = "org.jspecify:jspecify", version = "1.0.0" }
99
junit-bom = { module = "org.junit:junit-bom", version = "5.11.4" }

invui-kotlin/src/main/kotlin/xyz/xenondevs/invui/PropertyWrappers.kt

+16-8
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,25 @@ import xyz.xenondevs.commons.provider.MutableProvider
44
import xyz.xenondevs.commons.provider.Provider
55
import xyz.xenondevs.invui.state.MutableProperty
66
import xyz.xenondevs.invui.state.Property
7+
import java.util.function.Consumer
78

89
internal class PropertyAdapter<T : Any>(private val provider: Provider<T>) : Property<T> {
910

1011
override fun get(): T {
1112
return provider.get()
1213
}
1314

14-
override fun observe(observer: Runnable) {
15-
provider.observe(observer::run)
15+
override fun <O : Any> observeWeak(owner: O, observer: Consumer<in O>) {
16+
provider.observeWeak(owner, observer::accept)
1617
}
1718

18-
override fun unobserve(observer: Runnable) {
19-
provider.unobserve(observer::run)
19+
override fun <O : Any> unobserveWeak(owner: O, observer: Consumer<in O>) {
20+
provider.unobserveWeak(owner, observer::accept)
2021
}
2122

23+
override fun unobserveWeak(owner: Any) {
24+
provider.unobserveWeak(owner)
25+
}
2226

2327
}
2428

@@ -32,12 +36,16 @@ internal class MutablePropertyAdapter<T : Any>(private val provider: MutableProv
3236
provider.set(value)
3337
}
3438

35-
override fun observe(observer: Runnable) {
36-
provider.observe(observer::run)
39+
override fun <O : Any> observeWeak(owner: O, observer: Consumer<in O>) {
40+
provider.observeWeak(owner, observer::accept)
41+
}
42+
43+
override fun <O : Any> unobserveWeak(owner: O, observer: Consumer<in O>) {
44+
provider.unobserveWeak(owner, observer::accept)
3745
}
3846

39-
override fun unobserve(observer: Runnable) {
40-
provider.unobserve(observer::run)
47+
override fun unobserveWeak(owner: Any) {
48+
provider.unobserveWeak(owner)
4149
}
4250

4351
}

invui/src/main/java/xyz/xenondevs/invui/gui/AbstractPagedGui.java

+7-8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.List;
1313
import java.util.SequencedSet;
1414
import java.util.function.BiConsumer;
15+
import java.util.function.Consumer;
1516

1617
sealed abstract class AbstractPagedGui<C>
1718
extends AbstractGui
@@ -21,9 +22,7 @@ sealed abstract class AbstractPagedGui<C>
2122

2223
protected int[] contentListSlots;
2324

24-
private final Runnable bakeFn = this::bake;
25-
26-
private MutableProperty<Integer> page;
25+
private final MutableProperty<Integer> page;
2726
private Property<? extends List<? extends C>> content;
2827
private final List<BiConsumer<? super Integer, ? super Integer>> pageChangeHandlers = new ArrayList<>(0);
2928
private final List<BiConsumer<? super Integer, ? super Integer>> pageCountChangeHandlers = new ArrayList<>(0);
@@ -36,9 +35,9 @@ public AbstractPagedGui(
3635
) {
3736
super(width, height);
3837
this.page = MutableProperty.of(0);
39-
page.observe(this::update);
38+
page.observeWeak(this, AbstractPagedGui::update);
4039
this.content = content;
41-
content.observe(bakeFn);
40+
content.observeWeak(this, AbstractPagedGui::bake);
4241
this.contentListSlots = SlotUtils.toSlotIndices(contentListSlots, getWidth());
4342
}
4443

@@ -49,9 +48,9 @@ public AbstractPagedGui(
4948
) {
5049
super(structure.getWidth(), structure.getHeight());
5150
this.page = page;
52-
page.observe(this::update);
51+
page.observeWeak(this, AbstractPagedGui::update);
5352
this.content = content;
54-
content.observe(bakeFn);
53+
content.observeWeak(this, AbstractPagedGui::bake);
5554
this.contentListSlots = structure.getIngredientMatrix().findContentListSlots();
5655
super.applyStructure(structure); // super call to avoid bake() through applyStructure override
5756
}
@@ -116,7 +115,7 @@ private void updatePageContent() {
116115

117116
@Override
118117
public void setContent(List<? extends C> content) {
119-
this.content.unobserve(bakeFn);
118+
this.content.unobserveWeak(this);
120119
this.content = Property.of(content);
121120
bake();
122121
}

invui/src/main/java/xyz/xenondevs/invui/gui/AbstractScrollGui.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ sealed abstract class AbstractScrollGui<C>
2323
private int lineLength;
2424
private int[] contentListSlots = new int[0];
2525

26-
private final Runnable bakeFn = this::bake;
27-
2826
private final MutableProperty<Integer> line;
2927
private Property<? extends List<? extends C>> content;
3028
private final List<BiConsumer<? super Integer, ? super Integer>> scrollHandlers = new ArrayList<>(0);
@@ -39,9 +37,9 @@ public AbstractScrollGui(
3937
) {
4038
super(width, height);
4139
this.line = MutableProperty.of(0);
42-
line.observe(this::update);
40+
line.observeWeak(this, AbstractScrollGui::update);
4341
this.content = content;
44-
content.observe(bakeFn);
42+
content.observeWeak(this, AbstractScrollGui::bake);
4543
setContentListSlots(SlotUtils.toSlotIndicesSet(contentListSlots, getWidth()), horizontalLines);
4644
}
4745

@@ -52,9 +50,9 @@ public AbstractScrollGui(
5250
) {
5351
super(structure.getWidth(), structure.getHeight());
5452
this.line = line;
55-
line.observe(this::update);
53+
line.observeWeak(this, AbstractScrollGui::update);
5654
this.content = content;
57-
content.observe(bakeFn);
55+
content.observeWeak(this, AbstractScrollGui::bake);
5856
super.applyStructure(structure); // super call to avoid bake() through applyStructure override
5957
setContentListSlotsFromStructure(structure);
6058
}
@@ -158,7 +156,7 @@ public int getMaxLine() {
158156

159157
@Override
160158
public void setContent(List<? extends C> content) {
161-
this.content.unobserve(bakeFn);
159+
this.content.unobserveWeak(this);
162160
this.content = Property.of(content);
163161
bake();
164162
}

invui/src/main/java/xyz/xenondevs/invui/gui/TabGuiImpl.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ final class TabGuiImpl extends AbstractGui implements TabGui {
1717

1818
private int[] contentListSlots;
1919

20-
private final Runnable bakeFn = this::bake;
21-
2220
private final MutableProperty<Integer> tab;
2321
private Property<? extends List<? extends @Nullable Gui>> tabs;
2422
private final List<BiConsumer<? super Integer, ? super Integer>> tabChangeHandlers = new ArrayList<>(0);
@@ -33,9 +31,9 @@ public TabGuiImpl(
3331
if (contentListSlots.isEmpty())
3432
throw new IllegalArgumentException("Content list slots must not be empty");
3533
this.tab = MutableProperty.of(0);
36-
tab.observe(this::update);
34+
tab.observeWeak(this, TabGuiImpl::update);
3735
this.tabs = tabs;
38-
tabs.observe(bakeFn);
36+
tabs.observeWeak(this, TabGuiImpl::bake);
3937
this.contentListSlots = SlotUtils.toSlotIndices(contentListSlots, getWidth());
4038
bake();
4139
}
@@ -47,9 +45,9 @@ public TabGuiImpl(
4745
) {
4846
super(structure.getWidth(), structure.getHeight());
4947
this.tab = tab;
50-
tab.observe(this::update);
48+
tab.observeWeak(this, TabGuiImpl::update);
5149
this.tabs = tabs;
52-
tabs.observe(bakeFn);
50+
tabs.observeWeak(this, TabGuiImpl::bake);
5351
super.applyStructure(structure); // super call to avoid bake() through applyStructure override
5452
this.contentListSlots = structure.getIngredientMatrix().findContentListSlots();
5553
bake();
@@ -117,7 +115,7 @@ private void updateContent() {
117115

118116
@Override
119117
public void setTabs(List<? extends @Nullable Gui> tabs) {
120-
this.tabs.unobserve(bakeFn);
118+
this.tabs.unobserveWeak(this);
121119
this.tabs = Property.of(tabs);
122120
bake();
123121
}

invui/src/main/java/xyz/xenondevs/invui/state/MutableProperty.java

+20-8
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
import org.jetbrains.annotations.ApiStatus;
44

5-
import java.util.HashSet;
6-
import java.util.Set;
5+
import java.util.*;
76
import java.util.function.Consumer;
87

98
@ApiStatus.Experimental
@@ -24,7 +23,7 @@ default void accept(T value) {
2423

2524
class MutablePropertyImpl<T> implements MutableProperty<T> {
2625

27-
private final Set<Runnable> observers = new HashSet<>();
26+
private final Map<Object, List<Consumer<Object>>> weakObservers = new WeakHashMap<>();
2827
private T value;
2928

3029
public MutablePropertyImpl(T value) {
@@ -34,22 +33,35 @@ public MutablePropertyImpl(T value) {
3433
@Override
3534
public void set(T value) {
3635
this.value = value;
37-
observers.forEach(Runnable::run);
36+
weakObservers.forEach((owner, observers) -> observers.forEach(observer -> observer.accept(owner)));
3837
}
3938

4039
@Override
4140
public T get() {
4241
return value;
4342
}
4443

44+
@SuppressWarnings("unchecked")
4545
@Override
46-
public void observe(Runnable observer) {
47-
observers.add(observer);
46+
public <O> void observeWeak(O owner, Consumer<? super O> observer) {
47+
weakObservers.computeIfAbsent(owner, k -> new ArrayList<>())
48+
.add((Consumer<Object>)observer);
4849
}
4950

5051
@Override
51-
public void unobserve(Runnable observer) {
52-
observers.remove(observer);
52+
public <O> void unobserveWeak(O owner, Consumer<? super O> observer) {
53+
List<Consumer<Object>> observers = weakObservers.get(owner);
54+
if (observers != null) {
55+
observers.remove(observer);
56+
if (observers.isEmpty()) {
57+
weakObservers.remove(owner);
58+
}
59+
}
60+
}
61+
62+
@Override
63+
public void unobserveWeak(Object owner) {
64+
weakObservers.remove(owner);
5365
}
5466

5567
}

invui/src/main/java/xyz/xenondevs/invui/state/Property.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.jetbrains.annotations.ApiStatus;
44

5+
import java.util.function.Consumer;
56
import java.util.function.Supplier;
67

78
@ApiStatus.Experimental
@@ -13,9 +14,11 @@ static <T> Property<T> of(T value) {
1314

1415
T get();
1516

16-
void observe(Runnable observer);
17+
<O> void observeWeak(O owner, Consumer<? super O> observer);
1718

18-
void unobserve(Runnable observer);
19+
<O> void unobserveWeak(O owner, Consumer<? super O> observer);
20+
21+
void unobserveWeak(Object owner);
1922

2023
}
2124

@@ -33,12 +36,17 @@ public T get() {
3336
}
3437

3538
@Override
36-
public void observe(Runnable observer) {
39+
public <O> void observeWeak(O owner, Consumer<? super O> observer) {
40+
// empty
41+
}
42+
43+
@Override
44+
public <O> void unobserveWeak(O owner, Consumer<? super O> observer) {
3745
// empty
3846
}
3947

4048
@Override
41-
public void unobserve(Runnable observer) {
49+
public void unobserveWeak(Object owner) {
4250
// empty
4351
}
4452

0 commit comments

Comments
 (0)