Text no longer jitters
[Mograsim.git] / net.mograsim.logic.model / src / net / mograsim / logic / model / LogicUICanvas.java
1 package net.mograsim.logic.model;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.function.Consumer;
6
7 import org.eclipse.swt.SWT;
8 import org.eclipse.swt.graphics.Color;
9 import org.eclipse.swt.layout.GridData;
10 import org.eclipse.swt.layout.GridLayout;
11 import org.eclipse.swt.widgets.Button;
12 import org.eclipse.swt.widgets.Combo;
13 import org.eclipse.swt.widgets.Composite;
14 import org.eclipse.swt.widgets.Event;
15 import org.eclipse.swt.widgets.Label;
16 import org.eclipse.swt.widgets.Listener;
17 import org.eclipse.swt.widgets.Shell;
18 import org.eclipse.swt.widgets.Text;
19
20 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
21 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
22 import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;
23 import net.mograsim.logic.core.types.Bit;
24 import net.mograsim.logic.core.types.BitVector;
25 import net.mograsim.logic.model.model.ViewModel;
26 import net.mograsim.logic.model.model.components.GUIComponent;
27 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
28 import net.mograsim.logic.model.model.components.submodels.SubmodelInterface;
29 import net.mograsim.logic.model.model.wires.WireCrossPoint;
30 import net.mograsim.preferences.Preferences;
31
32 /**
33  * Simulation visualizer canvas.
34  * 
35  * @author Daniel Kirschten
36  */
37 public class LogicUICanvas extends ZoomableCanvas
38 {
39         private static final boolean OPEN_DEBUG_SETHIGHLEVELSTATE_SHELL = false;
40
41         private final ViewModel model;
42
43         public LogicUICanvas(Composite parent, int style, ViewModel model)
44         {
45                 super(parent, style, Preferences.current().getBoolean("net.mograsim.logic.model.improvetext"));
46
47                 this.model = model;
48
49                 LogicUIRenderer renderer = new LogicUIRenderer(model);
50                 addZoomedRenderer(gc ->
51                 {
52                         Color background = Preferences.current().getColor("net.mograsim.logic.model.color.background");
53                         if (background != null)
54                                 setBackground(background);// this.setBackground, not gc.setBackground to have the background fill the canvas
55                         renderer.render(gc, new Rectangle(-offX / zoom, -offY / zoom, gW / zoom, gH / zoom));
56                 });
57                 model.setRedrawHandler(this::redrawThreadsafe);
58
59                 addListener(SWT.MouseDown, this::mouseDown);
60
61                 if (OPEN_DEBUG_SETHIGHLEVELSTATE_SHELL)
62                         openDebugSetHighLevelStateShell(model);
63         }
64
65         private void mouseDown(Event e)
66         {
67                 if (e.button == 1)
68                 {
69                         Point click = canvasToWorldCoords(e.x, e.y);
70                         for (GUIComponent component : model.getComponentsByName().values())
71                                 if (component.getBounds().contains(click) && component.clicked(click.x, click.y))
72                                 {
73                                         redraw();
74                                         break;
75                                 }
76                 }
77         }
78
79         private void openDebugSetHighLevelStateShell(ViewModel model)
80         {
81                 Shell debugShell = new Shell();
82                 debugShell.setLayout(new GridLayout(2, false));
83                 new Label(debugShell, SWT.NONE).setText("Target component: ");
84                 Combo componentSelector = new Combo(debugShell, SWT.DROP_DOWN | SWT.READ_ONLY);
85                 componentSelector.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
86                 List<GUIComponent> componentsByItemIndex = new ArrayList<>();
87                 Consumer<? super GUIComponent> compsChanged = c -> recalculateComponentSelector(componentsByItemIndex, componentSelector, model);
88                 model.addComponentAddedListener(compsChanged);
89                 model.addComponentRemovedListener(compsChanged);
90                 debugShell.addListener(SWT.Dispose, e ->
91                 {
92                         model.removeComponentAddedListener(compsChanged);
93                         model.removeComponentRemovedListener(compsChanged);
94                 });
95                 recalculateComponentSelector(componentsByItemIndex, componentSelector, model);
96                 new Label(debugShell, SWT.NONE).setText("Target state ID: ");
97                 Text stateIDText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
98                 stateIDText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
99                 new Label(debugShell, SWT.NONE).setText("Value type: ");
100                 Composite radioGroup = new Composite(debugShell, SWT.NONE);
101                 radioGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
102                 GridLayout radioGroupLayout = new GridLayout(2, false);
103                 radioGroupLayout.marginHeight = 0;
104                 radioGroupLayout.marginWidth = 0;
105                 radioGroup.setLayout(radioGroupLayout);
106                 Button radioBit = new Button(radioGroup, SWT.RADIO);
107                 radioBit.setText("Single bit");
108                 Button radioBitVector = new Button(radioGroup, SWT.RADIO);
109                 radioBitVector.setText("Bitvector");
110                 new Label(debugShell, SWT.NONE).setText("Value string representation: \n(Bit vectors: MSBit...LSBit)");
111                 Text valueText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
112                 valueText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
113                 Button send = new Button(debugShell, SWT.PUSH);
114                 send.setText("Send!");
115                 Button get = new Button(debugShell, SWT.PUSH);
116                 get.setText("Get!");
117                 Text output = new Text(debugShell, SWT.READ_ONLY);
118                 output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
119                 Listener sendAction = e ->
120                 {
121                         try
122                         {
123                                 int componentIndex = componentSelector.getSelectionIndex();
124                                 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
125                                         throw new RuntimeException("No component selected");
126                                 GUIComponent target = componentsByItemIndex.get(componentIndex);
127                                 String valueString = valueText.getText();
128                                 Object value;
129                                 if (radioBit.getSelection())
130                                         value = Bit.parse(valueString);
131                                 else if (radioBitVector.getSelection())
132                                         value = BitVector.parse(valueString);
133                                 else
134                                         throw new RuntimeException("No value type selected");
135                                 target.setHighLevelState(stateIDText.getText(), value);
136                                 output.setText("Success!");
137                         }
138                         catch (Exception x)
139                         {
140                                 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
141                         }
142                 };
143                 Listener getAction = e ->
144                 {
145                         try
146                         {
147                                 if (componentSelector.getSelectionIndex() >= componentsByItemIndex.size())
148                                         throw new RuntimeException("No valid component selected");
149                                 output.setText("Success! Value: \r\n"
150                                                 + componentsByItemIndex.get(componentSelector.getSelectionIndex()).getHighLevelState(stateIDText.getText()));
151                         }
152                         catch (Exception x)
153                         {
154                                 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
155                         }
156                 };
157                 send.addListener(SWT.Selection, sendAction);
158                 valueText.addListener(SWT.DefaultSelection, sendAction);
159                 get.addListener(SWT.Selection, getAction);
160                 stateIDText.addListener(SWT.DefaultSelection, getAction);
161                 debugShell.open();
162         }
163
164         private void recalculateComponentSelector(List<GUIComponent> componentsByItemIndex, Combo componentSelector, ViewModel model)
165         {
166                 componentsByItemIndex.clear();
167                 componentSelector.setItems();
168                 addComponentSelectorItems(componentsByItemIndex, "", componentSelector, model);
169         }
170
171         private void addComponentSelectorItems(List<GUIComponent> componentsByItemIndex, String base, Combo componentSelector, ViewModel model)
172         {
173                 model.getComponentsByName().values().stream().sorted((c1, c2) -> c1.name.compareTo(c2.name)).forEach(c ->
174                 {
175                         if (!(c instanceof WireCrossPoint || c instanceof SubmodelInterface))
176                         {
177                                 String item = base + c.name;
178                                 componentsByItemIndex.add(c);
179                                 componentSelector.add(item);
180                                 if (c instanceof SubmodelComponent)
181                                         addComponentSelectorItems(componentsByItemIndex, item + " -> ", componentSelector, ((SubmodelComponent) c).submodel);
182                         }
183                 });
184         }
185 }