Spike: KWin input-method backend with floating layer-shell window
Registers as KWin's virtual keyboard (input-method-unstable-v1), shows a non-focusable layer-shell overlay, and commits Unicode text. Verified in a nested headless KWin with the textprobe tool. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
#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();
|
||||
}
|
||||
|
||||
void FloatingWindow::setPosition(QPoint position)
|
||||
{
|
||||
position = clamped(position);
|
||||
if (position == m_position)
|
||||
return;
|
||||
m_position = position;
|
||||
apply();
|
||||
Q_EMIT positionChanged();
|
||||
}
|
||||
|
||||
void FloatingWindow::moveBy(qreal dx, qreal dy)
|
||||
{
|
||||
setPosition(m_position + QPoint(qRound(dx), qRound(dy)));
|
||||
}
|
||||
|
||||
void FloatingWindow::show()
|
||||
{
|
||||
m_window->setVisible(true);
|
||||
}
|
||||
|
||||
void FloatingWindow::hide()
|
||||
{
|
||||
m_window->setVisible(false);
|
||||
}
|
||||
|
||||
QPoint FloatingWindow::clamped(QPoint position) const
|
||||
{
|
||||
const QRect area = m_window->screen()->geometry();
|
||||
const int maxX = qMax(0, area.width() - m_window->width());
|
||||
const int maxY = qMax(0, area.height() - m_window->height());
|
||||
return {qBound(0, position.x(), maxX), qBound(0, position.y(), maxY)};
|
||||
}
|
||||
|
||||
QPoint FloatingWindow::defaultPosition() const
|
||||
{
|
||||
const QRect area = m_window->screen()->geometry();
|
||||
return clamped({(area.width() - m_window->width()) / 2,
|
||||
area.height() - m_window->height() - BottomGap});
|
||||
}
|
||||
|
||||
void FloatingWindow::apply()
|
||||
{
|
||||
if (m_layerSurface)
|
||||
m_layerSurface->setMargins({m_position.x(), m_position.y(), 0, 0});
|
||||
else
|
||||
m_window->setPosition(m_window->screen()->geometry().topLeft() + m_position);
|
||||
}
|
||||
Reference in New Issue
Block a user