Compare commits

...

6 Commits

Author SHA1 Message Date
e9b257954b RFC: Allow specifying margins in percents
Absolute margin values pose a problem if you want to place a surface
certain distance away from a screen edge (in percents) but you also want
the compositor to place the surface on the active output.

At the moment, this issue is worked around by using kwin dbus api to
query the active output. This is not good.

This change is a take on allowing to specify margin values in percents.
It can be used to drop a dbus call in krunner and assist us with porting
other components to layer shell, e.g. OSDs, they also need to be placed
one third away the bottom screen edge.
2025-11-20 00:39:51 +02:00
7629761b71 percent margins 2025-11-19 23:46:25 +02:00
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
10 changed files with 394 additions and 138 deletions

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.16)
project(layershellqt)
set(PROJECT_VERSION "6.4.90")
set(PROJECT_VERSION "6.5.80")
set(PROJECT_VERSION_MAJOR 6)
set(CMAKE_C_STANDARD 99)

View File

@ -4,7 +4,7 @@
ecm_add_qml_module(LayerShellQtQml
URI "org.kde.layershell"
VERSION 1.0
SOURCES types.h types.cpp
SOURCES types.h
GENERATE_PLUGIN_SOURCE)
target_link_libraries(LayerShellQtQml PRIVATE Qt::Qml LayerShellQtInterface)

View File

@ -1,49 +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 "types.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 1)
int QQmlMarginsValueType::left() const
{
return m.left();
}
int QQmlMarginsValueType::top() const
{
return m.top();
}
int QQmlMarginsValueType::right() const
{
return m.right();
}
int QQmlMarginsValueType::bottom() const
{
return m.bottom();
}
void QQmlMarginsValueType::setLeft(int left)
{
m.setLeft(left);
}
void QQmlMarginsValueType::setTop(int top)
{
m.setTop(top);
}
void QQmlMarginsValueType::setRight(int right)
{
m.setRight(right);
}
void QQmlMarginsValueType::setBottom(int bottom)
{
m.setBottom(bottom);
}
#endif

View File

@ -17,39 +17,3 @@ class WindowForeign
QML_UNCREATABLE("")
QML_ATTACHED(LayerShellQt::Window)
};
// available upstream since https://invent.kde.org/qt/qt/qtdeclarative/-/commit/a398101f715bfc447aa889fc9c58b13bfe75ab47
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 1)
struct Q_QML_EXPORT QQmlMarginsValueType {
QMargins m;
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)
Q_GADGET
QML_ANONYMOUS
QML_FOREIGN(QMargins)
QML_EXTENDED(QQmlMarginsValueType)
QML_STRUCTURED_VALUE
public:
QQmlMarginsValueType() = default;
Q_INVOKABLE QQmlMarginsValueType(const QMarginsF &margins)
: m(margins.toMargins())
{
}
int left() const;
int right() const;
int top() const;
int bottom() const;
void setLeft(int);
void setRight(int);
void setTop(int);
void setBottom(int);
operator QMargins() const
{
return m;
}
};
#endif

View File

@ -9,6 +9,7 @@
#include <layershellqt_logging.h>
#include <QPlatformSurfaceEvent>
#include <QPointer>
#include <optional>
@ -16,6 +17,49 @@
using namespace LayerShellQt;
Margin::Margin()
: m_value(0)
{
}
Margin::Margin(int pixels)
: m_value(pixels)
{
}
Margin::Margin(qreal percents)
: m_value(percents)
{
}
Margin Margin::fromPixels(int pixels)
{
return Margin(pixels);
}
Margin Margin::fromPercents(qreal percents)
{
return Margin(percents);
}
std::optional<int> Margin::pixels() const
{
if (std::holds_alternative<int>(m_value)) {
return std::get<int>(m_value);
} else {
return std::nullopt;
}
}
std::optional<qreal> Margin::percents() const
{
if (std::holds_alternative<qreal>(m_value)) {
return std::get<qreal>(m_value);
} else {
return std::nullopt;
}
}
class LayerShellQt::WindowPrivate
{
public:
@ -31,7 +75,10 @@ public:
Window::Anchor exclusiveEdge = Window::AnchorNone;
Window::KeyboardInteractivity keyboardInteractivity = Window::KeyboardInteractivityOnDemand;
Window::Layer layer = Window::LayerTop;
QMargins margins;
Margin leftMargin;
Margin topMargin;
Margin rightMargin;
Margin bottomMargin;
QSize desiredSize = QSize(0, 0);
Window::ScreenConfiguration screenConfiguration = Window::ScreenFromQWindow;
bool closeOnDismissed = true;
@ -88,15 +135,74 @@ Window::Anchor Window::exclusiveEdge() const
void Window::setMargins(const QMargins &margins)
{
if (d->margins != margins) {
d->margins = margins;
Q_EMIT marginsChanged();
}
setLeftMargin(Margin::fromPixels(margins.left()));
setTopMargin(Margin::fromPixels(margins.top()));
setRightMargin(Margin::fromPixels(margins.right()));
setBottomMargin(Margin::fromPixels(margins.bottom()));
}
QMargins Window::margins() const
{
return d->margins;
return QMargins(d->leftMargin.pixels().value_or(0),
d->topMargin.pixels().value_or(0),
d->rightMargin.pixels().value_or(0),
d->bottomMargin.pixels().value_or(0));
}
void Window::setLeftMargin(Margin margin)
{
if (d->leftMargin != margin) {
d->leftMargin = margin;
Q_EMIT leftMarginChanged();
Q_EMIT marginsChanged();
}
}
Margin Window::leftMargin() const
{
return d->leftMargin;
}
void Window::setTopMargin(Margin margin)
{
if (d->topMargin != margin) {
d->topMargin = margin;
Q_EMIT topMarginChanged();
Q_EMIT marginsChanged();
}
}
Margin Window::topMargin() const
{
return d->topMargin;
}
void Window::setRightMargin(Margin margin)
{
if (d->rightMargin != margin) {
d->rightMargin = margin;
Q_EMIT rightMarginChanged();
Q_EMIT marginsChanged();
}
}
Margin Window::rightMargin() const
{
return d->rightMargin;
}
void Window::setBottomMargin(Margin margin)
{
if (d->bottomMargin != margin) {
d->bottomMargin = margin;
Q_EMIT bottomMarginChanged();
Q_EMIT marginsChanged();
}
}
Margin Window::bottomMargin() const
{
return d->bottomMargin;
}
void Window::setDesiredSize(const QSize &size)
@ -186,12 +292,35 @@ Window::Window(QWindow *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;
}
@ -205,7 +334,6 @@ Window::Window(QWindow *window)
return;
}
}
waylandWindow->setShellIntegration(shellIntegration);
}

View File

@ -18,6 +18,34 @@ namespace LayerShellQt
{
class WindowPrivate;
/**
* The Margin type provides a way to specify how far a layer surface should be away from a screen edge.
*
* A margin can have an absolute value or a percent value. An absolute value indicates the distance
* in pixels. A percent value indicates the distance as a percentage of the output size, for example
* this can be used to tell the compositor that the surface should be one third of the output height from
* a screen edge, etc.
*/
class LAYERSHELLQT_EXPORT Margin
{
public:
static Margin fromPixels(int pixels);
static Margin fromPercents(qreal percents);
Margin();
std::optional<int> pixels() const;
std::optional<qreal> percents() const;
auto operator<=>(const Margin &other) const = default;
private:
Margin(int pixels);
Margin(qreal percents);
std::variant<int, qreal> m_value;
};
class LAYERSHELLQT_EXPORT Window : public QObject
{
Q_OBJECT
@ -87,6 +115,18 @@ public:
void setMargins(const QMargins &margins);
QMargins margins() const;
void setLeftMargin(Margin margin);
Margin leftMargin() const;
void setTopMargin(Margin margin);
Margin topMargin() const;
void setRightMargin(Margin margin);
Margin rightMargin() const;
void setBottomMargin(Margin margin);
Margin bottomMargin() const;
void setDesiredSize(const QSize &size);
QSize desiredSize() const;
@ -138,16 +178,24 @@ public:
static Window *qmlAttachedProperties(QObject *object);
bool eventFilter(QObject *watched, QEvent *event) override;
Q_SIGNALS:
void anchorsChanged();
void exclusionZoneChanged();
void exclusiveEdgeChanged();
void marginsChanged();
void leftMarginChanged();
void topMarginChanged();
void rightMarginChanged();
void bottomMarginChanged();
void desiredSizeChanged();
void keyboardInteractivityChanged();
void layerChanged();
private:
void initializeShell();
Window(QWindow *window);
QScopedPointer<WindowPrivate> d;
};

View File

@ -60,9 +60,24 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
setExclusiveEdge(m_interface->exclusiveEdge());
});
setMargins(m_interface->margins());
connect(m_interface, &Window::marginsChanged, this, [this]() {
setMargins(m_interface->margins());
setLeftMargin(m_interface->leftMargin());
connect(m_interface, &Window::leftMarginChanged, this, [this]() {
setLeftMargin(m_interface->leftMargin());
});
setTopMargin(m_interface->topMargin());
connect(m_interface, &Window::topMarginChanged, this, [this]() {
setTopMargin(m_interface->topMargin());
});
setRightMargin(m_interface->rightMargin());
connect(m_interface, &Window::rightMarginChanged, this, [this]() {
setRightMargin(m_interface->rightMargin());
});
setBottomMargin(m_interface->bottomMargin());
connect(m_interface, &Window::bottomMarginChanged, this, [this]() {
setBottomMargin(m_interface->bottomMargin());
});
connect(m_interface, &Window::desiredSizeChanged, this, [this]() {
@ -124,13 +139,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)
@ -165,10 +174,69 @@ void QWaylandLayerSurface::setExclusiveEdge(uint32_t edge)
}
}
void QWaylandLayerSurface::setMargins(const QMargins &margins)
void QWaylandLayerSurface::setLeftMargin(const Margin &margin)
{
if (zwlr_layer_surface_v1_get_version(object()) >= 6) {
if (const auto pixels = margin.pixels()) {
set_left_margin_absolute(*pixels);
} else if (const auto percents = margin.percents()) {
set_left_margin_percents(wl_fixed_from_double(*percents));
} else {
qCWarning(LAYERSHELLQT) << "Unspecified left margin for" << m_window->window();
}
} else {
const QMargins margins = m_interface->margins();
set_margin(margins.top(), margins.right(), margins.bottom(), margins.left());
}
}
void QWaylandLayerSurface::setTopMargin(const Margin &margin)
{
if (zwlr_layer_surface_v1_get_version(object()) >= 6) {
if (const auto pixels = margin.pixels()) {
set_top_margin_absolute(*pixels);
} else if (const auto percents = margin.percents()) {
set_top_margin_percents(wl_fixed_from_double(*percents));
} else {
qCWarning(LAYERSHELLQT) << "Unspecified top margin for" << m_window->window();
}
} else {
const QMargins margins = m_interface->margins();
set_margin(margins.top(), margins.right(), margins.bottom(), margins.left());
}
}
void QWaylandLayerSurface::setRightMargin(const Margin &margin)
{
if (zwlr_layer_surface_v1_get_version(object()) >= 6) {
if (const auto pixels = margin.pixels()) {
set_right_margin_absolute(*pixels);
} else if (const auto percents = margin.percents()) {
set_right_margin_percents(wl_fixed_from_double(*percents));
} else {
qCWarning(LAYERSHELLQT) << "Unspecified right margin for" << m_window->window();
}
} else {
const QMargins margins = m_interface->margins();
set_margin(margins.top(), margins.right(), margins.bottom(), margins.left());
}
}
void QWaylandLayerSurface::setBottomMargin(const Margin &margin)
{
if (zwlr_layer_surface_v1_get_version(object()) >= 6) {
if (const auto pixels = margin.pixels()) {
set_bottom_margin_absolute(*pixels);
} else if (const auto percents = margin.percents()) {
set_bottom_margin_percents(wl_fixed_from_double(*percents));
} else {
qCWarning(LAYERSHELLQT) << "Unspecified bottom margin for" << m_window->window();
}
} else {
const QMargins margins = m_interface->margins();
set_margin(margins.top(), margins.right(), margins.bottom(), margins.left());
}
}
void QWaylandLayerSurface::setKeyboardInteractivity(uint32_t interactivity)
{
@ -181,25 +249,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()
{
@ -268,13 +323,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

@ -19,6 +19,7 @@
namespace LayerShellQt
{
class Margin;
class Window;
class LAYERSHELLQT_EXPORT QWaylandLayerSurface : public QtWaylandClient::QWaylandShellSurface, public QtWayland::zwlr_layer_surface_v1
@ -38,16 +39,15 @@ public:
void setAnchor(uint32_t anchor);
void setExclusiveZone(int32_t zone);
void setExclusiveEdge(uint32_t edge);
void setMargins(const QMargins &margins);
void setLeftMargin(const Margin &margin);
void setTopMargin(const Margin &margin);
void setRightMargin(const Margin &margin);
void setBottomMargin(const Margin &margin);
void setKeyboardInteractivity(uint32_t interactivity);
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;
@ -66,9 +66,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

@ -25,7 +25,7 @@
THIS SOFTWARE.
</copyright>
<interface name="zwlr_layer_shell_v1" version="5">
<interface name="zwlr_layer_shell_v1" version="6">
<description summary="create surfaces that are layers of the desktop">
Clients can use this interface to assign the surface_layer role to
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
@ -100,7 +100,7 @@
</request>
</interface>
<interface name="zwlr_layer_surface_v1" version="5">
<interface name="zwlr_layer_surface_v1" version="6">
<description summary="layer metadata interface">
An interface that may be implemented by a wl_surface, for surfaces that
are designed to be rendered as a layer of a stacked desktop-like
@ -403,5 +403,83 @@
</description>
<arg name="edge" type="uint"/>
</request>
<!-- Version 6 additions -->
<request name="set_left_margin_absolute" since="6">
<description summary="set left margin in absolute pixels">
Sets the left margin in absolute pixels.
The left margin specified in percents will be overwritten.
</description>
<arg name="margin" type="int"/>
</request>
<request name="set_left_margin_percents" since="6">
<description summary="set the left margin proportional to area width">
Sets the left margin as a percentage of the area width. The
margin value is a number between 0 and 1.
The left margin specified in absolute values will be overwritten.
</description>
<arg name="margin" type="fixed"/>
</request>
<request name="set_top_margin_absolute" since="6">
<description summary="set top margin in absolute pixels">
Sets the top margin in absolute pixels.
The top margin specified in percents will be overwritten.
</description>
<arg name="margin" type="int"/>
</request>
<request name="set_top_margin_percents" since="6">
<description summary="set the top margin proportional to area width">
Sets the top margin as a percentage of the area width. The
margin value is a number between 0 and 1.
The top margin specified in absolute values will be overwritten.
</description>
<arg name="margin" type="fixed"/>
</request>
<request name="set_right_margin_absolute" since="6">
<description summary="set right margin in absolute pixels">
Sets the right margin in absolute pixels.
The right margin specified in percents will be overwritten.
</description>
<arg name="margin" type="int"/>
</request>
<request name="set_right_margin_percents" since="6">
<description summary="set the right margin proportional to area width">
Sets the right margin as a percentage of the area width. The
margin value is a number between 0 and 1.
The right margin specified in absolute values will be overwritten.
</description>
<arg name="margin" type="fixed"/>
</request>
<request name="set_bottom_margin_absolute" since="6">
<description summary="set bottom margin in absolute pixels">
Sets the bottom margin in absolute pixels.
The bottom margin specified in percents will be overwritten.
</description>
<arg name="margin" type="int"/>
</request>
<request name="set_bottom_margin_percents" since="6">
<description summary="set the bottom margin proportional to area width">
Sets the bottom margin as a percentage of the area width. The
margin value is a number between 0 and 1.
The bottom margin specified in absolute values will be overwritten.
</description>
<arg name="margin" type="fixed"/>
</request>
</interface>
</protocol>

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
}
}