/* This file is part of KDDockWidgets. SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Sérgio Martins SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only Contact KDAB at for commercial licensing options. */ /** * @file * @brief Application wide config to tune certain framework behaviors. * * @author Sérgio Martins \ */ #include "Config.h" #include "core/layouting/Item_p.h" #include "core/DockRegistry.h" #include "core/DockRegistry_p.h" #include "core/Utils_p.h" #include "core/DragController_p.h" #include "core/ViewFactory.h" #include "core/Separator.h" #include "core/Platform.h" #include "core/View.h" #include "core/Logging_p.h" #include #include using namespace KDDockWidgets::Core; namespace KDDockWidgets { static ViewFactory *createDefaultViewFactory() { if (auto platform = Platform::instance()) return platform->createDefaultViewFactory(); KDDW_ERROR("No Platform found. Forgot to call KDDockWidgets::initFrontend() ?"); std::terminate(); return nullptr; } class Config::Private { public: Private() : m_viewFactory(createDefaultViewFactory()) { } ~Private() { delete m_viewFactory; } void fixFlags(); DockWidgetFactoryFunc m_dockWidgetFactoryFunc = nullptr; MainWindowFactoryFunc m_mainWindowFactoryFunc = nullptr; DropIndicatorAllowedFunc m_dropIndicatorAllowedFunc = nullptr; DragAboutToStartFunc m_dragAboutToStartFunc = nullptr; DragEndedFunc m_dragEndedFunc = nullptr; DockWidgetTabIndexOverrideFunc m_restorePlaceholderTabIndexOverride = nullptr; ViewFactory *m_viewFactory = nullptr; Flags m_flags = Flag_Default; MDIFlags m_mdiFlags = MDIFlag_None; InternalFlags m_internalFlags = InternalFlag_None; CustomizableWidgets m_disabledPaintEvents = CustomizableWidget_None; double m_draggedWindowOpacity = std::numeric_limits::quiet_NaN(); bool m_transparencyOnlyOverDropIndicator = false; int m_mdiPopupThreshold = 250; int m_startDragDistance = -1; bool m_dropIndicatorsInhibited = false; bool m_layoutSaverStrictMode = false; bool m_showTabsAtBottom = false; }; Config::Config() : d(new Private()) { d->fixFlags(); } Config &Config::self() { static Config config; return config; } Config::~Config() { delete d; if (Platform::isInitialized()) { delete Platform::instance(); } } Config::Flags Config::flags() const { return d->m_flags; } Config::MDIFlags Config::mdiFlags() const { return d->m_mdiFlags; } void Config::setMDIFlags(MDIFlags flags) { d->m_mdiFlags = flags; } void Config::setFlags(Flags f) { // Most flags aren't allowed to be changed at runtime. // Some are. More can be supported but they need to be examined in a case-by-case // basis. static const Flags mutableFlags = Flag::Flag_AutoHideAsTabGroups; const Flags changedFlags = f ^ d->m_flags; const bool nonMutableFlagsChanged = (changedFlags & ~mutableFlags); if (nonMutableFlagsChanged) { auto dr = DockRegistry::self(); if (!dr->isEmpty(/*excludeBeingDeleted=*/true)) { std::cerr << "Config::setFlags: " << "Only use this function at startup before creating any DockWidget or MainWindow" << "; These are already created: " << dr->mainWindowsNames().size() << dr->dockWidgetNames().size() << dr->floatingWindows().size() << "\n"; return; } } d->m_flags = f; d->fixFlags(); } /** static*/ bool Config::hasFlag(Flag flag) { return (Config::self().flags() & flag) == flag; } /** static*/ bool Config::hasMDIFlag(MDIFlag flag) { return (Config::self().mdiFlags() & flag) == flag; } void Config::setDockWidgetFactoryFunc(DockWidgetFactoryFunc func) { d->m_dockWidgetFactoryFunc = func; } DockWidgetFactoryFunc Config::dockWidgetFactoryFunc() const { return d->m_dockWidgetFactoryFunc; } void Config::setMainWindowFactoryFunc(MainWindowFactoryFunc func) { d->m_mainWindowFactoryFunc = func; } MainWindowFactoryFunc Config::mainWindowFactoryFunc() const { return d->m_mainWindowFactoryFunc; } void Config::setViewFactory(ViewFactory *factory) { if (factory == d->m_viewFactory) return; assert(factory); delete d->m_viewFactory; d->m_viewFactory = factory; Core::Platform::instance()->onViewFactoryChanged(); } ViewFactory *Config::viewFactory() const { return d->m_viewFactory; } int Config::separatorThickness() const { return Item::separatorThickness; } int Config::layoutSpacing() const { return Item::layoutSpacing; } void Config::setSeparatorThickness(int value) { if (!DockRegistry::self()->isEmpty(/*excludeBeingDeleted=*/true)) { std::cerr << "Config::setSeparatorThickness: Only use this function at startup before creating any DockWidget or MainWindow\n"; return; } if (value < 0 || value >= 100) { std::cerr << "Config::setSeparatorThickness: Invalid value" << value << "\n"; return; } Item::separatorThickness = value; Item::layoutSpacing = value; } void Config::setLayoutSpacing(int value) { if (!DockRegistry::self()->isEmpty(/*excludeBeingDeleted=*/true)) { std::cerr << "Config::setLayoutSpacing: Only use this function at startup before creating any DockWidget or MainWindow\n"; return; } if (value < 0 || value >= 100) { std::cerr << "Config::setLayoutSpacing: Invalid value" << value << "\n"; return; } Item::layoutSpacing = value; } void Config::setDraggedWindowOpacity(double opacity) { d->m_draggedWindowOpacity = opacity; } void Config::setTransparencyOnlyOverDropIndicator(bool only) { d->m_transparencyOnlyOverDropIndicator = only; } double Config::draggedWindowOpacity() const { return d->m_draggedWindowOpacity; } bool Config::transparencyOnlyOverDropIndicator() const { return d->m_transparencyOnlyOverDropIndicator; } void Config::setDropIndicatorAllowedFunc(DropIndicatorAllowedFunc func) { d->m_dropIndicatorAllowedFunc = func; } DropIndicatorAllowedFunc Config::dropIndicatorAllowedFunc() const { return d->m_dropIndicatorAllowedFunc; } void Config::setDragAboutToStartFunc(DragAboutToStartFunc func) { d->m_dragAboutToStartFunc = func; } DragAboutToStartFunc Config::dragAboutToStartFunc() const { return d->m_dragAboutToStartFunc; } void Config::setDragEndedFunc(DragEndedFunc func) { d->m_dragEndedFunc = func; } DragEndedFunc Config::dragEndedFunc() const { return d->m_dragEndedFunc; } void Config::setDockWidgetTabIndexOverrideFunc(DockWidgetTabIndexOverrideFunc func) { d->m_restorePlaceholderTabIndexOverride = func; } DockWidgetTabIndexOverrideFunc Config::dockWidgetTabIndexOverrideFunc() const { return d->m_restorePlaceholderTabIndexOverride; } void Config::setAbsoluteWidgetMinSize(Size size) { if (!DockRegistry::self()->isEmpty(/*excludeBeingDeleted=*/false)) { std::cerr << "Config::setAbsoluteWidgetMinSize: Only use this function at startup before creating any DockWidget or MainWindow\n"; return; } Item::hardcodedMinimumSize = size; } Size Config::absoluteWidgetMinSize() const { return Item::hardcodedMinimumSize; } void Config::setAbsoluteWidgetMaxSize(Size size) { if (!DockRegistry::self()->isEmpty(/*excludeBeingDeleted=*/false)) { std::cerr << "Config::setAbsoluteWidgetMinSize: Only use this function at startup before creating any DockWidget or MainWindow\n"; return; } Item::hardcodedMaximumSize = size; } Size Config::absoluteWidgetMaxSize() const { return Item::hardcodedMaximumSize; } void Config::setTabsAtBottom(bool show) { d->m_showTabsAtBottom = show; } bool Config::tabsAtBottom() const { return d->m_showTabsAtBottom; } Config::InternalFlags Config::internalFlags() const { return d->m_internalFlags; } void Config::setInternalFlags(InternalFlags flags) { d->m_internalFlags = flags; } void Config::Private::fixFlags() { if (Platform::instance()->supportsAeroSnap()) { // Unconditional now m_flags |= Flag_AeroSnapWithClientDecos; } else { m_flags = m_flags & ~Flag_AeroSnapWithClientDecos; } // These are mutually exclusive: if ((m_flags & Flag_AeroSnapWithClientDecos) && (m_flags & Flag_NativeTitleBar)) { // We're either using native or client decorations, let's use native. m_flags = m_flags & ~Flag_AeroSnapWithClientDecos; } #if defined(Q_OS_LINUX) if (KDDockWidgets::isWayland()) { // Native title bar is forced on Wayland. Needed for moving the window. // The inner KDDW title bar is used for DnD. m_flags |= Flag_NativeTitleBar; } else { // On Linux, dragging the title bar of a window doesn't generate NonClientMouseEvents // at least with KWin anyway. We can make this more granular and allow it for other // X11 window managers m_flags = m_flags & ~Flag_NativeTitleBar; } #endif #if (!defined(KDDW_FRONTEND_QT_WINDOWS) && !defined(Q_OS_MACOS)) // QtQuick doesn't support AeroSnap yet. Some problem with the native events not being // received... m_flags = m_flags & ~Flag_AeroSnapWithClientDecos; #endif #if defined(DOCKS_DEVELOPER_MODE) // We allow to disable aero-snap during development if (m_internalFlags & InternalFlag_NoAeroSnap) { // The only way to disable AeroSnap m_flags = m_flags & ~Flag_AeroSnapWithClientDecos; } #endif if (m_flags & Flag_DontUseUtilityFloatingWindows) { m_internalFlags |= InternalFlag_DontUseParentForFloatingWindows; m_internalFlags |= InternalFlag_DontUseQtToolWindowsForFloatingWindows; } if (m_flags & Flag_ShowButtonsOnTabBarIfTitleBarHidden) { // Flag_ShowButtonsOnTabBarIfTitleBarHidden doesn't make sense if used alone m_flags |= Flag_HideTitleBarWhenTabsVisible; } } void Config::setDisabledPaintEvents(CustomizableWidgets widgets) { d->m_disabledPaintEvents = widgets; } Config::CustomizableWidgets Config::disabledPaintEvents() const { return d->m_disabledPaintEvents; } void Config::setMDIPopupThreshold(int threshold) { d->m_mdiPopupThreshold = threshold; } int Config::mdiPopupThreshold() const { return d->m_mdiPopupThreshold; } void Config::setDropIndicatorsInhibited(bool inhibit) const { if (d->m_dropIndicatorsInhibited != inhibit) { d->m_dropIndicatorsInhibited = inhibit; DockRegistry::self()->dptr()->dropIndicatorsInhibitedChanged.emit(inhibit); } } bool Config::dropIndicatorsInhibited() const { return d->m_dropIndicatorsInhibited; } void Config::setStartDragDistance(int pixels) { d->m_startDragDistance = pixels; } int Config::startDragDistance() const { return d->m_startDragDistance; } void Config::printDebug() { std::cerr << "Flags: " << d->m_flags << d->m_internalFlags << "\n"; } void Config::setLayoutSaverStrictMode(bool is) { d->m_layoutSaverStrictMode = is; } bool Config::layoutSaverUsesStrictMode() const { return d->m_layoutSaverStrictMode; } }