mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-16 16:34:47 -04:00
Interactor allows reactions on mouse pressed and mouse released.
Added a button
This commit is contained in:
parent
48b7ced179
commit
3aecf4150f
43
src/main/java/de/neemann/digital/core/io/Button.java
Normal file
43
src/main/java/de/neemann/digital/core/io/Button.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user