The three widgets over the game world view each cached a rect handed to them by MainWindow's resize, then re-placed themselves from it. But the build button bar re-centers on a building unlock and the controls panel re-fits on a 50 ms timer, neither of which goes through MainWindow, so the rects the others held went stale -- and each widget re-implemented its own avoidance against them. They now implement FloatingPanel and are placed in one ordered pass: the bar takes what it wants, the controls panel steps around the bar, and the selection panel keeps clear of both. A widget that changed size or visibility publishes FloatingLayoutInvalidatedEvent instead of moving itself, because what it may take depends on the widgets placed before it. The rule they step around each other by is one function in lib, where it can be tested without a display -- the only way any of this geometry gets automated cover, screen capture of the world view being blank here. The selection panel keeps its right edge and its vertical centering, but the space it centers in is now what its own column has left free rather than the full-width strip the bar used to reserve. It therefore sits lower than before where the centered bar does not reach it, and it now clears the controls panel, which it previously ignored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
122 lines
5.2 KiB
C++
122 lines
5.2 KiB
C++
#pragma once
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <QWidget>
|
|
|
|
#include "BlueprintSaveRequestedEvent.h"
|
|
#include "BlueprintSelectionRequestedEvent.h"
|
|
#include "BuildingId.h"
|
|
#include "EscapeMenuRequestedEvent.h"
|
|
#include "EventHandler.h"
|
|
#include "FloatingLayoutInvalidatedEvent.h"
|
|
#include "GameConfig.h"
|
|
#include "GameOverEvent.h"
|
|
#include "LayoutDialogRequestedEvent.h"
|
|
#include "ModalDimOverlay.h"
|
|
#include "WinEvent.h"
|
|
#include "RecipeSelectionRequestedEvent.h"
|
|
#include "SchematicChoicesAvailableEvent.h"
|
|
#include "ShipLayout.h"
|
|
#include "ShipLayoutBlueprint.h"
|
|
#include "Tick.h"
|
|
#include "VisualsConfig.h"
|
|
|
|
struct ParsedReplay;
|
|
class Simulation;
|
|
class GameWorldView;
|
|
class HeaderBar;
|
|
class SelectionPanel;
|
|
class ControlsPanel;
|
|
class BuildButtonBar;
|
|
class BlueprintLibrary;
|
|
class BuildingIconCache;
|
|
class ItemIconCache;
|
|
class QCloseEvent;
|
|
class QResizeEvent;
|
|
|
|
class MainWindow : public QWidget,
|
|
public CombinedEventHandler<SchematicChoicesAvailableEvent,
|
|
GameOverEvent,
|
|
WinEvent,
|
|
EscapeMenuRequestedEvent,
|
|
LayoutDialogRequestedEvent,
|
|
RecipeSelectionRequestedEvent,
|
|
BlueprintSaveRequestedEvent,
|
|
BlueprintSelectionRequestedEvent,
|
|
FloatingLayoutInvalidatedEvent>
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
MainWindow(Simulation* sim, const std::string& configDir,
|
|
std::shared_ptr<ParsedReplay> replay = nullptr, QWidget* parent = nullptr);
|
|
~MainWindow() override;
|
|
|
|
protected:
|
|
void resizeEvent(QResizeEvent* event) override;
|
|
void closeEvent(QCloseEvent* event) override;
|
|
|
|
private:
|
|
void handleEvent(std::shared_ptr<const SchematicChoicesAvailableEvent> event) override;
|
|
void handleEvent(std::shared_ptr<const GameOverEvent> event) override;
|
|
void handleEvent(std::shared_ptr<const WinEvent> event) override;
|
|
void handleEvent(std::shared_ptr<const EscapeMenuRequestedEvent> event) override;
|
|
void handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> event) override;
|
|
void handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent> event) override;
|
|
void handleEvent(std::shared_ptr<const BlueprintSaveRequestedEvent> event) override;
|
|
void handleEvent(std::shared_ptr<const BlueprintSelectionRequestedEvent> event) override;
|
|
void handleEvent(std::shared_ptr<const FloatingLayoutInvalidatedEvent> event) override;
|
|
|
|
// Reloads the game config and visuals.toml from disk (REQ-CFG-RELOAD), shared
|
|
// by every restart path. On success the reloaded visuals are applied to this
|
|
// window and the fresh GameConfig is returned; on failure a modal error dialog
|
|
// is shown and std::nullopt is returned, leaving the window state untouched.
|
|
std::optional<GameConfig> reloadConfig();
|
|
|
|
// Opens the shipyard layout configuration dialog for the given schematic and
|
|
// current layout, applying the result via SetShipLayoutCommand (REQ-MOD-UI-DIALOG).
|
|
void openShipLayoutDialog(BuildingId shipyardId,
|
|
const std::string& schematicId,
|
|
const ShipLayoutConfig& currentLayout);
|
|
|
|
// Runs the blueprint selection dialog and enters placement mode for whatever the
|
|
// player picked (REQ-UI-BLUEPRINT-DIALOG). Holds no pause or dim scope of its own:
|
|
// both callers already hold theirs, which is what keeps the dim continuous when a
|
|
// confirmed save hands straight over to this dialog (REQ-UI-MODAL-DIM).
|
|
void showBlueprintSelectionDialog();
|
|
// Places the widgets floating over the game world view, in one ordered pass
|
|
// (FloatingPanel.h). Runs on a resize and on every FloatingLayoutInvalidatedEvent.
|
|
void layoutPanels();
|
|
|
|
private:
|
|
std::string m_configDir;
|
|
VisualsConfig m_visuals;
|
|
Simulation* m_sim;
|
|
// One per-item icon cache for the whole window (REQ-UI-ITEM-ICON): the header,
|
|
// build bar, world view, and recipe dialog all rasterize the same SVGs.
|
|
std::unique_ptr<ItemIconCache> m_itemIcons;
|
|
// Likewise one per-building chip cache (REQ-UI-BUILD-ICON), shared by the build bar
|
|
// and the selection panel's card headers (REQ-UI-SELECTION-CARD).
|
|
std::unique_ptr<BuildingIconCache> m_buildingIcons;
|
|
GameWorldView* m_gameWorldView;
|
|
HeaderBar* m_headerBar;
|
|
SelectionPanel* m_selectionPanel;
|
|
ControlsPanel* m_controlsPanel;
|
|
BuildButtonBar* m_buildButtonBar;
|
|
// The saved blueprints themselves; they have no widget of their own any more and
|
|
// are reached through the two modal dialogs (REQ-UI-BLUEPRINT-DIALOG).
|
|
std::unique_ptr<BlueprintLibrary> m_blueprintLibrary;
|
|
ModalDimOverlay* m_dimOverlay = nullptr;
|
|
|
|
std::vector<ShipLayoutBlueprint> m_layoutBlueprints;
|
|
std::shared_ptr<ParsedReplay> m_replay; // non-null => view-only playback
|
|
|
|
// Set while the placement pass runs, so a widget placed in it cannot start a second
|
|
// pass from inside the first.
|
|
bool m_layingOut = false;
|
|
};
|