From 134f23d69b3d937f4d699b3aa8c6b51322a07563 Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Sat, 25 Apr 2026 21:27:39 +0200 Subject: [PATCH] add splitter filters to ui --- src/lib/sim/BeltSystem.cpp | 16 ++++ src/lib/sim/BeltSystem.h | 9 ++ src/ui/SelectedBuildingPanel.cpp | 142 +++++++++++++++++++++++++++++++ src/ui/SelectedBuildingPanel.h | 10 +++ 4 files changed, 177 insertions(+) diff --git a/src/lib/sim/BeltSystem.cpp b/src/lib/sim/BeltSystem.cpp index 29406ed..5eefa50 100644 --- a/src/lib/sim/BeltSystem.cpp +++ b/src/lib/sim/BeltSystem.cpp @@ -89,6 +89,22 @@ void BeltSystem::setSplitterFilters(QPoint tile, it->second.filterB = filterB; } +std::optional BeltSystem::getSplitterInfo(QPoint tile) const +{ + const std::map, SplitterTile>::const_iterator it = + m_splitters.find(key(tile)); + if (it == m_splitters.end()) + { + return std::nullopt; + } + return SplitterInfo{ + it->second.outputA, + it->second.outputB, + it->second.filterA, + it->second.filterB + }; +} + // --------------------------------------------------------------------------- // Port interface // --------------------------------------------------------------------------- diff --git a/src/lib/sim/BeltSystem.h b/src/lib/sim/BeltSystem.h index 744f527..2fcfbb8 100644 --- a/src/lib/sim/BeltSystem.h +++ b/src/lib/sim/BeltSystem.h @@ -52,6 +52,15 @@ public: const std::vector& filterA, const std::vector& filterB); + struct SplitterInfo + { + Rotation outputA; + Rotation outputB; + std::vector filterA; + std::vector filterB; + }; + std::optional getSplitterInfo(QPoint tile) const; + // -- Port interface (buildings <-> belts) -------------------------------- // port.tile = the belt tile adjacent to the building // port.direction = direction items flow on that tile diff --git a/src/ui/SelectedBuildingPanel.cpp b/src/ui/SelectedBuildingPanel.cpp index dc92ed9..ed59102 100644 --- a/src/ui/SelectedBuildingPanel.cpp +++ b/src/ui/SelectedBuildingPanel.cpp @@ -3,16 +3,21 @@ #include #include #include +#include #include #include #include +#include #include #include +#include "BeltSystem.h" #include "Building.h" #include "BuildingSystem.h" #include "BuildingType.h" +#include "ItemType.h" +#include "Rotation.h" #include "Simulation.h" namespace @@ -57,6 +62,18 @@ bool isBeltLike(BuildingType type) return type == BuildingType::Belt || type == BuildingType::Splitter; } +QString rotationLabel(Rotation r) +{ + switch (r) + { + case Rotation::North: return "North (↑)"; + case Rotation::East: return "East (→)"; + case Rotation::South: return "South (↓)"; + case Rotation::West: return "West (←)"; + } + return ""; +} + } // namespace @@ -67,6 +84,7 @@ SelectedBuildingPanel::SelectedBuildingPanel(Simulation* sim, , m_sim(sim) , m_config(config) , m_singleId(kInvalidEntityId) + , m_splitterTile(0, 0) { m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(8, 8, 8, 8); @@ -76,18 +94,33 @@ SelectedBuildingPanel::SelectedBuildingPanel(Simulation* sim, m_titleLabel = new QLabel(this); m_recipeCombo = new QComboBox(this); m_clearBeltBtn = new QPushButton("Clear Items", this); + m_filterALabel = new QLabel(this); + m_filterAList = new QListWidget(this); + m_filterBLabel = new QLabel(this); + m_filterBList = new QListWidget(this); m_buffersLabel = new QLabel(this); m_buffersLabel->setWordWrap(true); + m_filterAList->setMaximumHeight(100); + m_filterBList->setMaximumHeight(100); + m_layout->addWidget(m_titleLabel); m_layout->addWidget(m_recipeCombo); m_layout->addWidget(m_clearBeltBtn); + m_layout->addWidget(m_filterALabel); + m_layout->addWidget(m_filterAList); + m_layout->addWidget(m_filterBLabel); + m_layout->addWidget(m_filterBList); m_layout->addWidget(m_buffersLabel); connect(m_recipeCombo, qOverload(&QComboBox::currentIndexChanged), this, &SelectedBuildingPanel::onRecipeChanged); connect(m_clearBeltBtn, &QPushButton::clicked, this, &SelectedBuildingPanel::onClearBelt); + connect(m_filterAList, &QListWidget::itemChanged, + this, &SelectedBuildingPanel::onSplitterFilterChanged); + connect(m_filterBList, &QListWidget::itemChanged, + this, &SelectedBuildingPanel::onSplitterFilterChanged); buildEmpty(); } @@ -120,6 +153,10 @@ void SelectedBuildingPanel::buildEmpty() m_titleLabel->hide(); m_recipeCombo->hide(); m_clearBeltBtn->hide(); + m_filterALabel->hide(); + m_filterAList->hide(); + m_filterBLabel->hide(); + m_filterBList->hide(); m_buffersLabel->hide(); } @@ -189,6 +226,19 @@ void SelectedBuildingPanel::buildSingle(EntityId id) m_clearBeltBtn->hide(); } + if (b->type == BuildingType::Splitter) + { + m_splitterTile = b->anchor; + buildSplitterFilters(m_splitterTile); + } + else + { + m_filterALabel->hide(); + m_filterAList->hide(); + m_filterBLabel->hide(); + m_filterBList->hide(); + } + refreshBuffers(b); } @@ -329,6 +379,10 @@ void SelectedBuildingPanel::buildMulti(const std::vector& ids) m_singleId = kInvalidEntityId; m_recipeCombo->hide(); m_clearBeltBtn->hide(); + m_filterALabel->hide(); + m_filterAList->hide(); + m_filterBLabel->hide(); + m_filterBList->hide(); m_buffersLabel->hide(); std::map counts; @@ -371,6 +425,94 @@ void SelectedBuildingPanel::onRecipeChanged(int comboIndex) m_sim->buildings().setRecipe(m_singleId, recipeId.toStdString()); } +void SelectedBuildingPanel::buildSplitterFilters(QPoint splitterTile) +{ + const std::optional info = + m_sim->belts().getSplitterInfo(splitterTile); + if (!info.has_value()) + { + m_filterALabel->hide(); + m_filterAList->hide(); + m_filterBLabel->hide(); + m_filterBList->hide(); + return; + } + + const std::vector items = allItemIds(); + + auto populateList = [&](QListWidget* list, QLabel* label, + const QString& dirLabel, + const std::vector& filter) + { + label->setText(dirLabel + " filter (empty = all):"); + list->blockSignals(true); + list->clear(); + for (const std::string& itemId : items) + { + QListWidgetItem* row = new QListWidgetItem( + QString::fromStdString(itemId), list); + const bool checked = filter.empty() + ? false + : std::find(filter.begin(), filter.end(), + ItemType{itemId}) != filter.end(); + row->setCheckState(checked ? Qt::Checked : Qt::Unchecked); + row->setFlags(row->flags() | Qt::ItemIsUserCheckable); + } + list->blockSignals(false); + label->show(); + list->show(); + }; + + populateList(m_filterAList, m_filterALabel, + rotationLabel(info->outputA), info->filterA); + populateList(m_filterBList, m_filterBLabel, + rotationLabel(info->outputB), info->filterB); +} + +void SelectedBuildingPanel::onSplitterFilterChanged() +{ + if (m_singleId == kInvalidEntityId) + { + return; + } + + auto collectFilter = [](QListWidget* list) -> std::vector + { + std::vector filter; + for (int i = 0; i < list->count(); ++i) + { + const QListWidgetItem* row = list->item(i); + if (row->checkState() == Qt::Checked) + { + filter.push_back(ItemType{row->text().toStdString()}); + } + } + return filter; + }; + + m_sim->belts().setSplitterFilters( + m_splitterTile, + collectFilter(m_filterAList), + collectFilter(m_filterBList)); +} + +std::vector SelectedBuildingPanel::allItemIds() const +{ + std::set seen; + for (const RecipeDef& recipe : m_config->recipes.recipes) + { + for (const RecipeIngredient& ing : recipe.inputs) + { + seen.insert(ing.item); + } + for (const RecipeOutput& out : recipe.outputs) + { + seen.insert(out.item); + } + } + return std::vector(seen.begin(), seen.end()); +} + void SelectedBuildingPanel::onClearBelt() { std::vector tiles; diff --git a/src/ui/SelectedBuildingPanel.h b/src/ui/SelectedBuildingPanel.h index b117eaa..15391c9 100644 --- a/src/ui/SelectedBuildingPanel.h +++ b/src/ui/SelectedBuildingPanel.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "Building.h" @@ -15,6 +16,7 @@ class Simulation; class QLabel; class QComboBox; +class QListWidget; class QPushButton; class QVBoxLayout; @@ -33,6 +35,7 @@ public slots: private slots: void onRecipeChanged(int comboIndex); void onClearBelt(); + void onSplitterFilterChanged(); private: void rebuild(); @@ -41,8 +44,10 @@ private: void buildSingle(EntityId id); void buildMulti(const std::vector& ids); void refreshBuffers(const Building* b); + void buildSplitterFilters(QPoint splitterTile); const RecipeDef* findRecipe(const Building* b) const; const ShipDef* findShipDef(const std::string& id) const; + std::vector allItemIds() const; Simulation* m_sim; const GameConfig* m_config; @@ -52,8 +57,13 @@ private: QLabel* m_titleLabel; QComboBox* m_recipeCombo; QPushButton* m_clearBeltBtn; + QLabel* m_filterALabel; + QListWidget* m_filterAList; + QLabel* m_filterBLabel; + QListWidget* m_filterBList; QLabel* m_buffersLabel; EntityId m_singleId; + QPoint m_splitterTile; std::string m_currentRecipeId; };