Ensure we can set per-window properties before the intial commit
In the current implementation we cannot use a LayerShellQt before the shell surface is created. At the moment a shell surface is created, the constructor is run and then QtWayland commits the current state. This means the compositor configures the window before a client has any chance to set anchors or margins. This works whilst we're just being a simple fullscreen window, but won't scale for plasmashell in the future. This patch makes LayerShellQt::Window always creatable, and we can set and cache properties before the platform window is created, just like one can on QWindow and XDGShell properties. This also makes it less potentially crashy as ::get always returns a valid result, and sets up the public API to be QML-able as an attached property in future. Co-authored on Aleix's patch for the unit test
This commit is contained in:
parent
f684097c0f
commit
29d0078909
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <qglobal.h>
|
|
||||||
#include <layershellqt_logging.h>
|
#include <layershellqt_logging.h>
|
||||||
|
#include <qglobal.h>
|
||||||
|
|
||||||
using namespace LayerShellQt;
|
using namespace LayerShellQt;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#define LAYERSHELLQTSHELL_H
|
#define LAYERSHELLQTSHELL_H
|
||||||
|
|
||||||
#include "layershellqt_export.h"
|
#include "layershellqt_export.h"
|
||||||
|
#include "window.h"
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
namespace LayerShellQt
|
namespace LayerShellQt
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,58 +15,133 @@ using namespace LayerShellQt;
|
||||||
class LayerShellQt::WindowPrivate
|
class LayerShellQt::WindowPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WindowPrivate(QWaylandLayerSurface *surface)
|
WindowPrivate(QWindow *window)
|
||||||
: surface(surface)
|
: parentWindow(window)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QWaylandLayerSurface *const surface;
|
QWindow *parentWindow;
|
||||||
|
QString scope = QStringLiteral("qt");
|
||||||
|
Window::Anchors anchor = {Window::AnchorTop | Window::AnchorBottom | Window::AnchorLeft | Window::AnchorRight};
|
||||||
|
int32_t exclusionZone = 0;
|
||||||
|
bool keyboardInteractivity = false;
|
||||||
|
Window::Layer layer = Window::LayerTop;
|
||||||
|
QMargins margins;
|
||||||
|
QWaylandLayerSurface* getSurface() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
Window::~Window() = default;
|
static QMap<QWindow*, Window*> s_map;
|
||||||
|
|
||||||
void Window::setAnchor(Anchor anchor)
|
Window::~Window()
|
||||||
{
|
{
|
||||||
d->surface->setAnchor(anchor);
|
s_map.remove(d->parentWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::setAnchor(Anchors anchor)
|
||||||
|
{
|
||||||
|
d->anchor = anchor;
|
||||||
|
if (auto surface = d->getSurface()) {
|
||||||
|
surface->setAnchor(anchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::Anchors Window::anchor() const
|
||||||
|
{
|
||||||
|
return d->anchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setExclusiveZone(int32_t zone)
|
void Window::setExclusiveZone(int32_t zone)
|
||||||
{
|
{
|
||||||
d->surface->setExclusiveZone(zone);
|
d->exclusionZone = zone;
|
||||||
|
if (auto surface= d->getSurface()) {
|
||||||
|
surface->setExclusiveZone(zone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t Window::exclusionZone() const
|
||||||
|
{
|
||||||
|
return d->exclusionZone;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setMargins(const QMargins &margins)
|
void Window::setMargins(const QMargins &margins)
|
||||||
{
|
{
|
||||||
d->surface->setMargins(margins);
|
d->margins = margins;
|
||||||
|
if (auto surface= d->getSurface()) {
|
||||||
|
surface->setMargins(margins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QMargins Window::margins() const
|
||||||
|
{
|
||||||
|
return d->margins;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setKeyboardInteractivity(bool enabled)
|
void Window::setKeyboardInteractivity(bool enabled)
|
||||||
{
|
{
|
||||||
d->surface->setKeyboardInteractivity(enabled);
|
d->keyboardInteractivity = enabled;
|
||||||
|
if (auto surface= d->getSurface()) {
|
||||||
|
surface->setKeyboardInteractivity(enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Window::keyboardInteractivity() const
|
||||||
|
{
|
||||||
|
return d->keyboardInteractivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setLayer(Layer layer)
|
void Window::setLayer(Layer layer)
|
||||||
{
|
{
|
||||||
d->surface->setLayer(layer);
|
d->layer = layer;
|
||||||
|
if (auto surface= d->getSurface()) {
|
||||||
|
surface->setLayer(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::setScope(const QString &scope)
|
||||||
|
{
|
||||||
|
d->scope = scope;
|
||||||
|
//this is static and must be set before the platform window is created
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Window::scope() const
|
||||||
|
{
|
||||||
|
return d->scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::Layer Window::layer() const
|
||||||
|
{
|
||||||
|
return d->layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::Window(WindowPrivate *d)
|
Window::Window(WindowPrivate *d)
|
||||||
: d(d)
|
: QObject(d->parentWindow)
|
||||||
|
, d(d)
|
||||||
{
|
{
|
||||||
|
s_map.insert(d->parentWindow, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window *Window::get(QWindow *window)
|
QWaylandLayerSurface *WindowPrivate::getSurface() const
|
||||||
{
|
{
|
||||||
auto ww = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle());
|
if (!parentWindow) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto ww = dynamic_cast<QtWaylandClient::QWaylandWindow *>(parentWindow->handle());
|
||||||
if (!ww) {
|
if (!ww) {
|
||||||
qCDebug(LAYERSHELLQT) << "window not a wayland window" << window;
|
qCDebug(LAYERSHELLQT) << "window not a wayland window" << parentWindow;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
QWaylandLayerSurface *s = qobject_cast<QWaylandLayerSurface *>(ww->shellSurface());
|
QWaylandLayerSurface *s = qobject_cast<QWaylandLayerSurface *>(ww->shellSurface());
|
||||||
if (!s) {
|
if (!s) {
|
||||||
qCDebug(LAYERSHELLQT) << "window not using wlr-layer-shell" << window << ww->shellSurface();
|
qCDebug(LAYERSHELLQT) << "window not using wlr-layer-shell" << parentWindow << ww->shellSurface();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
return s;
|
||||||
return new Window(new WindowPrivate(s));
|
}
|
||||||
|
|
||||||
|
Window *Window::get(QWindow *window)
|
||||||
|
{
|
||||||
|
if (s_map.contains(window)) {
|
||||||
|
return s_map[window];
|
||||||
|
}
|
||||||
|
return new Window(new WindowPrivate(window));
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ public:
|
||||||
AnchorRight = 8, // the right edge of the anchor rectangle
|
AnchorRight = 8, // the right edge of the anchor rectangle
|
||||||
};
|
};
|
||||||
Q_ENUM(Anchor);
|
Q_ENUM(Anchor);
|
||||||
|
Q_DECLARE_FLAGS(Anchors, Anchor)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This enum type is used to specify the layer where a surface can be put in.
|
* This enum type is used to specify the layer where a surface can be put in.
|
||||||
|
@ -42,12 +43,28 @@ public:
|
||||||
};
|
};
|
||||||
Q_ENUM(Layer)
|
Q_ENUM(Layer)
|
||||||
|
|
||||||
void setAnchor(Anchor anchor);
|
void setAnchor(Anchors anchor);
|
||||||
void setExclusiveZone(int32_t zone);
|
Anchors anchor() const;
|
||||||
void setMargins(const QMargins &margins);
|
|
||||||
void setKeyboardInteractivity(bool enabled);
|
|
||||||
void setLayer(Layer layer);
|
|
||||||
|
|
||||||
|
void setExclusiveZone(int32_t zone);
|
||||||
|
int32_t exclusionZone() const;
|
||||||
|
|
||||||
|
void setMargins(const QMargins &margins);
|
||||||
|
QMargins margins() const;
|
||||||
|
|
||||||
|
void setKeyboardInteractivity(bool enabled);
|
||||||
|
bool keyboardInteractivity() const;
|
||||||
|
|
||||||
|
void setLayer(Layer layer);
|
||||||
|
Layer layer() const;
|
||||||
|
|
||||||
|
void setScope(const QString &scope);
|
||||||
|
QString scope() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LayerShell Window for a given Qt Window
|
||||||
|
* Ownership is not transferred
|
||||||
|
*/
|
||||||
static Window *get(QWindow *window);
|
static Window *get(QWindow *window);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* SPDX-License-Identifier: LGPL-3.0-or-later
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "interfaces/shell.h"
|
||||||
#include "qwaylandlayershell_p.h"
|
#include "qwaylandlayershell_p.h"
|
||||||
#include "qwaylandlayersurface_p.h"
|
#include "qwaylandlayersurface_p.h"
|
||||||
|
|
||||||
|
@ -16,14 +17,38 @@ namespace LayerShellQt
|
||||||
{
|
{
|
||||||
QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandClient::QWaylandWindow *window)
|
QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandClient::QWaylandWindow *window)
|
||||||
: QtWaylandClient::QWaylandShellSurface(window)
|
: QtWaylandClient::QWaylandShellSurface(window)
|
||||||
, QtWayland::zwlr_layer_surface_v1(
|
, QtWayland::zwlr_layer_surface_v1()
|
||||||
// TODO: Specify namespace
|
|
||||||
shell->get_layer_surface(window->waylandSurface()->object(),
|
|
||||||
window->waylandScreen()->output(),
|
|
||||||
QtWayland::zwlr_layer_shell_v1::layer_top,
|
|
||||||
QStringLiteral("qt")))
|
|
||||||
{
|
{
|
||||||
set_anchor(anchor_top | anchor_bottom | anchor_left | anchor_right);
|
Window::Layer layer =Window::LayerTop;
|
||||||
|
QString scope =QStringLiteral( "qt");
|
||||||
|
LayerShellQt::Window *interface = Window::get(window->window());
|
||||||
|
Window::Anchors anchors = {Window::AnchorTop | Window::AnchorBottom | Window::AnchorLeft | Window::AnchorRight};
|
||||||
|
|
||||||
|
if (interface) {
|
||||||
|
anchors = interface->anchor();
|
||||||
|
layer = interface->layer();
|
||||||
|
scope = interface->scope();
|
||||||
|
}
|
||||||
|
|
||||||
|
init(shell->get_layer_surface(window->waylandSurface()->object(), window->waylandScreen()->output(), layer, scope));
|
||||||
|
set_anchor(anchors);
|
||||||
|
|
||||||
|
if (interface) {
|
||||||
|
setMargins(interface->margins());
|
||||||
|
setKeyboardInteractivity(interface->keyboardInteractivity());
|
||||||
|
setExclusiveZone(interface->exclusionZone());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize size = window->surfaceSize();
|
||||||
|
if (anchors & Window::AnchorLeft && anchors & Window::AnchorRight) {
|
||||||
|
size.setWidth(0);
|
||||||
|
}
|
||||||
|
if (anchors & Window::AnchorTop && anchors & Window::AnchorBottom) {
|
||||||
|
size.setHeight(0);
|
||||||
|
}
|
||||||
|
if (size.isValid() && size != QSize(0,0)) {
|
||||||
|
set_size(size.width(), size.height());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QWaylandLayerSurface::~QWaylandLayerSurface()
|
QWaylandLayerSurface::~QWaylandLayerSurface()
|
||||||
|
|
|
@ -4,35 +4,96 @@
|
||||||
* SPDX-License-Identifier: LGPL-3.0-or-later
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QRasterWindow>
|
||||||
|
#include <QWindow>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <QMetaEnum>
|
||||||
|
|
||||||
#include <interfaces/shell.h>
|
#include <interfaces/shell.h>
|
||||||
#include <interfaces/window.h>
|
#include <interfaces/window.h>
|
||||||
|
|
||||||
|
using namespace LayerShellQt;
|
||||||
|
|
||||||
|
QStringList enumsToStringList(QMetaEnum metaEnum)
|
||||||
|
{
|
||||||
|
QStringList ret;
|
||||||
|
ret.reserve(metaEnum.keyCount());
|
||||||
|
for (int i = 0; i < metaEnum.keyCount(); ++i) {
|
||||||
|
ret.append(metaEnum.key(i));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T stringToEnum(QMetaEnum metaEnum, const QString &str)
|
||||||
|
{
|
||||||
|
T ret = {};
|
||||||
|
const auto splitted = str.split(QLatin1Char('|'));
|
||||||
|
for (const auto &value : splitted) {
|
||||||
|
ret |= T(metaEnum.keyToValue(qPrintable(value)));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BasicWindow : public QRasterWindow
|
||||||
|
{
|
||||||
|
void paintEvent(QPaintEvent *) {
|
||||||
|
QPainter p(this);
|
||||||
|
p.fillRect(QRect(0,0,width(), height()), Qt::red);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
LayerShellQt::Shell::useLayerShell();
|
Shell::useLayerShell();
|
||||||
|
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
QQmlApplicationEngine engine;
|
|
||||||
engine.loadData(
|
|
||||||
"import QtQuick.Controls 2.10\n"
|
|
||||||
"import QtQuick 2.10\n"
|
|
||||||
"\n"
|
|
||||||
"ApplicationWindow {"
|
|
||||||
" width: 100; height: 100\n"
|
|
||||||
" visible: true\n"
|
|
||||||
" Rectangle { color: 'red'; anchors.fill: parent }"
|
|
||||||
"}"
|
|
||||||
|
|
||||||
,
|
const auto layerMetaEnum = QMetaEnum::fromType<Window::Layer>();
|
||||||
QStringLiteral("bananaland:/potato.qml"));
|
const auto anchorMetaEnum = QMetaEnum::fromType<Window::Anchor>();
|
||||||
|
|
||||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [](QObject *object) {
|
QCommandLineParser parser;
|
||||||
auto layerWindow = LayerShellQt::Window::get(qobject_cast<QWindow *>(object));
|
QCommandLineOption marginsOption(QStringLiteral("margins"), QStringLiteral("Window margins"), QStringLiteral("pixels"), QStringLiteral("0"));
|
||||||
Q_ASSERT(layerWindow);
|
QCommandLineOption scopeOption(QStringLiteral("scope"), QStringLiteral("Window scope"), QStringLiteral("namespace"), QStringLiteral("normal"));
|
||||||
layerWindow->setMargins({50, 50, 50, 50});
|
QCommandLineOption anchorsOption(QStringLiteral("anchors"),
|
||||||
});
|
QStringLiteral("Either ") + enumsToStringList(anchorMetaEnum).join(QLatin1String("|")),
|
||||||
|
QStringLiteral("anchors"),
|
||||||
|
QStringLiteral("AnchorTop|AnchorBottom|AnchorLeft|AnchorRight"));
|
||||||
|
QCommandLineOption layerOption(QStringLiteral("layer"),
|
||||||
|
QStringLiteral("One of ") + enumsToStringList(layerMetaEnum).join(QLatin1String("|")),
|
||||||
|
QStringLiteral("layer"),
|
||||||
|
QStringLiteral("LayerTop"));
|
||||||
|
parser.addOptions({marginsOption, scopeOption, anchorsOption, layerOption});
|
||||||
|
parser.addHelpOption();
|
||||||
|
parser.process(app);
|
||||||
|
|
||||||
|
|
||||||
|
BasicWindow window;
|
||||||
|
|
||||||
|
LayerShellQt::Window* layerShell = LayerShellQt::Window::get(&window);
|
||||||
|
if (parser.isSet(marginsOption)) {
|
||||||
|
int margins = parser.value(marginsOption).toInt();
|
||||||
|
layerShell->setMargins({margins, margins, margins, margins});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(scopeOption)) {
|
||||||
|
layerShell->setScope(parser.value(scopeOption));
|
||||||
|
}
|
||||||
|
if (parser.isSet(layerOption)) {
|
||||||
|
layerShell->setLayer(Window::Layer(layerMetaEnum.keyToValue(qPrintable(parser.value(layerOption)))));
|
||||||
|
}
|
||||||
|
if (parser.isSet(anchorsOption)) {
|
||||||
|
layerShell->setAnchor(stringToEnum<Window::Anchors>(anchorMetaEnum, parser.value(anchorsOption)));
|
||||||
|
}
|
||||||
|
window.show();
|
||||||
|
|
||||||
|
|
||||||
|
// just so you don't block yourself out whilst testing
|
||||||
|
QTimer::singleShot(5000, &app, &QGuiApplication::quit);
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue