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.ElementAttributes;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.shapes.Drawable;
import de.neemann.digital.draw.shapes.Interactor;
import de.neemann.digital.draw.shapes.*;
import de.neemann.digital.draw.shapes.Shape;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.components.CircuitComponent;
import javax.swing.*;
@ -29,7 +27,7 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
private transient GraphicMinMax minMax;
private transient Shape shape;
private transient IOState ioState;
private transient Interactor interactor;
private transient InteractorInterface interactor;
private transient Element element;
private transient ShapeFactory shapeFactory;
private Vector pos;
@ -267,20 +265,51 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
}
/**
* Is called if this element is clicked by the mouse.
* the call is delegated to the {@link Interactor} of the {@link Shape}
* 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 clicked(CircuitComponent cc, Point pos) {
public boolean elementClicked(CircuitComponent cc, Point pos) {
if (interactor != null)
return interactor.clicked(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 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
public void attributeChanged(AttributeKey key) {
shape = null;

View File

@ -2,6 +2,8 @@ package de.neemann.digital.draw.graphics;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
/**
* Used to draw on a {@link Graphics2D} instance.
@ -32,13 +34,18 @@ public class GraphicSwing implements Graphic {
@Override
public void drawPolygon(Polygon p, Style style) {
applyStyle(style);
java.awt.Polygon poly = new java.awt.Polygon();
Path2D path = new GeneralPath();
boolean first = true;
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())
gr.fill(poly);
gr.draw(poly);
if (p.isClosed())
path.closePath();
gr.draw(path);
}
@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.RS_FF;
import de.neemann.digital.core.flipflops.T_FF;
import de.neemann.digital.core.io.Const;
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.io.*;
import de.neemann.digital.core.memory.*;
import de.neemann.digital.core.wiring.*;
import de.neemann.digital.gui.components.data.DummyElement;
@ -51,6 +48,7 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
add(In.DESCRIPTION, menu);
add(Out.DESCRIPTION, menu);
add(Out.LEDDESCRIPTION, menu);
add(Button.DESCRIPTION, menu);
add(Probe.DESCRIPTION, menu);
add(Clock.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.Observer;
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.draw.elements.IOState;
import de.neemann.digital.draw.elements.Pins;
import de.neemann.digital.draw.graphics.Graphic;
import de.neemann.digital.draw.model.ModelDescription;
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.data.DataSet;
import de.neemann.digital.gui.components.data.DataSetObserver;
import java.awt.*;
import java.util.ArrayList;
/**
@ -40,9 +43,12 @@ public class DataShape implements Shape {
@Override
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
return (cc, pos, ioState1, element) -> {
dataSet.clear();
return false;
return new Interactor() {
@Override
public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
dataSet.clear();
return false;
}
};
}

View File

@ -14,15 +14,15 @@ import java.awt.*;
* @author hneemann
* @see InputShape
*/
public interface Interactor {
public abstract class Interactor implements 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);
@Override
public boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element) {
return false;
}
@Override
public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element) {
return false;
}
}

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
* @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.

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.ElementAttributes;
import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.core.io.Const;
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.io.*;
import de.neemann.digital.core.memory.RAMDualPort;
import de.neemann.digital.core.memory.RAMSinglePort;
import de.neemann.digital.core.wiring.*;
@ -48,6 +45,7 @@ public final class ShapeFactory {
map.put(Const.DESCRIPTION.getName(), ConstShape::new);
map.put(Out.DESCRIPTION.getName(), OutputShape::new);
map.put(Out.LEDDESCRIPTION.getName(), LEDShape::new);
map.put(Button.DESCRIPTION.getName(), ButtonShape::new);
map.put(Probe.DESCRIPTION.getName(), ProbeShape::new);
map.put(Clock.DESCRIPTION.getName(), ClockShape::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
*
* @param circuit the circuit
*/
public void setCircuit(Circuit circuit) {
@ -709,25 +710,47 @@ public class CircuitComponent extends JComponent {
}
}
private interface Actor {
boolean interact(CircuitComponent cc, Point p);
}
private final class MouseControllerRun extends MouseController {
private MouseControllerRun(Cursor 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
void clicked(MouseEvent e) {
VisualElement ve = circuit.getElementAt(getPosVector(e));
if (ve != null) {
Point p = new Point(e.getX(), e.getY());
SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
boolean modelHasChanged = ve.clicked(CircuitComponent.this, p);
if (modelHasChanged) {
if (manualChangeObserver != null)
manualChangeObserver.hasChanged();
} else
repaint();
}
if (ve != null)
interact(e, ve::elementClicked);
}
private void interact(MouseEvent e, Actor actor) {
Point p = new Point(e.getX(), e.getY());
SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
boolean modelHasChanged = actor.interact(CircuitComponent.this, p);
if (modelHasChanged) {
if (manualChangeObserver != null)
manualChangeObserver.hasChanged();
} else
repaint();
}
}