wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
GraphicElement.cpp
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
5
6#include <cmath>
7#include <memory>
8
9#include <QCoreApplication>
10#include <QDir>
11#include <QFile>
12#include <QFileInfo>
13#include <QGraphicsScene>
14#include <QGraphicsSceneMouseEvent>
15#include <QKeyEvent>
16#include <QPainter>
17#include <QPixmap>
18#include <QRegularExpression>
19#include <QStyleOptionGraphicsItem>
20#include <QSvgRenderer>
21#include <QThread>
22
23#include "App/Core/Common.h"
24#include "App/Core/Constants.h"
30#include "App/Wiring/Port.h"
31
33static const QFont &labelFont()
34{
35 static const QFont font = [] {
36 QFont f("Sans Serif");
37 f.setBold(true);
38 return f;
39 }();
40 return font;
41}
42
44 : QGraphicsObject(parent)
45 , m_elementType(type)
46 , m_metadata(ElementMetadataRegistry::metadata(type))
47{
48 const ElementMetadata &metadata = m_metadata;
49 m_appearance.seedFromMetadata(metadata.defaultAppearances, metadata.alternativeAppearances, metadata.pixmapPath());
50 m_titleText = QCoreApplication::translate(metadata.trContext, metadata.titleText);
51 m_translatedName = QCoreApplication::translate(metadata.trContext, metadata.translatedName);
52 m_elementGroup = metadata.group;
53 m_hasColors = metadata.hasColors;
54 m_maxInputSize = metadata.maxInputSize;
55 m_maxOutputSize = metadata.maxOutputSize;
56 m_minInputSize = metadata.minInputSize;
57 m_minOutputSize = metadata.minOutputSize;
58
59 qCDebug(four) << "Setting flags of elements.";
60 // ItemSendsGeometryChanges is required so itemChange() receives ItemPositionChange and
61 // can snap movement to the grid
62 setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
63
64 qCDebug(four) << "Setting attributes.";
65 m_label->setVisible(metadata.hasLabel);
66 // Font is applied lazily in updateLabel() to avoid expensive text layout
67 // passes for elements that never display a label (e.g. IC sub-elements).
68 m_label->setParentItem(this);
69 m_label->setBrush(Qt::black);
70 // 64 px below origin keeps the label below the standard 64×64 element body
71 setLabelAnchor(QPointF(0, 64));
72
74 setToolTip(m_translatedName);
75
76 qCDebug(four) << "Including input and output ports.";
77 GraphicElement::setInputSize(static_cast<int>(metadata.minInputSize));
78 GraphicElement::setOutputSize(static_cast<int>(metadata.minOutputSize));
79
82
83 // DeviceCoordinateCache reuses the rendered pixmap when the device transform changes,
84 // giving a large speedup for elements that don't redraw on every pan/zoom
85 setCacheMode(QGraphicsItem::DeviceCoordinateCache);
86
87 if (m_appearance.hasDefaultAppearances()) {
88 m_appearance.setPixmap(0);
89 }
90}
91
93
95{
96 return m_elementType;
97}
98
100{
101 return m_elementGroup;
102}
103
105{
106 return m_appearance.pixmap();
107}
108
110{
111 return m_appearance.externalFiles();
112}
113
114void GraphicElement::setPixmap(const int index)
115{
116 m_appearance.setPixmap(index);
117}
118
119void GraphicElement::setPixmap(const QString &pixmapPath)
120{
121 m_appearance.setPixmap(pixmapPath);
122}
123
124const QVector<OutputPort *> &GraphicElement::outputs() const
125{
126 return m_ports.outputs();
127}
128
130{
131 return m_ports.inputPort(index);
132}
133
135{
136 return m_ports.outputPort(index);
137}
138
140{
141 return WirelessMode::None;
142}
143
145{
146 return false;
147}
148
149const QVector<InputPort *> &GraphicElement::inputs() const
150{
151 return m_ports.inputs();
152}
153
154QVector<Port *> GraphicElement::allPorts() const
155{
156 return m_ports.allPorts();
157}
158
159void GraphicElement::setInputs(const QVector<InputPort *> &inputs)
160{
161 m_ports.setInputs(inputs);
162}
163
165{
166 return m_appearance.pixmapCenter();
167}
168
170{
171 return portsBoundingRect().united(pixmap().rect());
172}
173
175{
176 QRectF rectChildren;
177 const auto children = childItems();
178
179 for (auto *child : children) {
180 if (auto *port = qgraphicsitem_cast<Port *>(child)) {
181 rectChildren = rectChildren.united(mapRectFromItem(port, port->boundingRect()));
182 }
183 }
184
185 return rectChildren;
186}
187
189{
190 return portsBoundingRect().united(QRectF(0, 0, 64, 64));
191}
192
193void GraphicElement::setLabelAnchor(const QPointF &pos)
194{
195 m_labelAnchor = pos;
196 m_label->setPos(pos); // baseline: always correct for non-rotatable elements
197 updateLabelOrientation(); // rotatable elements get position+orientation corrected on top
198}
199
200void GraphicElement::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
201{
202 Q_UNUSED(widget)
203 Q_UNUSED(option)
204
205 m_appearance.render(painter, boundingRect(), isSelected());
206}
207
208void GraphicElement::invalidateRenderCache()
209{
210 // Toggling the mode discards the cached device pixmap; re-enabling it forces the next
211 // paint to re-render the whole item from scratch — the same full regen a pan/zoom
212 // triggers. See the header for why a plain update() is not enough on a size change.
213 setCacheMode(QGraphicsItem::NoCache);
214 setCacheMode(QGraphicsItem::DeviceCoordinateCache);
215}
216
217void GraphicElement::setPortName(const QString &name)
218{
219 setObjectName(name);
220}
221
222void GraphicElement::setRotation(const qreal angle)
223{
224 m_orientation.setRotation(angle);
225}
226
228{
229 m_orientation.rotatePorts();
230}
231
233{
234 return m_orientation.angle();
235}
236
237void GraphicElement::setFlippedX(const bool flipped)
238{
239 m_orientation.setFlippedX(flipped);
240}
241
242void GraphicElement::setFlippedY(const bool flipped)
243{
244 m_orientation.setFlippedY(flipped);
245}
246
247void GraphicElement::setAppearance(const bool defaultAppearance, const QString &fileName)
248{
249 m_appearance.setAppearance(defaultAppearance, fileName);
250}
251
252void GraphicElement::setAppearanceAt(const int index, const QString &fileName)
253{
254 m_appearance.setAppearanceAt(index, fileName);
255}
256
257QList<std::pair<int, QString>> GraphicElement::appearanceStates() const
258{
259 // Default: single appearance at index 0
260 return {{0, tr("Default")}};
261}
262
263QPixmap GraphicElement::appearancePreviewPixmap(const int index, const QSize &size) const
264{
265 return m_appearance.previewPixmapAt(index, size);
266}
267
269{
270 qCDebug(five) << "Updating port positions that belong to the IC.";
271
272 // gridSize is 16 px; half that (8 px) is the port spacing unit so ports land
273 // on sub-grid snap points that wires can reach when snapped to the same grid.
274 const int step = Constants::gridSize / 2;
275
276 if (!m_ports.inputs().isEmpty()) {
277 // Centre the port column vertically around y=32 (the mid-point of a 64 px body).
278 // The first port starts at 32 - (count-1)*step so all ports are symmetrically distributed.
279 int y = 32 - (static_cast<int>(m_ports.inputs().size()) * step) + step;
280
281 for (auto *port : m_ports.inputs()) {
282 qCDebug(five) << "Setting input at " << 0 << ", " << y;
283
284 // Inputs are pinned to the left edge (x=0) of the 64 px body
285 port->setPos(0, y);
286
287 // Non-rotatable elements keep the pixmap fixed and orient their ports in place,
288 // applying the current rotation and flip about the element centre.
289 if (!rotatesGraphic()) {
290 m_orientation.orientPort(port);
291 }
292
293 // Ports are spaced 2*step (16 px) apart so they align with the
294 // full grid and remain easily connectable with straight wires.
295 y += step * 2;
296 }
297 }
298
299 if (!m_ports.outputs().isEmpty()) {
300 int y = 32 - (static_cast<int>(m_ports.outputs().size()) * step) + step;
301
302 for (auto *port : m_ports.outputs()) {
303 qCDebug(five) << "Setting output at " << 64 << ", " << y;
304
305 // Outputs are pinned to the right edge (x=64) of the 64 px body
306 port->setPos(64, y);
307
308 // Non-rotatable elements keep the pixmap fixed and orient their ports in place,
309 // applying the current rotation and flip about the element centre.
310 if (!rotatesGraphic()) {
311 m_orientation.orientPort(port);
312 }
313
314 y += step * 2;
315 }
316 }
317}
318
320{
321 // Reload appearance index 0, which is the currently active appearance for the element's
322 // present state (e.g. after a theme change or appearance file replacement).
323 setPixmap(0);
324}
325
326QVariant GraphicElement::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
327{
328 // Guard against changes fired during construction before the item is added to a scene
329 if (!scene()) {
330 return QGraphicsItem::itemChange(change, value);
331 }
332
333 if (change == ItemPositionChange) {
334 qCDebug(four) << "Align to grid.";
335 QPointF newPos = value.toPointF();
336 // Snap to half-grid (8 px steps) so elements always align with each other
337 // and with the port positions computed in updatePortsProperties().
338 const int gridSize = Constants::gridSize / 2;
339 const int xV = qRound(newPos.x() / gridSize) * gridSize;
340 const int yV = qRound(newPos.y() / gridSize) * gridSize;
341 return QPoint(xV, yV);
342 }
343
344 // Any geometric change (move, rotate, shear) requires redrawing all connected wires
345 if ((change == ItemScenePositionHasChanged) || (change == ItemRotationHasChanged) || (change == ItemTransformHasChanged)) {
346 qCDebug(four) << "Moves wires.";
347 for (auto *port : allPorts()) {
348 port->updateConnections();
349 }
350 }
351
352 if (change == ItemSelectedHasChanged) {
353 m_selected = value.toBool();
354 // Propagate selection highlight to all connected wires so users see which
355 // signals are attached to the selected element
356 highlight(m_selected);
357 }
358
359 return QGraphicsItem::itemChange(change, value);
360}
361
362bool GraphicElement::sceneEvent(QEvent *event)
363{
364 // Swallow Ctrl+click events so the scene's rubber-band selection logic can handle them
365 // without the element intercepting the press and starting a move instead
366 if (event->type() == QEvent::GraphicsSceneMousePress || event->type() == QEvent::GraphicsSceneMouseRelease) {
367 if (auto mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent *>(event)) {
368 if (mouseEvent->modifiers().testFlag(Qt::ControlModifier)) {
369 return true;
370 }
371 }
372 }
373
374 return QGraphicsItem::sceneEvent(event);
375}
376
377void GraphicElement::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
378{
379 if (hasLabel()) {
380 event->accept();
381 emit inlineEditRequested(this);
382 return;
383 }
384
385 QGraphicsItem::mouseDoubleClickEvent(event);
386}
387
389{
390 return m_metadata.hasAudio;
391}
392
393QKeySequence GraphicElement::trigger() const
394{
395 return m_trigger;
396}
397
398void GraphicElement::setTrigger(const QKeySequence &trigger)
399{
400 m_trigger = trigger;
401 updateLabel();
402}
403
405{
406 return {};
407}
408
410{
411 QString label = m_labelText;
412
413 // If a keyboard trigger is assigned, append it in parentheses so users can see
414 // the shortcut directly on the canvas, e.g. "Clock (Space)"
415 if (hasTrigger() && !trigger().toString().isEmpty()) {
416 if (!label.isEmpty()) {
417 label += " ";
418 }
419 label += QString("(%1)").arg(trigger().toString());
420 }
421
422 // Apply the shared font on first use — deferred from the constructor to
423 // avoid an expensive QTextDocumentLayout pass for elements that never show a label.
424 if (!label.isEmpty() && m_label->font() != labelFont()) {
425 m_label->setFont(labelFont());
426 }
427
428 m_label->setText(label);
429 // The counter-orientation pivots on the label's centre, which the new text just moved.
430 updateLabelOrientation();
432}
433
434void GraphicElement::updateLabelOrientation()
435{
436 // Non-rotatable elements never transform their graphic (or its children), so their label is
437 // already upright as authored.
438 if (!rotatesGraphic()) {
439 return;
440 }
441
442 // The label is a child item and inherits the element's Flip∘Rotate. Left alone, its anchor
443 // point (m_labelAnchor, in the element's un-rotated local frame) would orbit around the
444 // rotation pivot along with everything else -- fine for ports/pins, but it leaves the label
445 // sweeping past wherever the pivot's "below"/"beside" edge currently points to, which for a
446 // wide/tall body (or once ports are packed along an edge) can land it squarely on a pin.
447 // Counter-rotate the anchor's *position* about the same pivot the element itself rotates
448 // about (pixmapCenter()), so it stays fixed in world space -- built by replaying the exact
449 // transform the element applies to its children (its own extra transform(), i.e. the flip
450 // matrix already set by ElementOrientation::applyFlipTransform(), composed with a rotation
451 // about that pivot matching rotation()) and inverting it, rather than hand-deriving the
452 // reversed composition.
453 const QPointF pivot = pixmapCenter();
454
455 QTransform pivotRotate;
456 pivotRotate.translate(pivot.x(), pivot.y());
457 pivotRotate.rotate(rotation());
458 pivotRotate.translate(-pivot.x(), -pivot.y());
459
460 const QTransform elementRotateFlip = pivotRotate * transform();
461 m_label->setPos(elementRotateFlip.inverted().map(m_labelAnchor));
462
463 // Independently counter-orient the glyphs themselves so the text reads upright and
464 // unmirrored too. Pivots about the label's own local origin (0,0) -- the same point setPos()
465 // above just anchored -- rather than its bounding-rect centre: pivoting about any *other*
466 // point here would translate the label's own origin away from that anchor as rotation/flip
467 // change (rotating/scaling about a point off-origin necessarily moves the origin), quietly
468 // reintroducing the same kind of drift setPos() above was just added to eliminate. A pivot
469 // choice only ever affects translation, never the rotation/scale factors, so this is exactly
470 // as effective at keeping the glyphs upright as pivoting about the centre was.
471 QTransform t;
472 t.rotate(-rotation());
473 t.scale(isFlippedX() ? -1 : 1, isFlippedY() ? -1 : 1);
474 m_label->setTransform(t);
475}
476
477void GraphicElement::setLabel(const QString &label)
478{
479 m_labelText = label;
480 updateLabel();
481}
482
484{
485 return m_labelText;
486}
487
489{
490 return m_label->sceneBoundingRect();
491}
492
493// Color cycle (forward): White → Red → Green → Blue → Purple → White
494// These two functions implement previous/next steps in that cycle so the
495// property editor can wrap around without knowing the full list
497{
498 if (color() == "White") return "Purple";
499 if (color() == "Red") return "White";
500 if (color() == "Green") return "Red";
501 if (color() == "Blue") return "Green";
502 if (color() == "Purple") return "Blue";
503 return "White"; // Standard
504}
505
507{
508 if (color() == "White") return "Red";
509 if (color() == "Red") return "Green";
510 if (color() == "Green") return "Blue";
511 if (color() == "Blue") return "Purple";
512 if (color() == "Purple") return "White";
513 return "White"; // Standard
514}
515
517{
519
520 m_label->setBrush(theme.m_graphicElementLabelColor);
521 m_appearance.applyTheme(theme);
522
523 for (auto *input : m_ports.inputs()) {
524 input->updateTheme();
525 }
526
527 for (auto *output : m_ports.outputs()) {
528 output->updateTheme();
529 }
530
531 update();
532}
533
535{
536 qCDebug(four) << "Checking if the element has the required signals to compute its value.";
537 // An element is valid only when every input port is satisfied (connected or optional)
538 const bool valid = std::all_of(m_ports.inputs().cbegin(), m_ports.inputs().cend(),
539 [](auto *input) { return input->isValid(); });
540
541 if (!valid) {
542 // Propagate invalid status downstream so the visual chain shows where validity breaks
543 for (auto *output : m_ports.outputs()) {
544 for (auto *conn : output->connections()) {
545 conn->setStatus(Status::Error);
546
547 if (auto *port = conn->otherPort(output)) {
548 port->setStatus(Status::Error);
549 }
550 }
551 }
552 }
553
554 return valid;
555}
556
558{
559 return m_hasColors;
560}
561
563{
564 return m_metadata.hasTrigger;
565}
566
567void GraphicElement::setColor(const QString &color)
568{
569 Q_UNUSED(color)
570}
571
573{
574 m_hasColors = hasColors;
575}
576
578{
579 return {};
580}
581
583{
584 if (color == "White") { return 0; }
585 if (color == "Red") { return 1; }
586 if (color == "Green") { return 2; }
587 if (color == "Blue") { return 3; }
588 if (color == "Purple") { return 4; }
589 return 0;
590}
591
592void GraphicElement::setAudio(const QString &audio)
593{
594 Q_UNUSED(audio)
595}
596
598{
599 return {};
600}
601
603{
604 return m_metadata.hasFrequency;
605}
606
608{
609 return m_metadata.hasDelay;
610}
611
613{
614 return m_metadata.hasLabel;
615}
616
618{
619 return m_metadata.hasTruthTable;
620}
621
623{
624 return m_metadata.hasAudioBox;
625}
626
628{
629 return m_metadata.hasVolume;
630}
631
633{
634 return 0.0f;
635}
636
638{
639}
640
641QList<PropertyDescriptor> GraphicElement::editableProperties() const
642{
643 QList<PropertyDescriptor> props;
644 if (hasLabel()) props.append({PropertyDescriptor::Type::Label});
645 if (hasColors()) props.append({PropertyDescriptor::Type::Color});
647 if (hasDelay()) props.append({PropertyDescriptor::Type::Delay});
648 if (hasAudio()) props.append({PropertyDescriptor::Type::Audio});
649 if (hasAudioBox()) props.append({PropertyDescriptor::Type::AudioBox});
650 if (hasVolume()) props.append({PropertyDescriptor::Type::Volume});
651 if (hasTrigger()) props.append({PropertyDescriptor::Type::Trigger});
655 return props;
656}
657
659{
660 return m_metadata.canChangeAppearance;
661}
662
664{
665 return m_metadata.rotatesGraphic;
666}
667
669{
670 return m_ports.inputSize();
671}
672
673void GraphicElement::setPortSize(const int size, const bool isInput)
674{
675 const int minSize = isInput ? minInputSize() : minOutputSize();
676 const int maxSize = isInput ? maxInputSize() : maxOutputSize();
677
678 if ((size < minSize) || (size > maxSize)) {
679 return;
680 }
681
682 // Adding/removing ports changes portsBoundingRect(), and thus boundingRect(): notify the
683 // scene's spatial index before mutating so its stale bounds can't crash a later paint.
684 prepareGeometryChange();
685
686 if (isInput) {
687 m_ports.resizeInputs(size);
688 } else {
689 m_ports.resizeOutputs(size);
690 }
691
693}
694
696{
697 setPortSize(size, true);
698}
699
701{
702 return m_ports.outputSize();
703}
704
706{
707 setPortSize(size, false);
708}
709
711{
712 return 0.0;
713}
714
715void GraphicElement::setFrequency(const double freq)
716{
717 Q_UNUSED(freq)
718}
719
721{
722 return 0.0;
723}
724
726{
727 Q_UNUSED(delay)
728}
729
731{
732 // Default no-op — decorative elements (Line, Text) have no simulation behaviour.
733}
734
736{
737 m_sim.reset(m_ports.outputs());
738}
739
740void GraphicElement::connectPredecessor(const int inputIndex, GraphicElement *source, const int outputPort)
741{
742 m_sim.connectPredecessor(inputIndex, source, outputPort);
743}
744
745void GraphicElement::initSimulationVectors(const int inputCount, const int outputCount)
746{
747 m_sim.initVectors(inputCount, outputCount, m_ports.outputs());
748}
749
751{
752 return static_cast<int>(m_minOutputSize);
753}
754
756{
757 return static_cast<int>(m_minInputSize);
758}
759
761{
762 return static_cast<int>(m_maxOutputSize);
763}
764
766{
767 return static_cast<int>(m_maxInputSize);
768}
769
771{
772 m_maxInputSize = static_cast<quint64>(maxInputSize);
773}
774
776{
777 m_maxOutputSize = static_cast<quint64>(maxOutputSize);
778}
779
781{
782 m_minInputSize = static_cast<quint64>(minInputSize);
783}
784
786{
787 m_minOutputSize = static_cast<quint64>(minOutputSize);
788}
789
790void GraphicElement::highlight(const bool isSelected)
791{
792 for (auto *port : allPorts()) {
793 for (auto *connection : port->connections()) {
794 // Skip connections already in the desired highlight state to avoid
795 // triggering unnecessary repaints
796 if (connection->highLight() == isSelected) {
797 continue;
798 }
799
800 connection->setHighLight(isSelected);
801 }
802 }
803}
804
812
813void GraphicElement::loadFromDrop(const QString &fileName, const QString &contextDir)
814{
815 Q_UNUSED(fileName)
816 Q_UNUSED(contextDir)
817}
Common logging utilities, the Pandaception error type, and helper macros.
#define qCDebug(category)
Definition Common.h:29
Connection: a wire that connects an output port to an input port in the circuit scene.
Shared numeric constants used across layers.
Singleton factory for all circuit element types.
Element metadata struct and global registry for compile-time element registration.
Enums::ElementType ElementType
Definition Enums.h:107
Enums::WirelessMode WirelessMode
Definition Enums.h:109
Enums::ElementGroup ElementGroup
Definition Enums.h:108
static const QFont & labelFont()
Shared label font — constructed once to avoid repeated QFont creation and fontconfig lookups.
Abstract base class for all graphical circuit elements.
Port classes: Port (base), InputPort, and OutputPort.
Deserialization/serialization context structs passed through load()/save() call chains.
Theme management types and singleton ThemeManager.
static QString translatedName(const ElementType type)
Returns the translated human-readable name for type.
Global registry of ElementMetadata, one entry per ElementType.
virtual void updateLogic()
Computes this element's output values from its current inputs.
QPointF pixmapCenter() const
Returns the centre point of the element's pixmap in local coordinates.
GraphicElement(ElementType type, QGraphicsItem *parent=nullptr)
Constructs a graphic element of the given type, fetching all properties from the metadata registry.
virtual void updateTheme()
Updates the element's visual theme according to the current dark/light palette.
void setFlippedY(bool flipped)
Sets the vertical mirror state and updates the item transform.
QPixmap appearancePreviewPixmap(const int index, const QSize &size) const
Returns a small preview pixmap for the appearance at index (see appearanceStates()).
QString m_translatedName
Translated element name used as tooltip and port object name.
virtual WirelessMode wirelessMode() const
Returns the wireless routing mode for this element.
virtual void setFrequency(const double freq)
Sets the clock frequency to freq (overridden by clock elements).
ElementType elementType() const
Returns the type identifier for this element.
void setHasColors(const bool hasColors)
Sets whether this element type supports color selection.
void setFlippedX(bool flipped)
Sets the horizontal mirror state and updates the item transform.
bool hasDelay() const
Returns true if this element type exposes a configurable clock phase delay.
virtual void setColor(const QString &color)
Sets the element color to color and refreshes the pixmap.
virtual QStringList externalFiles() const
ElementAppearance m_appearance
bool hasVolume() const
Returns true if this element type supports volume control.
virtual void setAudio(const QString &audio)
Sets the audio file associated with this element to audio.
int inputSize() const
Returns the current number of input ports.
QList< PropertyDescriptor > editableProperties() const
Returns the list of editable properties this element exposes in the ElementEditor.
bool hasTruthTable() const
Returns true if this element type has an editable truth table.
bool hasAudioBox() const
Returns true if this element type shows an audio selection box.
void retranslate()
Updates the translated display name, tooltip, and port object name after a locale change.
qreal rotation() const
Returns the current rotation angle of this element in degrees.
void setRotation(const qreal angle)
Rotates the element to angle degrees and updates port positions.
bool isFlippedY() const
Returns true if this element is mirrored along the Y axis (vertical flip).
QRectF portsBoundingRect() const
Returns the bounding rectangle that encompasses all child ports.
QVector< Port * > allPorts() const
Returns a combined list of all input and output ports as Port pointers.
void setLabelAnchor(const QPointF &pos)
void inlineEditRequested(GraphicElement *element)
QPixmap pixmap() const
Returns the pixmap currently displayed by this element.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
Paints the element onto the scene.
QRectF renderBodyBounds() const
Footprint of a "procedural render body" (IC/Mux/Demux/TruthTable): the nominal 64x64 body unioned wit...
QString label() const
Returns the user-visible label text for this element.
int maxOutputSize() const
Returns the maximum allowed number of output ports.
virtual QList< std::pair< int, QString > > appearanceStates() const
Returns the list of editable appearance states for this element. Each pair is (appearance list index,...
void setTrigger(const QKeySequence &trigger)
Sets the keyboard shortcut to trigger and updates the label.
InputPort * inputPort(const int index=0) const
Returns the input port at index (default 0).
virtual void loadFromDrop(const QString &fileName, const QString &contextDir)
Polymorphic interface for drag-drop initialization (replaces elementType() == IC checks).
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override
Requests inline label editing on double-click.
void setAppearanceAt(const int index, const QString &fileName)
Sets a custom appearance at a specific index in the appearance list.
int minInputSize() const
Returns the minimum allowed number of input ports.
void setInputs(const QVector< InputPort * > &inputs)
Replaces the input port vector with inputs.
virtual QString audio() const
Returns the name of the audio file currently associated with this element.
bool hasLabel() const
Returns true if this element type supports a user-editable label.
virtual double delay() const
Returns the clock phase delay in seconds (overridden by Clock; returns 0 for other elements).
virtual void setInputSize(const int size)
Adjusts the number of input ports to size, adding or removing ports as needed.
virtual double frequency() const
Returns the clock frequency in Hz (overridden by Clock; returns 0 for other elements).
~GraphicElement() override
Out-of-line so the unique_ptr to the forward-declared QSvgRenderer can be destroyed.
QKeySequence trigger() const
Returns the keyboard shortcut that activates this element.
void setLabel(const QString &label)
Sets the label text to label and refreshes the display.
void updateLabel()
Repositions and updates the label child item to reflect current state.
virtual QString color() const
Returns the name of the color currently applied to this element.
int outputSize() const
Returns the current number of output ports.
void initSimulationVectors(const int inputCount, const int outputCount)
Allocates simulation I/O vectors with inputs inputs and outputs outputs.
QGraphicsSimpleTextItem * m_label
Child text item that displays the label and optional trigger shortcut.
QRectF labelSceneBoundingRect() const
void setPortName(const QString &name)
Sets the object name of all ports to name for identification.
OutputPort * outputPort(const int index=0) const
Returns the output port at index (default 0).
QString m_titleText
Translated title text shown in UI panels (from metadata).
bool hasFrequency() const
Returns true if this element type exposes a configurable clock frequency.
QString nextColor() const
Returns the name of the next color in the element's color list.
static int colorNameToIndex(const QString &color)
Maps a color name ("White","Red","Green","Blue","Purple") to indices 0–4.
bool hasColors() const
Returns true if this element type supports color selection.
ElementGroup elementGroup() const
Returns the group this element belongs to.
int maxInputSize() const
Returns the maximum allowed number of input ports.
void setMaxInputSize(const int maxInputSize)
Sets the maximum number of input ports to maxInputSize.
QPointF m_labelAnchor
The label's intended anchor point in the element's un-rotated local frame; see setLabelAnchor().
virtual void setDelay(const double delay)
Sets the clock phase delay to delay (overridden by clock elements).
void setMaxOutputSize(const int maxOutputSize)
Sets the maximum number of output ports to maxOutputSize.
void setPixmap(const QString &pixmapPath)
Loads and applies the pixmap located at pixmapPath.
bool canChangeAppearance() const
Returns true if the user is allowed to choose a custom appearance for this element.
virtual void setAppearance(const bool defaultAppearance, const QString &fileName)
Switches the element's appearance.
int minOutputSize() const
Returns the minimum allowed number of output ports.
void connectPredecessor(const int inputIndex, GraphicElement *source, const int outputPort)
Connects simulation input inputIndex to output outputPort of source element.
bool isFlippedX() const
Returns true if this element is mirrored along the X axis (horizontal flip).
ElementPorts m_ports
virtual void resetSimState()
Resets all simulation-visible state to power-on defaults.
virtual QString genericProperties()
Returns a string encoding element-specific properties for serialization or display.
bool isValid()
Returns true if the element is fully initialised and connected correctly.
int type() const override
Returns the custom type identifier for this item.
virtual void setVolume(float vol)
Sets the audio playback volume to vol (0.0–1.0).
void setMinInputSize(const int minInputSize)
Sets the minimum number of input ports to minInputSize.
QRectF boundingRect() const override
Returns the bounding rectangle of this element in local coordinates.
const QVector< OutputPort * > & outputs() const
Returns a const reference to the vector of all output ports.
virtual void labelContentChanged()
virtual bool hasWirelessMode() const
Returns true if this element supports a configurable wireless routing mode.
virtual float volume() const
Returns the audio playback volume (0.0–1.0).
virtual void setOutputSize(const int size)
Adjusts the number of output ports to size, adding or removing ports as needed.
void setMinOutputSize(const int minOutputSize)
Sets the minimum number of output ports to minOutputSize.
QString previousColor() const
Returns the name of the previous color in the element's color list.
bool hasAudio() const
Returns true if this element type supports audio output.
bool sceneEvent(QEvent *event) override
Intercepts mouse-press and mouse-release events to handle Ctrl+click.
bool rotatesGraphic() const
virtual void updatePortsProperties()
Repositions and reconfigures all ports after the port count changes.
virtual void refresh()
Forces a visual refresh of the element's pixmap and ports.
bool hasTrigger() const
Returns true if this element type supports a keyboard trigger.
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
Handles item state changes such as position, rotation, and selection.
const QVector< InputPort * > & inputs() const
Returns a const reference to the vector of all input ports.
A port that receives a signal (the destination end of a wire).
Definition Port.h:229
A port that drives a signal (the source end of a wire).
Definition Port.h:261
Contains all color attributes for a theme.
QColor m_graphicElementLabelColor
static const ThemeAttributes & attributes()
Returns the current ThemeAttributes color set.
constexpr int gridSize
Scene grid unit in pixels (elements snap to gridSize/2).
Definition Constants.h:12
Compile-time-registered properties for one element type.
quint64 maxInputSize
Maximum number of input ports.
bool hasLabel
True if the element supports a user-editable label.
ElementGroup group
UI palette group this element belongs to.
quint64 maxOutputSize
Maximum number of output ports.
QStringList defaultAppearances
Built-in appearance resource paths.
QStringList alternativeAppearances
User-selectable alternative appearance paths.
bool hasColors
True if the element supports color selection.
const char * trContext
Translation context string for QCoreApplication::translate().
quint64 minInputSize
Minimum number of input ports.
const char * translatedName
Untranslated element name used for tooltips (QT_TRANSLATE_NOOP).
quint64 minOutputSize
Minimum number of output ports.
std::function< QString()> pixmapPath
Callable returning the default pixmap resource path (lazily evaluated).
const char * titleText
Untranslated title shown in the element palette (QT_TRANSLATE_NOOP).
@ Frequency
Clock oscillation frequency in Hz.
@ Delay
Phase delay as fraction of the clock period.
@ Appearance
Custom pixmap appearance selection.
@ Label
User-visible text label.
@ Audio
Audio note selection (buzzer tone).
@ WirelessModeSelector
Wireless routing mode (None / Tx / Rx) — Node elements only.
@ Volume
Audio playback volume (AudioBox, Buzzer).
@ Color
Color selection (LEDs, displays).
@ AudioBox
AudioBox file selection dialog.
@ Trigger
Keyboard trigger shortcut.
@ TruthTable
Editable truth table dialog.