First Commit

This commit is contained in:
2025-11-18 14:18:26 -07:00
parent 33eb6e3707
commit 27277ec342
6106 changed files with 3571167 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Joshua Goins <joshua.goins@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
find_package(KF${QT_VERSION_MAJOR}Wayland CONFIG)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS WaylandClient Concurrent)
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
find_package(PlasmaWaylandProtocols)
find_package(QtWaylandScanner)
find_package(Wayland REQUIRED Client)
ecm_add_qtwayland_client_protocol(
wayland_SRCS PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/fake-input.xml BASENAME fake-input
)
# add wayland test
function(add_wayland_test test srcs)
add_executable(${test} ${srcs})
target_link_libraries(
${test}
kddockwidgets
KDAB::KDBindings
Wayland::Client
Qt::WaylandClient
KF${QT_VERSION_MAJOR}::WaylandClient
Qt::Concurrent
)
if(KDDockWidgets_HAS_SPDLOG)
target_link_libraries(${test} spdlog::spdlog)
endif()
target_sources(${test} PRIVATE ${wayland_SRCS})
kddw_add_nlohmann(${test})
set_compiler_flags(${test})
# FIXME: Use find_program to check if kwin_wayland exists first
add_test(NAME ${test} COMMAND kwin_wayland --no-kactivities --no-global-shortcuts --no-lockscreen --virtual
--exit-with-session $<TARGET_FILE:${test}>
)
endfunction()
add_wayland_test(tst_wayland_qtwidgets tst_wayland.cpp)

View File

@@ -0,0 +1,255 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Joshua Goins <joshua.goins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "kddockwidgets/DockWidget.h"
#include "kddockwidgets/MainWindow.h"
#include "core/Group.h"
#include "core/Platform.h"
#include "core/TabBar.h"
#include "core/TitleBar.h"
#include "core/FloatingWindow.h"
#include "qtwidgets/views/FloatingWindow.h"
#include "qtwidgets/views/TabBar.h"
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/fakeinput.h>
#include <KWayland/Client/registry.h>
#include <QApplication>
#include <QLabel>
#include <QSignalSpy>
#include <QStyle>
#include <QVBoxLayout>
#include <QtConcurrent>
// WAYLAND_TODO:
// - Remove QtConcurrent. We shouldn't run widget code on secondary threads.
// - Replace QThread::msleep() with QTest::qWait(), so event loop still runs
static KWayland::Client::FakeInput *input = nullptr;
QPoint getWindowTopLeft()
{
const int titlebarHeight = QApplication::style()->pixelMetric(QStyle::PM_TitleBarHeight);
return { 0, titlebarHeight + 10 };
}
QPoint getCenterOfTitlebar(const KDDockWidgets::QtWidgets::DockWidget &dockWidget)
{
return dockWidget.actualTitleBar()->rect().center();
}
QPoint getCenterOfDockWidget(const KDDockWidgets::QtWidgets::DockWidget &dockWidget)
{
return dockWidget.rect().center();
}
KDDockWidgets::QtWidgets::MainWindow *createMainWindow(const QString &name)
{
auto mainWindow = new KDDockWidgets::QtWidgets::MainWindow(name);
mainWindow->showMaximized();
return mainWindow;
}
KDDockWidgets::QtWidgets::DockWidget *createDockWidget(const QString &name)
{
auto dockWidget = new KDDockWidgets::QtWidgets::DockWidget(name);
dockWidget->setWidget(new QWidget());
return dockWidget;
}
// A QFuture for running the test, and the testing/cleanup function.
using TestFunc = std::pair<QFuture<void>, std::function<void()>>;
TestFunc tst_detachTitlebar()
{
auto mainWindow = createMainWindow("detachtitle_window");
auto dockWidget = createDockWidget(QStringLiteral("detachtitle_dock"));
mainWindow->addDockWidget(dockWidget, KDDockWidgets::Location::Location_OnTop);
auto tabbedDockWidget = createDockWidget(QStringLiteral("detachtitle_dock2"));
mainWindow->addDockWidget(tabbedDockWidget, KDDockWidgets::Location::Location_OnRight);
return { QtConcurrent::run([=]() {
// wait to settle
QThread::msleep(1000);
input->requestPointerMoveAbsolute(getWindowTopLeft() + getCenterOfTitlebar(*dockWidget));
input->requestPointerButtonPress(Qt::MouseButton::LeftButton);
QThread::msleep(1000);
input->requestPointerMoveAbsolute(getWindowTopLeft() + getCenterOfDockWidget(*dockWidget));
QThread::msleep(4000);
input->requestPointerButtonRelease(Qt::MouseButton::LeftButton);
QThread::msleep(1000);
}),
[=]() {
Q_ASSERT(dockWidget->isFloating());
mainWindow->close();
tabbedDockWidget->close();
mainWindow->deleteLater();
tabbedDockWidget->deleteLater();
} };
}
TestFunc tst_detachTab()
{
auto mainWindow = createMainWindow("detachtab_window");
auto dockWidget = createDockWidget(QStringLiteral("detachtab_dock"));
mainWindow->addDockWidget(dockWidget, KDDockWidgets::Location::Location_OnTop);
auto tabbedDockWidget = createDockWidget(QStringLiteral("detachtab_dock2"));
dockWidget->addDockWidgetAsTab(tabbedDockWidget);
auto tabBar = dynamic_cast<KDDockWidgets::QtWidgets::TabBar *>(dockWidget->group()->tabBar()->view());
return { QtConcurrent::run([=]() {
// wait to settle
QThread::msleep(1000);
const QRect secondTabRect = tabBar->rectForTab(1);
input->requestPointerMoveAbsolute(getWindowTopLeft() + tabBar->mapToGlobal(secondTabRect.center()));
input->requestPointerButtonClick(Qt::MouseButton::LeftButton);
QThread::msleep(10);
input->requestPointerButtonClick(Qt::MouseButton::LeftButton);
QThread::msleep(1000);
}),
[=]() {
Q_ASSERT(tabbedDockWidget->isFloating());
mainWindow->close();
tabbedDockWidget->close();
mainWindow->deleteLater();
tabbedDockWidget->deleteLater();
} };
}
TestFunc tst_dragAndDrop()
{
auto mainWindow = createMainWindow("draganddrop_window");
auto dummyWidget = new QWidget();
auto layout = new QVBoxLayout();
dummyWidget->setLayout(layout);
auto timeLabel = new QLabel("time:");
layout->addWidget(timeLabel);
// FIXME: This timer seems to be required for the tests to work? QEventLoop weirdness I bet...
auto timer = new QTimer();
timer->setInterval(1000);
timer->setSingleShot(false);
QObject::connect(timer, &QTimer::timeout, timer, [timeLabel] {
static int time = 0;
timeLabel->setText(QString("time: %1").arg(time++));
});
timer->start();
auto dockWidget = createDockWidget(QStringLiteral("draganddrop_dock"));
dockWidget->setWidget(dummyWidget);
mainWindow->addDockWidget(dockWidget, KDDockWidgets::Location::Location_OnTop);
auto tabbedDockWidget = createDockWidget(QStringLiteral("draganddrop_dock2"));
tabbedDockWidget->open();
auto window = dynamic_cast<KDDockWidgets::QtWidgets::FloatingWindow *>(tabbedDockWidget->group()->floatingWindow()->view());
window->showMaximized();
return { QtConcurrent::run([=]() {
// wait to settle
QThread::msleep(5000);
input->requestPointerMoveAbsolute(getWindowTopLeft() + getCenterOfTitlebar(*tabbedDockWidget));
QThread::msleep(1000);
input->requestPointerButtonPress(Qt::MouseButton::LeftButton);
QThread::msleep(1000);
input->requestPointerMove({ 100, 100 });
QThread::msleep(1000);
window->hide();
input->requestPointerMoveAbsolute(getWindowTopLeft() + getCenterOfDockWidget(*dockWidget));
QThread::msleep(1000);
input->requestPointerButtonRelease(Qt::MouseButton::LeftButton);
QThread::msleep(1000);
}),
[=]() {
Q_ASSERT(!tabbedDockWidget->isFloating());
mainWindow->close();
tabbedDockWidget->close();
mainWindow->deleteLater();
tabbedDockWidget->deleteLater();
} };
}
const std::vector tests = {
tst_dragAndDrop,
tst_detachTab,
tst_detachTitlebar,
};
static QFutureWatcher<void> watcher;
static std::function<void()> currentCleanupFunction;
void nextTest()
{
static int currentTest = 0;
if (currentTest >= static_cast<int>(tests.size())) {
QApplication::quit();
return;
}
const auto [future, cleanupFunction] = tests[currentTest++]();
currentCleanupFunction = cleanupFunction;
watcher.setFuture(future);
}
int main(int argc, char **argv)
{
qputenv("QT_QPA_PLATFORM", "wayland");
QApplication app(argc, argv);
KWayland::Client::ConnectionThread *connection = KWayland::Client::ConnectionThread::fromApplication(qGuiApp);
QObject::connect(&watcher, &QFutureWatcher<void>::finished, [] {
currentCleanupFunction();
nextTest();
});
KWayland::Client::Registry registry;
registry.create(connection);
registry.setup();
QSignalSpy fakeInputSpy(&registry, &KWayland::Client::Registry::fakeInputAnnounced);
fakeInputSpy.wait();
const QList<QVariant> arguments = fakeInputSpy.takeFirst();
input = registry.createFakeInput(arguments[0].toInt(), arguments[1].toInt(), connection);
input->authenticate(QStringLiteral("KDDockWidgets Wayland Test"), QStringLiteral("For testing"));
KDDockWidgets::initFrontend(KDDockWidgets::FrontendType::QtWidgets);
nextTest();
return QApplication::exec();
}