feat: add layershell support

This commit is contained in:
outfoxxed 2024-02-04 04:58:58 -08:00
parent 56502b79f6
commit d76100781f
Signed by untrusted user: outfoxxed
GPG key ID: 4C88A185FB89301E
12 changed files with 677 additions and 11 deletions

View file

@ -11,6 +11,7 @@ Checks: >
-cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-non-private-member-variables-in-classes,
google-build-using-namespace. google-build-using-namespace.
google-explicit-constructor, google-explicit-constructor,
google-global-names-in-headers, google-global-names-in-headers,
@ -41,6 +42,7 @@ CheckOptions:
readability-identifier-naming.ClassCase: CamelCase readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.ConstantCase: UPPER_CASE readability-identifier-naming.ConstantCase: UPPER_CASE
readability-identifier-naming.EnumCase: CamelCase readability-identifier-naming.EnumCase: CamelCase
readability-identifier-naming.EnumConstantCase: CamelCase
readability-identifier-naming.FunctionCase: camelBack readability-identifier-naming.FunctionCase: camelBack
readability-identifier-naming.MemberCase: camelBack readability-identifier-naming.MemberCase: camelBack
readability-identifier-naming.NamespaceCase: lower_case readability-identifier-naming.NamespaceCase: lower_case

View file

@ -30,6 +30,9 @@ qt_add_executable(qtshell
src/cpp/proxywindow.cpp src/cpp/proxywindow.cpp
src/cpp/scavenge.cpp src/cpp/scavenge.cpp
src/cpp/rootwrapper.cpp src/cpp/rootwrapper.cpp
src/cpp/layershell.cpp
src/cpp/qmlglobal.cpp
src/cpp/qmlscreen.cpp
) )
qt_add_qml_module(qtshell URI QtShell) qt_add_qml_module(qtshell URI QtShell)

View file

@ -11,8 +11,8 @@
domain = "git.outfoxxed.me"; domain = "git.outfoxxed.me";
owner = "outfoxxed"; owner = "outfoxxed";
repo = "layer-shell-qt-nokde"; repo = "layer-shell-qt-nokde";
rev = "a50d30687cc03ae4da177033faf5f038c3e1a8b2"; rev = "2ebe7b313efbacfcd62ec39e2fda6b4c740d0770";
sha256 = "5fNwoCce74SSqR5XB3fJ8GI+D5cbkcLRok42k8R3XSw="; sha256 = "N/nMwf5LQMMwCJvG7J/6xug/EUppHedQCngzCkH8Auk=";
})) {}; #pkgs.callPackage (import /home/admin/programming/outfoxxed/layer-shell-qt) {}; })) {}; #pkgs.callPackage (import /home/admin/programming/outfoxxed/layer-shell-qt) {};
in pkgs.mkShell { in pkgs.mkShell {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [

253
src/cpp/layershell.cpp Normal file
View file

@ -0,0 +1,253 @@
#include "layershell.hpp"
#include <LayerShellQt/window.h>
#include <qmargins.h>
#include <qobject.h>
#include <qqmllist.h>
#include <qquickitem.h>
#include <qquickwindow.h>
#include <qscreen.h>
#include <qtypes.h>
#include <qwindow.h>
#include "proxywindow.hpp"
#include "qmlscreen.hpp"
void ProxyShellWindow::earlyInit(QObject* old) {
ProxyWindowBase::earlyInit(old);
QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyShellWindow::screenChanged);
this->shellWindow = LayerShellQt::Window::get(this->window);
// dont want to steal focus unless actually configured to
this->shellWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityNone);
// this dosent reset if its unset
this->shellWindow->setExclusiveZone(0);
// clang-format off
QObject::connect(this->shellWindow, &LayerShellQt::Window::anchorsChanged, this, &ProxyShellWindow::anchorsChanged);
QObject::connect(this->shellWindow, &LayerShellQt::Window::marginsChanged, this, &ProxyShellWindow::marginsChanged);
QObject::connect(this->shellWindow, &LayerShellQt::Window::layerChanged, this, &ProxyShellWindow::layerChanged);
QObject::connect(this->shellWindow, &LayerShellQt::Window::keyboardInteractivityChanged, this, &ProxyShellWindow::keyboardFocusChanged);
// clang-format on
}
void ProxyShellWindow::componentComplete() {
this->complete = true;
// The default anchor settings are a hazard because they cover the entire screen.
// We opt for 0 anchors by default to avoid blocking user input.
this->setAnchors(this->stagingAnchors);
// Make sure we signal changes from anchors, but only if this is a reload.
// If we do it on first load then it sends an extra change at 0px.
if (this->stagingAnchors.mLeft && this->stagingAnchors.mRight && this->width() != 0)
this->widthChanged(this->width());
if (this->stagingAnchors.mTop && this->stagingAnchors.mBottom && this->height() != 0)
this->heightChanged(this->height());
this->window->setVisible(this->stagingVisible);
ProxyWindowBase::componentComplete();
}
QQuickWindow* ProxyShellWindow::disownWindow() {
QObject::disconnect(this->shellWindow, nullptr, this, nullptr);
return ProxyWindowBase::disownWindow();
}
void ProxyShellWindow::setVisible(bool visible) {
if (!this->complete) this->stagingVisible = visible;
else ProxyWindowBase::setVisible(visible);
}
bool ProxyShellWindow::isVisible() {
return this->complete ? ProxyWindowBase::isVisible() : this->stagingVisible;
}
void ProxyShellWindow::setWidth(qint32 width) {
this->requestedWidth = width;
// only update the actual size if not blocked by anchors
auto anchors = this->anchors();
if (this->complete && (!anchors.mLeft || !anchors.mRight)) ProxyWindowBase::setWidth(width);
}
qint32 ProxyShellWindow::width() {
return this->complete ? ProxyWindowBase::width() : this->requestedWidth;
}
void ProxyShellWindow::setHeight(qint32 height) {
this->requestedHeight = height;
// only update the actual size if not blocked by anchors
auto anchors = this->anchors();
if (this->complete && (!anchors.mTop || !anchors.mBottom)) ProxyWindowBase::setHeight(height);
}
qint32 ProxyShellWindow::height() {
return this->complete ? ProxyWindowBase::height() : this->requestedHeight;
}
void ProxyShellWindow::setScreen(QtShellScreenInfo* screen) {
this->window->setScreen(screen->screen);
}
QtShellScreenInfo* ProxyShellWindow::screen() const {
return new QtShellScreenInfo(
const_cast<ProxyShellWindow*>(this), // NOLINT
this->window->screen()
);
}
void ProxyShellWindow::setAnchors(Anchors anchors) {
if (!this->complete) {
this->stagingAnchors = anchors;
return;
}
auto lsAnchors = LayerShellQt::Window::Anchors();
if (anchors.mLeft) lsAnchors |= LayerShellQt::Window::AnchorLeft;
if (anchors.mRight) lsAnchors |= LayerShellQt::Window::AnchorRight;
if (anchors.mTop) lsAnchors |= LayerShellQt::Window::AnchorTop;
if (anchors.mBottom) lsAnchors |= LayerShellQt::Window::AnchorBottom;
if (!anchors.mLeft || !anchors.mRight) ProxyWindowBase::setWidth(this->requestedWidth);
if (!anchors.mTop || !anchors.mBottom) ProxyWindowBase::setHeight(this->requestedHeight);
this->shellWindow->setAnchors(lsAnchors);
}
Anchors ProxyShellWindow::anchors() const {
if (!this->complete) return this->stagingAnchors;
auto lsAnchors = this->shellWindow->anchors();
Anchors anchors;
anchors.mLeft = lsAnchors.testFlag(LayerShellQt::Window::AnchorLeft);
anchors.mRight = lsAnchors.testFlag(LayerShellQt::Window::AnchorRight);
anchors.mTop = lsAnchors.testFlag(LayerShellQt::Window::AnchorTop);
anchors.mBottom = lsAnchors.testFlag(LayerShellQt::Window::AnchorBottom);
return anchors;
}
void ProxyShellWindow::setExclusiveZone(qint32 zone) { this->shellWindow->setExclusiveZone(zone); }
qint32 ProxyShellWindow::exclusiveZone() const { return this->shellWindow->exclusionZone(); }
void ProxyShellWindow::setMargins(Margins margins) {
auto lsMargins = QMargins(margins.mLeft, margins.mTop, margins.mRight, margins.mBottom);
this->shellWindow->setMargins(lsMargins);
}
Margins ProxyShellWindow::margins() const {
auto lsMargins = this->shellWindow->margins();
auto margins = Margins();
margins.mLeft = lsMargins.left();
margins.mRight = lsMargins.right();
margins.mTop = lsMargins.top();
margins.mBottom = lsMargins.bottom();
return margins;
}
void ProxyShellWindow::setLayer(Layer::Enum layer) {
auto lsLayer = LayerShellQt::Window::LayerBackground;
// clang-format off
switch (layer) {
case Layer::Background: lsLayer = LayerShellQt::Window::LayerBackground; break;
case Layer::Bottom: lsLayer = LayerShellQt::Window::LayerBottom; break;
case Layer::Top: lsLayer = LayerShellQt::Window::LayerTop; break;
case Layer::Overlay: lsLayer = LayerShellQt::Window::LayerOverlay; break;
}
// clang-format on
this->shellWindow->setLayer(lsLayer);
}
Layer::Enum ProxyShellWindow::layer() const {
auto layer = Layer::Top;
auto lsLayer = this->shellWindow->layer();
// clang-format off
switch (lsLayer) {
case LayerShellQt::Window::LayerBackground: layer = Layer::Background; break;
case LayerShellQt::Window::LayerBottom: layer = Layer::Bottom; break;
case LayerShellQt::Window::LayerTop: layer = Layer::Top; break;
case LayerShellQt::Window::LayerOverlay: layer = Layer::Overlay; break;
}
// clang-format on
return layer;
}
void ProxyShellWindow::setScope(const QString& scope) { this->shellWindow->setScope(scope); }
QString ProxyShellWindow::scope() const { return this->shellWindow->scope(); }
void ProxyShellWindow::setKeyboardFocus(KeyboardFocus::Enum focus) {
auto lsFocus = LayerShellQt::Window::KeyboardInteractivityNone;
// clang-format off
switch (focus) {
case KeyboardFocus::None: lsFocus = LayerShellQt::Window::KeyboardInteractivityNone; break;
case KeyboardFocus::Exclusive: lsFocus = LayerShellQt::Window::KeyboardInteractivityExclusive; break;
case KeyboardFocus::OnDemand: lsFocus = LayerShellQt::Window::KeyboardInteractivityOnDemand; break;
}
// clang-format on
this->shellWindow->setKeyboardInteractivity(lsFocus);
}
KeyboardFocus::Enum ProxyShellWindow::keyboardFocus() const {
auto focus = KeyboardFocus::None;
auto lsFocus = this->shellWindow->keyboardInteractivity();
// clang-format off
switch (lsFocus) {
case LayerShellQt::Window::KeyboardInteractivityNone: focus = KeyboardFocus::None; break;
case LayerShellQt::Window::KeyboardInteractivityExclusive: focus = KeyboardFocus::Exclusive; break;
case LayerShellQt::Window::KeyboardInteractivityOnDemand: focus = KeyboardFocus::OnDemand; break;
}
// clang-format on
return focus;
}
void ProxyShellWindow::setScreenConfiguration(ScreenConfiguration::Enum configuration) {
auto lsConfiguration = LayerShellQt::Window::ScreenFromQWindow;
// clang-format off
switch (configuration) {
case ScreenConfiguration::Window: lsConfiguration = LayerShellQt::Window::ScreenFromQWindow; break;
case ScreenConfiguration::Compositor: lsConfiguration = LayerShellQt::Window::ScreenFromCompositor; break;
}
// clang-format on
this->shellWindow->setScreenConfiguration(lsConfiguration);
}
ScreenConfiguration::Enum ProxyShellWindow::screenConfiguration() const {
auto configuration = ScreenConfiguration::Window;
auto lsConfiguration = this->shellWindow->screenConfiguration();
// clang-format off
switch (lsConfiguration) {
case LayerShellQt::Window::ScreenFromQWindow: configuration = ScreenConfiguration::Window; break;
case LayerShellQt::Window::ScreenFromCompositor: configuration = ScreenConfiguration::Compositor; break;
}
// clang-format on
return configuration;
}
void ProxyShellWindow::setCloseOnDismissed(bool close) {
this->shellWindow->setCloseOnDismissed(close);
}
bool ProxyShellWindow::closeOnDismissed() const { return this->shellWindow->closeOnDismissed(); }

166
src/cpp/layershell.hpp Normal file
View file

@ -0,0 +1,166 @@
#pragma once
#include <LayerShellQt/window.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qqmllist.h>
#include <qquickwindow.h>
#include <qscreen.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include <qvariant.h>
#include <qwindow.h>
#include "proxywindow.hpp"
#include "qmlscreen.hpp"
class Anchors {
Q_GADGET;
Q_PROPERTY(bool left MEMBER mLeft);
Q_PROPERTY(bool right MEMBER mRight);
Q_PROPERTY(bool top MEMBER mTop);
Q_PROPERTY(bool bottom MEMBER mBottom);
public:
bool mLeft = false;
bool mRight = false;
bool mTop = false;
bool mBottom = false;
};
class Margins {
Q_GADGET;
Q_PROPERTY(qint32 left MEMBER mLeft);
Q_PROPERTY(qint32 right MEMBER mRight);
Q_PROPERTY(qint32 top MEMBER mTop);
Q_PROPERTY(qint32 bottom MEMBER mBottom);
public:
qint32 mLeft = 0;
qint32 mRight = 0;
qint32 mTop = 0;
qint32 mBottom = 0;
};
namespace Layer { // NOLINT
Q_NAMESPACE;
QML_ELEMENT;
enum Enum {
Background = 0,
Bottom = 1,
Top = 2,
Overlay = 3,
};
Q_ENUM_NS(Enum);
} // namespace Layer
namespace KeyboardFocus { // NOLINT
Q_NAMESPACE;
QML_ELEMENT;
enum Enum {
None = 0,
Exclusive = 1,
OnDemand = 2,
};
Q_ENUM_NS(Enum);
} // namespace KeyboardFocus
namespace ScreenConfiguration { // NOLINT
Q_NAMESPACE;
QML_ELEMENT;
enum Enum {
Window = 0,
Compositor = 1,
};
Q_ENUM_NS(Enum);
} // namespace ScreenConfiguration
class ProxyShellWindow: public ProxyWindowBase {
// clang-format off
Q_OBJECT;
Q_PROPERTY(QtShellScreenInfo* screen READ screen WRITE setScreen NOTIFY screenChanged);
Q_PROPERTY(Anchors anchors READ anchors WRITE setAnchors NOTIFY anchorsChanged);
Q_PROPERTY(qint32 exclusionZone READ exclusiveZone WRITE setExclusiveZone NOTIFY exclusionZoneChanged)
Q_PROPERTY(Margins margins READ margins WRITE setMargins NOTIFY marginsChanged)
Q_PROPERTY(Layer::Enum layer READ layer WRITE setLayer NOTIFY layerChanged)
Q_PROPERTY(QString scope READ scope WRITE setScope)
Q_PROPERTY(KeyboardFocus::Enum keyboardFocus READ keyboardFocus WRITE setKeyboardFocus NOTIFY keyboardFocusChanged)
Q_PROPERTY(ScreenConfiguration::Enum screenConfiguration READ screenConfiguration WRITE setScreenConfiguration)
Q_PROPERTY(bool closeOnDismissed READ closeOnDismissed WRITE setCloseOnDismissed);
Q_CLASSINFO("DefaultProperty", "data");
QML_ELEMENT;
// clang-format on
protected:
void earlyInit(QObject* old) override;
public:
void componentComplete() override;
QQuickWindow* disownWindow() override;
QQmlListProperty<QObject> data();
void setVisible(bool visible) override;
bool isVisible() override;
void setWidth(qint32 width) override;
qint32 width() override;
void setHeight(qint32 height) override;
qint32 height() override;
void setScreen(QtShellScreenInfo* screen);
[[nodiscard]] QtShellScreenInfo* screen() const;
void setAnchors(Anchors anchors);
[[nodiscard]] Anchors anchors() const;
void setExclusiveZone(qint32 zone);
[[nodiscard]] qint32 exclusiveZone() const;
void setMargins(Margins margins);
[[nodiscard]] Margins margins() const;
void setLayer(Layer::Enum layer);
[[nodiscard]] Layer::Enum layer() const;
void setScope(const QString& scope);
[[nodiscard]] QString scope() const;
void setKeyboardFocus(KeyboardFocus::Enum focus);
[[nodiscard]] KeyboardFocus::Enum keyboardFocus() const;
void setScreenConfiguration(ScreenConfiguration::Enum configuration);
[[nodiscard]] ScreenConfiguration::Enum screenConfiguration() const;
void setCloseOnDismissed(bool close);
[[nodiscard]] bool closeOnDismissed() const;
signals:
void screenChanged();
void anchorsChanged();
void marginsChanged();
void exclusionZoneChanged();
void layerChanged();
void keyboardFocusChanged();
private:
LayerShellQt::Window* shellWindow = nullptr;
bool anchorsInitialized = false;
// needed to ensure size dosent fuck up when changing layershell attachments
// along with setWidth and setHeight overrides
qint32 requestedWidth = 100;
qint32 requestedHeight = 100;
// width/height must be set before anchors, so we have to track anchors and apply them late
bool complete = false;
bool stagingVisible = false;
Anchors stagingAnchors;
};

View file

@ -20,9 +20,17 @@ void ProxyWindowBase::earlyInit(QObject* old) {
} else { } else {
this->window = oldpw->disownWindow(); this->window = oldpw->disownWindow();
} }
// clang-format off
QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged);
QObject::connect(this->window, &QWindow::widthChanged, this, &ProxyWindowBase::widthChanged);
QObject::connect(this->window, &QWindow::heightChanged, this, &ProxyWindowBase::heightChanged);
// clang-format on
} }
QQuickWindow* ProxyWindowBase::disownWindow() { QQuickWindow* ProxyWindowBase::disownWindow() {
QObject::disconnect(this->window, nullptr, this, nullptr);
auto data = this->data(); auto data = this->data();
ProxyWindowBase::dataClear(&data); ProxyWindowBase::dataClear(&data);
data.clear(&data); data.clear(&data);

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <qcolor.h> #include <qcolor.h>
#include <qevent.h>
#include <qobject.h> #include <qobject.h>
#include <qqmllist.h> #include <qqmllist.h>
#include <qqmlparserstatus.h> #include <qqmlparserstatus.h>
@ -20,15 +21,16 @@
class ProxyWindowBase: public Scavenger { class ProxyWindowBase: public Scavenger {
Q_OBJECT; Q_OBJECT;
Q_PROPERTY(QQuickItem* item READ item CONSTANT); Q_PROPERTY(QQuickItem* item READ item CONSTANT);
Q_PROPERTY(bool visible READ isVisible WRITE setVisible); Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged);
Q_PROPERTY(qint32 width READ width WRITE setWidth); Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged);
Q_PROPERTY(qint32 height READ height WRITE setHeight); Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged);
Q_PROPERTY(QColor color READ color WRITE setColor); Q_PROPERTY(QColor color READ color WRITE setColor);
Q_PROPERTY(QQmlListProperty<QObject> data READ data); Q_PROPERTY(QQmlListProperty<QObject> data READ data);
Q_CLASSINFO("DefaultProperty", "data"); Q_CLASSINFO("DefaultProperty", "data");
protected: protected:
void earlyInit(QObject* old) override; void earlyInit(QObject* old) override;
QQuickWindow* window = nullptr;
public: public:
explicit ProxyWindowBase(QObject* parent = nullptr): Scavenger(parent) {} explicit ProxyWindowBase(QObject* parent = nullptr): Scavenger(parent) {}
@ -40,17 +42,17 @@ public:
void operator=(ProxyWindowBase&&) = delete; void operator=(ProxyWindowBase&&) = delete;
// Disown the backing window and delete all its children. // Disown the backing window and delete all its children.
QQuickWindow* disownWindow(); virtual QQuickWindow* disownWindow();
QQuickItem* item(); QQuickItem* item();
bool isVisible(); virtual bool isVisible();
virtual void setVisible(bool value); virtual void setVisible(bool value);
qint32 width(); virtual qint32 width();
virtual void setWidth(qint32 value); virtual void setWidth(qint32 value);
qint32 height(); virtual qint32 height();
virtual void setHeight(qint32 value); virtual void setHeight(qint32 value);
QColor color(); QColor color();
@ -58,6 +60,11 @@ public:
QQmlListProperty<QObject> data(); QQmlListProperty<QObject> data();
signals:
void visibleChanged(bool visible);
void widthChanged(qint32 width);
void heightChanged(qint32 width);
private: private:
static QQmlListProperty<QObject> dataBacker(QQmlListProperty<QObject>* prop); static QQmlListProperty<QObject> dataBacker(QQmlListProperty<QObject>* prop);
static void dataAppend(QQmlListProperty<QObject>* prop, QObject* obj); static void dataAppend(QQmlListProperty<QObject>* prop, QObject* obj);
@ -66,8 +73,6 @@ private:
static void dataClear(QQmlListProperty<QObject>* prop); static void dataClear(QQmlListProperty<QObject>* prop);
static void dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj); static void dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj);
static void dataRemoveLast(QQmlListProperty<QObject>* prop); static void dataRemoveLast(QQmlListProperty<QObject>* prop);
QQuickWindow* window = nullptr;
}; };
// qt attempts to resize the window but fails because wayland // qt attempts to resize the window but fails because wayland

59
src/cpp/qmlglobal.cpp Normal file
View file

@ -0,0 +1,59 @@
#include "qmlglobal.hpp"
#include <qcontainerfwd.h>
#include <qcoreapplication.h>
#include <qguiapplication.h>
#include <qobject.h>
#include <qqmllist.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include "qmlscreen.hpp"
QtShellGlobal::QtShellGlobal(QObject* parent): QObject(parent) {
auto* app = QCoreApplication::instance();
auto* guiApp = qobject_cast<QGuiApplication*>(app);
if (guiApp != nullptr) {
// clang-format off
QObject::connect(guiApp, &QGuiApplication::primaryScreenChanged, this, &QtShellGlobal::updateScreens);
QObject::connect(guiApp, &QGuiApplication::screenAdded, this, &QtShellGlobal::updateScreens);
QObject::connect(guiApp, &QGuiApplication::screenRemoved, this, &QtShellGlobal::updateScreens);
// clang-format on
this->updateScreens();
}
}
qsizetype QtShellGlobal::screensCount(QQmlListProperty<QtShellScreenInfo>* prop) {
return static_cast<QtShellGlobal*>(prop->object)->mScreens.size(); // NOLINT
}
QtShellScreenInfo* QtShellGlobal::screenAt(QQmlListProperty<QtShellScreenInfo>* prop, qsizetype i) {
return static_cast<QtShellGlobal*>(prop->object)->mScreens.at(i); // NOLINT
}
QQmlListProperty<QtShellScreenInfo> QtShellGlobal::screens() {
return QQmlListProperty<QtShellScreenInfo>(
this,
nullptr,
&QtShellGlobal::screensCount,
&QtShellGlobal::screenAt
);
}
void QtShellGlobal::updateScreens() {
auto screens = QGuiApplication::screens();
this->mScreens.resize(screens.size());
for (auto i = 0; i < screens.size(); i++) {
if (this->mScreens[i] != nullptr) {
this->mScreens[i]->screen = nullptr;
this->mScreens[i]->setParent(nullptr); // delete if not owned by the js engine
}
this->mScreens[i] = new QtShellScreenInfo(this, screens[i]);
}
emit this->screensChanged();
}

34
src/cpp/qmlglobal.hpp Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#include <qcontainerfwd.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qqmllist.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include "qmlscreen.hpp"
class QtShellGlobal: public QObject {
Q_OBJECT;
Q_PROPERTY(QQmlListProperty<QtShellScreenInfo> screens READ screens NOTIFY screensChanged);
QML_SINGLETON;
QML_ELEMENT;
public:
QtShellGlobal(QObject* parent = nullptr);
QQmlListProperty<QtShellScreenInfo> screens();
signals:
void screensChanged();
public slots:
void updateScreens();
private:
static qsizetype screensCount(QQmlListProperty<QtShellScreenInfo>* prop);
static QtShellScreenInfo* screenAt(QQmlListProperty<QtShellScreenInfo>* prop, qsizetype i);
QVector<QtShellScreenInfo*> mScreens;
};

84
src/cpp/qmlscreen.cpp Normal file
View file

@ -0,0 +1,84 @@
#include "qmlscreen.hpp"
#include <qdebug.h>
#include <qlogging.h>
#include <qnamespace.h>
#include <qtypes.h>
bool QtShellScreenInfo::operator==(QtShellScreenInfo& other) const {
return this->screen == other.screen;
}
void warnNull() { qWarning() << "attempted to use dangling screen object"; }
QString QtShellScreenInfo::name() const {
if (this->screen == nullptr) {
warnNull();
return "{ DANGLING SCREEN POINTER }";
}
return this->screen->name();
}
qint32 QtShellScreenInfo::width() const {
if (this->screen == nullptr) {
warnNull();
return 0;
}
return this->screen->size().width();
}
qint32 QtShellScreenInfo::height() const {
if (this->screen == nullptr) {
warnNull();
return 0;
}
return this->screen->size().height();
}
qreal QtShellScreenInfo::pixelDensity() const {
if (this->screen == nullptr) {
warnNull();
return 0.0;
}
return this->screen->physicalDotsPerInch() / 25.4;
}
qreal QtShellScreenInfo::logicalPixelDensity() const {
if (this->screen == nullptr) {
warnNull();
return 0.0;
}
return this->screen->logicalDotsPerInch() / 25.4;
}
qreal QtShellScreenInfo::devicePixelRatio() const {
if (this->screen == nullptr) {
warnNull();
return 0.0;
}
return this->screen->devicePixelRatio();
}
Qt::ScreenOrientation QtShellScreenInfo::orientation() const {
if (this->screen == nullptr) {
warnNull();
return Qt::PrimaryOrientation;
}
return this->screen->orientation();
}
Qt::ScreenOrientation QtShellScreenInfo::primaryOrientation() const {
if (this->screen == nullptr) {
warnNull();
return Qt::PrimaryOrientation;
}
return this->screen->primaryOrientation();
}

51
src/cpp/qmlscreen.hpp Normal file
View file

@ -0,0 +1,51 @@
#pragma once
#include <qnamespace.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qscreen.h>
#include <qtmetamacros.h>
#include <qtypes.h>
// unfortunately QQuickScreenInfo is private.
class QtShellScreenInfo: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_UNCREATABLE("QtShellScreenInfo can only be obtained via QtShell.screens");
// clang-format off
Q_PROPERTY(QString name READ name NOTIFY nameChanged);
Q_PROPERTY(qint32 width READ width NOTIFY widthChanged);
Q_PROPERTY(qint32 height READ height NOTIFY heightChanged);
Q_PROPERTY(qreal pixelDensity READ pixelDensity NOTIFY logicalPixelDensityChanged);
Q_PROPERTY(qreal logicalPixelDensity READ logicalPixelDensity NOTIFY logicalPixelDensityChanged);
Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged);
Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged);
Q_PROPERTY(Qt::ScreenOrientation primatyOrientation READ primaryOrientation NOTIFY primaryOrientationChanged);
// clang-format on
public:
QtShellScreenInfo(QObject* parent, QScreen* screen): QObject(parent), screen(screen) {}
bool operator==(QtShellScreenInfo& other) const;
[[nodiscard]] QString name() const;
[[nodiscard]] qint32 width() const;
[[nodiscard]] qint32 height() const;
[[nodiscard]] qreal pixelDensity() const;
[[nodiscard]] qreal logicalPixelDensity() const;
[[nodiscard]] qreal devicePixelRatio() const;
[[nodiscard]] Qt::ScreenOrientation orientation() const;
[[nodiscard]] Qt::ScreenOrientation primaryOrientation() const;
QScreen* screen;
signals:
void nameChanged();
void widthChanged();
void heightChanged();
void pixelDensityChanged();
void logicalPixelDensityChanged();
void devicePixelRatioChanged();
void orientationChanged();
void primaryOrientationChanged();
};

View file

@ -1,6 +1,7 @@
#include "scavenge.hpp" #include "scavenge.hpp"
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qlogging.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlcomponent.h> #include <qqmlcomponent.h>
#include <qqmlengine.h> #include <qqmlengine.h>