Skip to content

Commit c2d4154

Browse files
Bound item unbinding
1 parent 343ae02 commit c2d4154

7 files changed

Lines changed: 141 additions & 14 deletions

File tree

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,9 +606,25 @@ public void setSlotElement(int index, @Nullable SlotElement slotElement) {
606606
if (isAnimationRunning() && !isInAnimationContext)
607607
throw new IllegalStateException("Cannot set slot element while an animation is running");
608608

609+
var previousElement = slotElements[index];
609610
slotElements[index] = slotElement;
610611

611-
// set the gui if it is a bound item
612+
// replacing a bound item may require unbinding
613+
if (previousElement instanceof SlotElement.Item(BoundItem item)) {
614+
boolean found = false;
615+
for (var otherElement : slotElements) {
616+
if (otherElement instanceof SlotElement.Item(BoundItem otherItem) && otherItem == item) {
617+
found = true;
618+
break;
619+
}
620+
}
621+
622+
if (!found) {
623+
item.unbind();
624+
}
625+
}
626+
627+
// set the gui if it is an unbound bound item
612628
if (slotElement instanceof SlotElement.Item(BoundItem item) && !item.isBound()) {
613629
item.bind(this);
614630
}

invui/src/main/java/xyz/xenondevs/invui/item/AbstractBoundItem.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ public void bind(Gui gui) {
2626
this.gui = gui;
2727
}
2828

29+
@Override
30+
public void unbind() {
31+
if (gui == null)
32+
throw new IllegalStateException("Item is not bound to a gui");
33+
this.gui = null;
34+
}
35+
2936
@Override
3037
public boolean isBound() {
3138
return gui != null;

invui/src/main/java/xyz/xenondevs/invui/item/AbstractPagedGuiBoundItem.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,33 @@
33
import xyz.xenondevs.invui.gui.Gui;
44
import xyz.xenondevs.invui.gui.PagedGui;
55

6+
import java.util.function.BiConsumer;
7+
68
/**
79
* A {@link BoundItem} that is bound to a {@link PagedGui} and updated when the page or page count changes.
810
*/
911
public abstract class AbstractPagedGuiBoundItem extends AbstractBoundItem {
1012

13+
private final BiConsumer<Integer, Integer> notifier = (i1, i2) -> notifyWindows();
14+
1115
@Override
1216
public void bind(Gui gui) {
1317
if (!(gui instanceof PagedGui<?> pagedGui))
1418
throw new IllegalArgumentException("PageItem can only be used in a PagedGui");
1519

1620
super.bind(gui);
17-
pagedGui.addPageChangeHandler((oldPage, newPage) -> notifyWindows());
18-
pagedGui.addPageCountChangeHandler((oldCount, newCount) -> notifyWindows());
21+
pagedGui.addPageChangeHandler(notifier);
22+
pagedGui.addPageCountChangeHandler(notifier);
23+
}
24+
25+
@Override
26+
public void unbind() {
27+
var gui = getGui();
28+
29+
super.unbind();
30+
31+
gui.removePageChangeHandler(notifier);
32+
gui.removePageCountChangeHandler(notifier);
1933
}
2034

2135
@Override

invui/src/main/java/xyz/xenondevs/invui/item/AbstractScrollGuiBoundItem.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,33 @@
33
import xyz.xenondevs.invui.gui.Gui;
44
import xyz.xenondevs.invui.gui.ScrollGui;
55

6+
import java.util.function.BiConsumer;
7+
68
/**
79
* A {@link BoundItem} that is bound to a {@link ScrollGui} and updated when the line or line count changes.
810
*/
911
public abstract class AbstractScrollGuiBoundItem extends AbstractBoundItem {
1012

13+
private final BiConsumer<Integer, Integer> notifier = (i1, i2) -> notifyWindows();
14+
1115
@Override
1216
public void bind(Gui gui) {
1317
if (!(gui instanceof ScrollGui<?> scrollGui))
1418
throw new IllegalArgumentException("ScrollItem can only be used in a ScrollGui");
1519

1620
super.bind(gui);
17-
scrollGui.addScrollHandler((fromLine, toLine) -> notifyWindows());
18-
scrollGui.addLineCountChangeHandler((oldCount, newCount) -> notifyWindows());
21+
scrollGui.addScrollHandler(notifier);
22+
scrollGui.addLineCountChangeHandler(notifier);
23+
}
24+
25+
@Override
26+
public void unbind() {
27+
var gui = getGui();
28+
29+
super.unbind();
30+
31+
gui.removeScrollHandler(notifier);
32+
gui.removeLineCountChangeHandler(notifier);
1933
}
2034

2135
@Override

invui/src/main/java/xyz/xenondevs/invui/item/AbstractTabGuiBoundItem.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,31 @@
33
import xyz.xenondevs.invui.gui.Gui;
44
import xyz.xenondevs.invui.gui.TabGui;
55

6+
import java.util.function.BiConsumer;
7+
68
/**
79
* A {@link BoundItem} that is bound to a {@link TabGui} and updated when the current tab changes.
810
*/
911
public abstract class AbstractTabGuiBoundItem extends AbstractBoundItem {
1012

13+
private final BiConsumer<Integer, Integer> notifier = (i1, i2) -> notifyWindows();
14+
1115
@Override
1216
public void bind(Gui gui) {
1317
if (!(gui instanceof TabGui tabGui))
1418
throw new IllegalArgumentException("TabItem can only be used in a TabGui");
1519

1620
super.bind(gui);
17-
tabGui.addTabChangeHandler((oldTab, newTab) -> notifyWindows());
21+
tabGui.addTabChangeHandler(notifier);
22+
}
23+
24+
@Override
25+
public void unbind() {
26+
var gui = getGui();
27+
28+
super.unbind();
29+
30+
gui.removeTabChangeHandler(notifier);
1831
}
1932

2033
@Override

invui/src/main/java/xyz/xenondevs/invui/item/BoundItem.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ public sealed interface BoundItem extends Item permits AbstractBoundItem {
3434
*/
3535
void bind(Gui gui);
3636

37+
/**
38+
* Unbinds this item from the currently bound {@link Gui}.
39+
* Called when the item is fully removed from a {@link Gui}.
40+
*
41+
* @throws IllegalStateException If this item is not bound to a {@link Gui}.
42+
*/
43+
void unbind();
44+
3745
/**
3846
* Checks if this item is already bound to a {@link Gui}.
3947
*
@@ -90,11 +98,19 @@ sealed interface Builder<G extends Gui> extends Item.Builder<Builder<G>> permits
9098
/**
9199
* Adds a bind handler that is called when the item is bound to a {@link Gui}.
92100
*
93-
* @param handler The bind handler.
101+
* @param handler The bind handler, receiving the {@link Item} itself and the bound {@link Gui}.
94102
* @return This builder.
95103
*/
96104
Builder<G> addBindHandler(BiConsumer<? super Item, ? super G> handler);
97105

106+
/**
107+
* Adds an unbind handler that is called when the item is unbound from a {@link Gui}.
108+
*
109+
* @param handler The unbind handler, receiving the {@link Item} itself and the bound {@link Gui}.
110+
* @return This builder.
111+
*/
112+
Builder<G> addUnbindHandler(BiConsumer<? super Item, ? super G> handler);
113+
98114
/**
99115
* Adds a click handler that is called when the item is clicked.
100116
*

invui/src/main/java/xyz/xenondevs/invui/item/CustomBoundItem.java

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.List;
2020
import java.util.concurrent.CompletableFuture;
21+
import java.util.concurrent.atomic.AtomicReference;
2122
import java.util.function.*;
2223

2324
class CustomBoundItem<G extends Gui> extends AbstractBoundItem {
@@ -26,17 +27,20 @@ class CustomBoundItem<G extends Gui> extends AbstractBoundItem {
2627
private final QuadConsumer<? super Item, ? super G, ? super Player, ? super Integer> selectHandler;
2728
private volatile BiFunction<? super Player, ? super G, ? extends ItemProvider> itemProvider;
2829
private final BiConsumer<? super Item, ? super G> bindHandler;
30+
private final BiConsumer<? super Item, ? super G> unbindHandler;
2931
private final int updatePeriod;
3032
private @Nullable BukkitTask updateTask;
3133

3234
public CustomBoundItem(
3335
BiConsumer<? super Item, ? super G> bindHandler,
36+
BiConsumer<? super Item, ? super G> unbindHandler,
3437
TriConsumer<? super Item, ? super G, ? super Click> clickHandler,
3538
QuadConsumer<? super Item, ? super G, ? super Player, ? super Integer> selectHandler,
3639
BiFunction<? super Player, ? super G, ? extends ItemProvider> itemProvider,
3740
int updatePeriod
3841
) {
3942
this.bindHandler = bindHandler;
43+
this.unbindHandler = unbindHandler;
4044
this.clickHandler = clickHandler;
4145
this.selectHandler = selectHandler;
4246
this.itemProvider = itemProvider;
@@ -61,6 +65,12 @@ public void bind(Gui gui) {
6165
super.bind(gui);
6266
}
6367

68+
@Override
69+
public void unbind() {
70+
unbindHandler.accept(this, getGui());
71+
super.unbind();
72+
}
73+
6474
@Override
6575
public void handleClick(ClickType clickType, Player player, Click click) {
6676
clickHandler.accept(this, getGui(), click);
@@ -96,6 +106,7 @@ public void removeViewer(AbstractWindow<?> who, int how) {
96106
non-sealed static class Builder<G extends Gui> implements BoundItem.Builder<G> {
97107

98108
protected BiConsumer<Item, G> bindHandler = (item, gui) -> {};
109+
protected BiConsumer<Item, G> unbindHandler = (item, gui) -> {};
99110
private TriConsumer<Item, G, Click> clickHandler = (item, gui, click) -> {};
100111
private QuadConsumer<Item, G, Player, Integer> selectHandler = (item, gui, player, slot) -> {};
101112
private @Nullable BiFunction<? super Player, ? super G, ? extends ItemProvider> itemProviderFn;
@@ -197,6 +208,12 @@ public Builder<G> addBindHandler(BiConsumer<? super Item, ? super G> handler) {
197208
return this;
198209
}
199210

211+
@Override
212+
public BoundItem.Builder<G> addUnbindHandler(BiConsumer<? super Item, ? super G> handler) {
213+
unbindHandler = unbindHandler.andThen(handler);
214+
return this;
215+
}
216+
200217
@Override
201218
public Builder<G> addModifier(Consumer<? super Item> modifier) {
202219
this.modifier = this.modifier.andThen(modifier);
@@ -213,6 +230,7 @@ public BoundItem build() {
213230
if (asyncPlaceholder != null && itemProviderFn != null) {
214231
customItem = new CustomBoundItem<>(
215232
bindHandler,
233+
unbindHandler,
216234
clickHandler,
217235
selectHandler,
218236
(viewer, gui) -> asyncPlaceholder,
@@ -237,6 +255,7 @@ public BoundItem build() {
237255
} else {
238256
customItem = new CustomBoundItem<>(
239257
bindHandler,
258+
unbindHandler,
240259
clickHandler,
241260
selectHandler,
242261
itemProviderFn != null ? itemProviderFn : (viewer, gui) -> ItemProvider.EMPTY,
@@ -252,9 +271,19 @@ public BoundItem build() {
252271
static class Paged extends Builder<PagedGui<?>> {
253272

254273
Paged() {
274+
var pageChangeHandler = new AtomicReference<BiConsumer<Integer, Integer>>();
275+
var pageCountChangeHandler = new AtomicReference<BiConsumer<Integer, Integer>>();
276+
255277
bindHandler = bindHandler.andThen((item, gui) -> {
256-
gui.addPageChangeHandler((oldPage, newPage) -> item.notifyWindows());
257-
gui.addPageCountChangeHandler((oldCount, newCount) -> item.notifyWindows());
278+
pageChangeHandler.set((oldPage, newPage) -> item.notifyWindows());
279+
pageCountChangeHandler.set((oldCount, newCount) -> item.notifyWindows());
280+
gui.addPageChangeHandler(pageChangeHandler.get());
281+
gui.addPageCountChangeHandler(pageCountChangeHandler.get());
282+
});
283+
284+
unbindHandler = unbindHandler.andThen((item, gui) -> {
285+
gui.removePageChangeHandler(pageChangeHandler.get());
286+
gui.removePageCountChangeHandler(pageCountChangeHandler.get());
258287
});
259288
}
260289

@@ -263,9 +292,20 @@ static class Paged extends Builder<PagedGui<?>> {
263292
static class Scroll extends Builder<ScrollGui<?>> {
264293

265294
Scroll() {
295+
var scrollHandler = new AtomicReference<BiConsumer<Integer, Integer>>();
296+
var lineCountChangeHandler = new AtomicReference<BiConsumer<Integer, Integer>>();
297+
266298
bindHandler = bindHandler.andThen((item, gui) -> {
267-
gui.addScrollHandler((oldScroll, newScroll) -> item.notifyWindows());
268-
gui.addLineCountChangeHandler((oldCount, newCount) -> item.notifyWindows());
299+
scrollHandler.set((oldScroll, newScroll) -> item.notifyWindows());
300+
lineCountChangeHandler.set((oldCount, newCount) -> item.notifyWindows());
301+
302+
gui.addScrollHandler(scrollHandler.get());
303+
gui.addLineCountChangeHandler(lineCountChangeHandler.get());
304+
});
305+
306+
unbindHandler = unbindHandler.andThen((item, gui) -> {
307+
gui.removeScrollHandler(scrollHandler.get());
308+
gui.removeLineCountChangeHandler(lineCountChangeHandler.get());
269309
});
270310
}
271311

@@ -274,9 +314,16 @@ static class Scroll extends Builder<ScrollGui<?>> {
274314
static class Tab extends Builder<TabGui> {
275315

276316
Tab() {
277-
bindHandler = bindHandler.andThen((item, gui) ->
278-
gui.addTabChangeHandler((oldTab, newTab) -> item.notifyWindows())
279-
);
317+
var tabChangeHandler = new AtomicReference<BiConsumer<Integer, Integer>>();
318+
319+
bindHandler = bindHandler.andThen((item, gui) -> {
320+
tabChangeHandler.set((oldTab, newTab) -> item.notifyWindows());
321+
gui.addTabChangeHandler(tabChangeHandler.get());
322+
});
323+
324+
unbindHandler = unbindHandler.andThen((item, gui) -> {
325+
gui.removeTabChangeHandler(tabChangeHandler.get());
326+
});
280327
}
281328

282329
}

0 commit comments

Comments
 (0)