#pragma once #include #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 QScrollArea; 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. // // The panel floats over the game world view rather than occupying a column of its own // (REQ-UI-SELECTION-PANEL): it sizes itself to its content, anchors to the right edge of // the band its owner hands it, and hides itself entirely while nothing is selected // (REQ-UI-EMPTY-SELECTION). class SelectionPanel : public QWidget, public CombinedEventHandler { Q_OBJECT public: SelectionPanel(Simulation* sim, const GameConfig* config, QWidget* parent = nullptr); ~SelectionPanel() override; // Confines the panel to the given band of the game world view: it right-aligns // within it and centers vertically in it. The band is the world view less the strip // the build button bar occupies, so the two never overlap and the bar never has to // move (REQ-UI-SELECTION-PANEL, REQ-UI-BUILD-BAR). void anchorTo(const QRect& bandRect); 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(); // Shows the panel while either category has a selection and hides it otherwise // (REQ-UI-EMPTY-SELECTION), re-fitting it to its current content while it is shown. // Every path that changes the content ends here, because the content is what sizes // the panel. void updateVisibility(); // Re-fits the panel to its content within the anchored band. void refit(); 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; // The band the panel confines itself to, in the coordinates of its parent; null // until the owner has anchored it for the first time. QRect m_bandRect; // Scrolls the content once it outgrows the band (REQ-UI-SELECTION-PANEL). All the // content widgets below are children of m_content, not of the panel itself. QScrollArea* m_scrollArea; QWidget* m_content; 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; };