From b675b3676c547951760a418ac778acdefc6cd203 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Mon, 11 Mar 2024 05:44:56 -0700 Subject: [PATCH] popups: add popup windows --- CMakeLists.txt | 1 + docs | 2 +- examples | 2 +- src/core/CMakeLists.txt | 5 + src/core/doc.hpp | 3 + src/core/floatingwindow.cpp | 3 +- src/core/floatingwindow.hpp | 1 + src/core/module.md | 1 + src/core/popupwindow.cpp | 151 +++++++++++++++++++++++++++ src/core/popupwindow.hpp | 102 ++++++++++++++++++ src/core/proxywindow.cpp | 16 +++ src/core/proxywindow.hpp | 7 +- src/core/test/CMakeLists.txt | 15 +++ src/core/test/popupwindow.cpp | 182 +++++++++++++++++++++++++++++++++ src/core/test/popupwindow.hpp | 18 ++++ src/core/windowinterface.hpp | 7 +- src/io/test/datastream.cpp | 124 +++++++++++----------- src/io/test/datastream.hpp | 13 +++ src/wayland/wlr_layershell.cpp | 3 + src/wayland/wlr_layershell.hpp | 1 + 20 files changed, 586 insertions(+), 71 deletions(-) create mode 100644 src/core/popupwindow.cpp create mode 100644 src/core/popupwindow.hpp create mode 100644 src/core/test/CMakeLists.txt create mode 100644 src/core/test/popupwindow.cpp create mode 100644 src/core/test/popupwindow.hpp create mode 100644 src/io/test/datastream.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 67f8a1f..b27bc29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ set(QT_FPDEPS Gui Qml Quick QuickControls2) if (BUILD_TESTING) enable_testing() + add_definitions(-DQS_TEST) list(APPEND QT_FPDEPS Test) endif() diff --git a/docs b/docs index b218d3e..2d0b15b 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit b218d3ec30f8ff2c51d4caf17509b9d21cf0c088 +Subproject commit 2d0b15bbd52ea61bd79880b89fae0a589010d1f3 diff --git a/examples b/examples index f76b43d..9437c6a 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit f76b43db25fb06a016ccf64ec2b28079c325c346 +Subproject commit 9437c6a840faf7180ab7dfb5425a402ca8a4b58c diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c1fdee8..2e34746 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -15,9 +15,14 @@ qt_add_executable(quickshell windowinterface.cpp floatingwindow.cpp panelinterface.cpp + popupwindow.cpp ) set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}") qt_add_qml_module(quickshell URI Quickshell VERSION 0.1) target_link_libraries(quickshell PRIVATE ${QT_DEPS}) + +if (BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/src/core/doc.hpp b/src/core/doc.hpp index e4c907a..b619b0a 100644 --- a/src/core/doc.hpp +++ b/src/core/doc.hpp @@ -9,3 +9,6 @@ // make the type visible in the docs even if not a QML_ELEMENT #define QSDOC_ELEMENT #define QSDOC_NAMED_ELEMENT(name) + +// overridden properties +#define QSDOC_PROPERTY_OVERRIDE(...) diff --git a/src/core/floatingwindow.cpp b/src/core/floatingwindow.cpp index 0f909c2..73f2b1b 100644 --- a/src/core/floatingwindow.cpp +++ b/src/core/floatingwindow.cpp @@ -42,6 +42,7 @@ void FloatingWindowInterface::onReload(QObject* oldInstance) { } QQmlListProperty FloatingWindowInterface::data() { return this->window->data(); } +ProxyWindowBase* FloatingWindowInterface::proxyWindow() const { return this->window; } QQuickItem* FloatingWindowInterface::contentItem() const { return this->window->contentItem(); } // NOLINTBEGIN @@ -57,6 +58,4 @@ proxyPair(QColor, color, setColor); proxyPair(PendingRegion*, mask, setMask); #undef proxyPair -#undef proxySet -#undef proxyGet // NOLINTEND diff --git a/src/core/floatingwindow.hpp b/src/core/floatingwindow.hpp index 408b1e9..5f03277 100644 --- a/src/core/floatingwindow.hpp +++ b/src/core/floatingwindow.hpp @@ -27,6 +27,7 @@ public: void onReload(QObject* oldInstance) override; + [[nodiscard]] ProxyWindowBase* proxyWindow() const override; [[nodiscard]] QQuickItem* contentItem() const override; // NOLINTBEGIN diff --git a/src/core/module.md b/src/core/module.md index 70b2c8c..73ecbb8 100644 --- a/src/core/module.md +++ b/src/core/module.md @@ -12,5 +12,6 @@ headers = [ "windowinterface.hpp", "panelinterface.hpp", "floatingwindow.hpp", + "popupwindow.hpp", ] ----- diff --git a/src/core/popupwindow.cpp b/src/core/popupwindow.cpp new file mode 100644 index 0000000..392e665 --- /dev/null +++ b/src/core/popupwindow.cpp @@ -0,0 +1,151 @@ +#include "popupwindow.hpp" + +#include +#include +#include +#include +#include +#include + +#include "proxywindow.hpp" +#include "qmlscreen.hpp" +#include "windowinterface.hpp" + +ProxyPopupWindow::ProxyPopupWindow(QObject* parent): ProxyWindowBase(parent) { + this->mVisible = false; +} + +void ProxyPopupWindow::setupWindow() { + this->ProxyWindowBase::setupWindow(); + + this->window->setFlag(Qt::ToolTip); + this->updateTransientParent(); +} + +qint32 ProxyPopupWindow::x() const { + return this->ProxyWindowBase::x() + 1; // QTBUG-121550 +} + +void ProxyPopupWindow::setParentWindow(QObject* parent) { + if (parent == this->mParentWindow) return; + + if (this->mParentWindow != nullptr) { + QObject::disconnect(this->mParentWindow, nullptr, this, nullptr); + QObject::disconnect(this->mParentProxyWindow, nullptr, this, nullptr); + } + + if (parent == nullptr) { + this->mParentWindow = nullptr; + this->mParentProxyWindow = nullptr; + } else { + if (auto* proxy = qobject_cast(parent)) { + this->mParentProxyWindow = proxy; + } else if (auto* interface = qobject_cast(parent)) { + this->mParentProxyWindow = interface->proxyWindow(); + } else { + qWarning() << "Tried to set popup parent window to something that is not a quickshell window:" + << parent; + this->mParentWindow = nullptr; + this->mParentProxyWindow = nullptr; + this->updateTransientParent(); + return; + } + + this->mParentWindow = parent; + + // clang-format off + QObject::connect(this->mParentWindow, &QObject::destroyed, this, &ProxyPopupWindow::onParentDestroyed); + + QObject::connect(this->mParentProxyWindow, &ProxyWindowBase::xChanged, this, &ProxyPopupWindow::updateX); + QObject::connect(this->mParentProxyWindow, &ProxyWindowBase::yChanged, this, &ProxyPopupWindow::updateY); + QObject::connect(this->mParentProxyWindow, &ProxyWindowBase::windowConnected, this, &ProxyPopupWindow::onParentConnected); + // clang-format on + } + + this->updateTransientParent(); +} + +QObject* ProxyPopupWindow::parentWindow() const { return this->mParentWindow; } + +void ProxyPopupWindow::updateTransientParent() { + if (this->window == nullptr) return; + this->updateX(); + this->updateY(); + + this->window->setTransientParent( + this->mParentProxyWindow == nullptr ? nullptr : this->mParentProxyWindow->backingWindow() + ); + + this->updateVisible(); +} + +void ProxyPopupWindow::onParentConnected() { this->updateTransientParent(); } + +void ProxyPopupWindow::onParentDestroyed() { + this->mParentWindow = nullptr; + this->mParentProxyWindow = nullptr; + this->updateVisible(); + emit this->parentWindowChanged(); +} + +void ProxyPopupWindow::setScreen(QuickshellScreenInfo* /*unused*/) { + qWarning() << "Cannot set screen of popup window, as that is controlled by the parent window"; +} + +void ProxyPopupWindow::setVisible(bool visible) { + if (visible == this->wantsVisible) return; + this->wantsVisible = visible; + this->updateVisible(); +} + +void ProxyPopupWindow::updateVisible() { + auto target = this->wantsVisible && this->mParentWindow != nullptr; + + if (target && this->window != nullptr && !this->window->isVisible()) { + this->updateX(); // QTBUG-121550 + } + + this->ProxyWindowBase::setVisible(target); +} + +void ProxyPopupWindow::setRelativeX(qint32 x) { + if (x == this->mRelativeX) return; + this->mRelativeX = x; + this->updateX(); +} + +qint32 ProxyPopupWindow::relativeX() const { return this->mRelativeX; } + +void ProxyPopupWindow::setRelativeY(qint32 y) { + if (y == this->mRelativeY) return; + this->mRelativeY = y; + this->updateY(); +} + +qint32 ProxyPopupWindow::relativeY() const { return this->mRelativeY; } + +void ProxyPopupWindow::updateX() { + if (this->mParentWindow == nullptr || this->window == nullptr) return; + + // use the backing window's x to account for popups in popups with overridden x positions + auto target = this->mParentProxyWindow->backingWindow()->x() + this->relativeX(); + + auto reshow = this->window->isVisible() && (this->window->x() != target && this->x() != target); + if (reshow) this->window->setVisible(false); + this->window->setX(target - 1); // -1 due to QTBUG-121550 + if (reshow && this->wantsVisible) this->window->setVisible(true); +} + +void ProxyPopupWindow::updateY() { + if (this->mParentWindow == nullptr || this->window == nullptr) return; + + auto target = this->mParentProxyWindow->y() + this->relativeY(); + + auto reshow = this->window->isVisible() && this->window->y() != target; + if (reshow) { + this->window->setVisible(false); + this->updateX(); // QTBUG-121550 + } + this->window->setY(target); + if (reshow && this->wantsVisible) this->window->setVisible(true); +} diff --git a/src/core/popupwindow.hpp b/src/core/popupwindow.hpp new file mode 100644 index 0000000..cb691a5 --- /dev/null +++ b/src/core/popupwindow.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "doc.hpp" +#include "proxywindow.hpp" +#include "qmlscreen.hpp" +#include "windowinterface.hpp" + +///! Popup window. +/// Popup window that can display in a position relative to a floating +/// or panel window. +/// +/// #### Example +/// The following snippet creates a panel with a popup centered over it. +/// +/// ```qml +/// PanelWindow { +/// id: toplevel +/// +/// anchors { +/// bottom: true +/// left: true +/// right: true +/// } +/// +/// PopupWindow { +/// parentWindow: toplevel +/// relativeX: parentWindow.width / 2 - width / 2 +/// relativeY: parentWindow.height +/// width: 500 +/// height: 500 +/// visible: true +/// } +/// } +/// ``` +class ProxyPopupWindow: public ProxyWindowBase { + QSDOC_BASECLASS(WindowInterface); + Q_OBJECT; + // clang-format off + /// The parent window of this popup. + /// + /// Changing this property reparents the popup. + Q_PROPERTY(QObject* parentWindow READ parentWindow WRITE setParentWindow NOTIFY parentWindowChanged); + /// The X position of the popup relative to the parent window. + Q_PROPERTY(qint32 relativeX READ relativeX WRITE setRelativeX NOTIFY relativeXChanged); + /// The Y position of the popup relative to the parent window. + Q_PROPERTY(qint32 relativeY READ relativeY WRITE setRelativeY NOTIFY relativeYChanged); + /// If the window is shown or hidden. Defaults to false. + QSDOC_PROPERTY_OVERRIDE(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged); + /// The screen that the window currently occupies. + /// + /// This may be modified to move the window to the given screen. + QSDOC_PROPERTY_OVERRIDE(QuickshellScreenInfo* screen READ screen NOTIFY screenChanged); + // clang-format on + QML_NAMED_ELEMENT(PopupWindow); + +public: + explicit ProxyPopupWindow(QObject* parent = nullptr); + + void setupWindow() override; + + void setScreen(QuickshellScreenInfo* screen) override; + void setVisible(bool visible) override; + + [[nodiscard]] qint32 x() const override; + + [[nodiscard]] QObject* parentWindow() const; + void setParentWindow(QObject* parent); + + [[nodiscard]] qint32 relativeX() const; + void setRelativeX(qint32 x); + + [[nodiscard]] qint32 relativeY() const; + void setRelativeY(qint32 y); + +signals: + void parentWindowChanged(); + void relativeXChanged(); + void relativeYChanged(); + +private slots: + void onParentConnected(); + void onParentDestroyed(); + void updateX(); + void updateY(); + +private: + QQuickWindow* parentBackingWindow(); + void updateTransientParent(); + void updateVisible(); + + QObject* mParentWindow = nullptr; + ProxyWindowBase* mParentProxyWindow = nullptr; + qint32 mRelativeX = 0; + qint32 mRelativeY = 0; + bool wantsVisible = false; +}; diff --git a/src/core/proxywindow.cpp b/src/core/proxywindow.cpp index ce3b8c4..467addf 100644 --- a/src/core/proxywindow.cpp +++ b/src/core/proxywindow.cpp @@ -61,6 +61,8 @@ QQuickWindow* ProxyWindowBase::createWindow(QObject* oldInstance) { void ProxyWindowBase::setupWindow() { // clang-format off QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged); + QObject::connect(this->window, &QWindow::xChanged, this, &ProxyWindowBase::xChanged); + QObject::connect(this->window, &QWindow::yChanged, this, &ProxyWindowBase::yChanged); QObject::connect(this->window, &QWindow::widthChanged, this, &ProxyWindowBase::widthChanged); QObject::connect(this->window, &QWindow::heightChanged, this, &ProxyWindowBase::heightChanged); QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyWindowBase::screenChanged); @@ -76,6 +78,10 @@ void ProxyWindowBase::setupWindow() { this->setHeight(this->mHeight); this->setColor(this->mColor); this->updateMask(); + + // notify initial x and y positions + emit this->xChanged(); + emit this->yChanged(); } QQuickWindow* ProxyWindowBase::disownWindow() { @@ -103,6 +109,16 @@ void ProxyWindowBase::setVisible(bool visible) { } else this->window->setVisible(visible); } +qint32 ProxyWindowBase::x() const { + if (this->window == nullptr) return 0; + else return this->window->x(); +} + +qint32 ProxyWindowBase::y() const { + if (this->window == nullptr) return 0; + else return this->window->y(); +} + qint32 ProxyWindowBase::width() const { if (this->window == nullptr) return this->mWidth; else return this->window->width(); diff --git a/src/core/proxywindow.hpp b/src/core/proxywindow.hpp index accd434..8259c38 100644 --- a/src/core/proxywindow.hpp +++ b/src/core/proxywindow.hpp @@ -34,7 +34,7 @@ class ProxyWindowBase: public Reloadable { /// > /// > Use **only** if you know what you are doing. Q_PROPERTY(QQuickWindow* _backingWindow READ backingWindow); - Q_PROPERTY(QQuickItem* contentItem READ contentItem); + Q_PROPERTY(QQuickItem* contentItem READ contentItem CONSTANT); Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged); Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged); Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged); @@ -67,6 +67,9 @@ public: [[nodiscard]] virtual bool isVisible() const; virtual void setVisible(bool visible); + [[nodiscard]] virtual qint32 x() const; + [[nodiscard]] virtual qint32 y() const; + [[nodiscard]] virtual qint32 width() const; virtual void setWidth(qint32 width); @@ -87,6 +90,8 @@ public: signals: void windowConnected(); void visibleChanged(); + void xChanged(); + void yChanged(); void widthChanged(); void heightChanged(); void screenChanged(); diff --git a/src/core/test/CMakeLists.txt b/src/core/test/CMakeLists.txt new file mode 100644 index 0000000..82d7ce0 --- /dev/null +++ b/src/core/test/CMakeLists.txt @@ -0,0 +1,15 @@ +function (qs_test name) + add_executable(${name} ${ARGN}) + target_link_libraries(${name} PRIVATE ${QT_DEPS} Qt6::Test) + add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $) +endfunction() + +qs_test(popupwindow + popupwindow.cpp + ../popupwindow.cpp + ../proxywindow.cpp + ../qmlscreen.cpp + ../region.cpp + ../reload.cpp + ../windowinterface.cpp +) diff --git a/src/core/test/popupwindow.cpp b/src/core/test/popupwindow.cpp new file mode 100644 index 0000000..a7d90d1 --- /dev/null +++ b/src/core/test/popupwindow.cpp @@ -0,0 +1,182 @@ +#include "popupwindow.hpp" + +#include +#include +#include +#include +#include +#include + +#include "../popupwindow.hpp" +#include "../proxywindow.hpp" + +void TestPopupWindow::initiallyVisible() { // NOLINT + auto parent = ProxyWindowBase(); + auto popup = ProxyPopupWindow(); + + popup.setParentWindow(&parent); + popup.setVisible(true); + + parent.onReload(nullptr); + popup.onReload(nullptr); + + QVERIFY(popup.isVisible()); + QVERIFY(popup.backingWindow()->isVisible()); + QCOMPARE(popup.backingWindow()->transientParent(), parent.backingWindow()); +} + +void TestPopupWindow::reloadReparent() { // NOLINT + // first generation + auto parent = ProxyWindowBase(); + auto popup = ProxyPopupWindow(); + + auto* win2 = new QQuickWindow(); + win2->setVisible(true); + + parent.setVisible(true); + popup.setParentWindow(&parent); + popup.setVisible(true); + + parent.onReload(nullptr); + popup.onReload(nullptr); + + // second generation + auto newParent = ProxyWindowBase(); + auto newPopup = ProxyPopupWindow(); + + newPopup.setParentWindow(&newParent); + newPopup.setVisible(true); + + auto* oldWindow = popup.backingWindow(); + auto* oldTransientParent = oldWindow->transientParent(); + + auto spy = QSignalSpy(oldWindow, &QWindow::visibleChanged); + + qDebug() << "reload"; + newParent.onReload(&parent); + newPopup.onReload(&popup); + + QVERIFY(newPopup.isVisible()); + QVERIFY(newPopup.backingWindow()->isVisible()); + QCOMPARE(newPopup.backingWindow()->transientParent(), oldTransientParent); + QCOMPARE(newPopup.backingWindow()->transientParent(), newParent.backingWindow()); + QCOMPARE(spy.length(), 0); +} + +void TestPopupWindow::reloadUnparent() { // NOLINT + // first generation + auto parent = ProxyWindowBase(); + auto popup = ProxyPopupWindow(); + + popup.setParentWindow(&parent); + popup.setVisible(true); + + parent.onReload(nullptr); + popup.onReload(nullptr); + + // second generation + auto newPopup = ProxyPopupWindow(); + + // parent not set + newPopup.setVisible(true); + newPopup.onReload(&popup); + + QVERIFY(!newPopup.isVisible()); + QVERIFY(!newPopup.backingWindow()->isVisible()); + QCOMPARE(newPopup.backingWindow()->transientParent(), nullptr); +} + +void TestPopupWindow::invisibleWithoutParent() { // NOLINT + auto popup = ProxyPopupWindow(); + + popup.setVisible(true); + popup.onReload(nullptr); + + QVERIFY(!popup.isVisible()); +} + +void TestPopupWindow::moveWithParent() { // NOLINT + auto parent = ProxyWindowBase(); + auto popup = ProxyPopupWindow(); + + popup.setParentWindow(&parent); + popup.setRelativeX(10); + popup.setRelativeY(10); + popup.setVisible(true); + + parent.onReload(nullptr); + popup.onReload(nullptr); + + QCOMPARE(popup.x(), parent.x() + 10); + QCOMPARE(popup.y(), parent.y() + 10); + + parent.backingWindow()->setX(10); + parent.backingWindow()->setY(10); + + QCOMPARE(popup.x(), parent.x() + 10); + QCOMPARE(popup.y(), parent.y() + 10); +} + +void TestPopupWindow::attachParentLate() { // NOLINT + auto parent = ProxyWindowBase(); + auto popup = ProxyPopupWindow(); + + popup.setVisible(true); + + parent.onReload(nullptr); + popup.onReload(nullptr); + + QVERIFY(!popup.isVisible()); + + popup.setParentWindow(&parent); + QVERIFY(popup.isVisible()); + QVERIFY(popup.backingWindow()->isVisible()); + QCOMPARE(popup.backingWindow()->transientParent(), parent.backingWindow()); +} + +void TestPopupWindow::reparentLate() { // NOLINT + auto parent = ProxyWindowBase(); + auto popup = ProxyPopupWindow(); + + popup.setParentWindow(&parent); + popup.setVisible(true); + + parent.onReload(nullptr); + popup.onReload(nullptr); + + QCOMPARE(popup.x(), parent.x()); + QCOMPARE(popup.y(), parent.y()); + + auto parent2 = ProxyWindowBase(); + parent2.onReload(nullptr); + + parent2.backingWindow()->setX(10); + parent2.backingWindow()->setY(10); + + popup.setParentWindow(&parent2); + QVERIFY(popup.isVisible()); + QVERIFY(popup.backingWindow()->isVisible()); + QCOMPARE(popup.backingWindow()->transientParent(), parent2.backingWindow()); + QCOMPARE(popup.x(), parent2.x()); + QCOMPARE(popup.y(), parent2.y()); +} + +void TestPopupWindow::xMigrationFix() { // NOLINT + auto parent = ProxyWindowBase(); + auto popup = ProxyPopupWindow(); + + popup.setParentWindow(&parent); + popup.setVisible(true); + + parent.onReload(nullptr); + popup.onReload(nullptr); + + QCOMPARE(popup.x(), parent.x()); + + popup.setVisible(false); + popup.setVisible(true); + + QCOMPARE(popup.x(), parent.x()); +} + +QTEST_MAIN(TestPopupWindow); diff --git a/src/core/test/popupwindow.hpp b/src/core/test/popupwindow.hpp new file mode 100644 index 0000000..bebc515 --- /dev/null +++ b/src/core/test/popupwindow.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +class TestPopupWindow: public QObject { + Q_OBJECT; + +private slots: + void initiallyVisible(); + void reloadReparent(); + void reloadUnparent(); + void invisibleWithoutParent(); + void moveWithParent(); + void attachParentLate(); + void reparentLate(); + void xMigrationFix(); +}; diff --git a/src/core/windowinterface.hpp b/src/core/windowinterface.hpp index 4f20d9c..f32e427 100644 --- a/src/core/windowinterface.hpp +++ b/src/core/windowinterface.hpp @@ -12,17 +12,19 @@ #include "region.hpp" #include "reload.hpp" +class ProxyWindowBase; + class WindowInterface: public Reloadable { Q_OBJECT; // clang-format off - Q_PROPERTY(QQuickItem* contentItem READ contentItem); + Q_PROPERTY(QQuickItem* contentItem READ contentItem CONSTANT); /// If the window is shown or hidden. Defaults to true. Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged); Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged); Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged); /// The screen that the window currently occupies. /// - /// > [!INFO] This cannot be changed after windowConnected. + /// This may be modified to move the window to the given screen. Q_PROPERTY(QuickshellScreenInfo* screen READ screen WRITE setScreen NOTIFY screenChanged); /// The background color of the window. Defaults to white. /// @@ -92,6 +94,7 @@ class WindowInterface: public Reloadable { public: explicit WindowInterface(QObject* parent = nullptr): Reloadable(parent) {} + [[nodiscard]] virtual ProxyWindowBase* proxyWindow() const = 0; [[nodiscard]] virtual QQuickItem* contentItem() const = 0; [[nodiscard]] virtual bool isVisible() const = 0; diff --git a/src/io/test/datastream.cpp b/src/io/test/datastream.cpp index f0f36be..ea7f300 100644 --- a/src/io/test/datastream.cpp +++ b/src/io/test/datastream.cpp @@ -1,4 +1,4 @@ -#include "../datastream.hpp" +#include "datastream.hpp" #include #include @@ -7,20 +7,18 @@ #include #include #include -#include -class TestSplitParser: public QObject { - Q_OBJECT; -private slots: - void splits_data() { // NOLINT - QTest::addColumn("mark"); - QTest::addColumn("buffer"); // max that can go in the buffer - QTest::addColumn("incoming"); // data that has to be tested on the end in one go - QTest::addColumn>("results"); - QTest::addColumn("remainder"); +#include "../datastream.hpp" - // NOLINTBEGIN - // clang-format off +void TestSplitParser::splits_data() { // NOLINT + QTest::addColumn("mark"); + QTest::addColumn("buffer"); // max that can go in the buffer + QTest::addColumn("incoming"); // data that has to be tested on the end in one go + QTest::addColumn>("results"); + QTest::addColumn("remainder"); + + // NOLINTBEGIN + // clang-format off QTest::addRow("simple") << "-" << "foo" << "-" << QList("foo") << ""; @@ -40,71 +38,69 @@ private slots: QTest::addRow("longsplit-incomplete") << "123" << "foo12" << "3bar123baz" << QList({ "foo", "bar" }) << "baz"; - // clang-format on - // NOLINTEND - } + // clang-format on + // NOLINTEND +} - void splits() { // NOLINT - // NOLINTBEGIN - QFETCH(QString, mark); - QFETCH(QString, buffer); - QFETCH(QString, incoming); - QFETCH(QList, results); - QFETCH(QString, remainder); - // NOLINTEND +void TestSplitParser::splits() { // NOLINT + // NOLINTBEGIN + QFETCH(QString, mark); + QFETCH(QString, buffer); + QFETCH(QString, incoming); + QFETCH(QList, results); + QFETCH(QString, remainder); + // NOLINTEND - auto bufferArray = buffer.toUtf8(); - auto incomingArray = incoming.toUtf8(); + auto bufferArray = buffer.toUtf8(); + auto incomingArray = incoming.toUtf8(); - for (auto i = 0; i <= bufferArray.length(); i++) { - auto buffer = bufferArray.sliced(0, i); - auto incoming = bufferArray.sliced(i); - incoming.append(incomingArray); + for (auto i = 0; i <= bufferArray.length(); i++) { + auto buffer = bufferArray.sliced(0, i); + auto incoming = bufferArray.sliced(i); + incoming.append(incomingArray); - qInfo() << "BUFFER" << QString(buffer); - qInfo() << "INCOMING" << QString(incoming); + qInfo() << "BUFFER" << QString(buffer); + qInfo() << "INCOMING" << QString(incoming); - auto parser = SplitParser(); - auto spy = QSignalSpy(&parser, &DataStreamParser::read); - - parser.setSplitMarker(mark); - parser.parseBytes(incoming, buffer); - - auto actualResults = QList(); - for (auto& read: spy) { - actualResults.push_back(read[0].toString()); - } - - qInfo() << "EXPECTED RESULTS" << results; - qInfo() << "ACTUAL RESULTS" << actualResults; - qInfo() << "EXPECTED REMAINDER" << remainder; - qInfo() << "ACTUAL REMAINDER" << remainder; - QCOMPARE(actualResults, results); - QCOMPARE(buffer, remainder); - } - } - - void initBuffer() { // NOLINT auto parser = SplitParser(); auto spy = QSignalSpy(&parser, &DataStreamParser::read); - auto buf = QString("foo-bar-baz").toUtf8(); - auto expected = QList({"foo", "bar"}); - - parser.setSplitMarker("-"); - parser.parseBytes(buf, buf); + parser.setSplitMarker(mark); + parser.parseBytes(incoming, buffer); auto actualResults = QList(); for (auto& read: spy) { actualResults.push_back(read[0].toString()); } - qInfo() << "EXPECTED RESULTS" << expected; + qInfo() << "EXPECTED RESULTS" << results; qInfo() << "ACTUAL RESULTS" << actualResults; - QCOMPARE(actualResults, expected); - QCOMPARE(buf, "baz"); + qInfo() << "EXPECTED REMAINDER" << remainder; + qInfo() << "ACTUAL REMAINDER" << remainder; + QCOMPARE(actualResults, results); + QCOMPARE(buffer, remainder); } -}; +} -QTEST_MAIN(TestSplitParser) -#include "datastream.moc" +void TestSplitParser::initBuffer() { // NOLINT + auto parser = SplitParser(); + auto spy = QSignalSpy(&parser, &DataStreamParser::read); + + auto buf = QString("foo-bar-baz").toUtf8(); + auto expected = QList({"foo", "bar"}); + + parser.setSplitMarker("-"); + parser.parseBytes(buf, buf); + + auto actualResults = QList(); + for (auto& read: spy) { + actualResults.push_back(read[0].toString()); + } + + qInfo() << "EXPECTED RESULTS" << expected; + qInfo() << "ACTUAL RESULTS" << actualResults; + QCOMPARE(actualResults, expected); + QCOMPARE(buf, "baz"); +} + +QTEST_MAIN(TestSplitParser); diff --git a/src/io/test/datastream.hpp b/src/io/test/datastream.hpp new file mode 100644 index 0000000..cd89bb8 --- /dev/null +++ b/src/io/test/datastream.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class TestSplitParser: public QObject { + Q_OBJECT; + +private slots: + void splits_data(); // NOLINT + void splits(); + void initBuffer(); +}; diff --git a/src/wayland/wlr_layershell.cpp b/src/wayland/wlr_layershell.cpp index bc4fc5c..30e9e3b 100644 --- a/src/wayland/wlr_layershell.cpp +++ b/src/wayland/wlr_layershell.cpp @@ -187,6 +187,7 @@ void WaylandPanelInterface::onReload(QObject* oldInstance) { } QQmlListProperty WaylandPanelInterface::data() { return this->layer->data(); } +ProxyWindowBase* WaylandPanelInterface::proxyWindow() const { return this->layer; } QQuickItem* WaylandPanelInterface::contentItem() const { return this->layer->contentItem(); } // NOLINTBEGIN @@ -206,4 +207,6 @@ proxyPair(Anchors, anchors, setAnchors); proxyPair(Margins, margins, setMargins); proxyPair(qint32, exclusiveZone, setExclusiveZone); proxyPair(ExclusionMode::Enum, exclusionMode, setExclusionMode); + +#undef proxyPair // NOLINTEND diff --git a/src/wayland/wlr_layershell.hpp b/src/wayland/wlr_layershell.hpp index f86d513..0b7128d 100644 --- a/src/wayland/wlr_layershell.hpp +++ b/src/wayland/wlr_layershell.hpp @@ -121,6 +121,7 @@ public: void onReload(QObject* oldInstance) override; + [[nodiscard]] ProxyWindowBase* proxyWindow() const override; [[nodiscard]] QQuickItem* contentItem() const override; // NOLINTBEGIN