Compare commits

...

12 Commits

Author SHA1 Message Date
b9e1260949 Window: Ensure we integrate windows that were already were shown in a different shell 2025-10-25 13:02:31 +02:00
cb79d3f60a window: Improve how we tell the window to do layer-shell
Instead of telling it in the construction after forcing the window
creation, install an event handler that sets it when we get the
PlatformSurface event.

It has the advantage that it will also trigger in subsequent platform
surface events (e.g. after hide and show).
2025-10-25 13:02:31 +02:00
2b8544f5a0 Drop obsolete code for Qt versions lower than 6.9 2025-10-16 14:02:29 +02:00
e4b5bdbf1b Update version for new release 6.5.80 2025-09-18 20:56:39 +05:30
ebd64dd395 Update version for new release 6.4.90 2025-09-18 17:26:46 +05:30
ca0ac57fb4 Update Qt version requirement to 6.9.0
GIT_SILENT
2025-09-18 14:00:16 +05:30
d085a88c81 Update Frameworks version requirement to 6.18.0
GIT_SILENT
2025-09-18 14:00:16 +05:30
5e57a060c6 Register anchors flags to QML
CCBUG: 507602
2025-08-05 13:04:50 +02:00
d436a779d7 Request activate on show
Unless the window doesn't have keyboard interactivity or the caller
wants it not to.

To match XDG Shell behavior and general Qt window behavior.
2025-08-03 10:08:40 +00:00
f599e829ad When activating, also try token from XDG_ACTIVATION_TOKEN
This matches upstream QWaylandXdgSurface behavior.

It also makes various activation magic e.g. KDBusService work.
2025-07-30 23:29:13 +00:00
397398dfd8 Port to declarative type registration
BUG: 507602
2025-07-30 21:39:23 +02:00
7a074a3684 update version for new release 2025-05-19 18:33:35 +05:30
11 changed files with 148 additions and 95 deletions

View File

@ -4,13 +4,13 @@
cmake_minimum_required(VERSION 3.16)
project(layershellqt)
set(PROJECT_VERSION "6.3.90")
set(PROJECT_VERSION "6.5.80")
set(PROJECT_VERSION_MAJOR 6)
set(CMAKE_C_STANDARD 99)
set(QT_MIN_VERSION "6.8.0")
set(KF6_MIN_VERSION "6.14.0")
set(QT_MIN_VERSION "6.9.0")
set(KF6_MIN_VERSION "6.18.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
set(CMAKE_CXX_STANDARD 20)

View File

@ -5,6 +5,8 @@ remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS)
add_library(LayerShellQtInterface)
qt6_extract_metatypes(LayerShellQtInterface)
if (Qt6_VERSION VERSION_GREATER_EQUAL "6.8.0")
set(private_code_option "PRIVATE_CODE")
endif()

View File

@ -4,7 +4,8 @@
ecm_add_qml_module(LayerShellQtQml
URI "org.kde.layershell"
VERSION 1.0
SOURCES layershellqtplugin.cpp)
SOURCES types.h
GENERATE_PLUGIN_SOURCE)
target_link_libraries(LayerShellQtQml PRIVATE Qt::Qml LayerShellQtInterface)
ecm_finalize_qml_module(LayerShellQtQml DESTINATION ${KDE_INSTALL_QMLDIR})
ecm_finalize_qml_module(LayerShellQtQml DESTINATION ${KDE_INSTALL_QMLDIR})

View File

@ -1,51 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleix.pol_gonzalez@mercedes-benz.com>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "../interfaces/window.h"
#include <QQmlExtensionPlugin>
#include <qqml.h>
QML_DECLARE_TYPEINFO(LayerShellQt::Window, QML_HAS_ATTACHED_PROPERTIES)
class ExtQMargins
{
QMargins m_margins;
Q_GADGET
Q_PROPERTY(int left READ left WRITE setLeft FINAL)
Q_PROPERTY(int right READ right WRITE setRight FINAL)
Q_PROPERTY(int top READ top WRITE setTop FINAL)
Q_PROPERTY(int bottom READ bottom WRITE setBottom FINAL)
QML_FOREIGN(QMargins)
QML_EXTENDED(ExtQMargins)
public:
ExtQMargins(const QMargins &margins);
int left() const { return m_margins.left(); }
void setLeft(int left) { m_margins.setLeft(left); }
int right() const { return m_margins.right(); }
void setRight(int right) { m_margins.setRight(right); }
int top() const { return m_margins.top(); }
void setTop(int top) { m_margins.setTop(top); }
int bottom() const { return m_margins.bottom(); }
void setBottom(int bottom) { m_margins.setBottom(bottom); }
};
class Plugin : public QQmlExtensionPlugin
{
Q_PLUGIN_METADATA(IID "org.kde.layershellqt")
Q_OBJECT
public:
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.layershell"));
qmlRegisterType<LayerShellQt::Window>(uri, 1, 0, "Window");
qmlRegisterExtendedUncreatableType<QMargins, ExtQMargins>(uri, 1, 0, "ExtQMargins", QStringLiteral("Only created from C++"));
}
};
#include "layershellqtplugin.moc"

19
src/declarative/types.h Normal file
View File

@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleix.pol_gonzalez@mercedes-benz.com>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "../interfaces/window.h"
#include <qqml.h>
QML_DECLARE_TYPEINFO(LayerShellQt::Window, QML_HAS_ATTACHED_PROPERTIES)
class WindowForeign
{
Q_GADGET
QML_NAMED_ELEMENT(Window)
QML_FOREIGN(LayerShellQt::Window)
QML_UNCREATABLE("")
QML_ATTACHED(LayerShellQt::Window)
};

View File

@ -9,6 +9,7 @@
#include <layershellqt_logging.h>
#include <QPlatformSurfaceEvent>
#include <QPointer>
#include <optional>
@ -35,6 +36,7 @@ public:
QSize desiredSize = QSize(0, 0);
Window::ScreenConfiguration screenConfiguration = Window::ScreenFromQWindow;
bool closeOnDismissed = true;
bool activateOnShow = true;
};
static QMap<QWindow *, Window *> s_map;
@ -170,17 +172,50 @@ void Window::setCloseOnDismissed(bool close)
d->closeOnDismissed = close;
}
bool Window::activateOnShow() const
{
return d->activateOnShow;
}
void Window::setActivateOnShow(bool activateOnShow)
{
d->activateOnShow = activateOnShow;
}
Window::Window(QWindow *window)
: QObject(window)
, d(new WindowPrivate(window))
{
s_map.insert(d->parentWindow, this);
window->installEventFilter(this);
if (window->isVisible()) {
qCWarning(LAYERSHELLQT) << d->parentWindow << "already has a shell integration. Call QWindow::close() first and show it again.";
}
window->create();
if (window->handle()) {
initializeShell();
}
}
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle());
bool Window::eventFilter(QObject *watched, QEvent *event)
{
auto window = qobject_cast<QWindow *>(watched);
if (!window) {
return false;
}
if (event->type() == QEvent::PlatformSurface) {
if (auto pse = static_cast<QPlatformSurfaceEvent *>(event); pse->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
initializeShell();
}
}
return false;
}
void Window::initializeShell()
{
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(d->parentWindow->handle());
if (!waylandWindow) {
qCWarning(LAYERSHELLQT) << window << "is not a wayland window. Not creating zwlr_layer_surface";
qCWarning(LAYERSHELLQT) << d->parentWindow << "is not a wayland window. Not creating zwlr_layer_surface";
return;
}
@ -194,7 +229,6 @@ Window::Window(QWindow *window)
return;
}
}
waylandWindow->setShellIntegration(shellIntegration);
}

View File

@ -28,6 +28,7 @@ class LAYERSHELLQT_EXPORT Window : public QObject
Q_PROPERTY(Layer layer READ layer WRITE setLayer NOTIFY layerChanged)
Q_PROPERTY(KeyboardInteractivity keyboardInteractivity READ keyboardInteractivity WRITE setKeyboardInteractivity NOTIFY keyboardInteractivityChanged)
Q_PROPERTY(ScreenConfiguration screenConfiguration READ screenConfiguration WRITE setScreenConfiguration)
Q_PROPERTY(bool activateOnShow READ activateOnShow WRITE setActivateOnShow)
public:
~Window() override;
@ -39,8 +40,8 @@ public:
AnchorLeft = 4, ///< The left edge of the anchor rectangle
AnchorRight = 8, ///< The right edge of the anchor rectangle
};
Q_ENUM(Anchor);
Q_DECLARE_FLAGS(Anchors, Anchor)
Q_FLAG(Anchors)
/**
* This enum type is used to specify the layer where a surface can be put in.
@ -117,6 +118,18 @@ public:
void setCloseOnDismissed(bool close);
bool closeOnDismissed() const;
/**
* Whether the window should requestActivate on show.
*
* Normally, you want this enabled but in case of e.g. a desktop window, this can be disabled.
*
* It does nothing when KeyboardInteractivity is KeyboardInteractivityNone.
*
* The default is true.
*/
void setActivateOnShow(bool activateOnShow);
bool activateOnShow() const;
/**
* Gets the LayerShell Window for a given Qt Window
* Ownership is not transferred
@ -125,6 +138,8 @@ public:
static Window *qmlAttachedProperties(QObject *object);
bool eventFilter(QObject *watched, QEvent *event) override;
Q_SIGNALS:
void anchorsChanged();
void exclusionZoneChanged();
@ -135,6 +150,8 @@ Q_SIGNALS:
void layerChanged();
private:
void initializeShell();
Window(QWindow *window);
QScopedPointer<WindowPrivate> d;
};

View File

@ -124,13 +124,7 @@ void QWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface *po
void QWaylandLayerSurface::applyConfigure()
{
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
m_configuring = true;
#endif
window()->resizeFromApplyConfigure(m_pendingSize);
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
m_configuring = false;
#endif
}
void QWaylandLayerSurface::setDesiredSize(const QSize &size)
@ -181,25 +175,12 @@ void QWaylandLayerSurface::setLayer(uint32_t layer)
set_layer(layer);
}
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
void QWaylandLayerSurface::setWindowGeometry(const QRect &geometry)
{
if (m_configuring) {
return;
}
if (m_interface->desiredSize().isNull()) {
setDesiredSize(geometry.size());
}
}
#else
void QWaylandLayerSurface::setWindowSize(const QSize &size)
{
if (m_interface->desiredSize().isNull()) {
setDesiredSize(size);
}
}
#endif
bool QWaylandLayerSurface::requestActivate()
{
@ -211,6 +192,10 @@ bool QWaylandLayerSurface::requestActivate()
activation->activate(m_activationToken, window()->wlSurface());
m_activationToken = {};
return true;
} else if (const auto token = qEnvironmentVariable("XDG_ACTIVATION_TOKEN"); !token.isEmpty()) {
activation->activate(token, window()->wlSurface());
qunsetenv("XDG_ACTIVATION_TOKEN");
return true;
} else {
const auto focusWindow = QGuiApplication::focusWindow();
const auto wlWindow = focusWindow ? static_cast<QtWaylandClient::QWaylandWindow *>(focusWindow->handle()) : window();
@ -226,6 +211,23 @@ bool QWaylandLayerSurface::requestActivate()
return false;
}
bool QWaylandLayerSurface::requestActivateOnShow()
{
if (!m_interface->activateOnShow()) {
return false;
}
if (m_interface->keyboardInteractivity() == Window::KeyboardInteractivityNone) {
return false;
}
if (m_window->window()->property("_q_showWithoutActivating").toBool()) {
return false;
}
return requestActivate();
}
void QWaylandLayerSurface::setXdgActivationToken(const QString &token)
{
m_activationToken = token;
@ -247,13 +249,6 @@ void QWaylandLayerSurface::requestXdgActivationToken(quint32 serial)
void QWaylandLayerSurface::sendExpose()
{
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
window()->handleExpose(QRect(QPoint(), m_pendingSize));
#elif QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
window()->sendRecursiveExposeEvent();
#else
window()->updateExposure();
#endif
}
}

View File

@ -43,13 +43,10 @@ public:
void setLayer(uint32_t layer);
void applyConfigure() override;
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
void setWindowGeometry(const QRect &geometry) override;
#else
void setWindowSize(const QSize &size) override;
#endif
bool requestActivate() override;
bool requestActivateOnShow() override;
void setXdgActivationToken(const QString &token) override;
void requestXdgActivationToken(quint32 serial) override;
@ -65,9 +62,6 @@ private:
QString m_activationToken;
bool m_configured = false;
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
bool m_configuring = false;
#endif
};
}

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2025 Aleix Pol i Gonzalez <aleixpol@kde.org>
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
import QtQuick
import QtQuick.Controls
import org.kde.layershell 1.0 as LayerShell
Item
{
Button {
text: "Convert"
anchors.centerIn: parent
onClicked: {
win.close()
win.LayerShell.Window.anchors = LayerShell.Window.AnchorLeft;
win.LayerShell.Window.layer = LayerShell.Window.LayerTop;
win.LayerShell.Window.exclusionZone = -1;
win.show()
}
}
Window {
id: win
width: 100
height: 100
Rectangle {
anchors.fill: parent
color: "red"
Text {
anchors.centerIn: parent
text: "top"
}
}
visible: true
}
}

View File

@ -53,7 +53,7 @@ int main(int argc, char **argv)
QGuiApplication app(argc, argv);
const auto layerMetaEnum = QMetaEnum::fromType<Window::Layer>();
const auto anchorMetaEnum = QMetaEnum::fromType<Window::Anchor>();
const auto anchorMetaEnum = QMetaEnum::fromType<Window::Anchors>();
QCommandLineParser parser;
QCommandLineOption marginsOption(QStringLiteral("margins"), QStringLiteral("Window margins"), QStringLiteral("pixels"), QStringLiteral("0"));