2026-04-06 00:20:51 -05:00

297 lines
6.9 KiB
C++

/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@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 "core/Platform.h"
#include "core/Platform_p.h"
#include "core/Logging_p.h"
#include "core/Window_p.h"
#include "core/Utils_p.h"
#include "core/EventFilterInterface.h"
#include "core/Separator.h"
#include "core/layouting/LayoutingSeparator_p.h"
#include <core/DockRegistry.h>
#ifdef KDDW_FRONTEND_QTWIDGETS
#include "qtwidgets/Platform.h"
#include <QApplication>
#endif
#include "Config.h"
#include "core/layouting/Item_p.h"
#include "core/Screen_p.h"
#include "QtCompat_p.h"
#include "kdbindings/signal.h"
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <QScopedValueRollback>
using namespace KDDockWidgets;
using namespace KDDockWidgets::Core;
static Platform *s_platform = nullptr;
EventFilterInterface::~EventFilterInterface() = default;
Platform::Platform()
: d(new Private())
{
assert(!s_platform);
s_platform = this;
Item::setDumpScreenInfoFunc([] {
const auto screens = Platform::instance()->screens();
for (const auto &screen : screens) {
std::cerr << "Screen: " << screen->geometry() << "; " << screen->availableGeometry()
<< "; drp=" << screen->devicePixelRatio() << "\n";
}
});
}
Platform::~Platform()
{
Item::setDumpScreenInfoFunc(nullptr);
s_platform = nullptr;
delete d;
}
Platform::Private::Private()
{
/// Out layouting engine can be used without KDDW, so by default doesn't
/// depend on Core::Separator. Here we tell the layouting that we want to use our
/// KDDW separators.
Core::Item::setCreateSeparatorFunc([](Core::LayoutingHost *host, Qt::Orientation orientation, Core::ItemBoxContainer *container) -> Core::LayoutingSeparator * {
return (new Core::Separator(host, orientation, container))->asLayoutingSeparator();
});
}
Platform *Platform::instance()
{
if (!s_platform) {
static bool guard = false;
if (guard)
return nullptr;
QScopedValueRollback<bool> guardRollback(guard, true);
// For convenience, if there's only 1 frontend supported then don't
// require the user to call initFrontend(), just do it here.
const auto types = Platform::frontendTypes();
if (types.size() == 1) {
KDDockWidgets::initFrontend(types[0]);
return s_platform;
}
}
return s_platform;
}
bool Platform::hasInstance()
{
return s_platform != nullptr;
}
bool Platform::hasActivePopup() const
{
return false;
}
bool Platform::isQtWidgets() const
{
return strcmp(name(), "qtwidgets") == 0;
}
bool Platform::isQtQuick() const
{
return strcmp(name(), "qtquick") == 0;
}
bool Platform::isQt() const
{
static const bool is = isQtWidgets() || isQtQuick();
return is;
}
int Platform::startDragDistance() const
{
const int userRequestedDistance = Config::self().startDragDistance();
if (userRequestedDistance > -1)
return userRequestedDistance;
return startDragDistance_impl();
}
int Platform::startDragDistance_impl() const
{
// Override this method in derived classes for some different value if needed
return 4;
}
/**static*/
std::vector<KDDockWidgets::FrontendType> Platform::frontendTypes()
{
std::vector<KDDockWidgets::FrontendType> types;
#ifdef DOCKS_DEVELOPER_MODE
// During development it's useful to quickly run tests only on the frontend we're developing.
// The developer can set, for example, KDDW_TEST_FRONTEND=2 to run only the QtQuick tests
bool ok = false;
const int frontendId = envVarIntValue("KDDW_TEST_FRONTEND", /*by-ref*/ ok);
if (ok) {
types.push_back(FrontendType(frontendId));
return types;
}
#endif
#ifdef KDDW_FRONTEND_QTWIDGETS
types.push_back(FrontendType::QtWidgets);
#endif
return types;
}
/*static */
bool Platform::isInitialized()
{
return s_platform != nullptr;
}
#ifdef DOCKS_TESTING_METHODS
void Platform::pauseForDebugger()
{
}
Platform::WarningObserver::~WarningObserver() = default;
#endif
#ifdef DOCKS_DEVELOPER_MODE
/*static*/
void Platform::tests_initPlatform(int &argc, char **argv, KDDockWidgets::FrontendType type, bool defaultToOffscreenQPA)
{
if (Platform::isInitialized())
return;
Platform *platform = nullptr;
switch (type) {
case FrontendType::QtWidgets:
#ifdef KDDW_FRONTEND_QTWIDGETS
platform = new QtWidgets::Platform(argc, argv, defaultToOffscreenQPA);
#endif
break;
case FrontendType::QtQuick:
break;
case FrontendType::Flutter:
KDDW_UNUSED(argc);
KDDW_UNUSED(argv);
KDDW_UNUSED(defaultToOffscreenQPA);
break;
}
if (!platform) {
KDDW_ERROR("Could not initialize platform for type={}. KDDockWidgets was built without support for it");
std::abort();
return;
}
/// Reset the default framework factory, so we can test several frontends in the same test run
Config::self().setViewFactory(Platform::instance()->createDefaultViewFactory());
/// Any additional setup
Platform::instance()->tests_initPlatform_impl();
}
/*static */
void Platform::tests_deinitPlatform()
{
auto plat = Platform::instance();
plat->d->m_inDestruction = true;
plat->tests_deinitPlatform_impl();
delete DockRegistry::self();
delete plat;
}
#endif
void Platform::installGlobalEventFilter(EventFilterInterface *filter)
{
d->m_globalEventFilters.push_back(filter);
}
void Platform::removeGlobalEventFilter(EventFilterInterface *filter)
{
d->m_globalEventFilters.erase(
std::remove(d->m_globalEventFilters.begin(), d->m_globalEventFilters.end(), filter),
d->m_globalEventFilters.end());
}
void Platform::onFloatingWindowCreated(Core::FloatingWindow *)
{
}
void Platform::onFloatingWindowDestroyed(Core::FloatingWindow *)
{
}
void Platform::onMainWindowCreated(Core::MainWindow *)
{
}
void Platform::onMainWindowDestroyed(Core::MainWindow *)
{
}
QByteArray Platform::readFile(const QString &fileName, bool &ok) const
{
ok = true;
std::ifstream file(fileName.toStdString(), std::ios::binary);
if (!file.is_open()) {
KDDW_ERROR("Failed to open {}", fileName);
ok = false;
return {};
}
QByteArray data;
file.seekg(0, std::ios::end);
std::streampos fileSize = file.tellg();
file.seekg(0, std::ios::beg);
data.resize(int(fileSize));
file.read(data.data(), fileSize);
file.close();
return data;
}
bool Platform::supportsAeroSnap() const
{
return false;
}
bool EventFilterInterface::enabled() const
{
return m_enabled;
}
void EventFilterInterface::setEnabled(bool enabled)
{
m_enabled = enabled;
}