11#include <QRegularExpression>
25 m_ui->lineEditSearch->setAccessibleName(tr(
"Search elements"));
26 m_ui->lineEditSearch->setWhatsThis(tr(
"Type to filter the palette by element name. Press "
27 "Enter to add the first match to the circuit."));
28 m_ui->tabElements->setAccessibleName(tr(
"Element palette"));
29 m_ui->tabElements->setWhatsThis(tr(
"Elements grouped by category. Drag one onto the canvas, "
30 "or double-click to add it to the active circuit."));
32 connect(m_ui->lineEditSearch, &QLineEdit::textChanged,
this, &ElementPalette::onSearchTextChanged);
33 connect(m_ui->lineEditSearch, &QLineEdit::returnPressed,
this, &ElementPalette::onSearchReturnPressed);
41 const QRegularExpression regex(
".*" + QRegularExpression::escape(query) +
".*",
42 QRegularExpression::CaseInsensitiveOption);
43 return regex.match(name).hasMatch();
50 const int ioTabIndex = tabIndex(
"io");
51 if (ioTabIndex != -1) {
52 m_ui->tabElements->setCurrentIndex(ioTabIndex);
54 m_ui->tabElements->setCurrentIndex(0);
57 populateMenu(m_ui->verticalSpacer_InOut, {
"InputVcc",
"InputGnd",
"InputButton",
"InputSwitch",
"InputRotary",
"Clock",
"Led",
"Display7",
"Display14",
"Display16",
"Buzzer",
"AudioBox"}, m_ui->scrollAreaWidgetContents_InOut->layout());
58 populateMenu(m_ui->verticalSpacer_Gates, {
"And",
"Or",
"Not",
"Nand",
"Nor",
"Xor",
"Xnor",
"Node"}, m_ui->scrollAreaWidgetContents_Gates->layout());
59 populateMenu(m_ui->verticalSpacer_Combinational,{
"TruthTable",
"Mux",
"Demux"}, m_ui->scrollAreaWidgetContents_Combinational->layout());
60 populateMenu(m_ui->verticalSpacer_Memory, {
"DLatch",
"SRLatch",
"DFlipFlop",
"JKFlipFlop",
"TFlipFlop"}, m_ui->scrollAreaWidgetContents_Memory->layout());
61 populateMenu(m_ui->verticalSpacer_Misc, {
"Text",
"Line"}, m_ui->scrollAreaWidgetContents_Misc->layout());
67 m_ui->scrollAreaWidgetContents_IC->layout()->removeItem(m_ui->verticalSpacer_IC);
70 const auto items = m_ui->scrollAreaWidgetContents_IC->findChildren<
ElementLabel *>();
71 for (
auto *item : items) {
75 const auto items2 = m_ui->scrollAreaWidgetContents_Search->findChildren<
ElementLabel *>();
76 for (
auto *item : items2) {
77 if (item->elementType() == ElementType::IC) {
82 if (currentFile.exists()) {
83 QDir directory(currentFile.absoluteDir());
85 QStringList files = directory.entryList({
"*.panda",
"*.PANDA"}, QDir::Files);
87 files.removeAll(currentFile.fileName());
88 for (qsizetype i = files.size() - 1; i >= 0; --i) {
89 if (files.at(i).at(0) ==
'.') {
94 for (
const QString &file : std::as_const(files)) {
95 const QPixmap pixmap(
":/Components/Logic/ic-panda.svg");
100 auto *item =
new ElementLabel(pixmap, ElementType::IC, file, m_ui->scrollAreaWidgetContents_IC);
101 item->setObjectName(
"label_ic");
102 connectDoubleClickAdd(item);
103 m_ui->scrollAreaWidgetContents_IC->layout()->addWidget(item);
105 auto *item2 =
new ElementLabel(pixmap, ElementType::IC, file, m_ui->scrollAreaWidgetContents_Search);
106 item2->setObjectName(
"label_ic");
107 connectDoubleClickAdd(item2);
108 m_ui->scrollAreaWidgetContents_Search->layout()->addWidget(item2);
112 m_ui->scrollAreaWidgetContents_IC->layout()->addItem(m_ui->verticalSpacer_IC);
118 const auto existingLabels = m_ui->scrollAreaWidgetContents_EmbeddedIC->findChildren<
ElementLabel *>(
"label_embedded_ic");
119 for (
auto *item : existingLabels) {
123 const auto searchLabels = m_ui->scrollAreaWidgetContents_Search->findChildren<
ElementLabel *>(
"label_embedded_ic");
124 for (
auto *item : searchLabels) {
133 QStringList seenBlobNames;
139 m_ui->scrollAreaWidgetContents_EmbeddedIC->layout()->removeItem(m_ui->verticalSpacer_EmbeddedIC);
141 const QPixmap pixmap(
":/Components/Logic/ic-panda-embedded.svg");
143 for (
const QString &bn : std::as_const(seenBlobNames)) {
144 auto *item =
new ElementLabel(pixmap, ElementType::IC, bn, m_ui->scrollAreaWidgetContents_EmbeddedIC,
true);
145 item->setObjectName(
"label_embedded_ic");
146 connectDoubleClickAdd(item);
147 m_ui->scrollAreaWidgetContents_EmbeddedIC->layout()->addWidget(item);
149 auto *item2 =
new ElementLabel(pixmap, ElementType::IC, bn, m_ui->scrollAreaWidgetContents_Search,
true);
150 item2->setObjectName(
"label_embedded_ic");
151 connectDoubleClickAdd(item2);
152 m_ui->scrollAreaWidgetContents_Search->layout()->addWidget(item2);
155 m_ui->scrollAreaWidgetContents_EmbeddedIC->layout()->addItem(m_ui->verticalSpacer_EmbeddedIC);
160 const auto items = m_ui->tabElements->findChildren<
ElementLabel *>();
161 for (
auto *item : items) {
168 const int memoryTabIndex = tabIndex(
"memory");
169 if (memoryTabIndex != -1) {
173 const auto labels = m_ui->memory->findChildren<
ElementLabel *>();
174 for (
auto *label : labels) {
175 label->updateTheme();
179void ElementPalette::onSearchTextChanged(
const QString &text)
183 const int searchTabIndex = tabIndex(
"search");
185 if (text.isEmpty()) {
188 m_ui->
tabElements->setCurrentIndex(m_lastTabIndex);
189 if (searchTabIndex != -1) {
190 m_ui->
tabElements->setTabEnabled(searchTabIndex,
false);
195 if (m_lastTabIndex == -1) {
196 m_lastTabIndex = m_ui->tabElements->currentIndex();
200 m_ui->tabElements->tabBar()->hide();
201 if (searchTabIndex != -1) {
202 m_ui->tabElements->setCurrentIndex(searchTabIndex);
203 m_ui->tabElements->setTabEnabled(searchTabIndex,
true);
208 const auto allItems = m_ui->scrollArea_Search->findChildren<ElementLabel *>();
214 const QRegularExpression regex1(QString(
"^label_.*%1.*").arg(QRegularExpression::escape(text)), QRegularExpression::CaseInsensitiveOption);
215 QList<ElementLabel *> searchResults;
216 for (
auto *item : allItems) {
217 if (regex1.match(item->objectName()).hasMatch()) {
218 searchResults.append(item);
223 for (
auto *item : allItems) {
225 searchResults.append(item);
230 for (
auto *item : allItems) {
231 if (item->objectName() == QLatin1String(
"label_ic") &&
nameMatchesSearch(item->icFileName(), text)) {
232 searchResults.append(item);
236 for (
auto *item : allItems) {
237 item->setHidden(
true);
239 for (
auto *item : std::as_const(searchResults)) {
240 item->setVisible(
true);
244 m_ui->scrollAreaWidgetContents_Search->layout()->addItem(m_ui->verticalSpacer_Search);
247void ElementPalette::onSearchReturnPressed()
249 const auto allLabels = m_ui->scrollArea_Search->findChildren<ElementLabel *>();
252 for (
auto *label : std::as_const(allLabels)) {
253 if (label->isVisible()) {
255 m_ui->lineEditSearch->clear();
261void ElementPalette::populateMenu(QSpacerItem *spacer,
const QStringList &names, QLayout *layout)
263 layout->removeItem(spacer);
267 QWidget *
const container = layout->parentWidget();
268 for (
const auto &name : names) {
274 const QString objectName =
"label_" + name.toLower();
276 auto *categoryLabel =
new ElementLabel(pixmap, type, name, container);
277 categoryLabel->setObjectName(objectName);
278 connectDoubleClickAdd(categoryLabel);
279 layout->addWidget(categoryLabel);
281 auto *searchLabel =
new ElementLabel(pixmap, type, name, m_ui->scrollAreaWidgetContents_Search);
282 searchLabel->setObjectName(objectName);
283 connectDoubleClickAdd(searchLabel);
284 m_ui->scrollAreaWidgetContents_Search->layout()->addWidget(searchLabel);
287 layout->addItem(spacer);
290void ElementPalette::connectDoubleClickAdd(
ElementLabel *label)
297void ElementPalette::setupTabIcons()
300 const int ioTabIndex = tabIndex(
"io");
301 const int gatesTabIndex = tabIndex(
"gates");
302 const int combinationalTabIndex = tabIndex(
"combinational");
303 const int memoryTabIndex = tabIndex(
"memory");
304 const int icTabIndex = tabIndex(
"ic");
305 const int miscTabIndex = tabIndex(
"misc");
306 const int searchTabIndex = tabIndex(
"search");
308 if (ioTabIndex != -1) m_ui->tabElements->setTabIcon(ioTabIndex, QIcon(
":/Components/Input/buttonOff.svg"));
309 if (gatesTabIndex != -1) m_ui->tabElements->setTabIcon(gatesTabIndex, QIcon(
":/Components/Logic/xor.svg"));
310 if (combinationalTabIndex != -1) m_ui->tabElements->setTabIcon(combinationalTabIndex, QIcon(
":/Components/Logic/truthtable-rotated.svg"));
311 if (memoryTabIndex != -1) m_ui->tabElements->setTabIcon(memoryTabIndex, QIcon(
DFlipFlop::pixmapPath()));
312 if (icTabIndex != -1) m_ui->tabElements->setTabIcon(icTabIndex, QIcon(
":/Components/Logic/ic-panda.svg"));
313 if (miscTabIndex != -1) m_ui->tabElements->setTabIcon(miscTabIndex, QIcon(
":/Components/Misc/text.png"));
315 if (searchTabIndex != -1) m_ui->tabElements->setTabEnabled(searchTabIndex,
false);
318int ElementPalette::tabIndex(
const QString &objectName)
const
320 for (
int i = 0; i < m_ui->tabElements->count(); ++i) {
321 QWidget *tabWidget = m_ui->tabElements->widget(i);
322 if (tabWidget && tabWidget->objectName() == objectName) {
Graphic element for the D flip-flop.
Singleton factory for all circuit element types.
Draggable element icon+label widget used in the element palette.
ElementPalette: manages the left-panel element palette and search UI.
Abstract base class for all graphical circuit elements.
MainWindowUi: hand-written UI class for the MainWindow.
Main circuit editing scene with undo/redo and user interaction.
static QString pixmapPath()
static QPixmap pixmap(const ElementType type)
Returns the pixmap icon for the given element type.
static ElementType textToType(const QString &text)
Converts an element type name string to its ElementType enum value.
Icon and name widget shown in the left-panel element palette.
void addToSceneRequested(QMimeData *mimeData)
ElementPalette(MainWindowUi *ui, QObject *parent=nullptr)
Constructs the palette controller.
void updateICList(const QFileInfo ¤tFile)
Refreshes the IC tab to reflect .panda files next to currentFile.
void updateTheme()
Updates the memory tab icon and all memory-tab ElementLabel themes.
void populate()
Populates all category tabs with ElementLabel widgets and sets tab icons.
void retranslateLabels()
Calls updateName() on every ElementLabel in the palette (on language change).
static bool nameMatchesSearch(const QString &name, const QString &query)
void updateEmbeddedICList(Scene *scene)
Refreshes the embedded-IC section of the palette from the scene's blob registry.
void addElementRequested(QMimeData *mimeData)
Emitted when the user presses Enter in the search box.
const QMap< QString, QByteArray > & blobMap() const
Returns a const reference to the full blob map (name → .panda bytes).
QSpacerItem * verticalSpacer_Search
QWidget * scrollAreaWidgetContents_Search
Main circuit editing scene.
ICRegistry * icRegistry()
Returns the IC definition registry for this scene.