Fix recipe button unclickable on construction site during play
A selected construction site rebuilt the entire panel on every TickAdvancedEvent (~30x/s at 1x), because refreshSelectionDisplay() called rebuild() for sites. Each rebuild runs buildSingle() -> hideAllWidgets(), which hides and re-shows the recipe-select button. Hiding a QPushButton mid-press clears its pressed state, so a rebuild landing between the user's mouse press and release cancelled the click. While paused no tick advances, so no rebuild occurred and the button worked -- matching the report. Give sites a lightweight per-tick refresh that updates only the progress label, mirroring refreshBuffers() for live buildings. The site progress block is extracted from buildSingle() into refreshSiteProgress() (no duplicated arithmetic). refreshSelectionDisplay() now takes a RefreshReason: a PeriodicTick updates progress only, while a CommandApplied still rebuilds so a site's newly chosen recipe/layout is reflected. The site -> completed building transition remains handled by the existing "(Building) " title branch. No UI test added: the test target links only lib (no QtWidgets), per the simulation/presentation split, so a widget-level test does not fit the harness. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
This commit is contained in:
@@ -338,6 +338,16 @@ void SelectedBuildingPanel::buildSingle(BuildingId id)
|
|||||||
|
|
||||||
if (m_singleIsSite)
|
if (m_singleIsSite)
|
||||||
{
|
{
|
||||||
|
refreshSiteProgress(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
refreshBuffers(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectedBuildingPanel::refreshSiteProgress(const ConstructionSite* s)
|
||||||
|
{
|
||||||
QString progress;
|
QString progress;
|
||||||
if (s->completesAt == 0)
|
if (s->completesAt == 0)
|
||||||
{
|
{
|
||||||
@@ -364,11 +374,6 @@ void SelectedBuildingPanel::buildSingle(BuildingId id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_buffersLabel->setText(progress);
|
m_buffersLabel->setText(progress);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
refreshBuffers(b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectedBuildingPanel::refreshBuffers(const Building* b)
|
void SelectedBuildingPanel::refreshBuffers(const Building* b)
|
||||||
@@ -562,7 +567,7 @@ const ShipDef* SelectedBuildingPanel::findShipDef(const std::string& id) const
|
|||||||
|
|
||||||
void SelectedBuildingPanel::handleEvent(std::shared_ptr<const TickAdvancedEvent> /*event*/)
|
void SelectedBuildingPanel::handleEvent(std::shared_ptr<const TickAdvancedEvent> /*event*/)
|
||||||
{
|
{
|
||||||
refreshSelectionDisplay();
|
refreshSelectionDisplay(RefreshReason::PeriodicTick);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectedBuildingPanel::handleEvent(
|
void SelectedBuildingPanel::handleEvent(
|
||||||
@@ -572,10 +577,10 @@ void SelectedBuildingPanel::handleEvent(
|
|||||||
// queued drain, not synchronously. When the game is paused no tick advances,
|
// queued drain, not synchronously. When the game is paused no tick advances,
|
||||||
// so TickAdvancedEvent never fires; refresh here too, otherwise the panel
|
// so TickAdvancedEvent never fires; refresh here too, otherwise the panel
|
||||||
// would not reflect the change until the next tick or a re-selection.
|
// would not reflect the change until the next tick or a re-selection.
|
||||||
refreshSelectionDisplay();
|
refreshSelectionDisplay(RefreshReason::CommandApplied);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectedBuildingPanel::refreshSelectionDisplay()
|
void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason)
|
||||||
{
|
{
|
||||||
if (m_selectedEntity.has_value())
|
if (m_selectedEntity.has_value())
|
||||||
{
|
{
|
||||||
@@ -599,8 +604,19 @@ void SelectedBuildingPanel::refreshSelectionDisplay()
|
|||||||
}
|
}
|
||||||
const ConstructionSite* s = m_sim->buildings().findSite(m_singleBuildingId);
|
const ConstructionSite* s = m_sim->buildings().findSite(m_singleBuildingId);
|
||||||
if (s)
|
if (s)
|
||||||
|
{
|
||||||
|
// A periodic tick only advances construction progress, so update just the
|
||||||
|
// progress label. Rebuilding every tick would hide/re-show all widgets and
|
||||||
|
// cancel any in-progress click on the recipe button. An applied command
|
||||||
|
// may have changed the site's recipe/layout, so rebuild in that case.
|
||||||
|
if (reason == RefreshReason::CommandApplied)
|
||||||
{
|
{
|
||||||
rebuild();
|
rebuild();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
refreshSiteProgress(s);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buildEmpty();
|
buildEmpty();
|
||||||
|
|||||||
@@ -59,8 +59,18 @@ private slots:
|
|||||||
void onSplitterFilterChanged();
|
void onSplitterFilterChanged();
|
||||||
|
|
||||||
private:
|
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);
|
void onSelectionChanged(const std::vector<BuildingId>& ids);
|
||||||
void refreshSelectionDisplay();
|
void refreshSelectionDisplay(RefreshReason reason);
|
||||||
void rebuild();
|
void rebuild();
|
||||||
void hideAllWidgets();
|
void hideAllWidgets();
|
||||||
void clearContent();
|
void clearContent();
|
||||||
@@ -68,6 +78,7 @@ private:
|
|||||||
void buildSingle(BuildingId id);
|
void buildSingle(BuildingId id);
|
||||||
void buildMulti(const std::vector<BuildingId>& ids);
|
void buildMulti(const std::vector<BuildingId>& ids);
|
||||||
void refreshBuffers(const Building* b);
|
void refreshBuffers(const Building* b);
|
||||||
|
void refreshSiteProgress(const ConstructionSite* s);
|
||||||
void updateShipyardLayoutWidgets(BuildingType type,
|
void updateShipyardLayoutWidgets(BuildingType type,
|
||||||
const std::string& recipeId,
|
const std::string& recipeId,
|
||||||
const std::optional<ShipLayoutConfig>& shipLayout);
|
const std::optional<ShipLayoutConfig>& shipLayout);
|
||||||
|
|||||||
Reference in New Issue
Block a user