give the keyboard back when a modal closes
Hiding the layer takes the focus off whatever stood on it and leaves the window with no focus widget at all, so the world view answered no keys afterwards: Escape opened the menu once, closed it, and then did nothing. Each modal now notes what held the keyboard when it opened and gives it back on the way out -- the world view for the first of a stack, the modal beneath for one above it, which was dead the same way. The window's own focus guard now also catches the focus going nowhere, rather than only landing on the wrong widget. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QEventLoop>
|
||||
#include <QFrame>
|
||||
#include <QMetaObject>
|
||||
@@ -40,6 +41,11 @@ int ModalLayer::execute(ModalDialog& content, const QRect& anchorRect)
|
||||
// instead of being cut off by the window edge (REQ-UI-MODAL-CHROME). When the
|
||||
// content fits -- which is the normal case -- the host is exactly its size and no
|
||||
// scroll bar appears, so the player sees the modal alone.
|
||||
// Noted before anything here touches the focus, and given back when this modal
|
||||
// closes: the game world beneath for the first modal of a stack, and the modal it was
|
||||
// opened from for one above that.
|
||||
QWidget* const focusBefore = QApplication::focusWidget();
|
||||
|
||||
QScrollArea* host = new QScrollArea(this);
|
||||
host->setFrameShape(QFrame::NoFrame);
|
||||
host->setWidgetResizable(false);
|
||||
@@ -52,7 +58,7 @@ int ModalLayer::execute(ModalDialog& content, const QRect& anchorRect)
|
||||
: (m_stack.empty() ? rect()
|
||||
: m_stack.back().host->geometry());
|
||||
|
||||
m_stack.push_back(HostedModal{ &content, host });
|
||||
m_stack.push_back(HostedModal{ &content, host, focusBefore });
|
||||
|
||||
// Placed and shown before the layer itself is, so that the frame the layer first
|
||||
// paints already carries the modal. Showing the layer first put the dim on screen a
|
||||
@@ -90,15 +96,24 @@ int ModalLayer::execute(ModalDialog& content, const QRect& anchorRect)
|
||||
content.hide();
|
||||
delete host;
|
||||
|
||||
QPointer<QWidget> focusAfter;
|
||||
for (std::size_t i = m_stack.size(); i > 0; --i)
|
||||
{
|
||||
if (m_stack[i - 1].content == &content)
|
||||
{
|
||||
focusAfter = m_stack[i - 1].focusBefore;
|
||||
m_stack.erase(m_stack.begin() + static_cast<std::ptrdiff_t>(i - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateVisibility();
|
||||
|
||||
// After the layer is hidden, not before: hiding a widget takes the focus off
|
||||
// everything on it, which would undo this again.
|
||||
if (!focusAfter.isNull())
|
||||
{
|
||||
focusAfter->setFocus();
|
||||
}
|
||||
return content.result();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user