popups: add popup windows

This commit is contained in:
outfoxxed 2024-03-11 05:44:56 -07:00
parent 8cf0659444
commit b675b3676c
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
20 changed files with 586 additions and 71 deletions

View file

@ -46,6 +46,7 @@ set(QT_FPDEPS Gui Qml Quick QuickControls2)
if (BUILD_TESTING) if (BUILD_TESTING)
enable_testing() enable_testing()
add_definitions(-DQS_TEST)
list(APPEND QT_FPDEPS Test) list(APPEND QT_FPDEPS Test)
endif() endif()

2
docs

@ -1 +1 @@
Subproject commit b218d3ec30f8ff2c51d4caf17509b9d21cf0c088 Subproject commit 2d0b15bbd52ea61bd79880b89fae0a589010d1f3

@ -1 +1 @@
Subproject commit f76b43db25fb06a016ccf64ec2b28079c325c346 Subproject commit 9437c6a840faf7180ab7dfb5425a402ca8a4b58c

View file

@ -15,9 +15,14 @@ qt_add_executable(quickshell
windowinterface.cpp windowinterface.cpp
floatingwindow.cpp floatingwindow.cpp
panelinterface.cpp panelinterface.cpp
popupwindow.cpp
) )
set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}") set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}")
qt_add_qml_module(quickshell URI Quickshell VERSION 0.1) qt_add_qml_module(quickshell URI Quickshell VERSION 0.1)
target_link_libraries(quickshell PRIVATE ${QT_DEPS}) target_link_libraries(quickshell PRIVATE ${QT_DEPS})
if (BUILD_TESTING)
add_subdirectory(test)
endif()

View file

@ -9,3 +9,6 @@
// make the type visible in the docs even if not a QML_ELEMENT // make the type visible in the docs even if not a QML_ELEMENT
#define QSDOC_ELEMENT #define QSDOC_ELEMENT
#define QSDOC_NAMED_ELEMENT(name) #define QSDOC_NAMED_ELEMENT(name)
// overridden properties
#define QSDOC_PROPERTY_OVERRIDE(...)

View file

@ -42,6 +42,7 @@ void FloatingWindowInterface::onReload(QObject* oldInstance) {
} }
QQmlListProperty<QObject> FloatingWindowInterface::data() { return this->window->data(); } QQmlListProperty<QObject> FloatingWindowInterface::data() { return this->window->data(); }
ProxyWindowBase* FloatingWindowInterface::proxyWindow() const { return this->window; }
QQuickItem* FloatingWindowInterface::contentItem() const { return this->window->contentItem(); } QQuickItem* FloatingWindowInterface::contentItem() const { return this->window->contentItem(); }
// NOLINTBEGIN // NOLINTBEGIN
@ -57,6 +58,4 @@ proxyPair(QColor, color, setColor);
proxyPair(PendingRegion*, mask, setMask); proxyPair(PendingRegion*, mask, setMask);
#undef proxyPair #undef proxyPair
#undef proxySet
#undef proxyGet
// NOLINTEND // NOLINTEND

View file

@ -27,6 +27,7 @@ public:
void onReload(QObject* oldInstance) override; void onReload(QObject* oldInstance) override;
[[nodiscard]] ProxyWindowBase* proxyWindow() const override;
[[nodiscard]] QQuickItem* contentItem() const override; [[nodiscard]] QQuickItem* contentItem() const override;
// NOLINTBEGIN // NOLINTBEGIN

View file

@ -12,5 +12,6 @@ headers = [
"windowinterface.hpp", "windowinterface.hpp",
"panelinterface.hpp", "panelinterface.hpp",
"floatingwindow.hpp", "floatingwindow.hpp",
"popupwindow.hpp",
] ]
----- -----

151
src/core/popupwindow.cpp Normal file
View file

@ -0,0 +1,151 @@
#include "popupwindow.hpp"
#include <qlogging.h>
#include <qnamespace.h>
#include <qobject.h>
#include <qquickwindow.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#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<ProxyWindowBase*>(parent)) {
this->mParentProxyWindow = proxy;
} else if (auto* interface = qobject_cast<WindowInterface*>(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);
}

102
src/core/popupwindow.hpp Normal file
View file

@ -0,0 +1,102 @@
#pragma once
#include <qobject.h>
#include <qqmlintegration.h>
#include <qquickwindow.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#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;
};

View file

@ -61,6 +61,8 @@ QQuickWindow* ProxyWindowBase::createWindow(QObject* oldInstance) {
void ProxyWindowBase::setupWindow() { void ProxyWindowBase::setupWindow() {
// clang-format off // clang-format off
QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged); 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::widthChanged, this, &ProxyWindowBase::widthChanged);
QObject::connect(this->window, &QWindow::heightChanged, this, &ProxyWindowBase::heightChanged); QObject::connect(this->window, &QWindow::heightChanged, this, &ProxyWindowBase::heightChanged);
QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyWindowBase::screenChanged); QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyWindowBase::screenChanged);
@ -76,6 +78,10 @@ void ProxyWindowBase::setupWindow() {
this->setHeight(this->mHeight); this->setHeight(this->mHeight);
this->setColor(this->mColor); this->setColor(this->mColor);
this->updateMask(); this->updateMask();
// notify initial x and y positions
emit this->xChanged();
emit this->yChanged();
} }
QQuickWindow* ProxyWindowBase::disownWindow() { QQuickWindow* ProxyWindowBase::disownWindow() {
@ -103,6 +109,16 @@ void ProxyWindowBase::setVisible(bool visible) {
} else this->window->setVisible(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 { qint32 ProxyWindowBase::width() const {
if (this->window == nullptr) return this->mWidth; if (this->window == nullptr) return this->mWidth;
else return this->window->width(); else return this->window->width();

View file

@ -34,7 +34,7 @@ class ProxyWindowBase: public Reloadable {
/// > /// >
/// > Use **only** if you know what you are doing. /// > Use **only** if you know what you are doing.
Q_PROPERTY(QQuickWindow* _backingWindow READ backingWindow); 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(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged);
Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged); Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged);
Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged); Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged);
@ -67,6 +67,9 @@ public:
[[nodiscard]] virtual bool isVisible() const; [[nodiscard]] virtual bool isVisible() const;
virtual void setVisible(bool visible); virtual void setVisible(bool visible);
[[nodiscard]] virtual qint32 x() const;
[[nodiscard]] virtual qint32 y() const;
[[nodiscard]] virtual qint32 width() const; [[nodiscard]] virtual qint32 width() const;
virtual void setWidth(qint32 width); virtual void setWidth(qint32 width);
@ -87,6 +90,8 @@ public:
signals: signals:
void windowConnected(); void windowConnected();
void visibleChanged(); void visibleChanged();
void xChanged();
void yChanged();
void widthChanged(); void widthChanged();
void heightChanged(); void heightChanged();
void screenChanged(); void screenChanged();

View file

@ -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 $<TARGET_FILE:${name}>)
endfunction()
qs_test(popupwindow
popupwindow.cpp
../popupwindow.cpp
../proxywindow.cpp
../qmlscreen.cpp
../region.cpp
../reload.cpp
../windowinterface.cpp
)

View file

@ -0,0 +1,182 @@
#include "popupwindow.hpp"
#include <qlogging.h>
#include <qquickwindow.h>
#include <qsignalspy.h>
#include <qtest.h>
#include <qtestcase.h>
#include <qwindow.h>
#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);

View file

@ -0,0 +1,18 @@
#pragma once
#include <qobject.h>
#include <qtmetamacros.h>
class TestPopupWindow: public QObject {
Q_OBJECT;
private slots:
void initiallyVisible();
void reloadReparent();
void reloadUnparent();
void invisibleWithoutParent();
void moveWithParent();
void attachParentLate();
void reparentLate();
void xMigrationFix();
};

View file

@ -12,17 +12,19 @@
#include "region.hpp" #include "region.hpp"
#include "reload.hpp" #include "reload.hpp"
class ProxyWindowBase;
class WindowInterface: public Reloadable { class WindowInterface: public Reloadable {
Q_OBJECT; Q_OBJECT;
// clang-format off // 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. /// If the window is shown or hidden. Defaults to true.
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged); Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged);
Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged); Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged);
Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged); Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged);
/// The screen that the window currently occupies. /// 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); Q_PROPERTY(QuickshellScreenInfo* screen READ screen WRITE setScreen NOTIFY screenChanged);
/// The background color of the window. Defaults to white. /// The background color of the window. Defaults to white.
/// ///
@ -92,6 +94,7 @@ class WindowInterface: public Reloadable {
public: public:
explicit WindowInterface(QObject* parent = nullptr): Reloadable(parent) {} explicit WindowInterface(QObject* parent = nullptr): Reloadable(parent) {}
[[nodiscard]] virtual ProxyWindowBase* proxyWindow() const = 0;
[[nodiscard]] virtual QQuickItem* contentItem() const = 0; [[nodiscard]] virtual QQuickItem* contentItem() const = 0;
[[nodiscard]] virtual bool isVisible() const = 0; [[nodiscard]] virtual bool isVisible() const = 0;

View file

@ -1,4 +1,4 @@
#include "../datastream.hpp" #include "datastream.hpp"
#include <qbytearray.h> #include <qbytearray.h>
#include <qlist.h> #include <qlist.h>
@ -7,20 +7,18 @@
#include <qsignalspy.h> #include <qsignalspy.h>
#include <qtest.h> #include <qtest.h>
#include <qtestcase.h> #include <qtestcase.h>
#include <qtmetamacros.h>
class TestSplitParser: public QObject { #include "../datastream.hpp"
Q_OBJECT;
private slots:
void splits_data() { // NOLINT
QTest::addColumn<QString>("mark");
QTest::addColumn<QString>("buffer"); // max that can go in the buffer
QTest::addColumn<QString>("incoming"); // data that has to be tested on the end in one go
QTest::addColumn<QList<QString>>("results");
QTest::addColumn<QString>("remainder");
// NOLINTBEGIN void TestSplitParser::splits_data() { // NOLINT
// clang-format off QTest::addColumn<QString>("mark");
QTest::addColumn<QString>("buffer"); // max that can go in the buffer
QTest::addColumn<QString>("incoming"); // data that has to be tested on the end in one go
QTest::addColumn<QList<QString>>("results");
QTest::addColumn<QString>("remainder");
// NOLINTBEGIN
// clang-format off
QTest::addRow("simple") << "-" QTest::addRow("simple") << "-"
<< "foo" << "-" << "foo" << "-"
<< QList<QString>("foo") << ""; << QList<QString>("foo") << "";
@ -40,71 +38,69 @@ private slots:
QTest::addRow("longsplit-incomplete") << "123" QTest::addRow("longsplit-incomplete") << "123"
<< "foo12" << "3bar123baz" << "foo12" << "3bar123baz"
<< QList<QString>({ "foo", "bar" }) << "baz"; << QList<QString>({ "foo", "bar" }) << "baz";
// clang-format on // clang-format on
// NOLINTEND // NOLINTEND
} }
void splits() { // NOLINT void TestSplitParser::splits() { // NOLINT
// NOLINTBEGIN // NOLINTBEGIN
QFETCH(QString, mark); QFETCH(QString, mark);
QFETCH(QString, buffer); QFETCH(QString, buffer);
QFETCH(QString, incoming); QFETCH(QString, incoming);
QFETCH(QList<QString>, results); QFETCH(QList<QString>, results);
QFETCH(QString, remainder); QFETCH(QString, remainder);
// NOLINTEND // NOLINTEND
auto bufferArray = buffer.toUtf8(); auto bufferArray = buffer.toUtf8();
auto incomingArray = incoming.toUtf8(); auto incomingArray = incoming.toUtf8();
for (auto i = 0; i <= bufferArray.length(); i++) { for (auto i = 0; i <= bufferArray.length(); i++) {
auto buffer = bufferArray.sliced(0, i); auto buffer = bufferArray.sliced(0, i);
auto incoming = bufferArray.sliced(i); auto incoming = bufferArray.sliced(i);
incoming.append(incomingArray); incoming.append(incomingArray);
qInfo() << "BUFFER" << QString(buffer); qInfo() << "BUFFER" << QString(buffer);
qInfo() << "INCOMING" << QString(incoming); qInfo() << "INCOMING" << QString(incoming);
auto parser = SplitParser();
auto spy = QSignalSpy(&parser, &DataStreamParser::read);
parser.setSplitMarker(mark);
parser.parseBytes(incoming, buffer);
auto actualResults = QList<QString>();
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 parser = SplitParser();
auto spy = QSignalSpy(&parser, &DataStreamParser::read); auto spy = QSignalSpy(&parser, &DataStreamParser::read);
auto buf = QString("foo-bar-baz").toUtf8(); parser.setSplitMarker(mark);
auto expected = QList<QString>({"foo", "bar"}); parser.parseBytes(incoming, buffer);
parser.setSplitMarker("-");
parser.parseBytes(buf, buf);
auto actualResults = QList<QString>(); auto actualResults = QList<QString>();
for (auto& read: spy) { for (auto& read: spy) {
actualResults.push_back(read[0].toString()); actualResults.push_back(read[0].toString());
} }
qInfo() << "EXPECTED RESULTS" << expected; qInfo() << "EXPECTED RESULTS" << results;
qInfo() << "ACTUAL RESULTS" << actualResults; qInfo() << "ACTUAL RESULTS" << actualResults;
QCOMPARE(actualResults, expected); qInfo() << "EXPECTED REMAINDER" << remainder;
QCOMPARE(buf, "baz"); qInfo() << "ACTUAL REMAINDER" << remainder;
QCOMPARE(actualResults, results);
QCOMPARE(buffer, remainder);
} }
}; }
QTEST_MAIN(TestSplitParser) void TestSplitParser::initBuffer() { // NOLINT
#include "datastream.moc" auto parser = SplitParser();
auto spy = QSignalSpy(&parser, &DataStreamParser::read);
auto buf = QString("foo-bar-baz").toUtf8();
auto expected = QList<QString>({"foo", "bar"});
parser.setSplitMarker("-");
parser.parseBytes(buf, buf);
auto actualResults = QList<QString>();
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);

View file

@ -0,0 +1,13 @@
#pragma once
#include <qobject.h>
#include <qtmetamacros.h>
class TestSplitParser: public QObject {
Q_OBJECT;
private slots:
void splits_data(); // NOLINT
void splits();
void initBuffer();
};

View file

@ -187,6 +187,7 @@ void WaylandPanelInterface::onReload(QObject* oldInstance) {
} }
QQmlListProperty<QObject> WaylandPanelInterface::data() { return this->layer->data(); } QQmlListProperty<QObject> WaylandPanelInterface::data() { return this->layer->data(); }
ProxyWindowBase* WaylandPanelInterface::proxyWindow() const { return this->layer; }
QQuickItem* WaylandPanelInterface::contentItem() const { return this->layer->contentItem(); } QQuickItem* WaylandPanelInterface::contentItem() const { return this->layer->contentItem(); }
// NOLINTBEGIN // NOLINTBEGIN
@ -206,4 +207,6 @@ proxyPair(Anchors, anchors, setAnchors);
proxyPair(Margins, margins, setMargins); proxyPair(Margins, margins, setMargins);
proxyPair(qint32, exclusiveZone, setExclusiveZone); proxyPair(qint32, exclusiveZone, setExclusiveZone);
proxyPair(ExclusionMode::Enum, exclusionMode, setExclusionMode); proxyPair(ExclusionMode::Enum, exclusionMode, setExclusionMode);
#undef proxyPair
// NOLINTEND // NOLINTEND

View file

@ -121,6 +121,7 @@ public:
void onReload(QObject* oldInstance) override; void onReload(QObject* oldInstance) override;
[[nodiscard]] ProxyWindowBase* proxyWindow() const override;
[[nodiscard]] QQuickItem* contentItem() const override; [[nodiscard]] QQuickItem* contentItem() const override;
// NOLINTBEGIN // NOLINTBEGIN