1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
package gregtech.common.gui.modularui.widget;
import java.io.IOException;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.network.PacketBuffer;
import com.gtnewhorizons.modularui.api.widget.ISyncedWidget;
import com.gtnewhorizons.modularui.api.widget.Widget;
import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils;
import com.gtnewhorizons.modularui.common.widget.MultiChildWidget;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import gregtech.api.gui.modularui.IDataFollowerWidget;
import gregtech.api.util.ISerializableObject;
/**
* Controls state of child widgets with specific data, and allows centralized control of multiple widgets. e.g. clicking
* button B will set machine mode to B, so button A, whose state is bound to the mode, will be automatically deactivated
* by this widget. <br>
* This widget wraps data and handles validation, e.g. tell client to close GUI when tile is broken or cover is removed.
* <br>
* Data can be anything, e.g. {@link ISerializableObject} or machine recipe mode.
*
* @param <T> Data type stored in this widget
* @see IDataFollowerWidget
*/
public abstract class DataControllerWidget<T> extends MultiChildWidget implements ISyncedWidget {
private final Supplier<T> dataGetter;
private final Function<T, Boolean> dataSetter;
protected T lastData;
private boolean needsUpdate;
/**
* @param dataGetter () -> data this widget handles
* @param dataSetter data to set -> if setting data is successful
*/
public DataControllerWidget(Supplier<T> dataGetter, Function<T, Boolean> dataSetter) {
this.dataGetter = dataGetter;
this.dataSetter = dataSetter;
}
protected T getLastData() {
return lastData;
}
@Override
public void onPostInit() {
super.onPostInit();
// client _should_ have received initial cover data from `GT_UIInfos#openCoverUI`
lastData = dataGetter.get();
if (NetworkUtils.isClient()) {
updateChildren(true);
}
}
@Override
public void detectAndSendChanges(boolean init) {
T actualValue = dataGetter.get();
if (actualValue == null) {
// data is in invalid state e.g. tile is broken, cover is removed
getWindow().tryClose();
return;
}
if (init || !actualValue.equals(getLastData())) {
// init sync or someone else edited data
lastData = actualValue;
syncDataToClient(actualValue);
}
}
protected void syncDataToClient(T data) {
syncToClient(0, buffer -> writeToPacket(buffer, data));
}
protected void syncDataToServer(T data) {
syncToServer(0, buffer -> writeToPacket(buffer, data));
updateChildren();
}
@Override
public void readOnClient(int id, PacketBuffer buf) throws IOException {
if (id == 0) {
lastData = readFromPacket(buf);
dataSetter.apply(getLastData());
updateChildren();
}
}
@Override
public void readOnServer(int id, PacketBuffer buf) throws IOException {
if (id == 0) {
lastData = readFromPacket(buf);
if (dataSetter.apply(getLastData())) {
markForUpdate();
} else {
getWindow().closeWindow();
}
}
}
@SuppressWarnings("unchecked")
@SideOnly(Side.CLIENT)
protected void updateChildren(boolean postInit) {
for (Widget child : getChildren()) {
if (child instanceof IDataFollowerWidget) {
((IDataFollowerWidget<T, ?>) child).updateState(getLastData());
if (postInit) {
((IDataFollowerWidget<T, ?>) child).onPostInit();
}
}
}
}
@SideOnly(Side.CLIENT)
protected void updateChildren() {
updateChildren(false);
}
protected abstract void writeToPacket(PacketBuffer buffer, T data);
protected abstract T readFromPacket(PacketBuffer buffer) throws IOException;
@Override
public void markForUpdate() {
needsUpdate = true;
}
@Override
public void unMarkForUpdate() {
needsUpdate = false;
}
@Override
public boolean isMarkedForUpdate() {
return needsUpdate;
}
/**
* @param widget widget to add that implements {@link IDataFollowerWidget}
* @param dataToStateGetter given data -> state of the widget to add
* @param dataUpdater (current data, state of the widget to add) -> new data to set
* @param applyForWidget methods to call for the widget to add
* @param <U> state type stored in the widget to add
* @param <W> widget type to add
*/
public <U, W extends Widget & IDataFollowerWidget<T, U>> DataControllerWidget<T> addFollower(W widget,
Function<T, U> dataToStateGetter, BiFunction<T, U, T> dataUpdater, Consumer<W> applyForWidget) {
widget.setDataToStateGetter(dataToStateGetter);
widget.setStateSetter(state -> {
T newData = dataUpdater.apply(getLastData(), state);
lastData = newData;
dataSetter.apply(getLastData());
syncDataToServer(newData);
});
applyForWidget.accept(widget);
addChild(widget);
return this;
}
}
|