Shrink to a floating button instead of hiding

With "Shrink to Button" on, the dismiss key shrinks the window to just
itself: a see-through button that can be dragged anywhere. Tapping it
grows the keyboard back around it, kept on screen. KWin moves and
resizes the window through a one-off script, as Wayland clients can't
place themselves.

Also moves the window back on screen whenever it appears (KWin remembers
where the button was), and releases the mouse after a window manager
move or resize, which used to eat the next press on the grab bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-31 01:05:37 +01:00
co-authored by Claude Opus 5.5
parent ac5f43ca5f
commit 032fbd8ba4
15 changed files with 401 additions and 14 deletions
+74
View File
@@ -0,0 +1,74 @@
#include "CollapseController.h"
#include "FloatingWindow.h"
#include <QQuickWindow>
namespace {
constexpr int ExpandTimeoutMs = 500;
bool roughlyEqual(QSize a, QSize b)
{
return qAbs(a.width() - b.width()) <= 1 && qAbs(a.height() - b.height()) <= 1;
}
} // namespace
CollapseController::CollapseController(QQuickWindow &window, FloatingWindow &placement, QObject *parent)
: QObject(parent)
, m_window(window)
, m_placement(placement)
{
m_expanding.setSingleShot(true);
m_expanding.setInterval(ExpandTimeoutMs);
connect(&m_expanding, &QTimer::timeout, this, &CollapseController::finishExpanding);
connect(&window, &QWindow::widthChanged, this, &CollapseController::windowResized);
connect(&window, &QWindow::heightChanged, this, &CollapseController::windowResized);
}
bool CollapseController::collapse()
{
if (m_collapsed)
return true;
QVariant rect;
QMetaObject::invokeMethod(&m_window, "dismissKeyRect", Q_RETURN_ARG(QVariant, rect));
if (rect.toRectF().isEmpty())
return false;
m_keyRect = rect.toRectF();
m_expandedSize = m_window.size();
m_collapsed = true;
Q_EMIT collapsedChanged();
// After the next frame, once the compositor knows the lower minimum size.
connect(&m_window, &QQuickWindow::frameSwapped, this, [this] {
if (m_collapsed)
m_placement.moveResize(m_keyRect, false);
}, Qt::SingleShotConnection);
return true;
}
void CollapseController::expand()
{
if (!m_collapsed || m_expanding.isActive())
return;
if (roughlyEqual(m_window.size(), m_expandedSize)) {
finishExpanding(); // never shrank: nothing to move
return;
}
// Stays collapsed until the window has grown, so the keys don't flash squeezed.
m_placement.moveResize(QRectF(-m_keyRect.topLeft(), m_expandedSize), true);
m_expanding.start();
}
void CollapseController::windowResized()
{
if (m_expanding.isActive() && roughlyEqual(m_window.size(), m_expandedSize))
finishExpanding();
}
void CollapseController::finishExpanding()
{
m_expanding.stop();
if (!roughlyEqual(m_window.size(), m_expandedSize))
m_window.resize(m_expandedSize); // no KWin to do it
m_collapsed = false;
Q_EMIT collapsedChanged();
}