wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
SignalDelegate.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 <QPainter>
7#include <QStyleOptionViewItem>
8
10#include "App/Core/Enums.h"
11
12namespace
13{
14// Waveform colors: green for output rows, blue for input rows.
15const QColor kOutputColor(0, 128, 0);
16const QColor kInputColor(0x75, 0x8e, 0xff);
19const QColor kUnknownColor(0x9e, 0x9e, 0x9e);
20const QColor kErrorColor(0xd3, 0x2f, 0x2f);
21
22// Geometry, as fractions of the cell, decoded from the original 100x30 SVGs.
23constexpr double kLineThickness = 4.0 / 30.0;
24constexpr double kBarWidth = 4.0 / 100.0;
25constexpr double kHighTop = 8.0 / 30.0;
26constexpr double kLowTop = 20.0 / 30.0;
27constexpr double kHighBottom = 12.0 / 30.0;
28constexpr double kLowBottom = 24.0 / 30.0;
29} // namespace
30
32 : QItemDelegate(parent)
33{
34}
35
37{
38 m_plotType = plotType;
39}
40
41WaveSegment SignalDelegate::segmentFor(const int value, const bool hasPrev, const int prevValue)
42{
43 // Cells carry a four-state Status, not a bit: Unknown is -1 and Error is 2. Treating the
44 // cell as "0 is low, everything else is high" would draw an undefined signal as a confident
45 // logic HIGH, while the canvas renders that same signal as a distinct grey wire
46 // (Connection.cpp / theme.m_connectionUnknown). Give them their own segments so the two views
47 // agree -- routinely reachable, since the engine canonicalises oscillating regions to Unknown
48 // and propagates it to every reader.
49 if (value == static_cast<int>(Status::Unknown)) {
51 }
52 if (value == static_cast<int>(Status::Error)) {
53 return WaveSegment::Error;
54 }
55 if (value == 0) {
56 return (hasPrev && (prevValue == 1)) ? WaveSegment::Falling : WaveSegment::Low;
57 }
58
59 return (hasPrev && (prevValue == 0)) ? WaveSegment::Rising : WaveSegment::High;
60}
61
62void SignalDelegate::drawWaveform(QPainter *painter, const QRectF &cell, const WaveSegment seg, const bool isInput) const
63{
64 const double x = cell.x();
65 const double y = cell.y();
66 const double w = cell.width();
67 const double h = cell.height();
68
69 // Undefined and conflicting signals get their own rendering rather than being folded into a
70 // plateau: a mid-level bar in the same vocabulary the canvas uses for wires -- grey for
71 // Unknown, red for Error (see Connection.cpp / ThemeManager's m_connectionUnknown and
72 // m_connectionError). Deliberately NOT a high or low plateau: the whole point is that the
73 // value is neither.
74 if ((seg == WaveSegment::Unknown) || (seg == WaveSegment::Error)) {
75 const QColor stateColor = (seg == WaveSegment::Unknown) ? kUnknownColor : kErrorColor;
76 QColor stateBand = stateColor;
77 stateBand.setAlpha(64);
78 painter->fillRect(QRectF(x, y, w, h), stateBand);
79 const double midTop = ((kHighTop + kLowTop) / 2.0) * h;
80 painter->fillRect(QRectF(x, y + midTop, w, kLineThickness * h), stateColor);
81 return;
82 }
83
84 const QColor color = isInput ? kInputColor : kOutputColor;
85 QColor bandColor = color;
86 bandColor.setAlpha(128); // 50% opacity translucent fill
87
88 // High plateau for High/Rising, low plateau for Low/Falling.
89 const bool high = (seg == WaveSegment::High) || (seg == WaveSegment::Rising);
90 const double lineTop = (high ? kHighTop : kLowTop) * h;
91 const double lineBottom = (high ? kHighBottom : kLowBottom) * h;
92 const double thickness = kLineThickness * h;
93
94 // Draw order matches the original SVGs: band, then plateau line, then edge bar.
95 painter->fillRect(QRectF(x, y + lineBottom, w, h - lineBottom), bandColor);
96 painter->fillRect(QRectF(x, y + lineTop, w, thickness), color);
97
98 if ((seg == WaveSegment::Rising) || (seg == WaveSegment::Falling)) {
99 // Vertical connector joining the high and low plateau levels at the cell's left edge.
100 const double barTop = kHighTop * h;
101 const double barBottom = kLowBottom * h;
102 painter->fillRect(QRectF(x, y + barTop, kBarWidth * w, barBottom - barTop), color);
103 }
104}
105
106void SignalDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
107{
108 // Number mode: render the "0"/"1" centered (no per-cell alignment role needed).
109 if (m_plotType == PlotType::Number) {
110 QStyleOptionViewItem opt(option);
111 opt.displayAlignment = Qt::AlignCenter;
112 QItemDelegate::paint(painter, opt, index);
113 return;
114 }
115
116 // Waveform mode: derive the segment from this cell's value and its left neighbour,
117 // and the input/output colour from the model's row partition — nothing is stored.
118 const int value = index.data().toInt();
119 const QModelIndex prev = index.siblingAtColumn(index.column() - 1);
120 const bool hasPrev = prev.isValid();
121 const WaveSegment seg = segmentFor(value, hasPrev, hasPrev ? prev.data().toInt() : 0);
122
123 bool isInput = true;
124 if (const auto *model = qobject_cast<const SignalModel *>(index.model())) {
125 isInput = model->isInputRow(index.row());
126 }
127
128 // Draw the selection highlight behind the waveform rather than over it
129 if (option.state & QStyle::State_Selected) {
130 painter->fillRect(option.rect, option.palette.highlight());
131 }
132
133 drawWaveform(painter, option.rect, seg, isInput);
134}
Central enumeration types for element types, groups, and signal status.
SignalDelegate: paints digital waveform graphics into table cells.
WaveSegment
Identifies which waveform segment a Line-mode cell draws.
@ Falling
High → low transition (low plateau + leading edge).
@ Low
Logic-low plateau (signal stays at 0).
@ Rising
Low → high transition (high plateau + leading edge).
@ High
Logic-high plateau (signal stays at 1).
@ Unknown
Undefined (Status::Unknown): drawn mid-level in the canvas's "unknown" grey.
@ Error
Conflicting drivers (Status::Error): drawn mid-level in the canvas's red.
PlotType
Controls how signal cells are rendered in the waveform table.
@ Number
Cells display the numeric value (0/1).
SignalModel for the beWavedDolphin waveform table.
void setPlotType(PlotType plotType)
Selects the cell rendering mode (waveform graphics vs. numeric text).
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
PlotType plotType() const
Returns the current cell rendering mode.
static WaveSegment segmentFor(int value, bool hasPrev, int prevValue)
Returns the waveform segment a cell should draw.
SignalDelegate(QObject *parent=nullptr)
Constructs the delegate.