Compare commits

..

1 Commits

Author SHA1 Message Date
7409bd4cc0 Make sure that the layer property gets initialised
Much like with the rest of the properties, we should set whichever value
was in the interface and then subscribe to changes in the property.
Otherwise, the layer doesn't get forwarded to the surface if the value
was set before its initialisation.

Signed-off-by: Victoria Fischer <victoria.fischer@mbition.io>
2024-09-24 15:06:13 +02:00
14 changed files with 123 additions and 276 deletions

View File

@ -7,6 +7,3 @@ include:
- /gitlab-templates/reuse-lint.yml
- /gitlab-templates/linux-qt6.yml
- /gitlab-templates/freebsd-qt6.yml
- /gitlab-templates/xml-lint.yml
- /gitlab-templates/yaml-lint.yml
- /gitlab-templates/linux-qt6-next.yml

View File

@ -2,11 +2,11 @@
# SPDX-License-Identifier: CC0-1.0
Dependencies:
- 'on': ['@all']
'require':
- 'on': ['@all']
'require':
'frameworks/extra-cmake-modules': '@latest-kf6'
'third-party/wayland': '@latest'
'third-party/wayland-protocols': '@latest'
Options:
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']

View File

@ -4,13 +4,13 @@
cmake_minimum_required(VERSION 3.16)
project(layershellqt)
set(PROJECT_VERSION "6.5.80")
set(PROJECT_VERSION "6.1.90")
set(PROJECT_VERSION_MAJOR 6)
set(CMAKE_C_STANDARD 99)
set(QT_MIN_VERSION "6.9.0")
set(KF6_MIN_VERSION "6.18.0")
set(QT_MIN_VERSION "6.7.0")
set(KF6_MIN_VERSION "6.5.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
set(CMAKE_CXX_STANDARD 20)
@ -34,11 +34,6 @@ include(ECMQmlModule)
include(KDEGitCommitHooks)
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS WaylandClient Qml)
if (Qt6WaylandClient_VERSION VERSION_GREATER_EQUAL "6.10.0")
find_package(Qt6WaylandClientPrivate ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
endif()
find_package(WaylandScanner REQUIRED)
find_package(Wayland 1.3 COMPONENTS Client Server)
find_package(WaylandProtocols REQUIRED)
@ -60,8 +55,8 @@ kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
ecm_set_disabled_deprecation_versions(QT 6.8.1
KF 6.9.0
ecm_set_disabled_deprecation_versions(QT 6.5
KF 5.240
)
add_subdirectory(src)

View File

@ -2,7 +2,7 @@
# SPDX-License-Identifier: CC0-1.0
maintainer:
- vladz
- vladz
description: Layer Shell Qt
platforms:
- name: Linux

View File

@ -5,8 +5,6 @@ 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,8 +4,7 @@
ecm_add_qml_module(LayerShellQtQml
URI "org.kde.layershell"
VERSION 1.0
SOURCES types.h
GENERATE_PLUGIN_SOURCE)
SOURCES layershellqtplugin.cpp)
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

@ -0,0 +1,51 @@
/*
* 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"

View File

@ -1,19 +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 <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,7 +9,6 @@
#include <layershellqt_logging.h>
#include <QPlatformSurfaceEvent>
#include <QPointer>
#include <optional>
@ -33,11 +32,8 @@ public:
Window::KeyboardInteractivity keyboardInteractivity = Window::KeyboardInteractivityOnDemand;
Window::Layer layer = Window::LayerTop;
QMargins margins;
QSize desiredSize = QSize(0, 0);
QPointer<QScreen> desiredScreen;
bool desiredActiveScreen = false;
Window::ScreenConfiguration screenConfiguration = Window::ScreenFromQWindow;
bool closeOnDismissed = true;
bool activateOnShow = true;
};
static QMap<QWindow *, Window *> s_map;
@ -101,21 +97,6 @@ QMargins Window::margins() const
return d->margins;
}
void Window::setDesiredSize(const QSize &size)
{
if (size == d->desiredSize) {
return;
}
d->desiredSize = size;
Q_EMIT desiredSizeChanged();
}
QSize Window::desiredSize() const
{
return d->desiredSize;
}
void Window::setKeyboardInteractivity(KeyboardInteractivity interactivity)
{
if (d->keyboardInteractivity != interactivity) {
@ -153,46 +134,14 @@ Window::Layer Window::layer() const
return d->layer;
}
void Window::setDesiredActiveScreen(bool set)
Window::ScreenConfiguration Window::screenConfiguration() const
{
if (d->desiredActiveScreen == set) {
return;
}
d->desiredActiveScreen = set;
if (d->desiredActiveScreen && d->desiredScreen) {
d->desiredScreen = nullptr;
Q_EMIT desiredScreenChanged();
}
Q_EMIT desiredActiveScreenChanged();
return d->screenConfiguration;
}
bool Window::desiredActiveScreen() const
void Window::setScreenConfiguration(Window::ScreenConfiguration screenConfiguration)
{
return d->desiredActiveScreen;
}
void Window::setDesiredScreen(QScreen *screen)
{
if (d->desiredScreen == screen) {
return;
}
d->desiredScreen = screen;
if (d->desiredScreen && d->desiredActiveScreen) {
d->desiredActiveScreen = false;
Q_EMIT desiredActiveScreenChanged();
}
Q_EMIT desiredScreenChanged();
}
QScreen *Window::desiredScreen() const
{
return d->desiredScreen;
d->screenConfiguration = screenConfiguration;
}
bool Window::closeOnDismissed() const
@ -205,50 +154,17 @@ 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.";
}
if (window->handle()) {
initializeShell();
}
}
window->create();
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());
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle());
if (!waylandWindow) {
qCWarning(LAYERSHELLQT) << d->parentWindow << "is not a wayland window. Not creating zwlr_layer_surface";
qCWarning(LAYERSHELLQT) << window << "is not a wayland window. Not creating zwlr_layer_surface";
return;
}
@ -262,6 +178,7 @@ void Window::initializeShell()
return;
}
}
waylandWindow->setShellIntegration(shellIntegration);
}

View File

@ -27,9 +27,7 @@ class LAYERSHELLQT_EXPORT Window : public QObject
Q_PROPERTY(qint32 exclusionZone READ exclusionZone WRITE setExclusiveZone NOTIFY exclusionZoneChanged)
Q_PROPERTY(Layer layer READ layer WRITE setLayer NOTIFY layerChanged)
Q_PROPERTY(KeyboardInteractivity keyboardInteractivity READ keyboardInteractivity WRITE setKeyboardInteractivity NOTIFY keyboardInteractivityChanged)
Q_PROPERTY(bool activateOnShow READ activateOnShow WRITE setActivateOnShow)
Q_PROPERTY(bool desiredActiveScreen READ desiredActiveScreen WRITE setDesiredActiveScreen NOTIFY desiredActiveScreenChanged)
Q_PROPERTY(QScreen *desiredScreen READ desiredScreen WRITE setDesiredScreen NOTIFY desiredScreenChanged)
Q_PROPERTY(ScreenConfiguration screenConfiguration READ screenConfiguration WRITE setScreenConfiguration)
public:
~Window() override;
@ -41,8 +39,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.
@ -65,6 +63,17 @@ public:
};
Q_ENUM(KeyboardInteractivity)
/**
* This enum type is used to specify which screen to place the surface on.
* ScreenFromQWindow (the default) reads QWindow::screen() while ScreenFromCompositor
* passes nil and lets the compositor decide.
*/
enum ScreenConfiguration {
ScreenFromQWindow = 0,
ScreenFromCompositor = 1,
};
Q_ENUM(ScreenConfiguration)
void setAnchors(Anchors anchor);
Anchors anchors() const;
@ -77,42 +86,14 @@ public:
void setMargins(const QMargins &margins);
QMargins margins() const;
void setDesiredSize(const QSize &size);
QSize desiredSize() const;
void setKeyboardInteractivity(KeyboardInteractivity interactivity);
KeyboardInteractivity keyboardInteractivity() const;
void setLayer(Layer layer);
Layer layer() const;
/**
* Indicates whether the layer shell surface should be placed on the active screen based on @a set.
*
* The active screen depends on the compositor policies.
*
* If no explicit desired screen has been specified with the setDesiredScreen() function or the layer
* surface doesn't need to be placed on the active screen, i.e. setDesiredActiveScreen() has not
* been called, the QWindow::screen() will be used to decide what screen the layer shell surface
* should be placed on.
*
* The desiredScreen() will be reset if @a set is @c true.
*/
void setDesiredActiveScreen(bool set);
bool desiredActiveScreen() const;
/**
* Indicates that the layer shell surface should be placed on the specified @a screen.
*
* If no explicit desired screen has been specified with the setDesiredScreen() function or the layer
* surface doesn't need to be placed on the active screen, i.e. setDesiredActiveScreen() has not
* been called, the QWindow::screen() will be used to decide what screen the layer shell surface
* should be placed on.
*
* The desiredActiveScreen() will be reset to @c false after calling this function.
*/
void setDesiredScreen(QScreen *screen);
QScreen *desiredScreen() const;
void setScreenConfiguration(ScreenConfiguration screenConfiguration);
ScreenConfiguration screenConfiguration() const;
/**
* Sets a string based identifier for this window.
@ -133,18 +114,6 @@ 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
@ -153,22 +122,15 @@ public:
static Window *qmlAttachedProperties(QObject *object);
bool eventFilter(QObject *watched, QEvent *event) override;
Q_SIGNALS:
void anchorsChanged();
void exclusionZoneChanged();
void exclusiveEdgeChanged();
void marginsChanged();
void desiredSizeChanged();
void keyboardInteractivityChanged();
void layerChanged();
void desiredActiveScreenChanged();
void desiredScreenChanged();
private:
void initializeShell();
Window(QWindow *window);
QScopedPointer<WindowPrivate> d;
};

View File

@ -26,12 +26,7 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
, m_window(window)
{
wl_output *output = nullptr;
if (!m_interface->desiredActiveScreen()) {
QScreen *desiredScreen = m_interface->desiredScreen();
if (!desiredScreen) {
window->window()->screen();
}
if (m_interface->screenConfiguration() == Window::ScreenFromQWindow) {
auto waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen *>(window->window()->screen()->handle());
// Qt will always assign a screen to a window, but if the compositor has no screens available a dummy QScreen object is created
// this will not cast to a QWaylandScreen
@ -42,6 +37,7 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
}
}
init(shell->get_layer_surface(window->waylandSurface()->object(), output, m_interface->layer(), m_interface->scope()));
setLayer(m_interface->layer());
connect(m_interface, &Window::layerChanged, this, [this]() {
setLayer(m_interface->layer());
});
@ -49,11 +45,7 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
setAnchor(m_interface->anchors());
connect(m_interface, &Window::anchorsChanged, this, [this]() {
setAnchor(m_interface->anchors());
if (m_interface->desiredSize().isNull()) {
setDesiredSize(m_window->windowContentGeometry().size());
} else {
setDesiredSize(m_interface->desiredSize());
}
setDesiredSize(m_window->windowContentGeometry().size());
});
setExclusiveZone(m_interface->exclusionZone());
@ -70,22 +62,12 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
setMargins(m_interface->margins());
});
connect(m_interface, &Window::desiredSizeChanged, this, [this]() {
if (!m_interface->desiredSize().isNull()) {
setDesiredSize(m_interface->desiredSize());
}
});
setKeyboardInteractivity(m_interface->keyboardInteractivity());
connect(m_interface, &Window::keyboardInteractivityChanged, this, [this]() {
setKeyboardInteractivity(m_interface->keyboardInteractivity());
});
if (m_interface->desiredSize().isNull()) {
setDesiredSize(window->windowContentGeometry().size());
} else {
setDesiredSize(m_interface->desiredSize());
}
setDesiredSize(window->windowContentGeometry().size());
}
QWaylandLayerSurface::~QWaylandLayerSurface()
@ -129,7 +111,13 @@ 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)
@ -180,12 +168,21 @@ 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;
}
setDesiredSize(geometry.size());
}
#else
void QWaylandLayerSurface::setWindowSize(const QSize &size)
{
if (m_interface->desiredSize().isNull()) {
setDesiredSize(size);
}
setDesiredSize(size);
}
#endif
bool QWaylandLayerSurface::requestActivate()
{
@ -197,10 +194,6 @@ 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();
@ -216,23 +209,6 @@ 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;
@ -254,6 +230,13 @@ 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,10 +43,13 @@ 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;
@ -62,6 +65,9 @@ private:
QString m_activationToken;
bool m_configured = false;
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
bool m_configuring = false;
#endif
};
}

View File

@ -1,42 +0,0 @@
/*
* 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::Anchors>();
const auto anchorMetaEnum = QMetaEnum::fromType<Window::Anchor>();
QCommandLineParser parser;
QCommandLineOption marginsOption(QStringLiteral("margins"), QStringLiteral("Window margins"), QStringLiteral("pixels"), QStringLiteral("0"));