mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2025-11-04 19:04:56 +11:00
212 lines
6.8 KiB
C++
212 lines
6.8 KiB
C++
#include "wlr_layershell.hpp"
|
|
|
|
#include <qlogging.h>
|
|
#include <qnamespace.h>
|
|
#include <qobject.h>
|
|
#include <qqmllist.h>
|
|
#include <qquickitem.h>
|
|
#include <qtypes.h>
|
|
|
|
#include "../../core/qmlscreen.hpp"
|
|
#include "../../window/panelinterface.hpp"
|
|
#include "../../window/proxywindow.hpp"
|
|
#include "surface.hpp"
|
|
|
|
namespace qs::wayland::layershell {
|
|
|
|
WlrLayershell::WlrLayershell(QObject* parent): ProxyWindowBase(parent) {
|
|
this->bcExclusiveZone.setBinding([this]() -> qint32 {
|
|
switch (this->bExclusionMode.value()) {
|
|
case ExclusionMode::Ignore: return -1;
|
|
case ExclusionMode::Normal: return this->bExclusiveZone;
|
|
case ExclusionMode::Auto:
|
|
const auto margins = this->bMargins.value();
|
|
|
|
// add reverse edge margins which are ignored by wlr-layer-shell
|
|
switch (this->bcExclusionEdge.value()) {
|
|
case Qt::TopEdge: return this->bImplicitHeight + margins.bottom;
|
|
case Qt::BottomEdge: return this->bImplicitHeight + margins.top;
|
|
case Qt::LeftEdge: return this->bImplicitWidth + margins.right;
|
|
case Qt::RightEdge: return this->bImplicitWidth + margins.left;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
});
|
|
|
|
this->bcExclusionEdge.setBinding([this] { return this->bAnchors.value().exclusionEdge(); });
|
|
}
|
|
|
|
ProxiedWindow* WlrLayershell::retrieveWindow(QObject* oldInstance) {
|
|
auto* old = qobject_cast<WlrLayershell*>(oldInstance);
|
|
auto* window = old == nullptr ? nullptr : old->disownWindow();
|
|
|
|
if (window != nullptr) {
|
|
this->connectBridge(LayerSurfaceBridge::init(window, this->computeState()));
|
|
|
|
if (this->bridge) {
|
|
return window;
|
|
} else {
|
|
window->deleteLater();
|
|
}
|
|
}
|
|
|
|
return this->createQQuickWindow();
|
|
}
|
|
|
|
ProxiedWindow* WlrLayershell::createQQuickWindow() {
|
|
auto* window = this->ProxyWindowBase::createQQuickWindow();
|
|
|
|
this->connectBridge(LayerSurfaceBridge::init(window, this->computeState()));
|
|
if (!this->bridge) {
|
|
qWarning() << "Could not attach Layershell extension to new QQuickWindow. Layer will not "
|
|
"behave correctly.";
|
|
}
|
|
|
|
return window;
|
|
}
|
|
|
|
void WlrLayershell::connectWindow() {
|
|
this->ProxyWindowBase::connectWindow();
|
|
|
|
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &WlrLayershell::updateAutoExclusion);
|
|
|
|
QObject::connect(
|
|
this,
|
|
&ProxyWindowBase::heightChanged,
|
|
this,
|
|
&WlrLayershell::updateAutoExclusion
|
|
);
|
|
|
|
this->updateAutoExclusion();
|
|
}
|
|
|
|
ProxiedWindow* WlrLayershell::disownWindow(bool keepItemOwnership) {
|
|
auto* window = this->ProxyWindowBase::disownWindow(keepItemOwnership);
|
|
|
|
if (this->bridge) {
|
|
this->connectBridge(nullptr);
|
|
}
|
|
|
|
return window;
|
|
}
|
|
|
|
void WlrLayershell::connectBridge(LayerSurfaceBridge* bridge) {
|
|
if (this->bridge) {
|
|
QObject::disconnect(this->bridge, nullptr, this, nullptr);
|
|
}
|
|
|
|
this->bridge = bridge;
|
|
|
|
if (bridge) {
|
|
QObject::connect(this->bridge, &QObject::destroyed, this, &WlrLayershell::onBridgeDestroyed);
|
|
}
|
|
}
|
|
|
|
void WlrLayershell::onBridgeDestroyed() { this->bridge = nullptr; }
|
|
|
|
bool WlrLayershell::deleteOnInvisible() const {
|
|
// Qt windows behave weirdly when geometry is modified and setVisible(false)
|
|
// is subsequently called in the same frame.
|
|
// It will attach buffers to the wayland surface unconditionally before
|
|
// the surface recieves a configure event, causing a protocol error.
|
|
// To remedy this we forcibly disallow window reuse.
|
|
return true;
|
|
}
|
|
|
|
void WlrLayershell::onPolished() {
|
|
if (this->bridge) {
|
|
this->bridge->state = this->computeState();
|
|
this->bridge->commitState();
|
|
}
|
|
|
|
this->ProxyWindowBase::onPolished();
|
|
}
|
|
|
|
void WlrLayershell::trySetWidth(qint32 /*implicitWidth*/) { this->onStateChanged(); }
|
|
void WlrLayershell::trySetHeight(qint32 /*implicitHeight*/) { this->onStateChanged(); }
|
|
|
|
void WlrLayershell::setScreen(QuickshellScreenInfo* screen) {
|
|
this->compositorPicksScreen = screen == nullptr;
|
|
this->ProxyWindowBase::setScreen(screen);
|
|
}
|
|
|
|
void WlrLayershell::onStateChanged() { this->schedulePolish(); }
|
|
|
|
bool WlrLayershell::aboveWindows() const { return this->layer() > WlrLayer::Bottom; }
|
|
|
|
void WlrLayershell::setAboveWindows(bool aboveWindows) {
|
|
this->setLayer(aboveWindows ? WlrLayer::Top : WlrLayer::Bottom);
|
|
}
|
|
|
|
bool WlrLayershell::focusable() const { return this->keyboardFocus() != WlrKeyboardFocus::None; }
|
|
|
|
void WlrLayershell::setFocusable(bool focusable) {
|
|
this->setKeyboardFocus(focusable ? WlrKeyboardFocus::OnDemand : WlrKeyboardFocus::None);
|
|
}
|
|
|
|
LayerSurfaceState WlrLayershell::computeState() const {
|
|
return LayerSurfaceState {
|
|
.implicitSize = QSize(this->implicitWidth(), this->implicitHeight()),
|
|
.anchors = this->bAnchors,
|
|
.margins = this->bMargins,
|
|
.layer = this->bLayer,
|
|
.exclusiveZone = this->bcExclusiveZone,
|
|
.keyboardFocus = this->bKeyboardFocus,
|
|
.compositorPickesScreen = this->compositorPicksScreen,
|
|
.mNamespace = this->bNamespace,
|
|
};
|
|
}
|
|
|
|
void WlrLayershell::updateAutoExclusion() { this->bcExclusiveZone.notify(); }
|
|
|
|
WlrLayershell* WlrLayershell::qmlAttachedProperties(QObject* object) {
|
|
if (auto* obj = qobject_cast<WaylandPanelInterface*>(object)) {
|
|
return obj->layer;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// WaylandPanelInterface
|
|
|
|
WaylandPanelInterface::WaylandPanelInterface(QObject* parent)
|
|
: PanelWindowInterface(parent)
|
|
, layer(new WlrLayershell(this)) {
|
|
this->connectSignals();
|
|
|
|
// clang-format off
|
|
QObject::connect(this->layer, &WlrLayershell::anchorsChanged, this, &WaylandPanelInterface::anchorsChanged);
|
|
QObject::connect(this->layer, &WlrLayershell::marginsChanged, this, &WaylandPanelInterface::marginsChanged);
|
|
QObject::connect(this->layer, &WlrLayershell::exclusiveZoneChanged, this, &WaylandPanelInterface::exclusiveZoneChanged);
|
|
QObject::connect(this->layer, &WlrLayershell::exclusionModeChanged, this, &WaylandPanelInterface::exclusionModeChanged);
|
|
QObject::connect(this->layer, &WlrLayershell::layerChanged, this, &WaylandPanelInterface::aboveWindowsChanged);
|
|
QObject::connect(this->layer, &WlrLayershell::keyboardFocusChanged, this, &WaylandPanelInterface::focusableChanged);
|
|
// clang-format on
|
|
}
|
|
|
|
void WaylandPanelInterface::onReload(QObject* oldInstance) {
|
|
QQmlEngine::setContextForObject(this->layer, QQmlEngine::contextForObject(this));
|
|
|
|
auto* old = qobject_cast<WaylandPanelInterface*>(oldInstance);
|
|
this->layer->reload(old != nullptr ? old->layer : nullptr);
|
|
}
|
|
|
|
ProxyWindowBase* WaylandPanelInterface::proxyWindow() const { return this->layer; }
|
|
|
|
// NOLINTBEGIN
|
|
#define proxyPair(type, get, set) \
|
|
type WaylandPanelInterface::get() const { return this->layer->get(); } \
|
|
void WaylandPanelInterface::set(type value) { this->layer->set(value); }
|
|
|
|
proxyPair(Anchors, anchors, setAnchors);
|
|
proxyPair(Margins, margins, setMargins);
|
|
proxyPair(qint32, exclusiveZone, setExclusiveZone);
|
|
proxyPair(ExclusionMode::Enum, exclusionMode, setExclusionMode);
|
|
proxyPair(bool, focusable, setFocusable);
|
|
proxyPair(bool, aboveWindows, setAboveWindows);
|
|
|
|
#undef proxyPair
|
|
// NOLINTEND
|
|
|
|
} // namespace qs::wayland::layershell
|