package era.mi.gui;
-import java.util.HashSet;
-import java.util.Set;
-
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
+import era.mi.gui.model.ViewModel;
import era.mi.gui.model.components.GUIComponent;
-import era.mi.gui.model.wires.GUIWire;
import era.mi.gui.model.wires.Pin;
import net.haspamelodica.swt.helper.gcs.GeneralGC;
import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
*/
public class LogicUICanvas extends ZoomableCanvas
{
- private final Set<GUIComponent> components;
- private final Set<GUIWire> wires;
+ private final ViewModel model;
- public LogicUICanvas(Composite parent, int style)
+ public LogicUICanvas(Composite parent, int style, ViewModel model)
{
super(parent, style);
- components = new HashSet<>();
- wires = new HashSet<>();
+ this.model = model;
+
+ model.addComponentAddedListener(c -> redrawThreadsafe());
+ model.addWireAddedListener(c -> redrawThreadsafe());
addZoomedRenderer(gc ->
{
Rectangle visibleRegion = new Rectangle(offX, offY, gW / zoom, gH / zoom);
- components.forEach(c -> drawComponent(gc, c, visibleRegion));
+ model.getComponents().forEach(c -> drawComponent(gc, c, visibleRegion));
});
- addZoomedRenderer(gc -> wires.forEach(w -> w.render(gc)));
+ addZoomedRenderer(gc -> model.getWires().forEach(w -> w.render(gc)));
addListener(SWT.MouseDown, this::mouseDown);
}
- /**
- * Add a component to be drawn. Returns the given component for convenience.
- *
- * @author Daniel Kirschten
- */
- // TODO replace with model change listener
- public <C extends GUIComponent> C addComponent(C component)
- {
- components.add(component);
- return component;
- }
-
- /**
- * Add a graphical wire between the given connection points of the given components. The given components have to be added and the given
- * connection points have to be connected logically first.
- *
- * @author Daniel Kirschten
- */
- // TODO replace with model change listener
- public void addWire(Pin pin1, Pin pin2, Point... path)
- {
- wires.add(new GUIWire(this::redrawThreadsafe, pin1, pin2, path));
- }
-
private void drawComponent(GeneralGC gc, GUIComponent component, Rectangle visibleRegion)
{
component.render(gc, visibleRegion);
if (e.button == 1)
{
Point click = displayToWorldCoords(e.x, e.y);
- for (GUIComponent component : components)
- if (component.getBounds().contains(click))
+ for (GUIComponent component : model.getComponents())
+ if (component.getBounds().contains(click) && component.clicked(click.x, click.y))
{
- if (component.clicked(click.x, click.y))
- redraw();
+ redraw();
break;
}
}
display = new Display();
shell = new Shell(display);
shell.setLayout(new FillLayout());
- ui = new LogicUICanvas(shell, SWT.NONE);
+ ui = new LogicUICanvas(shell, SWT.NONE, model);
ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(ui);
userInput.buttonDrag = 3;
import era.mi.gui.model.ViewModel;
import era.mi.gui.model.components.GUIAndGate;
import era.mi.gui.model.components.GUINotGate;
+import era.mi.gui.model.wires.GUIWire;
public class Playground
{
public static void addComponentsAndWires(LogicUICanvas ui, ViewModel model)
{
- GUIAndGate andGate = ui.addComponent(new GUIAndGate(model));
+ GUIAndGate andGate = new GUIAndGate(model);
andGate.moveTo(10, 10);
- GUINotGate notGate = ui.addComponent(new GUINotGate(model));
+ GUINotGate notGate = new GUINotGate(model);
notGate.moveTo(10, 40);
- ui.addWire(andGate.getPins().get(0), notGate.getPins().get(1));
+ new GUIWire(model, andGate.getPins().get(0), notGate.getPins().get(1));
}
}
\ No newline at end of file
package era.mi.gui.model;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
public class ViewModel
{
private final List<GUIComponent> components;
+ private final List<GUIComponent> componentsUnmodifiable;
private final List<GUIWire> wires;
+ private final List<GUIWire> wiresUnmodifiable;
private final List<Consumer<GUIComponent>> componentAddedListeners;
private final List<Consumer<GUIComponent>> componentRemovedListeners;
public ViewModel()
{
components = new ArrayList<>();
+ componentsUnmodifiable = Collections.unmodifiableList(components);
wires = new ArrayList<>();
+ wiresUnmodifiable = Collections.unmodifiableList(wires);
componentAddedListeners = new ArrayList<>();
componentRemovedListeners = new ArrayList<>();
* Adds the given component to the list of components and calls all componentAddedListeners. Don't call this method from application
* code as it is automatically called in GUIComponent::new.
*/
- public void addComponent(GUIComponent component)
+ public void componentCreated(GUIComponent component)
{
if (components.contains(component))
throw new IllegalStateException("Don't add the same component twice!");
* Removes the given component from the list of components and calls all componentRemovedListeners. Don't call this method from
* application code as it is automatically called in GUIComponent::destroy.
*/
- public void removeComponent(GUIComponent component)
+ public void componentDestroyed(GUIComponent component)
{
if (!components.contains(component))
throw new IllegalStateException("Don't remove the same component twice!");
callComponentRemovedListeners(component);
}
+ /**
+ * Adds the given component to the list of components and calls all componentAddedListeners. Don't call this method from application
+ * code as it is automatically called in GUIComponent::new.
+ */
+ public void wireCreated(GUIWire wire)
+ {
+ if (wires.contains(wire))
+ throw new IllegalStateException("Don't add the same wire twice!");
+ wires.add(wire);
+ callWireAddedListeners(wire);
+ }
+
+ /**
+ * Removes the given component from the list of components and calls all componentRemovedListeners. Don't call this method from
+ * application code as it is automatically called in GUIComponent::destroy.
+ */
+ public void wireDestroyed(GUIWire wire)
+ {
+ if (!wires.contains(wire))
+ throw new IllegalStateException("Don't remove the same wire twice!");
+ wires.remove(wire);
+ callWireRemovedListeners(wire);
+ }
+
+ public List<GUIComponent> getComponents()
+ {
+ return componentsUnmodifiable;
+ }
+
+ public List<GUIWire> getWires()
+ {
+ return wiresUnmodifiable;
+ }
+
// @formatter:off
public void addComponentAddedListener (Consumer<GUIComponent> listener){componentAddedListeners .add (listener);}
public void addComponentRemovedListener (Consumer<GUIComponent> listener){componentRemovedListeners.add (listener);}
this.componentMovedListeners = new ArrayList<>();
this.pinAddedListeners = new ArrayList<>();
this.pinRemovedListeners = new ArrayList<>();
+
+ model.componentCreated(this);
}
public void moveTo(double x, double y)
return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
}
- // TODO
/**
- * Called when this component is clicked. Relative coordinates of the click are given. Returns true if this component has to be redrawn.
+ * Called when this component is clicked. Absolute coordinates of the click are given. Returns true if this component consumed this
+ * click.
*/
public boolean clicked(double x, double y)
{
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
+import era.mi.gui.model.ViewModel;
import era.mi.logic.types.Bit;
import era.mi.logic.wires.Wire;
import net.haspamelodica.swt.helper.gcs.GeneralGC;
private Wire wire;
- public GUIWire(Runnable redraw, Pin pin1, Pin pin2, Point... path)
+ public GUIWire(ViewModel model, Pin pin1, Pin pin2, Point... path)
{
this.path = new double[path.length * 2 + 4];
for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2)
pos = pin2.getPos();
this.path[this.path.length - 2] = pos.x;
this.path[this.path.length - 1] = pos.y;
+
+ model.wireCreated(this);
}
public void render(GeneralGC gc)