/* This file is part of KDDockWidgets. SPDX-FileCopyrightText: 2020 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. */ #pragma once #include "kddockwidgets/docks_export.h" #include "View.h" #include "kddockwidgets/KDDockWidgets.h" #include "kddockwidgets/QtCompat_p.h" #include #include namespace KDDockWidgets { namespace Core { class DelayedCall; class EventFilterInterface; struct CreateViewOptions; class ClassicDropIndicatorOverlay; class SegmentedDropIndicatorOverlay; class ViewFactory; class Window; /// @brief implements functions specific to a particular platform /// A platform can be for example qtwidgets, qtquick, etc. class DOCKS_EXPORT Platform { public: /// @brief Enum describing the graphics stack type enum class DisplayType { Other = 0, X11 = 1, Wayland = 2, QtOffscreen = 3, QtEGLFS = 4, Windows = 5 }; virtual ~Platform(); /// @brief Returns the name of the platform, only "qtwidgets" and "qtquick" virtual const char *name() const = 0; /// @brief Returns the platform singleton static Platform *instance(); /// Returns whether a Platform instance exists // Will be false at start up right before using KDDW and at shutdown after dtor runs static bool hasInstance(); /// @brief Returns whether a popup is open /// Usually not needed to override. Investigate further in case side bars aren't auto hiding virtual bool hasActivePopup() const; /// @brief Returns the focused view, if any virtual std::shared_ptr focusedView() const = 0; /// @brief Returns all windows virtual Vector> windows() const = 0; /// @brief Creates and returns the default ViewFactory virtual ViewFactory *createDefaultViewFactory() = 0; /// @brief Returns the window at the specified global coordinates virtual std::shared_ptr windowAt(Point globalPos) const = 0; /// @brief Sends the specified event to the specified view virtual void sendEvent(View *, Event *) const = 0; /// @brief Returns the screen index for the specified view or window. /// It's up to the platform to decide how screens are ordered, kddw won't care. virtual int screenNumberForView(View *) const = 0; virtual int screenNumberForWindow(std::shared_ptr) const = 0; /// @brief Returns the size of the screen where this view is in virtual Size screenSizeFor(View *) const = 0; /// @brief Returns which screen the point is in /// -1 if not found virtual int screenNumberForPoint(Point) const = 0; /// @brief Create an empty view /// For Qt this would just returns a empty QWidget or QQuickItem /// other frontends can return something as basic. virtual View *createView(Controller *, View *parent = nullptr) const = 0; /// @brief Returns whether this platform is QtWidgets bool isQtWidgets() const; /// @brief Returns whether this platform is QtQuick bool isQtQuick() const; /// @brief Returns whether this platform is Qt based bool isQt() const; /// @brief Returns how many pixels the mouse must move for a drag to start /// This is usually 4 by default (QApplication::startDragDistance() for QtWidgets) /// You can override by calling Config::setStartDragDistance(), so you don't need to create /// a new Platform class. int startDragDistance() const; /// @brief Return whether we use the global event filter based mouse grabber virtual bool usesFallbackMouseGrabber() const = 0; /// @brief Returns whether the specified global position is on top of a view /// that isn't draggable. This is needed since not the entire title bar is draggable. /// For example, clicking on the close button shouldn't start a drag. virtual bool inDisallowedDragView(Point globalPos) const = 0; /// @brief Releases the mouse grab, if any virtual void ungrabMouse() = 0; /// runs the specified all after ms /// Equivalent to QTimer::singleShot in Qt virtual void runDelayed(int ms, Core::DelayedCall *c) = 0; /** * @brief Returns whether we're processing a Event::Quit * * Used internally to know if we should let Qt close a NonClosable dock widget at shutdown time. */ virtual bool isProcessingAppQuitEvent() const = 0; /// @brief Installs a global event filter /// Events will be forwarded to the specified EventFilterInterface void installGlobalEventFilter(EventFilterInterface *); /// @brief Removes a global event filter void removeGlobalEventFilter(EventFilterInterface *); /// @brief Returns the application name /// This name will be used as title of floating dock widgets which contain more than 1 group virtual QString applicationName() const = 0; /// @brief Sets the mouse cursor to the specified shape, this has an application-wide effect /// Call restoreMouseCursor() to set the previous cursor shape /// @param discardLast If true, then the previous shape is discarded instead of /// being saved into a stack for restoreMouseCursor() virtual void setMouseCursor(Qt::CursorShape, bool discardLast = false) = 0; /// @brief Undoes the call to setMouseCursor() virtual void restoreMouseCursor() = 0; /// @brief Returns the type of graphics stack being used virtual DisplayType displayType() const = 0; /// @brief Returns whether the left mouse button is pressed virtual bool isLeftMouseButtonPressed() const = 0; /// @brief Returns all available screens virtual Vector> screens() const = 0; virtual std::shared_ptr primaryScreen() const = 0; /// @brief For non-C++, managed languages (having a VM) prints a non-native back-trace /// For example, the flutter frontend implements this to get a dart backtrace /// Used for debugging only. Can be called by gdb. virtual void dumpManagedBacktrace() { } /// @brief Called when a floating window is created. /// Overridden by flutter, so it can create a window virtual void onFloatingWindowCreated(Core::FloatingWindow *); /// @brief Called when a floating window is created. /// Overridden by flutter, so it can destroy the window virtual void onFloatingWindowDestroyed(Core::FloatingWindow *); /// @brief Called when a main window is created. /// Overridden by flutter, so it can create a window /// Used by tests only. In real life users will instantiate a MainWindow in dart directly. virtual void onMainWindowCreated(Core::MainWindow *); /// @brief Called when a main window is created. /// Overridden by flutter, so it can destroy the window virtual void onMainWindowDestroyed(Core::MainWindow *); /// Returns the mouse cursor position in screen coordinates virtual Point cursorPos() const = 0; /// Sets the mouse cursor position in screen coordinates virtual void setCursorPos(Point) = 0; /// Reads the specified and returns its content /// The default implementation uses std::ifstream while the Qt implementation /// uses QFile, as it needs to support QRC virtual QByteArray readFile(const QString &, bool &ok) const; /// Only supported on Qt, for windows virtual bool supportsAeroSnap() const; /// Called when the Config::viewFactory has changed virtual void onViewFactoryChanged() { } /// @brief list the list of frontend types supported by this build static std::vector frontendTypes(); /// @brief Returns whether the Platform was already initialized static bool isInitialized(); #if defined(DOCKS_DEVELOPER_MODE) && !defined(DARTAGNAN_BINDINGS_RUN) /// Big timeout since lsan on github actions is very slow #define DEFAULT_TIMEOUT 20000 // Stuff required by the tests only and not used by dart bindings. /// @brief halts the test during the specified number of milliseconds /// The event loop keeps running. Use this for debugging purposes so you can interact with your /// test and see what's going on virtual bool tests_wait(int ms) const = 0; /// @brief Waits for the specified view to receive a resize event /// Returns true if the view was resized until timeout was reached virtual bool tests_waitForResize(View *, int timeout = DEFAULT_TIMEOUT) const = 0; virtual bool tests_waitForResize(Controller *, int timeout = DEFAULT_TIMEOUT) const = 0; virtual bool tests_waitForDeleted(View *, int timeout = DEFAULT_TIMEOUT) const = 0; virtual bool tests_waitForDeleted(Controller *, int timeout = DEFAULT_TIMEOUT) const = 0; /// @brief Waits for the specified window to be active (have the keyboard focus) /// Window::isActive() should return true /// @sa Window::isActive() virtual bool tests_waitForWindowActive(std::shared_ptr, int timeout = DEFAULT_TIMEOUT) const = 0; /// @brief Waits for the specified view to receive the specified event /// Returns true if the view received said event until timeout was reached virtual bool tests_waitForEvent(Core::Object *w, Event::Type type, int timeout = DEFAULT_TIMEOUT) const = 0; virtual bool tests_waitForEvent(View *, Event::Type type, int timeout = DEFAULT_TIMEOUT) const = 0; virtual bool tests_waitForEvent(std::shared_ptr, Event::Type type, int timeout = DEFAULT_TIMEOUT) const = 0; virtual void tests_doubleClickOn(Point globalPos, View *receiver) = 0; virtual void tests_doubleClickOn(Point globalPos, std::shared_ptr receiver) = 0; virtual void tests_pressOn(Point globalPos, View *receiver) = 0; virtual void tests_pressOn(Point globalPos, std::shared_ptr receiver) = 0; virtual bool tests_releaseOn(Point globalPos, View *receiver) = 0; virtual bool tests_mouseMove(Point globalPos, View *receiver) = 0; /// @brief Creates a Window. For the sole purpose of unit-testing Window. /// The created window should be visible. virtual std::shared_ptr tests_createWindow() = 0; /// @brief Creates the platform. Called by the tests at startup. /// For any custom behaviour in your derived Platform override tests_initPlatform_impl() static void tests_initPlatform(int &argc, char *argv[], KDDockWidgets::FrontendType, bool defaultToOffscreenQPA = true); /// @brief Deletes the platform. Called at end of tests. /// For any custom behaviour in your derived Platform override tests_deinitPlatform_impl() static void tests_deinitPlatform(); std::string m_expectedWarning; #endif #ifdef DOCKS_TESTING_METHODS class WarningObserver { KDDW_DELETE_COPY_CTOR(WarningObserver) public: WarningObserver() = default; virtual ~WarningObserver(); virtual void onFatal() = 0; }; /// @brief Creates a view with the specified parent /// If the parent is null then a new window is created and the returned view will be the root /// view virtual View *tests_createView(CreateViewOptions, View *parent = nullptr) = 0; /// @brief Returns a view that can have keyboard focus /// For example a line edit. This is used to for testing focus related features. virtual View *tests_createFocusableView(CreateViewOptions, View *parent = nullptr) = 0; /// @brief Returns a view that rejects close events virtual View *tests_createNonClosableView(View *parent = nullptr) = 0; virtual void installMessageHandler() = 0; virtual void uninstallMessageHandler() = 0; /// @brief Creates a main window. This is not API that the user will use, but used /// internally by some tools that need a main window virtual Core::MainWindow * createMainWindow(const QString &uniqueName, CreateViewOptions, MainWindowOptions options = MainWindowOption_HasCentralGroup, View *parent = nullptr, Qt::WindowFlags = {}) const = 0; virtual void pauseForDebugger(); protected: /// @brief Implement any needed initializations before tests starting to run, if any /// Override in derived classes for custom behavior. virtual void tests_initPlatform_impl() { } /// @brief Implement any needed cleanup after the tests runs, if any /// Override in derived classes for custom behavior. virtual void tests_deinitPlatform_impl() { } #endif public: class Private; Private *const d; protected: virtual int startDragDistance_impl() const; Platform(); Platform(const Platform &) = delete; Platform &operator=(const Platform &) = delete; }; #if defined(DOCKS_DEVELOPER_MODE) #if !defined(DARTAGNAN_BINDINGS_RUN) struct SetExpectedWarning { explicit SetExpectedWarning(const std::string &s) { if (!s.empty()) Platform::instance()->m_expectedWarning = s; } ~SetExpectedWarning() { Platform::instance()->m_expectedWarning.clear(); } KDDW_DELETE_COPY_CTOR(SetExpectedWarning) }; #endif // bindings #endif // dev-mode #if defined(DOCKS_TESTING_METHODS) struct CreateViewOptions { bool isVisible = false; Size sizeHint = {}; Size minSize = { 0, 0 }; Size maxSize = Size(16777215, 16777215); Size size = { 1000, 1000 }; bool createWindow = false; Size getMinSize() const { return minSize; } Size getMaxSize() const { return maxSize; } Size getSize() const { return size; } }; #endif } // Core } // KDDockWidgets