forked from quickshell/quickshell
		
	popups: add popup windows
This commit is contained in:
		
							parent
							
								
									8cf0659444
								
							
						
					
					
						commit
						b675b3676c
					
				
					 20 changed files with 586 additions and 71 deletions
				
			
		| 
						 | 
				
			
			@ -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,12 +7,10 @@
 | 
			
		|||
#include <qsignalspy.h>
 | 
			
		||||
#include <qtest.h>
 | 
			
		||||
#include <qtestcase.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
 | 
			
		||||
class TestSplitParser: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
private slots:
 | 
			
		||||
	void splits_data() { // NOLINT
 | 
			
		||||
#include "../datastream.hpp"
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +42,7 @@ private slots:
 | 
			
		|||
	// NOLINTEND
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	void splits() { // NOLINT
 | 
			
		||||
void TestSplitParser::splits() { // NOLINT
 | 
			
		||||
	// NOLINTBEGIN
 | 
			
		||||
	QFETCH(QString, mark);
 | 
			
		||||
	QFETCH(QString, buffer);
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +82,7 @@ private slots:
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	void initBuffer() { // NOLINT
 | 
			
		||||
void TestSplitParser::initBuffer() { // NOLINT
 | 
			
		||||
	auto parser = SplitParser();
 | 
			
		||||
	auto spy = QSignalSpy(&parser, &DataStreamParser::read);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +102,5 @@ private slots:
 | 
			
		|||
	QCOMPARE(actualResults, expected);
 | 
			
		||||
	QCOMPARE(buf, "baz");
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
QTEST_MAIN(TestSplitParser)
 | 
			
		||||
#include "datastream.moc"
 | 
			
		||||
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…
	
	Add table
		Add a link
		
	
		Reference in a new issue