import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;
import net.mograsim.logic.core.types.Bit;
import net.mograsim.logic.core.types.BitVector;
-import net.mograsim.logic.model.model.ViewModel;
-import net.mograsim.logic.model.model.components.GUIComponent;
+import net.mograsim.logic.model.model.LogicModel;
+import net.mograsim.logic.model.model.components.ModelComponent;
import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
import net.mograsim.logic.model.model.components.submodels.SubmodelInterface;
-import net.mograsim.logic.model.model.wires.WireCrossPoint;
+import net.mograsim.logic.model.model.wires.ModelWireCrossPoint;
import net.mograsim.preferences.Preferences;
/**
*/
public class LogicUICanvas extends ZoomableCanvas
{
- private static final boolean OPEN_DEBUG_SETHIGHLEVELSTATE_SHELL = false;
+ private static final boolean OPEN_DEBUG_SETHIGHLEVELSTATE_SHELL = true;
- private final ViewModel model;
+ private final LogicModel model;
- public LogicUICanvas(Composite parent, int style, ViewModel model)
+ public LogicUICanvas(Composite parent, int style, LogicModel model)
{
- super(parent, style);
+ super(parent, style, Preferences.current().getBoolean("net.mograsim.logic.model.improvetext"));
this.model = model;
LogicUIRenderer renderer = new LogicUIRenderer(model);
addZoomedRenderer(gc ->
{
- Color background = Preferences.current().getColor("net.mograsim.logic.ui.color.background");
+ Color background = Preferences.current().getColor("net.mograsim.logic.model.color.background");
if (background != null)
setBackground(background);// this.setBackground, not gc.setBackground to have the background fill the canvas
renderer.render(gc, new Rectangle(-offX / zoom, -offY / zoom, gW / zoom, gH / zoom));
});
- model.addRedrawListener(this::redrawThreadsafe);
+ model.setRedrawHandler(this::redrawThreadsafe);
addListener(SWT.MouseDown, this::mouseDown);
if (e.button == 1)
{
Point click = canvasToWorldCoords(e.x, e.y);
- for (GUIComponent component : model.getComponentsByName().values())
+ for (ModelComponent component : model.getComponentsByName().values())
if (component.getBounds().contains(click) && component.clicked(click.x, click.y))
{
redraw();
}
}
- private void openDebugSetHighLevelStateShell(ViewModel model)
+ private void openDebugSetHighLevelStateShell(LogicModel model)
{
Shell debugShell = new Shell();
debugShell.setLayout(new GridLayout(2, false));
new Label(debugShell, SWT.NONE).setText("Target component: ");
Combo componentSelector = new Combo(debugShell, SWT.DROP_DOWN | SWT.READ_ONLY);
componentSelector.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- List<GUIComponent> componentsByItemIndex = new ArrayList<>();
- model.addComponentAddedListener(c -> recalculateComponentSelector(componentsByItemIndex, componentSelector, model));
- model.addComponentRemovedListener(c -> recalculateComponentSelector(componentsByItemIndex, componentSelector, model));
- recalculateComponentSelector(componentsByItemIndex, componentSelector, model);
+ List<ModelComponent> componentsByItemIndex = new ArrayList<>();
+ List<LogicModel> models = new ArrayList<>();
+ AtomicBoolean recalculateQueued = new AtomicBoolean();
+ AtomicReference<Consumer<? super ModelComponent>> compAdded = new AtomicReference<>();
+ AtomicReference<Consumer<? super ModelComponent>> compRemoved = new AtomicReference<>();
+ compAdded.set(c -> compsChanged(compAdded.get(), compRemoved.get(), c, models, componentsByItemIndex, componentSelector, model,
+ recalculateQueued, true));
+ compRemoved.set(c -> compsChanged(compAdded.get(), compRemoved.get(), c, models, componentsByItemIndex, componentSelector, model,
+ recalculateQueued, false));
+ iterateModelTree(compAdded.get(), compRemoved.get(), model, models, true);
+ debugShell.addListener(SWT.Dispose, e -> models.forEach(m ->
+ {
+ m.removeComponentAddedListener(compAdded.get());
+ m.removeComponentRemovedListener(compRemoved.get());
+ }));
+ queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model);
new Label(debugShell, SWT.NONE).setText("Target state ID: ");
Text stateIDText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
stateIDText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
int componentIndex = componentSelector.getSelectionIndex();
if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
throw new RuntimeException("No component selected");
- GUIComponent target = componentsByItemIndex.get(componentIndex);
+ ModelComponent target = componentsByItemIndex.get(componentIndex);
String valueString = valueText.getText();
Object value;
if (radioBit.getSelection())
debugShell.open();
}
- private void recalculateComponentSelector(List<GUIComponent> componentsByItemIndex, Combo componentSelector, ViewModel model)
+ private void compsChanged(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved, ModelComponent c,
+ List<LogicModel> models, List<ModelComponent> componentsByItemIndex, Combo componentSelector, LogicModel model,
+ AtomicBoolean recalculateQueued, boolean add)
+ {
+ iterateSubmodelTree(compAdded, compRemoved, c, models, add);
+ queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model);
+ }
+
+ private void iterateSubmodelTree(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved,
+ ModelComponent c, List<LogicModel> models, boolean add)
{
+ if (c instanceof SubmodelComponent)
+ iterateModelTree(compAdded, compRemoved, ((SubmodelComponent) c).submodel, models, add);
+ }
+
+ private void iterateModelTree(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved,
+ LogicModel model, List<LogicModel> models, boolean add)
+ {
+ if (add ^ models.contains(model))
+ {
+ if (add)
+ {
+ models.add(model);
+ model.addComponentAddedListener(compAdded);
+ model.addComponentRemovedListener(compRemoved);
+ } else
+ {
+ models.remove(model);
+ model.removeComponentAddedListener(compAdded);
+ model.removeComponentRemovedListener(compRemoved);
+ }
+ for (ModelComponent c : model.getComponentsByName().values())
+ iterateSubmodelTree(compAdded, compRemoved, c, models, add);
+ }
+ }
+
+ private void queueRecalculateComponentSelector(AtomicBoolean recalculateQueued, List<ModelComponent> componentsByItemIndex,
+ Combo componentSelector, LogicModel model)
+ {
+ if (recalculateQueued.compareAndSet(false, true))
+ getDisplay().asyncExec(() -> recalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model));
+ }
+
+ private void recalculateComponentSelector(AtomicBoolean recalculateQueued, List<ModelComponent> componentsByItemIndex,
+ Combo componentSelector, LogicModel model)
+ {
+ recalculateQueued.set(false);
componentsByItemIndex.clear();
componentSelector.setItems();
addComponentSelectorItems(componentsByItemIndex, "", componentSelector, model);
}
- private void addComponentSelectorItems(List<GUIComponent> componentsByItemIndex, String base, Combo componentSelector, ViewModel model)
+ private void addComponentSelectorItems(List<ModelComponent> componentsByItemIndex, String base, Combo componentSelector,
+ LogicModel model)
{
- for (GUIComponent c : model.getComponentsByName().values())
- if (!(c instanceof WireCrossPoint || c instanceof SubmodelInterface))
+ model.getComponentsByName().values().stream().sorted((c1, c2) -> c1.name.compareTo(c2.name)).forEach(c ->
+ {
+ if (!(c instanceof ModelWireCrossPoint || c instanceof SubmodelInterface))
{
String item = base + c.name;
componentsByItemIndex.add(c);
if (c instanceof SubmodelComponent)
addComponentSelectorItems(componentsByItemIndex, item + " -> ", componentSelector, ((SubmodelComponent) c).submodel);
}
+ });
}
-
}
\ No newline at end of file