#pragma once #include #include #include #include #include #include "BeltSystem.h" #include "Building.h" #include "BuildingId.h" #include "EntitySelectionChangedEvent.h" #include "EventHandler.h" #include "GameConfig.h" #include "PlayerCommandsAppliedEvent.h" #include "RecipesConfig.h" #include "DebrisSelectionChangedEvent.h" #include "SelectionChangedEvent.h" #include "ShipLayout.h" #include "ShipsConfig.h" #include "Tick.h" #include "TickAdvancedEvent.h" class Simulation; class FieldSelectionPanel; class ShipLayoutPreview; class QLabel; class QListWidget; class QPushButton; class QVBoxLayout; // Shows the current selection. The building category (buildings and construction sites) // is rendered by this panel itself; the field category (ships, defence stations, debris) // is rendered by the embedded FieldSelectionPanel. // // The two categories are mutually exclusive (REQ-UI-SELECTION-CATEGORIES) and this panel // is the sole arbiter of which one owns the content: it listens to all three selection // events, forwards the field ones to the child panel, and drops the losing category's // content. Neither panel touches the other's widgets. class SelectedBuildingPanel : public QWidget, public CombinedEventHandler { Q_OBJECT public: SelectedBuildingPanel(Simulation* sim, const GameConfig* config, QWidget* parent = nullptr); ~SelectedBuildingPanel() override; private: void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; private slots: void onSelectRecipeClicked(); void onClearBelt(); void onSplitterFilterChanged(); private: // Why the selection display is being refreshed. A periodic tick only needs a // lightweight content update (e.g. a construction site's progress label), // whereas an applied player command may have changed the configuration and // needs a full structural rebuild. enum class RefreshReason { PeriodicTick, CommandApplied }; void onSelectionChanged(const std::vector& ids); // Gives the panel to the field category once it has anything selected. void yieldToFieldSelection(); void refreshSelectionDisplay(RefreshReason reason); void rebuild(); void hideAllWidgets(); void buildEmpty(); void buildSingle(BuildingId id); void buildMulti(const std::vector& ids); void refreshBuffers(const Building* b); void refreshSiteProgress(const ConstructionSite* s); void updateShipyardLayoutWidgets(BuildingType type, const std::string& recipeId, const std::optional& shipLayout); void buildSplitterFilters(const std::optional& info); const RecipeDef* findRecipe(const Building* b) const; const ShipDef* findShipDef(const std::string& id) const; std::vector getAllItemIds() const; Simulation* m_sim; const GameConfig* m_config; std::vector m_selectedBuildingIds; QVBoxLayout* m_layout; QLabel* m_titleLabel; QPushButton* m_recipeSelectButton; QPushButton* m_clearBeltBtn; QLabel* m_filterALabel; QListWidget* m_filterAList; QLabel* m_filterBLabel; QListWidget* m_filterBList; QLabel* m_buffersLabel; ShipLayoutPreview* m_layoutPreview; QPushButton* m_configureLayoutBtn; std::optional m_singleBuildingId; bool m_singleIsSite = false; // selected single entity is a construction site QPoint m_splitterTile; std::string m_currentRecipeId; // Renders the field selection (actors + debris) below the building content // (REQ-UI-FIELD-MULTI-SELECTION). Hides itself while nothing field-side is selected. FieldSelectionPanel* m_fieldSelectionPanel; };