Files
sloposk/src/window/FloatingWindow.cpp
T
ericek111andClaude Opus 5.5 c6c977eb4c Floating polish: visibility, resize, saved geometry, D-Bus, desktop entry
- VisibilityController: show on field focus, dismiss until next focus,
  manual show/hide/toggle overrides
- FloatingWindow: resize from the corner grip, clamp to screen
- WindowSettings: geometry per screen, debounced saves
- DBusControl: org.sloposk.Keyboard Show/Hide/Toggle for global shortcuts
- org.sloposk.keyboard.desktop so System Settings lists it as a virtual keyboard
- README with build/run/layout docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 15:02:25 +02:00

121 lines
3.3 KiB
C++

#include "FloatingWindow.h"
#include <LayerShellQt/Window>
#include <QGuiApplication>
#include <QScreen>
#include <QWindow>
namespace {
bool isWayland()
{
return QGuiApplication::platformName().startsWith(QLatin1String("wayland"));
}
constexpr int BottomGap = 24;
} // namespace
FloatingWindow::FloatingWindow(QWindow *window, QObject *parent)
: QObject(parent)
, m_window(window)
{
if (isWayland()) {
m_layerSurface = LayerShellQt::Window::get(window);
m_layerSurface->setLayer(LayerShellQt::Window::LayerOverlay);
m_layerSurface->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityNone);
m_layerSurface->setAnchors({LayerShellQt::Window::AnchorTop | LayerShellQt::Window::AnchorLeft});
m_layerSurface->setExclusiveZone(-1);
m_layerSurface->setScope(QStringLiteral("sloposk-keyboard"));
m_layerSurface->setActivateOnShow(false);
} else {
window->setFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
| Qt::WindowDoesNotAcceptFocus);
}
m_position = defaultPosition();
apply();
}
QRect FloatingWindow::geometry() const
{
return {m_position, m_window->size()};
}
void FloatingWindow::setGeometry(const QRect &geometry)
{
const QSize size = geometry.size().expandedTo(m_window->minimumSize()).boundedTo(screenArea().size());
m_window->resize(size);
m_position = clamped(geometry.topLeft(), size);
apply();
}
QString FloatingWindow::screenName() const
{
return m_window->screen()->name();
}
void FloatingWindow::moveBy(qreal dx, qreal dy)
{
const QPoint position = clamped(m_position + QPoint(qRound(dx), qRound(dy)), m_window->size());
if (position == m_position)
return;
m_position = position;
apply();
Q_EMIT geometryChanged();
}
void FloatingWindow::resizeBy(qreal dw, qreal dh)
{
const QSize maximum = screenArea().size() - QSize(m_position.x(), m_position.y());
const QSize size = (m_window->size() + QSize(qRound(dw), qRound(dh)))
.expandedTo(m_window->minimumSize())
.boundedTo(maximum);
if (size == m_window->size())
return;
m_window->resize(size);
Q_EMIT geometryChanged();
}
void FloatingWindow::setInputRegion(const QRectF &region)
{
m_window->setMask(QRegion(region.toAlignedRect()));
}
void FloatingWindow::show()
{
m_window->setVisible(true);
}
void FloatingWindow::hide()
{
m_window->setVisible(false);
}
QRect FloatingWindow::screenArea() const
{
return m_window->screen()->geometry();
}
QPoint FloatingWindow::clamped(QPoint position, QSize size) const
{
const QRect area = screenArea();
const int maxX = qMax(0, area.width() - size.width());
const int maxY = qMax(0, area.height() - size.height());
return {qBound(0, position.x(), maxX), qBound(0, position.y(), maxY)};
}
QPoint FloatingWindow::defaultPosition() const
{
const QRect area = screenArea();
return clamped({(area.width() - m_window->width()) / 2, area.height() - m_window->height() - BottomGap},
m_window->size());
}
void FloatingWindow::apply()
{
if (m_layerSurface)
m_layerSurface->setMargins({m_position.x(), m_position.y(), 0, 0});
else
m_window->setPosition(screenArea().topLeft() + m_position);
}