From b7fe5cfcf2b9bd0b4788823975944838e1a4cfd5 Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Tue, 18 Aug 2026 22:05:28 +0200 Subject: [PATCH] put the caret in the name prompt's line edit The layer asked the modal's focusWidget() what to focus, but hosting a modal reparents it into a scroll area and a reparent clears the focus its constructor set, so the answer was always nothing and the dialog itself took the keyboard. Modals now name their initial focus widget outright, which survives the hosting. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x --- src/ui/ModalDialog.cpp | 5 +++++ src/ui/ModalDialog.h | 6 ++++++ src/ui/ModalLayer.cpp | 12 +++++------- src/ui/NameInputDialog.cpp | 9 ++++++++- src/ui/NameInputDialog.h | 2 ++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/ui/ModalDialog.cpp b/src/ui/ModalDialog.cpp index fae07f9..074eed3 100644 --- a/src/ui/ModalDialog.cpp +++ b/src/ui/ModalDialog.cpp @@ -52,6 +52,11 @@ bool ModalDialog::isDismissible() const return false; } +QWidget* ModalDialog::getInitialFocusWidget() const +{ + return nullptr; +} + void ModalDialog::requestDismiss() { reject(); diff --git a/src/ui/ModalDialog.h b/src/ui/ModalDialog.h index 3c981ab..b229a0d 100644 --- a/src/ui/ModalDialog.h +++ b/src/ui/ModalDialog.h @@ -30,6 +30,12 @@ public: // has no way out but choosing (REQ-DEF-SCHEMATIC-DROP). virtual bool isDismissible() const; + // The widget that should hold the keyboard once the modal is open -- a name prompt's + // line edit -- or nullptr to leave the focus on the modal itself. Asked for by + // ModalLayer rather than read off focusWidget(), because hosting the modal reparents + // it and a reparent clears the focus its constructor set. + virtual QWidget* getInitialFocusWidget() const; + public slots: // What a dismissal does, whichever gesture asked for it. Cancelling is the default; // a dialog with modes of its own overrides this to back out of them one at a time diff --git a/src/ui/ModalLayer.cpp b/src/ui/ModalLayer.cpp index c5a526b..ed5fe93 100644 --- a/src/ui/ModalLayer.cpp +++ b/src/ui/ModalLayer.cpp @@ -66,17 +66,15 @@ int ModalLayer::execute(ModalDialog& content, const QRect& anchorRect) content.show(); updateVisibility(); - // The modal's own focus widget where it has one -- a name dialog puts the caret in + // The widget the modal names where it names one -- a name dialog puts the caret in // its line edit -- and the modal itself otherwise, so keys reach it and not the game // world behind (REQ-UI-MODAL-CHROME). - if (content.focusWidget() != nullptr) + QWidget* focusTarget = content.getInitialFocusWidget(); + if (focusTarget == nullptr) { - content.focusWidget()->setFocus(); - } - else - { - content.setFocus(); + focusTarget = &content; } + focusTarget->setFocus(); // The dialog's own loop, run here rather than by QDialog::exec(), so the layer knows // what is open and can place it, dim behind it, and take the clicks beside it. diff --git a/src/ui/NameInputDialog.cpp b/src/ui/NameInputDialog.cpp index cccce27..9c0082d 100644 --- a/src/ui/NameInputDialog.cpp +++ b/src/ui/NameInputDialog.cpp @@ -46,10 +46,17 @@ NameInputDialog::NameInputDialog(const QString& title, const QString& prompt, addHeader(mainLayout, title, false); // The line edit is what the player came here to use, and it takes Q as a character - // because it holds the focus (REQ-UI-DIALOG-DISMISS). + // because it holds the focus (REQ-UI-DIALOG-DISMISS). Set here for a dialog shown on + // its own, and named again through getInitialFocusWidget() for the usual case: being + // hosted on the modal layer reparents this dialog, which clears the focus set here. m_nameEdit->setFocus(); } +QWidget* NameInputDialog::getInitialFocusWidget() const +{ + return m_nameEdit; +} + QString NameInputDialog::getName() const { return m_nameEdit->text().trimmed(); diff --git a/src/ui/NameInputDialog.h b/src/ui/NameInputDialog.h index 77f1ba2..658c267 100644 --- a/src/ui/NameInputDialog.h +++ b/src/ui/NameInputDialog.h @@ -25,6 +25,8 @@ public: QString getName() const; + QWidget* getInitialFocusWidget() const override; + private: QLineEdit* m_nameEdit; };