Cet aperçu des bibliothèques Logisim se termine par un compteur Gray assez sophistiqué qui permet à l'utilisateur de modifier sa valeur actuelle à l'aide de l'outil Poke et de placer une étiquette sur le composant à l'aide de l'outil Text. Il personnalise également l'icône qui apparaît dans l'explorateur, associée à l'outil.
package com.cburch.gray; import java.net.URL; import javax.swing.ImageIcon; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.BitWidth; import com.cburch.logisim.data.Bounds; import com.cburch.logisim.data.Direction; import com.cburch.logisim.instance.Instance; import com.cburch.logisim.instance.InstanceFactory; import com.cburch.logisim.instance.InstancePainter; import com.cburch.logisim.instance.InstanceState; import com.cburch.logisim.instance.Port; import com.cburch.logisim.instance.StdAttr; import com.cburch.logisim.util.GraphicsUtil; import com.cburch.logisim.util.StringUtil; /** Fabrique un compteur qui itère sur les codes Gray. Ce compteur présente * plusieurs caractéristiques supplémentaires par rapport à la classe * SimpleGrayCounter. /* class GrayCounter extends InstanceFactory { public GrayCounter() { super("Gray Counter"); setOffsetBounds(Bounds.create(-30, -15, 30, 30)); setPorts(new Port[] { new Port(-30, 0, Port.INPUT, 1), new Port( 0, 0, Port.OUTPUT, StdAttr.WIDTH), }); /** Nous aurons des attributs de largeur, d'étiquette et de police * d'étiquette. Ces deux derniers attributs nous permettent d'associer * une étiquette au composant (bien que nous ayons également besoin de * configureNewInstance pour configurer l'emplacement de l'étiquette).*/ setAttributes( new Attribute[] { StdAttr.WIDTH, StdAttr.LABEL, StdAttr.LABEL_FONT }, new Object[] { BitWidth.create(4), "", StdAttr.DEFAULT_LABEL_FONT }); /** L'invocation de la méthode suivante permet de manipuler l'état * de l'instance à l'aide de l'outil Poke. */ setInstancePoker(CounterPoker.class); /** Les deux lignes suivantes font en sorte que la fenêtre de l'explorateur * affiche une icône personnalisée représentant le type de composant. * Il s'agit d'une image de 16x16. */ URL url = getClass().getClassLoader().getResource("com/cburch/gray/counter.gif"); if(url != null) setIcon(new ImageIcon(url)); } /** La méthode configureNewInstance est invoquée à chaque fois qu'une nouvelle * instance est créée. Dans la superclasse, la méthode ne fait rien, puisque * la nouvelle instance est déjà bien configurée par défaut. Mais il arrive * que l'on doive faire quelque chose de particulier pour chaque instance, * c'est pourquoi il faut surcharger la méthode. Dans ce cas, nous devons * configurer l'emplacement de son étiquette. */ protected void configureNewInstance(Instance instance) { Bounds bds = instance.getBounds(); instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT, bds.getX() + bds.getWidth() / 2, bds.getY() - 3, GraphicsUtil.H_CENTER, GraphicsUtil.V_BASELINE); } public void propagate(InstanceState state) { /** C'est la même chose que pour SimpleGrayCounter, sauf que nous utilisons * l'attribut StdAttr.WIDTH pour déterminer la largeur de bit avec laquelle * nous devons travailler. */ BitWidth width = state.getAttributeValue(StdAttr.WIDTH); CounterData cur = CounterData.get(state, width); boolean trigger = cur.updateClock(state.getPort(0)); if(trigger) cur.setValue(GrayIncrementer.nextGray(cur.getValue())); state.setPort(1, cur.getValue(), 9); } public void paintInstance(InstancePainter painter) { /** Il s'agit essentiellement de la même chose que pour SimpleGrayCounter, * à l'exception de l'invocation de painter.drawLabel pour dessiner * l'étiquette. */ painter.drawBounds(); painter.drawClock(0, Direction.EAST); painter.drawPort(1); painter.drawLabel(); if(painter.getShowState()) { BitWidth width = painter.getAttributeValue(StdAttr.WIDTH); CounterData state = CounterData.get(painter, width); Bounds bds = painter.getBounds(); GraphicsUtil.drawCenteredText(painter.getGraphics(), StringUtil.toHexString(width.getWidth(), state.getValue().toIntValue()), bds.getX() + bds.getWidth() / 2, bds.getY() + bds.getHeight() / 2); } } }
package com.cburch.gray; import java.awt.Color; import java.awt.Graphics; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import com.cburch.logisim.data.BitWidth; import com.cburch.logisim.data.Bounds; import com.cburch.logisim.data.Value; import com.cburch.logisim.instance.InstancePainter; import com.cburch.logisim.instance.InstancePoker; import com.cburch.logisim.instance.InstanceState; import com.cburch.logisim.instance.StdAttr; /** Lorsque l'utilisateur clique sur un compteur à l'aide de l'outil Poke, * un objet CounterPoker est créé et cet objet gère tous les événements de * l'utilisateur. Notez que CounterPoker est une classe spécifique à * GrayCounter, et qu'elle doit être une sous-classe de InstancePoker dans * le paquetage com.cburch.logisim.instance. */ public class CounterPoker extends InstancePoker { public CounterPoker() {} /** Détermine si l'endroit où la souris a été pressée doit entraîner * le déclenchement d'un poke. */ public boolean init(InstanceState state, MouseEvent e) { return state.getInstance().getBounds().contains(e.getX(), e.getY()); // N'importe quel endroit du rectangle principal déclenche le poke. L'utilisateur peut //avoir cliqué à l'intérieur d'une étiquette, mais celle-ci sera en dehors des limites. } /** Dessine un indicateur indiquant que le curseur est sélectionné. Ici, * nous allons dessiner un rectangle rouge autour de la valeur. */ public void paint(InstancePainter painter) { Bounds bds = painter.getBounds(); BitWidth width = painter.getAttributeValue(StdAttr.WIDTH); int len = (width.getWidth() + 3) / 4; Graphics g = painter.getGraphics(); g.setColor(Color.RED); int wid = 7 * len + 2; // width of caret rectangle int ht = 16; // height of caret rectangle g.drawRect(bds.getX() + (bds.getWidth() - wid) / 2, bds.getY() + (bds.getHeight() - ht) / 2, wid, ht); g.setColor(Color.BLACK); } /** Traite une touche en l'ajoutant simplement à la fin de la valeur actuelle. */ public void keyTyped(InstanceState state, KeyEvent e) { /* le convertit en chiffre hexadécimal ; s'il ne s'agit pas d'un chiffre hexadécimal, l'opération est interrompue.*/ int val = Character.digit(e.getKeyChar(), 16); BitWidth width = state.getAttributeValue(StdAttr.WIDTH); if(val < 0 || (val & width.getMask()) != val) return; /* calculer la valeur suivante*/ CounterData cur = CounterData.get(state, width); int newVal = (cur.getValue().toIntValue() * 16 + val) & width.getMask(); Value newValue = Value.createKnown(width, newVal); cur.setValue(newValue); state.fireInvalidated(); /** Vous pourriez être tenté de propager la valeur immédiatement ici, * en utilisant state.setPort. Cependant, le circuit peut actuellement * se propager dans un autre thread, et l'appel direct de setPort * pourrait interférer avec cela. L'utilisation de fireInvalidated * notifie le thread de propagation à Invoquer Propager sur le * compteur à sa prochaine occasion. } }
Suite : Lignes directrices.