Restructured the Preferences system
[Mograsim.git] / plugins / net.mograsim.logic.model / src / net / mograsim / logic / model / model / components / atomic / SimpleRectangularHardcodedModelComponent.java
1 package net.mograsim.logic.model.model.components.atomic;
2
3 import java.util.HashMap;
4 import java.util.Map;
5 import java.util.Objects;
6 import java.util.concurrent.atomic.AtomicReference;
7 import java.util.function.Consumer;
8
9 import net.haspamelodica.swt.helper.gcs.GeneralGC;
10 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
11 import net.mograsim.logic.core.wires.CoreWire.ReadEnd;
12 import net.mograsim.logic.core.wires.CoreWire.ReadWriteEnd;
13 import net.mograsim.logic.model.model.LogicModelModifiable;
14 import net.mograsim.logic.model.model.components.ModelComponent;
15 import net.mograsim.logic.model.model.wires.Pin;
16 import net.mograsim.logic.model.modeladapter.LogicCoreAdapter;
17 import net.mograsim.logic.model.modeladapter.componentadapters.SimpleRectangularHardcodedModelComponentAdapter;
18 import net.mograsim.logic.model.preferences.RenderPreferences;
19 import net.mograsim.logic.model.serializing.IdentifyParams;
20 import net.mograsim.logic.model.snippets.HighLevelStateHandler;
21 import net.mograsim.logic.model.snippets.outlinerenderers.DefaultOutlineRenderer;
22 import net.mograsim.logic.model.snippets.symbolrenderers.CenteredTextSymbolRenderer;
23 import net.mograsim.logic.model.snippets.symbolrenderers.CenteredTextSymbolRenderer.CenteredTextParams;
24 import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer;
25 import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer.PinNamesParams;
26 import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer.PinNamesParams.Position;
27 import net.mograsim.logic.model.util.ObservableAtomicReference;
28
29 public abstract class SimpleRectangularHardcodedModelComponent extends ModelComponent
30 {
31         private static final double centerTextHeight = 5;
32         private static final double pinNamesHeight = 3.5;
33         private static final double pinNamesMargin = .5;
34
35         private final String id;
36
37         private final DefaultOutlineRenderer outlineRenderer;
38         private final CenteredTextSymbolRenderer centerTextRenderer;
39         private final PinNamesSymbolRenderer pinNamesRenderer;
40
41         private ObservableAtomicReference<Object> state;
42         private Runnable recalculate;
43
44         private final Map<String, Map<Consumer<Object>, Consumer<ObservableAtomicReference<Object>>>> stateObsPerHLSListenerPerStateID;
45
46         // creation and destruction
47
48         public SimpleRectangularHardcodedModelComponent(LogicModelModifiable model, String id, String name, String centerText)
49         {
50                 this(model, id, name, centerText, true);
51         }
52
53         public SimpleRectangularHardcodedModelComponent(LogicModelModifiable model, String id, String name, String centerText, boolean callInit)
54         {
55                 super(model, name, false);
56                 this.id = id;
57                 this.outlineRenderer = new DefaultOutlineRenderer(this);
58                 CenteredTextParams centeredTextParams = new CenteredTextParams();
59                 centeredTextParams.text = centerText;
60                 centeredTextParams.fontHeight = centerTextHeight;
61                 this.centerTextRenderer = new CenteredTextSymbolRenderer(this, centeredTextParams);
62                 PinNamesParams pinNamesParams = new PinNamesParams();
63                 pinNamesParams.pinLabelHeight = pinNamesHeight;
64                 pinNamesParams.pinLabelMargin = pinNamesMargin;
65                 this.pinNamesRenderer = new PinNamesSymbolRenderer(this, pinNamesParams);
66                 addPinRemovedListener(this::pinRemoved);
67
68                 this.stateObsPerHLSListenerPerStateID = new HashMap<>();
69
70                 setHighLevelStateHandler(new HighLevelStateHandler()
71                 {
72
73                         @Override
74                         public Object get(String stateID)
75                         {
76                                 return getHighLevelState(state.get(), stateID);
77                         }
78
79                         @Override
80                         public void set(String stateID, Object newState)
81                         {
82                                 state.updateAndGet(s -> SimpleRectangularHardcodedModelComponent.this.setHighLevelState(s, stateID, newState));
83                                 recalculate.run();
84                         }
85
86                         @Override
87                         public void addListener(String stateID, Consumer<Object> stateChanged)
88                         {
89                                 addHighLevelStateListener(state.get(), stateID, stateChanged);
90                         }
91
92                         @Override
93                         public void removeListener(String stateID, Consumer<Object> stateChanged)
94                         {
95                                 removeHighLevelStateListener(state.get(), stateID, stateChanged);
96                         }
97
98                         @Override
99                         public String getIDForSerializing(IdentifyParams idParams)
100                         {
101                                 return null;// we don't need to serialize this; it's implicit since we are a SimpleRectangularHardcodedModelComponent
102                         }
103
104                         @Override
105                         public Object getParamsForSerializing(IdentifyParams idParams)
106                         {
107                                 return null;
108                         }
109                 });
110
111                 if (callInit)
112                         init();
113         }
114
115         // pins
116
117         protected void addPin(Pin pin, Position namePosition)
118         {
119                 super.addPin(pin); // do this first to catch errors
120                 pinNamesRenderer.setPinPosition(pin, namePosition);
121         }
122
123         private void pinRemoved(Pin pin)
124         {
125                 pinNamesRenderer.setPinPosition(pin, null);
126         }
127
128         // high-level access
129
130         @SuppressWarnings({ "static-method", "unused" }) // this method is intended to be overridden
131         protected Object getHighLevelState(Object state, String stateID)
132         {
133                 throw new IllegalArgumentException("No high level state with ID " + stateID);
134         }
135
136         @SuppressWarnings({ "static-method", "unused" }) // this method is intended to be overridden
137         protected Object setHighLevelState(Object lastState, String stateID, Object newHighLevelState)
138         {
139                 throw new IllegalArgumentException("No high level state with ID " + stateID);
140         }
141
142         protected void addHighLevelStateListener(Object state, String stateID, Consumer<Object> stateChanged)
143         {
144                 AtomicReference<Object> lastHLSRef = new AtomicReference<>(getHighLevelState(state, stateID));
145                 Consumer<ObservableAtomicReference<Object>> refObs = r ->
146                 {
147                         Object newHLS = getHighLevelState(stateID);
148                         if (!Objects.equals(lastHLSRef.getAndSet(newHLS), newHLS))
149                                 stateChanged.accept(newHLS);
150                 };
151                 stateObsPerHLSListenerPerStateID.computeIfAbsent(stateID, s -> new HashMap<>()).put(stateChanged, refObs);
152                 this.state.addObserver(refObs);
153         }
154
155         protected void removeHighLevelStateListener(Object state, String stateID, Consumer<Object> stateChanged)
156         {
157                 getHighLevelState(state, stateID);// if this throws, we know there is no HLS with this name
158                 var stateObsPerHLSListener = stateObsPerHLSListenerPerStateID.get(stateID);
159                 if (stateObsPerHLSListener == null)
160                         return;
161                 Consumer<ObservableAtomicReference<Object>> refObs = stateObsPerHLSListener.remove(stateChanged);
162                 this.state.removeObserver(refObs);
163         }
164
165         // logic
166
167         public abstract Object recalculate(Object lastState, Map<String, ReadEnd> readEnds, Map<String, ReadWriteEnd> readWriteEnds);
168
169         // core model binding
170
171         public void setCoreModelBindingAndResetState(ObservableAtomicReference<Object> state, Runnable recalculate)
172         {
173                 this.state = state;
174                 this.recalculate = recalculate;
175         }
176
177         // "graphical" operations
178
179         @Override
180         public void render(GeneralGC gc, RenderPreferences renderPrefs, Rectangle visibleRegion)
181         {
182                 outlineRenderer.render(gc, renderPrefs, visibleRegion);
183                 centerTextRenderer.render(gc, renderPrefs, visibleRegion);
184                 pinNamesRenderer.render(gc, renderPrefs, visibleRegion);
185         }
186
187         // serializing
188
189         @Override
190         public String getIDForSerializing(IdentifyParams idParams)
191         {
192                 return id;
193         }
194
195         // operations no longer supported
196
197         @Override
198         protected void addPin(Pin pin)
199         {
200                 throw new UnsupportedOperationException("Can't add pins without setting usage, call addPin(Pin [, Position]) instead");
201         }
202
203         static
204         {
205                 LogicCoreAdapter.addComponentAdapter(new SimpleRectangularHardcodedModelComponentAdapter());
206         }
207 }