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");
97 auto *item =
new ElementLabel(pixmap, ElementType::IC, file, m_ui->scrollAreaWidgetContents_IC);
98 connectDoubleClickAdd(item);
99 m_ui->scrollAreaWidgetContents_IC->layout()->addWidget(item);
101 auto *item2 =
new ElementLabel(pixmap, ElementType::IC, file, m_ui->scrollAreaWidgetContents_Search);
102 connectDoubleClickAdd(item2);
103 m_ui->scrollAreaWidgetContents_Search->layout()->addWidget(item2);
107 m_ui->scrollAreaWidgetContents_IC->layout()->addItem(m_ui->verticalSpacer_IC);
113 const auto existingLabels = m_ui->scrollAreaWidgetContents_EmbeddedIC->findChildren<
ElementLabel *>(
"label_embedded_ic");
114 for (
auto *item : existingLabels) {
118 const auto searchLabels = m_ui->scrollAreaWidgetContents_Search->findChildren<
ElementLabel *>(
"label_embedded_ic");
119 for (
auto *item : searchLabels) {
128 QStringList seenBlobNames;
134 m_ui->scrollAreaWidgetContents_EmbeddedIC->layout()->removeItem(m_ui->verticalSpacer_EmbeddedIC);
136 const QPixmap pixmap(
":/Components/Logic/ic-panda-embedded.svg");
138 for (
const QString &bn : std::as_const(seenBlobNames)) {
139 auto *item =
new ElementLabel(pixmap, ElementType::IC, bn, m_ui->scrollAreaWidgetContents_EmbeddedIC,
true);
140 item->setObjectName(
"label_embedded_ic");
141 connectDoubleClickAdd(item);
142 m_ui->scrollAreaWidgetContents_EmbeddedIC->layout()->addWidget(item);
144 auto *item2 =
new ElementLabel(pixmap, ElementType::IC, bn, m_ui->scrollAreaWidgetContents_Search,
true);
145 item2->setObjectName(
"label_embedded_ic");
146 connectDoubleClickAdd(item2);
147 m_ui->scrollAreaWidgetContents_Search->layout()->addWidget(item2);
150 m_ui->scrollAreaWidgetContents_EmbeddedIC->layout()->addItem(m_ui->verticalSpacer_EmbeddedIC);
155 const auto items = m_ui->tabElements->findChildren<
ElementLabel *>();
156 for (
auto *item : items) {
163 const int memoryTabIndex = tabIndex(
"memory");
164 if (memoryTabIndex != -1) {
168 const auto labels = m_ui->memory->findChildren<
ElementLabel *>();
169 for (
auto *label : labels) {
170 label->updateTheme();
174void ElementPalette::onSearchTextChanged(
const QString &text)
178 const int searchTabIndex = tabIndex(
"search");
180 if (text.isEmpty()) {
183 m_ui->
tabElements->setCurrentIndex(m_lastTabIndex);
184 if (searchTabIndex != -1) {
185 m_ui->
tabElements->setTabEnabled(searchTabIndex,
false);
190 if (m_lastTabIndex == -1) {
191 m_lastTabIndex = m_ui->tabElements->currentIndex();
195 m_ui->tabElements->tabBar()->hide();
196 if (searchTabIndex != -1) {
197 m_ui->tabElements->setCurrentIndex(searchTabIndex);
198 m_ui->tabElements->setTabEnabled(searchTabIndex,
true);
203 const auto allItems = m_ui->scrollArea_Search->findChildren<ElementLabel *>();
209 const QRegularExpression regex1(QString(
"^label_.*%1.*").arg(QRegularExpression::escape(text)), QRegularExpression::CaseInsensitiveOption);
210 QList<ElementLabel *> searchResults;
211 for (
auto *item : allItems) {
212 if (regex1.match(item->objectName()).hasMatch()) {
213 searchResults.append(item);
218 for (
auto *item : allItems) {
220 searchResults.append(item);
225 for (
auto *item : allItems) {
226 if (item->objectName() == QLatin1String(
"label_ic") &&
nameMatchesSearch(item->icFileName(), text)) {
227 searchResults.append(item);
231 for (
auto *item : allItems) {
232 item->setHidden(
true);
234 for (
auto *item : std::as_const(searchResults)) {
235 item->setVisible(
true);
239 m_ui->scrollAreaWidgetContents_Search->layout()->addItem(m_ui->verticalSpacer_Search);
242void ElementPalette::onSearchReturnPressed()
244 const auto allLabels = m_ui->scrollArea_Search->findChildren<ElementLabel *>();
247 for (
auto *label : std::as_const(allLabels)) {
248 if (label->isVisible()) {
250 m_ui->lineEditSearch->clear();
256void ElementPalette::populateMenu(QSpacerItem *spacer,
const QStringList &names, QLayout *layout)
258 layout->removeItem(spacer);
262 QWidget *
const container = layout->parentWidget();
263 for (
const auto &name : names) {
267 auto *categoryLabel =
new ElementLabel(pixmap, type, name, container);
268 connectDoubleClickAdd(categoryLabel);
269 layout->addWidget(categoryLabel);
271 auto *searchLabel =
new ElementLabel(pixmap, type, name, m_ui->scrollAreaWidgetContents_Search);
272 connectDoubleClickAdd(searchLabel);
273 m_ui->scrollAreaWidgetContents_Search->layout()->addWidget(searchLabel);
276 layout->addItem(spacer);
279void ElementPalette::connectDoubleClickAdd(
ElementLabel *label)
286void ElementPalette::setupTabIcons()
289 const int ioTabIndex = tabIndex(
"io");
290 const int gatesTabIndex = tabIndex(
"gates");
291 const int combinationalTabIndex = tabIndex(
"combinational");
292 const int memoryTabIndex = tabIndex(
"memory");
293 const int icTabIndex = tabIndex(
"ic");
294 const int miscTabIndex = tabIndex(
"misc");
295 const int searchTabIndex = tabIndex(
"search");
297 if (ioTabIndex != -1) m_ui->tabElements->setTabIcon(ioTabIndex, QIcon(
":/Components/Input/buttonOff.svg"));
298 if (gatesTabIndex != -1) m_ui->tabElements->setTabIcon(gatesTabIndex, QIcon(
":/Components/Logic/xor.svg"));
299 if (combinationalTabIndex != -1) m_ui->tabElements->setTabIcon(combinationalTabIndex, QIcon(
":/Components/Logic/truthtable-rotated.svg"));
300 if (memoryTabIndex != -1) m_ui->tabElements->setTabIcon(memoryTabIndex, QIcon(
DFlipFlop::pixmapPath()));
301 if (icTabIndex != -1) m_ui->tabElements->setTabIcon(icTabIndex, QIcon(
":/Components/Logic/ic-panda.svg"));
302 if (miscTabIndex != -1) m_ui->tabElements->setTabIcon(miscTabIndex, QIcon(
":/Components/Misc/text.png"));
304 if (searchTabIndex != -1) m_ui->tabElements->setTabEnabled(searchTabIndex,
false);
307int ElementPalette::tabIndex(
const QString &objectName)
const
309 for (
int i = 0; i < m_ui->tabElements->count(); ++i) {
310 QWidget *tabWidget = m_ui->tabElements->widget(i);
311 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.