Interactor allows reactions on mouse pressed and mouse released.

Added a button
This commit is contained in:
hneemann 2016-04-12 20:18:42 +02:00
parent 48b7ced179
commit 3aecf4150f
11 changed files with 289 additions and 44 deletions

View File

@ -0,0 +1,43 @@
package de.neemann.digital.core.io;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.NodeException;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.element.AttributeKey;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.lang.Lang;
/**
* @author hneemann
*/
public class Button implements Element {
public static final ElementTypeDescription DESCRIPTION = new ElementTypeDescription(Button.class)
.addAttribute(AttributeKey.Rotate)
.addAttribute(AttributeKey.Label);
private final ObservableValue output;
private final String label;
public Button(ElementAttributes attributes) {
output = new ObservableValue("out", 1);
label = attributes.get(AttributeKey.Label);
}
@Override
public void setInputs(ObservableValue... inputs) throws NodeException {
throw new NodeException(Lang.get("err_noInputsAvailable"));
}
@Override
public ObservableValue[] getOutputs() {
return new ObservableValue[]{output};
}
@Override
public void registerNodes(Model model) {
model.addSignal(label, output);
}
}

View File

@ -6,10 +6,8 @@ import de.neemann.digital.core.element.AttributeListener;
import de.neemann.digital.core.element.Element; import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.shapes.Drawable; import de.neemann.digital.draw.shapes.*;
import de.neemann.digital.draw.shapes.Interactor;
import de.neemann.digital.draw.shapes.Shape; import de.neemann.digital.draw.shapes.Shape;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.components.CircuitComponent; import de.neemann.digital.gui.components.CircuitComponent;
import javax.swing.*; import javax.swing.*;
@ -29,7 +27,7 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
private transient GraphicMinMax minMax; private transient GraphicMinMax minMax;
private transient Shape shape; private transient Shape shape;
private transient IOState ioState; private transient IOState ioState;
private transient Interactor interactor; private transient InteractorInterface interactor;
private transient Element element; private transient Element element;
private transient ShapeFactory shapeFactory; private transient ShapeFactory shapeFactory;
private Vector pos; private Vector pos;
@ -267,20 +265,51 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
} }
/** /**
* Is called if this element is clicked by the mouse. * Is called if this element is clicked with the mouse.
* the call is delegated to the {@link Interactor} of the {@link Shape} * The call is delegated to the {@link Interactor} of the {@link Shape}
* *
* @param cc the calling {@link CircuitComponent} * @param cc the calling {@link CircuitComponent}
* @param pos the position * @param pos the position
* @return true if model is changed * @return true if model is changed
*/ */
public boolean clicked(CircuitComponent cc, Point pos) { public boolean elementClicked(CircuitComponent cc, Point pos) {
if (interactor != null) if (interactor != null)
return interactor.clicked(cc, pos, ioState, element); return interactor.clicked(cc, pos, ioState, element);
else else
return false; return false;
} }
/**
* Is called if this element is clicked with the mouse.
* The call is delegated to the {@link Interactor} of the {@link Shape}
*
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @return true if model is changed
*/
public boolean elementPressed(CircuitComponent cc, Point pos) {
if (interactor != null)
return interactor.pressed(cc, pos, ioState, element);
else
return false;
}
/**
* Is called if this element is clicked with the mouse.
* The call is delegated to the {@link Interactor} of the {@link Shape}
*
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @return true if model is changed
*/
public boolean elementReleased(CircuitComponent cc, Point pos) {
if (interactor != null)
return interactor.released(cc, pos, ioState, element);
else
return false;
}
@Override @Override
public void attributeChanged(AttributeKey key) { public void attributeChanged(AttributeKey key) {
shape = null; shape = null;

View File

@ -2,6 +2,8 @@ package de.neemann.digital.draw.graphics;
import java.awt.*; import java.awt.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
/** /**
* Used to draw on a {@link Graphics2D} instance. * Used to draw on a {@link Graphics2D} instance.
@ -32,13 +34,18 @@ public class GraphicSwing implements Graphic {
@Override @Override
public void drawPolygon(Polygon p, Style style) { public void drawPolygon(Polygon p, Style style) {
applyStyle(style); applyStyle(style);
java.awt.Polygon poly = new java.awt.Polygon(); Path2D path = new GeneralPath();
boolean first = true;
for (Vector v : p) for (Vector v : p)
poly.addPoint(v.x, v.y); if (first) {
first = false;
path.moveTo(v.x, v.y);
} else
path.lineTo(v.x, v.y);
if (style.isFilled()) if (p.isClosed())
gr.fill(poly); path.closePath();
gr.draw(poly); gr.draw(path);
} }
@Override @Override

View File

@ -10,10 +10,7 @@ import de.neemann.digital.core.flipflops.D_FF;
import de.neemann.digital.core.flipflops.JK_FF; import de.neemann.digital.core.flipflops.JK_FF;
import de.neemann.digital.core.flipflops.RS_FF; import de.neemann.digital.core.flipflops.RS_FF;
import de.neemann.digital.core.flipflops.T_FF; import de.neemann.digital.core.flipflops.T_FF;
import de.neemann.digital.core.io.Const; import de.neemann.digital.core.io.*;
import de.neemann.digital.core.io.In;
import de.neemann.digital.core.io.Out;
import de.neemann.digital.core.io.Probe;
import de.neemann.digital.core.memory.*; import de.neemann.digital.core.memory.*;
import de.neemann.digital.core.wiring.*; import de.neemann.digital.core.wiring.*;
import de.neemann.digital.gui.components.data.DummyElement; import de.neemann.digital.gui.components.data.DummyElement;
@ -51,6 +48,7 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
add(In.DESCRIPTION, menu); add(In.DESCRIPTION, menu);
add(Out.DESCRIPTION, menu); add(Out.DESCRIPTION, menu);
add(Out.LEDDESCRIPTION, menu); add(Out.LEDDESCRIPTION, menu);
add(Button.DESCRIPTION, menu);
add(Probe.DESCRIPTION, menu); add(Probe.DESCRIPTION, menu);
add(Clock.DESCRIPTION, menu); add(Clock.DESCRIPTION, menu);
add(Reset.DESCRIPTION, menu); add(Reset.DESCRIPTION, menu);

View File

@ -0,0 +1,94 @@
package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.Observer;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.draw.elements.Pin;
import de.neemann.digital.draw.elements.Pins;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.graphics.Polygon;
import de.neemann.digital.gui.components.CircuitComponent;
import java.awt.*;
import static de.neemann.digital.draw.shapes.OutputShape.SIZE;
/**
* @author hneemann
*/
public class ButtonShape implements Shape {
private static final int HEIGHT = SIZE / 2;
private final String label;
private IOState ioState;
public ButtonShape(ElementAttributes attr) {
this.label = attr.getLabel();
}
@Override
public Pins getPins() {
return new Pins().add(new Pin(new Vector(0, 0), "out", Pin.Direction.output));
}
@Override
public InteractorInterface applyStateMonitor(IOState ioState, Observer guiObserver) {
this.ioState = ioState;
ioState.getOutput(0).addObserverToValue(guiObserver);
return new InteractorInterface() {
@Override
public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
return false;
}
@Override
public boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element) {
ObservableValue value = ioState.getOutput(0);
value.setValue(1);
return true;
}
@Override
public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element) {
ObservableValue value = ioState.getOutput(0);
value.setValue(0);
return true;
}
};
}
@Override
public void drawTo(Graphic graphic, boolean heighLight) {
boolean down = false;
if (ioState != null) down = ioState.getOutput(0).getBool();
if (down) {
graphic.drawPolygon(new Polygon(true)
.add(-SIZE * 2 - 1, -SIZE)
.add(-1, -SIZE)
.add(-1, SIZE)
.add(-SIZE * 2 - 1, SIZE), Style.NORMAL);
} else {
int t = Style.NORMAL.getThickness() / 4;
graphic.drawPolygon(new Polygon(true)
.add(-SIZE * 2 - 1 - HEIGHT, -SIZE - HEIGHT)
.add(-1 - HEIGHT, -SIZE - HEIGHT)
.add(-1, -SIZE)
.add(-1, SIZE)
.add(-SIZE * 2 - 1, SIZE)
.add(-SIZE * 2 - 1 - HEIGHT, SIZE - HEIGHT), Style.NORMAL);
graphic.drawPolygon(new Polygon(false)
.add(-1 - HEIGHT, -SIZE + t - HEIGHT)
.add(-1 - HEIGHT, SIZE - HEIGHT)
.add(t - SIZE * 2 - 1 - HEIGHT, SIZE - HEIGHT), Style.NORMAL);
graphic.drawLine(new Vector(-1 - HEIGHT, SIZE - HEIGHT), new Vector(-1 - t, SIZE - t), Style.NORMAL);
}
Vector textPos = new Vector(-SIZE * 3, -4);
graphic.drawText(textPos, textPos.add(1, 0), label, Orientation.RIGHTCENTER, Style.NORMAL);
}
}

View File

@ -4,16 +4,19 @@ import de.neemann.digital.core.Model;
import de.neemann.digital.core.ModelEvent; import de.neemann.digital.core.ModelEvent;
import de.neemann.digital.core.Observer; import de.neemann.digital.core.Observer;
import de.neemann.digital.core.element.AttributeKey; import de.neemann.digital.core.element.AttributeKey;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.draw.elements.IOState; import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.draw.elements.Pins; import de.neemann.digital.draw.elements.Pins;
import de.neemann.digital.draw.graphics.Graphic; import de.neemann.digital.draw.graphics.Graphic;
import de.neemann.digital.draw.model.ModelDescription; import de.neemann.digital.draw.model.ModelDescription;
import de.neemann.digital.draw.model.ModelEntry; import de.neemann.digital.draw.model.ModelEntry;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.components.OrderMerger; import de.neemann.digital.gui.components.OrderMerger;
import de.neemann.digital.gui.components.data.DataSet; import de.neemann.digital.gui.components.data.DataSet;
import de.neemann.digital.gui.components.data.DataSetObserver; import de.neemann.digital.gui.components.data.DataSetObserver;
import java.awt.*;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@ -40,9 +43,12 @@ public class DataShape implements Shape {
@Override @Override
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) { public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
return (cc, pos, ioState1, element) -> { return new Interactor() {
@Override
public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
dataSet.clear(); dataSet.clear();
return false; return false;
}
}; };
} }

View File

@ -14,15 +14,15 @@ import java.awt.*;
* @author hneemann * @author hneemann
* @see InputShape * @see InputShape
*/ */
public interface Interactor { public abstract class Interactor implements InteractorInterface {
/** @Override
* Called if clicked on running model public boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element) {
* return false;
* @param cc the CircuitComponent }
* @param pos the popuplocation on screen
* @param ioState the state of the element @Override
* @return true if model is changed public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element) {
*/ return false;
boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element); }
} }

View File

@ -0,0 +1,47 @@
package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.gui.components.CircuitComponent;
import java.awt.*;
/**
* The VisualParts InteractorInterface instance is called if the element is clicked
* during execution. So the User can interact with the element during execution.
* Used at the InputShape to let the user toggle the inputs state.
*
* @author hneemann
* @see InputShape
*/
public interface InteractorInterface {
/**
* Called if clicked on running model
*
* @param cc the CircuitComponent
* @param pos the popuplocation on screen
* @param ioState the state of the element
* @return true if model is changed
*/
boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element);
/**
* Called mouse is pressed on running model
*
* @param cc the CircuitComponent
* @param pos the popuplocation on screen
* @param ioState the state of the element
* @return true if model is changed
*/
boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element);
/**
* Called mouse is released on running model
*
* @param cc the CircuitComponent
* @param pos the popuplocation on screen
* @param ioState the state of the element
* @return true if model is changed
*/
boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element);
}

View File

@ -34,7 +34,7 @@ public interface Shape extends Drawable {
* @param guiObserver can be used to update the GUI by calling hasChanged, maybe null * @param guiObserver can be used to update the GUI by calling hasChanged, maybe null
* @return the interactor is called if the shape is clicked during running mode, maybe null * @return the interactor is called if the shape is clicked during running mode, maybe null
*/ */
Interactor applyStateMonitor(IOState ioState, Observer guiObserver); InteractorInterface applyStateMonitor(IOState ioState, Observer guiObserver);
/** /**
* Allows the shape to make its drawing dependent of the model. * Allows the shape to make its drawing dependent of the model.

View File

@ -6,10 +6,7 @@ import de.neemann.digital.core.basic.*;
import de.neemann.digital.core.element.AttributeKey; import de.neemann.digital.core.element.AttributeKey;
import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.ElementTypeDescription; import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.core.io.Const; import de.neemann.digital.core.io.*;
import de.neemann.digital.core.io.In;
import de.neemann.digital.core.io.Out;
import de.neemann.digital.core.io.Probe;
import de.neemann.digital.core.memory.RAMDualPort; import de.neemann.digital.core.memory.RAMDualPort;
import de.neemann.digital.core.memory.RAMSinglePort; import de.neemann.digital.core.memory.RAMSinglePort;
import de.neemann.digital.core.wiring.*; import de.neemann.digital.core.wiring.*;
@ -48,6 +45,7 @@ public final class ShapeFactory {
map.put(Const.DESCRIPTION.getName(), ConstShape::new); map.put(Const.DESCRIPTION.getName(), ConstShape::new);
map.put(Out.DESCRIPTION.getName(), OutputShape::new); map.put(Out.DESCRIPTION.getName(), OutputShape::new);
map.put(Out.LEDDESCRIPTION.getName(), LEDShape::new); map.put(Out.LEDDESCRIPTION.getName(), LEDShape::new);
map.put(Button.DESCRIPTION.getName(), ButtonShape::new);
map.put(Probe.DESCRIPTION.getName(), ProbeShape::new); map.put(Probe.DESCRIPTION.getName(), ProbeShape::new);
map.put(Clock.DESCRIPTION.getName(), ClockShape::new); map.put(Clock.DESCRIPTION.getName(), ClockShape::new);
map.put(Out.SEVENDESCRIPTION.getName(), SevenSegShape::new); map.put(Out.SEVENDESCRIPTION.getName(), SevenSegShape::new);

View File

@ -242,6 +242,7 @@ public class CircuitComponent extends JComponent {
/** /**
* Sets a circuit to this component * Sets a circuit to this component
*
* @param circuit the circuit * @param circuit the circuit
*/ */
public void setCircuit(Circuit circuit) { public void setCircuit(Circuit circuit) {
@ -709,19 +710,42 @@ public class CircuitComponent extends JComponent {
} }
} }
private interface Actor {
boolean interact(CircuitComponent cc, Point p);
}
private final class MouseControllerRun extends MouseController { private final class MouseControllerRun extends MouseController {
private MouseControllerRun(Cursor cursor) { private MouseControllerRun(Cursor cursor) {
super(cursor); super(cursor);
} }
@Override
void pressed(MouseEvent e) {
VisualElement ve = circuit.getElementAt(getPosVector(e));
if (ve != null)
interact(e, ve::elementPressed);
}
@Override
void released(MouseEvent e) {
VisualElement ve = circuit.getElementAt(getPosVector(e));
if (ve != null)
interact(e, ve::elementReleased);
}
@Override @Override
void clicked(MouseEvent e) { void clicked(MouseEvent e) {
VisualElement ve = circuit.getElementAt(getPosVector(e)); VisualElement ve = circuit.getElementAt(getPosVector(e));
if (ve != null) { if (ve != null)
interact(e, ve::elementClicked);
}
private void interact(MouseEvent e, Actor actor) {
Point p = new Point(e.getX(), e.getY()); Point p = new Point(e.getX(), e.getY());
SwingUtilities.convertPointToScreen(p, CircuitComponent.this); SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
boolean modelHasChanged = ve.clicked(CircuitComponent.this, p); boolean modelHasChanged = actor.interact(CircuitComponent.this, p);
if (modelHasChanged) { if (modelHasChanged) {
if (manualChangeObserver != null) if (manualChangeObserver != null)
manualChangeObserver.hasChanged(); manualChangeObserver.hasChanged();
@ -729,6 +753,5 @@ public class CircuitComponent extends JComponent {
repaint(); repaint();
} }
} }
}
} }