split the selection panel into one card per kind of selection
The panel rendered every selection out of a single pool of member widgets,
hidden and shown per branch, so each build path had to remember to hide the
other branches' widgets. That coupling produced the two defects fixed in
668ce0f, and the per-type detail the requirements now ask for would only add
more of it.
The pool is gone. SelectionPanel keeps the category arbitration, the float and
the hide-when-empty behaviour, and hosts exactly one SelectionContent at a
time; SelectionContentFactory picks which one from the selection alone. Each
card is one row of the catalog in REQ-UI-SELECTION-CONTENT and owns only its
own widgets.
The card structure (REQ-UI-SELECTION-CARD) lives in the base class: a header
with an identity symbol, a name and one right slot, then a configuration group
and a runtime group. A construction site keeps its configuration and has its
whole runtime group replaced by the construction progress, decided once there
rather than in every card (REQ-BLD-SITE-CONFIG).
Also implemented here:
- REQ-UI-SELECTION-STATUS: the header status dot, taken from the simulation's
own getProductionStatus() so the panel and the world's status light cannot
disagree.
- REQ-UI-SELECTION-AGGREGATE: belt-subsystem tiles and debris-only selections
collapse into one card with a count instead of a count summary.
- REQ-UI-HQ-PANEL: the HQ shows the global block stock and its HP, neither of
which is a buffer.
- BuildingIconCache, extracted from BuildButtonBar's file-local chip loading so
the card headers and the build buttons rasterize the same SVGs once.
FieldSelectionPanel is deleted: ships, stations, debris and the field count
summary are four more cards in the same factory, so the two-panel arbitration
collapses into one decision.
The card parts are still today's labels and buttons; the item chips, bars,
recipe summary and stat rows follow.
Build clean, 541 tests pass, app runs with no Qt warnings. Visual check
pending.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
This commit is contained in:
@@ -1,48 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
#include <QWidget>
|
||||
|
||||
#include "BeltSystem.h"
|
||||
#include "Building.h"
|
||||
#include "BuildingId.h"
|
||||
#include "DebrisSelectionChangedEvent.h"
|
||||
#include "DebugDrawToggledEvent.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"
|
||||
#include "selection/SelectionContentFactory.h"
|
||||
#include "selection/SelectionContext.h"
|
||||
|
||||
struct GameConfig;
|
||||
struct VisualsConfig;
|
||||
class BuildingIconCache;
|
||||
class ItemIconCache;
|
||||
class SelectionContent;
|
||||
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.
|
||||
// Shows the current selection. The panel itself renders nothing: it arbitrates between
|
||||
// the two selection categories (REQ-UI-SELECTION-CATEGORIES), picks the card that fits
|
||||
// what is selected (REQ-UI-SELECTION-CONTENT), and hosts exactly one of them at a time.
|
||||
// What each card looks like lives in src/ui/selection/.
|
||||
//
|
||||
// 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
|
||||
// (REQ-UI-SELECTION-PANEL): it sizes itself to its card, 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,
|
||||
@@ -50,13 +36,17 @@ class SelectionPanel : public QWidget,
|
||||
PlayerCommandsAppliedEvent,
|
||||
EntitySelectionChangedEvent,
|
||||
SelectionChangedEvent,
|
||||
DebrisSelectionChangedEvent>
|
||||
DebrisSelectionChangedEvent,
|
||||
DebugDrawToggledEvent>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// visuals, itemIcons and buildingIcons are window-wide rendering resources the cards
|
||||
// draw from; none is owned and all must outlive this widget.
|
||||
SelectionPanel(Simulation* sim, const GameConfig* config,
|
||||
QWidget* parent = nullptr);
|
||||
const VisualsConfig* visuals, ItemIconCache* itemIcons,
|
||||
BuildingIconCache* buildingIcons, QWidget* parent = nullptr);
|
||||
~SelectionPanel() override;
|
||||
|
||||
// Confines the panel to the given band of the game world view: it right-aligns
|
||||
@@ -66,86 +56,41 @@ public:
|
||||
void anchorTo(const QRect& bandRect);
|
||||
|
||||
private:
|
||||
void handleEvent(std::shared_ptr<const SelectionChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const EntitySelectionChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const DebrisSelectionChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const TickAdvancedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const PlayerCommandsAppliedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const EntitySelectionChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const SelectionChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const DebrisSelectionChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const DebugDrawToggledEvent> 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<BuildingId>& 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.
|
||||
// Re-reads the live values of the card on screen. When the selection now calls for a
|
||||
// different card -- a construction site finishing is the case that matters -- it
|
||||
// rebuilds instead, because a card never changes its own shape.
|
||||
void refreshContent();
|
||||
// Replaces the card with the one the current selection calls for.
|
||||
void rebuildContent();
|
||||
// Shows the panel while a card exists and hides it otherwise
|
||||
// (REQ-UI-EMPTY-SELECTION), re-fitting it to the card while it is shown.
|
||||
void updateVisibility();
|
||||
// Re-fits the panel to its content within the anchored band.
|
||||
// Re-fits the panel to its card 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<BuildingId>& ids);
|
||||
void refreshBuffers(const Building* b);
|
||||
void refreshSiteProgress(const ConstructionSite* s);
|
||||
void updateShipyardLayoutWidgets(BuildingType type,
|
||||
const std::string& recipeId,
|
||||
const std::optional<ShipLayoutConfig>& shipLayout);
|
||||
void buildSplitterFilters(const std::optional<BeltSystem::SplitterInfo>& info);
|
||||
const RecipeDef* findRecipe(const Building* b) const;
|
||||
const ShipDef* findShipDef(const std::string& id) const;
|
||||
std::vector<std::string> getAllItemIds() const;
|
||||
|
||||
Simulation* m_sim;
|
||||
const GameConfig* m_config;
|
||||
std::vector<BuildingId> m_selectedBuildingIds;
|
||||
SelectionContext m_context;
|
||||
// Read through m_context by the cards that need it, so a toggle reaches the card on
|
||||
// screen without it having to subscribe to the event itself.
|
||||
bool m_debugDrawEnabled = false;
|
||||
|
||||
SelectionRequest m_request;
|
||||
ContentKey m_contentKey;
|
||||
SelectionContent* m_content = nullptr;
|
||||
|
||||
// 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.
|
||||
// Scrolls the card once it outgrows the band (REQ-UI-SELECTION-PANEL). The card is a
|
||||
// child of m_body, 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<BuildingId> 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;
|
||||
QWidget* m_body;
|
||||
QVBoxLayout* m_bodyLayout;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user