forked from quickshell/quickshell
popups: add popup windows
This commit is contained in:
parent
8cf0659444
commit
b675b3676c
|
@ -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()
|
||||
|
||||
|
|
2
docs
2
docs
|
@ -1 +1 @@
|
|||
Subproject commit b218d3ec30f8ff2c51d4caf17509b9d21cf0c088
|
||||
Subproject commit 2d0b15bbd52ea61bd79880b89fae0a589010d1f3
|
2
examples
2
examples
|
@ -1 +1 @@
|
|||
Subproject commit f76b43db25fb06a016ccf64ec2b28079c325c346
|
||||
Subproject commit 9437c6a840faf7180ab7dfb5425a402ca8a4b58c
|
|
@ -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()
|
||||
|
|
|
@ -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(...)
|
||||
|
|
|
@ -42,6 +42,7 @@ void FloatingWindowInterface::onReload(QObject* oldInstance) {
|
|||
}
|
||||
|
||||
QQmlListProperty<QObject> 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
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
|
||||
void onReload(QObject* oldInstance) override;
|
||||
|
||||
[[nodiscard]] ProxyWindowBase* proxyWindow() const override;
|
||||
[[nodiscard]] QQuickItem* contentItem() const override;
|
||||
|
||||
// NOLINTBEGIN
|
||||
|
|
|
@ -12,5 +12,6 @@ headers = [
|
|||
"windowinterface.hpp",
|
||||
"panelinterface.hpp",
|
||||
"floatingwindow.hpp",
|
||||
"popupwindow.hpp",
|
||||
]
|
||||
-----
|
||||
|
|
151
src/core/popupwindow.cpp
Normal file
151
src/core/popupwindow.cpp
Normal 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
102
src/core/popupwindow.hpp
Normal 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;
|
||||
};
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
15
src/core/test/CMakeLists.txt
Normal file
15
src/core/test/CMakeLists.txt
Normal 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
|
||||
)
|
182
src/core/test/popupwindow.cpp
Normal file
182
src/core/test/popupwindow.cpp
Normal 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);
|
18
src/core/test/popupwindow.hpp
Normal file
18
src/core/test/popupwindow.hpp
Normal 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();
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "../datastream.hpp"
|
||||
#include "datastream.hpp"
|
||||
|
||||
#include <qbytearray.h>
|
||||
#include <qlist.h>
|
||||
|
@ -7,20 +7,18 @@
|
|||
#include <qsignalspy.h>
|
||||
#include <qtest.h>
|
||||
#include <qtestcase.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class TestSplitParser: public QObject {
|
||||
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");
|
||||
#include "../datastream.hpp"
|
||||
|
||||
// NOLINTBEGIN
|
||||
// clang-format off
|
||||
void TestSplitParser::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
|
||||
// clang-format off
|
||||
QTest::addRow("simple") << "-"
|
||||
<< "foo" << "-"
|
||||
<< QList<QString>("foo") << "";
|
||||
|
@ -40,71 +38,69 @@ private slots:
|
|||
QTest::addRow("longsplit-incomplete") << "123"
|
||||
<< "foo12" << "3bar123baz"
|
||||
<< QList<QString>({ "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<QString>, results);
|
||||
QFETCH(QString, remainder);
|
||||
// NOLINTEND
|
||||
void TestSplitParser::splits() { // NOLINT
|
||||
// NOLINTBEGIN
|
||||
QFETCH(QString, mark);
|
||||
QFETCH(QString, buffer);
|
||||
QFETCH(QString, incoming);
|
||||
QFETCH(QList<QString>, 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<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 spy = QSignalSpy(&parser, &DataStreamParser::read);
|
||||
|
||||
auto buf = QString("foo-bar-baz").toUtf8();
|
||||
auto expected = QList<QString>({"foo", "bar"});
|
||||
|
||||
parser.setSplitMarker("-");
|
||||
parser.parseBytes(buf, buf);
|
||||
parser.setSplitMarker(mark);
|
||||
parser.parseBytes(incoming, buffer);
|
||||
|
||||
auto actualResults = QList<QString>();
|
||||
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<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);
|
||||
|
|
13
src/io/test/datastream.hpp
Normal file
13
src/io/test/datastream.hpp
Normal 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();
|
||||
};
|
|
@ -187,6 +187,7 @@ void WaylandPanelInterface::onReload(QObject* oldInstance) {
|
|||
}
|
||||
|
||||
QQmlListProperty<QObject> 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
|
||||
|
|
|
@ -121,6 +121,7 @@ public:
|
|||
|
||||
void onReload(QObject* oldInstance) override;
|
||||
|
||||
[[nodiscard]] ProxyWindowBase* proxyWindow() const override;
|
||||
[[nodiscard]] QQuickItem* contentItem() const override;
|
||||
|
||||
// NOLINTBEGIN
|
||||
|
|
Loading…
Reference in a new issue