open the panel's modals on the panel
Implements REQ-UI-PANEL-MODAL. The recipe/schematic selection dialog and the ship layout configuration dialog are both opened from controls inside the selection panel, but Qt centered them on the game window, sending the cursor back across the view and then back again. MainWindow::placeOnSelectionPanel() centers a dialog on the panel's live global rectangle -- so a dragged panel carries the modal with it -- and clamps it into the window without resizing it, clamping the far edge before the near one so a dialog too large for the window lands on the window's top-left corner. It is called on the constructed dialog before exec(), which also stops QDialog from re-centering it on show. Placement is all that changes: modality, the auto-pause, the modal dim and dismissal are untouched, as are the modals not opened from the panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QDialog>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QInputDialog>
|
||||
@@ -317,6 +318,9 @@ void MainWindow::openShipLayoutDialog(BuildingId shipyardId,
|
||||
std::move(unlockedModuleIds),
|
||||
m_gameWorldView->isDebugDrawEnabled(),
|
||||
this);
|
||||
// Opened from the panel's "Configure" button (REQ-MOD-UI-PREVIEW) or straight after
|
||||
// a schematic change (REQ-MOD-UI-AUTO-DIALOG), so it opens on the panel either way.
|
||||
placeOnSelectionPanel(dialog);
|
||||
if (dialog.exec() == QDialog::Accepted && dialog.getResult().has_value())
|
||||
{
|
||||
std::shared_ptr<SetShipLayoutCommand> command =
|
||||
@@ -328,6 +332,45 @@ void MainWindow::openShipLayoutDialog(BuildingId shipyardId,
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::placeOnSelectionPanel(QDialog& dialog) const
|
||||
{
|
||||
// The panel is up whenever one of these modals opens -- they are opened from its own
|
||||
// controls, and it is shown whenever anything is selected (REQ-UI-EMPTY-SELECTION).
|
||||
// Were it not, there would be no rectangle to center on and Qt's own centering on
|
||||
// this window stands.
|
||||
if (!m_selectionPanel->isVisible()) { return; }
|
||||
|
||||
// The dialog has never been shown, so it is still at its default size until its
|
||||
// layout has run; centering it before that would use the wrong extent.
|
||||
dialog.adjustSize();
|
||||
const QSize dialogSize = dialog.size();
|
||||
|
||||
// The panel's live geometry, so a panel the player has dragged
|
||||
// (REQ-UI-SELECTION-PANEL-DRAG) carries the modal with it.
|
||||
const QRect panelRect(m_selectionPanel->mapToGlobal(QPoint(0, 0)),
|
||||
m_selectionPanel->size());
|
||||
const QRect windowRect(mapToGlobal(QPoint(0, 0)), size());
|
||||
|
||||
QPoint topLeft(panelRect.center().x() - dialogSize.width() / 2,
|
||||
panelRect.center().y() - dialogSize.height() / 2);
|
||||
|
||||
// Pushed back inside the window, never resized to fit (REQ-UI-PANEL-MODAL). The far
|
||||
// edge is clamped first and the near edge second, which is what aligns a dialog too
|
||||
// large for the window with the window's top-left corner rather than pushing it off
|
||||
// the opposite edge.
|
||||
topLeft.setX(qMax(windowRect.left(),
|
||||
qMin(topLeft.x(), windowRect.right() - dialogSize.width() + 1)));
|
||||
topLeft.setY(qMax(windowRect.top(),
|
||||
qMin(topLeft.y(), windowRect.bottom() - dialogSize.height() + 1)));
|
||||
|
||||
// Positions the dialog's frame, whose size is not known until it is first shown, so
|
||||
// the result sits low by the title bar height against a true center -- measuring it
|
||||
// would mean showing the dialog at the wrong place first. The move also marks the
|
||||
// dialog as positioned, which is what stops QDialog from centering it on this window
|
||||
// when it is shown.
|
||||
dialog.move(topLeft);
|
||||
}
|
||||
|
||||
void MainWindow::handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> event)
|
||||
{
|
||||
// A construction site has no Building yet; fall back to its site record so
|
||||
@@ -385,6 +428,7 @@ void MainWindow::handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent
|
||||
bool autoOpenLayout = false;
|
||||
std::string chosenSchematic;
|
||||
RecipeSelectionDialog dialog(options, title, m_itemIcons.get(), this);
|
||||
placeOnSelectionPanel(dialog);
|
||||
if (dialog.exec() == QDialog::Accepted && dialog.getChosenId().has_value())
|
||||
{
|
||||
std::shared_ptr<SetRecipeCommand> command = std::make_shared<SetRecipeCommand>();
|
||||
|
||||
@@ -36,6 +36,7 @@ class BlueprintLibrary;
|
||||
class BuildingIconCache;
|
||||
class ItemIconCache;
|
||||
class QCloseEvent;
|
||||
class QDialog;
|
||||
class QResizeEvent;
|
||||
|
||||
class MainWindow : public QWidget,
|
||||
@@ -83,6 +84,11 @@ private:
|
||||
const std::string& schematicId,
|
||||
const ShipLayoutConfig& currentLayout);
|
||||
|
||||
// Centers a modal opened from the selection panel on that panel, kept inside this
|
||||
// window (REQ-UI-PANEL-MODAL). Called on the constructed dialog before exec(), and
|
||||
// only for the two modals the panel opens.
|
||||
void placeOnSelectionPanel(QDialog& dialog) const;
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user