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:
David Edmundson 2021-04-15 09:58:57 +01:00
parent 29d0078909
commit 2b1219cfdd
4 changed files with 53 additions and 54 deletions

View file

@ -21,39 +21,39 @@ public:
}
QWindow *parentWindow;
QString scope = QStringLiteral("qt");
Window::Anchors anchor = {Window::AnchorTop | Window::AnchorBottom | Window::AnchorLeft | Window::AnchorRight};
QString scope = QStringLiteral("window");
Window::Anchors anchors = {Window::AnchorTop | Window::AnchorBottom | Window::AnchorLeft | Window::AnchorRight};
int32_t exclusionZone = 0;
bool keyboardInteractivity = false;
bool keyboardInteractivity = true;
Window::Layer layer = Window::LayerTop;
QMargins margins;
QWaylandLayerSurface* getSurface() const;
QWaylandLayerSurface *getSurface() const;
};
static QMap<QWindow*, Window*> s_map;
static QMap<QWindow *, Window *> s_map;
Window::~Window()
{
s_map.remove(d->parentWindow);
}
void Window::setAnchor(Anchors anchor)
void Window::setAnchors(Anchors anchors)
{
d->anchor = anchor;
d->anchors = anchors;
if (auto surface = d->getSurface()) {
surface->setAnchor(anchor);
surface->setAnchor(anchors);
}
}
Window::Anchors Window::anchor() const
Window::Anchors Window::anchors() const
{
return d->anchor;
return d->anchors;
}
void Window::setExclusiveZone(int32_t zone)
{
d->exclusionZone = zone;
if (auto surface= d->getSurface()) {
if (auto surface = d->getSurface()) {
surface->setExclusiveZone(zone);
}
}
@ -66,7 +66,7 @@ int32_t Window::exclusionZone() const
void Window::setMargins(const QMargins &margins)
{
d->margins = margins;
if (auto surface= d->getSurface()) {
if (auto surface = d->getSurface()) {
surface->setMargins(margins);
}
}
@ -79,7 +79,7 @@ QMargins Window::margins() const
void Window::setKeyboardInteractivity(bool enabled)
{
d->keyboardInteractivity = enabled;
if (auto surface= d->getSurface()) {
if (auto surface = d->getSurface()) {
surface->setKeyboardInteractivity(enabled);
}
}
@ -92,7 +92,7 @@ bool Window::keyboardInteractivity() const
void Window::setLayer(Layer layer)
{
d->layer = layer;
if (auto surface= d->getSurface()) {
if (auto surface = d->getSurface()) {
surface->setLayer(layer);
}
}
@ -100,7 +100,7 @@ void Window::setLayer(Layer layer)
void Window::setScope(const QString &scope)
{
d->scope = scope;
//this is static and must be set before the platform window is created
// this is static and must be set before the platform window is created
}
QString Window::scope() const
@ -113,13 +113,22 @@ Window::Layer Window::layer() const
return d->layer;
}
Window::Window(WindowPrivate *d)
: QObject(d->parentWindow)
, d(d)
Window::Window(QWindow *window)
: QObject(window)
, d(new WindowPrivate(window))
{
s_map.insert(d->parentWindow, this);
}
Window *Window::get(QWindow *window)
{
auto layerShellWindow = s_map.value(window);
if (layerShellWindow) {
return layerShellWindow;
}
return new Window(window);
}
QWaylandLayerSurface *WindowPrivate::getSurface() const
{
if (!parentWindow) {
@ -137,11 +146,3 @@ QWaylandLayerSurface *WindowPrivate::getSurface() const
}
return s;
}
Window *Window::get(QWindow *window)
{
if (s_map.contains(window)) {
return s_map[window];
}
return new Window(new WindowPrivate(window));
}

View file

@ -43,8 +43,8 @@ public:
};
Q_ENUM(Layer)
void setAnchor(Anchors anchor);
Anchors anchor() const;
void setAnchors(Anchors anchor);
Anchors anchors() const;
void setExclusiveZone(int32_t zone);
int32_t exclusionZone() const;
@ -58,6 +58,13 @@ public:
void setLayer(Layer layer);
Layer layer() const;
/**
* Sets a string based identifier for this window.
* This may be used by a compositor to determine stacking
* order within a given layer.
*
* May also be referred to as a role
*/
void setScope(const QString &scope);
QString scope() const;
@ -68,7 +75,7 @@ public:
static Window *get(QWindow *window);
private:
Window(WindowPrivate *d);
Window(QWindow *window);
QScopedPointer<WindowPrivate> d;
};

View file

@ -19,25 +19,17 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandC
: QtWaylandClient::QWaylandShellSurface(window)
, QtWayland::zwlr_layer_surface_v1()
{
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};
Q_ASSERT(interface);
if (interface) {
anchors = interface->anchor();
layer = interface->layer();
scope = interface->scope();
}
init(shell->get_layer_surface(window->waylandSurface()->object(), window->waylandScreen()->output(), interface->layer(), interface->scope()));
init(shell->get_layer_surface(window->waylandSurface()->object(), window->waylandScreen()->output(), layer, scope));
set_anchor(anchors);
Window::Anchors anchors = interface->anchors();
if (interface) {
set_anchor(interface->anchors());
setMargins(interface->margins());
setKeyboardInteractivity(interface->keyboardInteractivity());
setExclusiveZone(interface->exclusionZone());
}
QSize size = window->surfaceSize();
if (anchors & Window::AnchorLeft && anchors & Window::AnchorRight) {
@ -46,7 +38,7 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandC
if (anchors & Window::AnchorTop && anchors & Window::AnchorBottom) {
size.setHeight(0);
}
if (size.isValid() && size != QSize(0,0)) {
if (size.isValid() && size != QSize(0, 0)) {
set_size(size.width(), size.height());
}
}

View file

@ -7,10 +7,10 @@
#include <QCommandLineParser>
#include <QGuiApplication>
#include <QRasterWindow>
#include <QWindow>
#include <QPainter>
#include <QRasterWindow>
#include <QTimer>
#include <QWindow>
#include <QMetaEnum>
@ -42,9 +42,10 @@ T stringToEnum(QMetaEnum metaEnum, const QString &str)
class BasicWindow : public QRasterWindow
{
void paintEvent(QPaintEvent *) {
void paintEvent(QPaintEvent *)
{
QPainter p(this);
p.fillRect(QRect(0,0,width(), height()), Qt::red);
p.fillRect(QRect(0, 0, width(), height()), Qt::red);
}
};
@ -72,10 +73,9 @@ int main(int argc, char **argv)
parser.addHelpOption();
parser.process(app);
BasicWindow window;
LayerShellQt::Window* layerShell = LayerShellQt::Window::get(&window);
LayerShellQt::Window *layerShell = LayerShellQt::Window::get(&window);
if (parser.isSet(marginsOption)) {
int margins = parser.value(marginsOption).toInt();
layerShell->setMargins({margins, margins, margins, margins});
@ -88,11 +88,10 @@ int main(int argc, char **argv)
layerShell->setLayer(Window::Layer(layerMetaEnum.keyToValue(qPrintable(parser.value(layerOption)))));
}
if (parser.isSet(anchorsOption)) {
layerShell->setAnchor(stringToEnum<Window::Anchors>(anchorMetaEnum, parser.value(anchorsOption)));
layerShell->setAnchors(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();