close a dialog when the player clicks beside it
REQ-UI-DIALOG-DISMISS asks that a dialog be put away by clicking away from it, the gesture that already puts the selection panel away. The layer is the widget those clicks land on, so this is where it belongs -- and the three dialogs that take the gesture already say so through isDismissible(), which Q has been using since the base class arrived. A left press and a left release must both land beyond the open modal's rectangle. Where the click landed is what decides, never that the layer received it: a click on an inert part of a dialog -- a label, the gap between two controls -- propagates up from the widget that ignored it and arrives here with a position inside the modal, which is not a dismissal. A drag begun inside the dialog never reaches the layer at all, the widget it began on keeping the release, so no gesture ends by discarding the dialog it was made in. Every click on the layer is consumed either way, dismissing or not: the window behind a modal takes no input, and closing the modal does not turn that click into one for whatever lies under it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <QEventLoop>
|
||||
#include <QFrame>
|
||||
#include <QMetaObject>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPoint>
|
||||
#include <QScrollArea>
|
||||
@@ -133,6 +134,47 @@ void ModalLayer::paintEvent(QPaintEvent* /*event*/)
|
||||
painter.fillRect(rect(), m_dimColor);
|
||||
}
|
||||
|
||||
void ModalLayer::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
m_pressedOutside = event->button() == Qt::LeftButton
|
||||
&& isOutsideOpenModal(event->pos());
|
||||
|
||||
// Taken whether or not it dismisses anything: the window behind a modal receives no
|
||||
// input, and closing the modal does not turn this click into one for what lies under
|
||||
// it (REQ-UI-DIALOG-DISMISS).
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void ModalLayer::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
const bool pressedOutside = m_pressedOutside;
|
||||
m_pressedOutside = false;
|
||||
event->accept();
|
||||
|
||||
if (event->button() != Qt::LeftButton || !pressedOutside
|
||||
|| !isOutsideOpenModal(event->pos()) || m_stack.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// A drag that began inside the modal never gets here -- the widget it began on keeps
|
||||
// the release -- so no gesture ends by discarding the modal it was made in.
|
||||
ModalDialog* openModal = m_stack.back().content;
|
||||
if (openModal->isDismissible())
|
||||
{
|
||||
openModal->requestDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
bool ModalLayer::isOutsideOpenModal(const QPoint& position) const
|
||||
{
|
||||
if (m_stack.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return !m_stack.back().host->geometry().contains(position);
|
||||
}
|
||||
|
||||
void ModalLayer::place(QScrollArea& host, ModalDialog& content,
|
||||
const QRect& anchorRect) const
|
||||
{
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include <QColor>
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
#include <QWidget>
|
||||
|
||||
class ModalDialog;
|
||||
class QMouseEvent;
|
||||
class QPaintEvent;
|
||||
class QScrollArea;
|
||||
|
||||
@@ -52,6 +54,8 @@ public:
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
// A modal and the scroll area it is shown in. The host is what the layer places and
|
||||
@@ -69,7 +73,16 @@ private:
|
||||
|
||||
void updateVisibility();
|
||||
|
||||
// Whether a point in this layer's coordinates lies beyond the open modal. A click on
|
||||
// an inert part of the modal -- a label, the space between two controls -- arrives
|
||||
// here too, propagated by the widget that ignored it, so where the click landed is
|
||||
// what decides, never that the layer received it.
|
||||
bool isOutsideOpenModal(const QPoint& position) const;
|
||||
|
||||
QColor m_dimColor;
|
||||
// Set by a left press that landed outside the open modal, so that only a press and a
|
||||
// release both outside dismiss it (REQ-UI-DIALOG-DISMISS).
|
||||
bool m_pressedOutside = false;
|
||||
std::vector<HostedModal> m_stack; // bottom-most first; back() is the open one
|
||||
int m_holdCount = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user