/* This file is part of KDDockWidgets. SPDX-FileCopyrightText: 2019-2022 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 The DockWidget base-class that's shared between QtWidgets and QtQuick stack. * * @author Sérgio Martins \ */ #ifndef KD_DOCKWIDGET_BASE_H #define KD_DOCKWIDGET_BASE_H #include "docks_export.h" #include "KDDockWidgets.h" #include "QWidgetAdapter.h" #include "LayoutSaver.h" #include #include // clazy:excludeall=ctor-missing-parent-argument class TestDocks; namespace KDDockWidgets { class Frame; class FloatingWindow; class DragController; class DockRegistry; class LayoutSaver; class TabWidget; class TitleBar; class MainWindowBase; class StateDragging; class FrameQuick; class DockWidgetQuick; class LayoutWidget; /** * @brief The DockWidget base-class. DockWidget and DockWidgetBase are only * split in two so we can share some code with the QtQuick implementation, * which also derives from DockWidgetBase. * * Do not use instantiate directly in user code. Use DockWidget instead. */ #ifndef PYTHON_BINDINGS // Pyside bug: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1327 class DOCKS_EXPORT DockWidgetBase : public QWidgetAdapter #else class DOCKS_EXPORT DockWidgetBase : public QWidget #endif { Q_OBJECT Q_PROPERTY(bool isFocused READ isFocused NOTIFY isFocusedChanged) Q_PROPERTY(bool isFloating READ isFloating WRITE setFloating NOTIFY isFloatingChanged) Q_PROPERTY(QString uniqueName READ uniqueName CONSTANT) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(QObject *widget READ widget NOTIFY widgetChanged) Q_PROPERTY(KDDockWidgets::DockWidgetBase::Options options READ options WRITE setOptions NOTIFY optionsChanged) public: typedef QVector List; ///@brief DockWidget options to pass at construction time enum Option { Option_None = 0, ///< No option, the default Option_NotClosable = 1, ///< The DockWidget can't be closed on the [x], only programmatically Option_NotDockable = 2, ///< The DockWidget can't be docked, it's always floating Option_DeleteOnClose = 4, ///< Deletes the DockWidget when closed Option_MDINestable = 8 ///< EXPERIMENTAL. When this dock widget is being shown in a MDI area it will also allow other dock widgets to be dropped to its sides and tabbed /// Usually Each MDI "window" corresponds to one DockWidget, with this option each "window" will have a layout with 1 or more dock widgets /// Run "kddockwidgets_mdi_with_docking_example -n" to see it in action }; Q_DECLARE_FLAGS(Options, Option) Q_ENUM(Options) /// @brief Options which will affect LayoutSaver save/restore enum class LayoutSaverOption { None = 0, ///< Just use the defaults Skip = 1, ///< The dock widget won't participate in save/restore. Currently only available for floating windows. }; Q_DECLARE_FLAGS(LayoutSaverOptions, LayoutSaverOption) enum class IconPlace { TitleBar = 1, TabBar = 2, ToggleAction = 4, All = ToggleAction | TitleBar | TabBar }; Q_ENUM(IconPlace) Q_DECLARE_FLAGS(IconPlaces, IconPlace) /** * @brief constructs a new DockWidget * @param uniqueName the name of the dockwidget, should be unique. Use title for user visible text. * @param options the options controlling certain behaviours * @param layoutSaverOptions the options to control save/restore * * There's no parent argument. The DockWidget is either parented to FloatingWindow or MainWindow * when visible, or stays without a parent when hidden. */ explicit DockWidgetBase(const QString &uniqueName, Options options = KDDockWidgets::DockWidgetBase::Options(), LayoutSaverOptions layoutSaverOptions = KDDockWidgets::DockWidgetBase::LayoutSaverOptions()); ///@brief destructor ~DockWidgetBase() override; /** * @brief docks @p other widget into this one. Tabs will be shown if not already. * @param other The other dock widget to dock into this one. * @param initialOption Allows to specify an InitialOption. Which is useful to add the dock widget * as hidden, recording only a placeholder in the tab. So it's restored to tabbed when eventually * shown. * @sa MainWindow::addDockWidget(), DockWidget::addDockWidgetToContainingWindow() */ Q_INVOKABLE void addDockWidgetAsTab(KDDockWidgets::DockWidgetBase *other, KDDockWidgets::InitialOption initialOption = {}); /** * @brief docks @p other widget into the window that contains this one. * Equivalent to MainWindow::addDockWidget() with the difference * that it also supports the case where the top-level window is a * FloatingWindow instead of MainWindow. * * @param other The other dock widget to dock into the window. * @param location The location to dock. * @param relativeTo The dock widget that the @p location is relative to. If null then the window is considered * @param initialOption Allows to specify some extra options that are used while docking. * @sa MainWindow::addDockWidget(), DockWidget::addDockWidgetAsTab() */ Q_INVOKABLE void addDockWidgetToContainingWindow(KDDockWidgets::DockWidgetBase *other, KDDockWidgets::Location location, KDDockWidgets::DockWidgetBase *relativeTo = nullptr, KDDockWidgets::InitialOption initialOption = {}); /** * @brief sets the widget which this dock widget hosts. * @param widget the widget to show inside this dock widget. Must not be null. * * Ownership for @p widget is transferred to DockWidgetBase. * Ownsership for any previously existing widget is transferred back to the user. Meaning if you * call setWidget(A) followed by setWidget(B) then A will have to be deleted by you, while B is * owned by the dock widget. */ virtual void setWidget(QWidgetOrQuick *widget); /** * @brief returns the widget which this dock widget hosts */ QWidgetOrQuick *widget() const; /** * @brief Returns whether the dock widget is floating. * Floating means it's not docked and has a window of its own. * Note that if you dock a floating dock widget into another floating one * then they don't count as floating anymore, as they are * side-by-side (or tabbed). */ bool isFloating() const; /** * @brief setter to make the dock widget float or dock. * @param floats If true makes the dock widget float, otherwise docks it. * * Returns true if the request was accomplished */ bool setFloating(bool floats); /** * @brief Returns the QAction that allows to hide/show the dock widget * Useful to put in menus. */ Q_INVOKABLE QAction *toggleAction() const; /** * @brief Returns the QAction that allows to dock/undock the dock widget * Useful to put in menus. */ Q_INVOKABLE QAction *floatAction() const; /** * @brief the dock widget's unique name. * @internal */ QString uniqueName() const; /** * @brief Returns the dock widget's title. * This title is visible in title bars and tab bars. * @sa setTitle */ QString title() const; /** * @brief setter for the dock widget's title * @param title the dock widget's new title * @sa setTitle */ void setTitle(const QString &title); /** * @brief Returns the size of the dock widget's parent frame. * * This will always be bigger than the DockWidget's size, as there's margins and a title bar. * Also, a frame can contain more than 1 dock widget (tabbed), meaning the geometry will account * for the tab bar and title bar. * * The position of the rect is in layout coordinates. 0,0 is the top-left of the layout * holding the widgets. */ QRect frameGeometry() const; /** * @brief Returns the dock widget's options which control behaviour. * @sa setOptions(), optionsChanged() */ Options options() const; /// @brief returns the per-dockwidget options which will affect LayoutSaver /// These are the options which were passed to the constructor KDDockWidgets::DockWidgetBase::LayoutSaverOptions layoutSaverOptions() const; /** * @brief Setter for the options. * Only Option_NotClosable is allowed to change after construction. For the other options use * the constructor only. * * @sa options(), optionsChanged() */ void setOptions(Options); /** * @brief returns if this dock widget is tabbed into another * * Technically a docked DockWidget always lives in a tab widget, but from the user's perspective * it's not tabbed when there's only 1 dock widget, as there are no tabs displayed. Unless * the frame is using Option_AlwaysShowsTabs, in which case this method will return true regardless * if being the single one. */ bool isTabbed() const; /** * @brief Returns true if this dock widget is the current one in the tab * widget that contains it. If the dock widget is alone then true is * returned, as in this case there will also be a tab widget even * though it's invisible. */ bool isCurrentTab() const; /** * @brief Makes this dock widget current in its tab group. */ Q_INVOKABLE void setAsCurrentTab(); /** * @brief Returns the tab index this dock widget occupies * Note that dock widgets are almost always tabbed, even if you don't see the tab bar. * A single floating dock widget is still tabbed on a tab widget with a single tab. */ int tabIndex() const; /** * @brief Returns the index of the current tab of the tab group this dock widget is in. */ int currentTabIndex() const; /** * @brief Sets an icon to show on title bars and tab bars. * @param places Specifies where the icon will be shown (TitleBar, TabBar, ToggleAction, or All) * * By default there's no icon set. * * @sa icon() */ void setIcon(const QIcon &icon, IconPlaces places = IconPlace::All); /** * @brief Returns the dock widget's titlebar, tabbar, or toggle action icon (depending on the passed @p place) * * By default it's null. * * @sa setIcon() */ QIcon icon(IconPlace place = IconPlace::TitleBar) const; /** * @brief Like QWidget::close() but the hosted widget won't be asked if we * should close. */ Q_INVOKABLE void forceClose(); /** * @brief Returns this dock widget's title bar. * * Note that several dock widgets can have the same title bar, in case they are tabbed together. * Hidden dock widgets have no associated title bar. */ TitleBar *titleBar() const; /** * @brief Returns whether this dock widget is open. * Equivalent to calling toggleAction().isChecked() or isVisible() */ Q_INVOKABLE bool isOpen() const; /** * @brief Sets the affinity names. Dock widgets can only dock into dock widgets of the same affinity. * * By default the affinity is empty and a dock widget can dock into any main window and into any * floating window. Usually you won't ever need to call * this function, unless you have requirements where certain dock widgets can only dock into * certain other dock widgets and main windows. @sa MainWindowBase::setAffinities(). * * Note: Call this function right after creating your dock widget, before adding to a main window and * before restoring any layout. * * Note: Currently you can only call this function once, to keep the code simple and avoid * edge cases. This will only be changed if a good use case comes up that requires changing * affinities multiple times. * * @p names the affinity names */ void setAffinities(const QStringList &); /// @deprecated @overload /// @param name the affinity name void setAffinityName(const QString &name); /** * @brief Returns the affinity name. Empty by default. */ QStringList affinities() const; /// @brief Equivalent to QWidget::show(), but it's optimized to reduce flickering on some platforms Q_INVOKABLE void show(); /// @brief Brings the dock widget to the front. /// /// This means: /// - If the dock widget is tabbed with other dock widgets but its tab is not current, it's made current. /// - If the dock widget is floating, QWindow::raise() is called. /// /// This only applies if the dock widget is already open. If closed, does nothing. Q_INVOKABLE void raise(); /** * @brief Returns whether widget() is a KDDockWidget::MainWindow * * This function doesn't have anything to do with this dock widget being inside a main window or * not, but rather the inverse concept. It's not a very popular usage, but some applications * want to dock a main window into another main window. This is done by putting it into a * dock widget first. * * See also kddockwidgets_example -j */ bool isMainWindow() const; /** * @brief Returns whether this dock widget is docked into a main window (as opposed to floating) * * Note that isFloating() returning false might either mean the dock widget is docked into a * main window or into a floating window (groupped/nested with other dock widgets. Use this function * then if you need to disambiguate */ bool isInMainWindow() const; /// @brief Returns the main window this dock widget is in. nullptr if it's not inside a main window /// Also returns nullptr if it's minimized to a sidebar MainWindowBase *mainWindow() const; ///@brief Returns whether This or any child of this dock widget is focused /// Not to be confused with QWidget::hasFocus(), which just refers to 1 widget. This includes /// variant includes children. ///@sa isFocusedChanged() bool isFocused() const; /** * @brief Minimizes this dock widget to the MainWindow's side-bar. * * It will be undocked from current layout. It's previous docked position will be remembered. * * This action is only available if the dock widget is docked into a MainWindow. * The dockwidget will initially be visible and overlayed on top of the current layout (this is * the auto-hide feature). */ Q_INVOKABLE void moveToSideBar(); /// @brief Returns whether this dock widget is overlayed from the side-bar. /// /// This is only relevant when using the auto-hide and side-bar feature. /// Not to be confused with "floating", which means top-level window. bool isOverlayed() const; ///@brief Returns whether this dock widget is in a side bar, and which. /// SideBarLocation::None is returned if it's not in a sidebar. /// This is only relevant when using the auto-hide and side-bar feature. /// @sa isInSideBar SideBarLocation sideBarLocation() const; /// @brief Returns where this dockwidget is in a sidebar /// Similar to sideBarLocation(), but returns a bool bool isInSideBar() const; /// @brief Returns whether this floating dock widget knows its previous docked location /// Result only makes sense if it's floating. /// /// When you call dockWidget->setFloating(false) it will only dock if it knows where to. bool hasPreviousDockedLocation() const; /// @brief returns the last size the widget has when overlayed /// Empty otherwise QSize lastOverlayedSize() const; /// @brief Returns a dock widget by its name /// This is the same name you passed to DockWidget CTOR. /// nullptr is returned if the dock widget isn't found. static DockWidgetBase *byName(const QString &uniqueName); /// @brief Returns whether this widget has the LayoutSaverOption::Skip flag bool skipsRestore() const; /// @brief If this dock widget is floating, then sets its geometry to @p geo. /// /// If this dock widget is hidden then it stores the geometry so it can be used the next /// time it becomes floating. /// /// This is just convenience, equivalent to calling window()->setGeometry(rect), with the /// added bonus of remembering the requested geometry in case it's still hidden. void setFloatingGeometry(QRect geo); ///@brief Allows the user to set a type on this dock widget /// The type is opaque and will not be interpreted by KDDockWidgets. /// This type is passed to FrameWorkWidgetFactory::createTitleBar(), which the user can override /// and return different TitleBar subclasses, depending on the type. void setUserType(int userType); int userType() const; /// @brief Sets this dock widgets position to pos within the MDI layout /// This only applies if the main window is in MDI mode, which it is not by default void setMDIPosition(QPoint pos); /// @brief like setMDIPosition(), but for the size. void setMDISize(QSize size); /// @brief like setMDIPosition(), but for the Z /// only implemented for QtQuick void setMDIZ(int z); ///@brief Returns whether this dock widget is the main window persistent central widget /// This only applies when using MainWindowOption_HasCentralWidget bool isPersistentCentralDockWidget() const; /// @brief Sets the desired floating window flags, in case the defaults aren't desired. /// By default KDDW will use the flags specified in KDDockWidgets::Config to influence /// many aspects regarding windows (minimize,maximize support for example). /// You can use this function to override those "global flags" and specify per-dock widget /// flags. Be sure to not call this before the floating window has already been created. /// It's suggested to call it before the dock widget is shown. void setFloatingWindowFlags(FloatingWindowFlags); KDDockWidgets::FloatingWindowFlags floatingWindowFlags() const; Q_SIGNALS: #ifdef KDDOCKWIDGETS_QTWIDGETS ///@brief signal emitted when the parent changed /// QtQuick already has QQuickItem::parentChanged(), so add it only for QtWidgets here. void parentChanged(); #endif ///@brief signal emitted when the DockWidget is shown. As in QEvent::Show. void shown(); ///@brief signal emitted when the DockWidget is hidden. As in QEvent::Hide. void hidden(); ///@brief signal emitted when the icon changed void iconChanged(); ///@brief signal emitted when the title changed ///@param title the new title void titleChanged(const QString &title); ///@brief emitted when the hosted widget changed void widgetChanged(KDDockWidgets::QWidgetOrQuick *); ///@brief emitted when the options change ///@sa setOptions(), options() void optionsChanged(KDDockWidgets::DockWidgetBase::Options); ///@brief emitted when isFocused changes ///@sa isFocused void isFocusedChanged(bool); ///@brief emitted when isOverlayed changes ///@sa isOverlayed void isOverlayedChanged(bool); ///@brief emitted when isFloating changes void isFloatingChanged(bool); ///@brief emitted when this dock widget is removed from a side-bar. /// Only relevant for the auto-hide/sidebar feature void removedFromSideBar(); ///@brief Emitted when the top-level window this dock widget is in is activated or deactivated /// This is convenience to replace tracking dockWidget->window(), since the window changes when /// docking and undocking /// /// It's called 'aboutTo' because it's done in an event filter and the target window doesn't /// have it's 'activeWindow' property updated yet at this point. void windowActiveAboutToChange(bool activated); ///@brief Emitted when the title bar that serves this dock widget changes void actualTitleBarChanged(); /// @brief Emitted when this dock widget is about to be deleted due to Option_DeleteOnClose void aboutToDeleteOnClose(); protected: void onParentChanged(); void onShown(bool spontaneous); void onHidden(bool spontaneous); #ifndef PYTHON_BINDINGS // Pyside bug: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1327 void onCloseEvent(QCloseEvent *e) override; bool onResize(QSize newSize) override; #endif #if defined(DOCKS_DEVELOPER_MODE) public: #else private: #endif Q_DISABLE_COPY(DockWidgetBase) friend class MultiSplitter; friend class LayoutWidget; friend class MDILayoutWidget; friend class FloatingWindow; friend class Frame; friend class DropArea; friend class ::TestDocks; friend class StateDragging; friend class KDDockWidgets::TabWidget; friend class KDDockWidgets::TitleBar; friend class KDDockWidgets::DragController; friend class KDDockWidgets::DockRegistry; friend class KDDockWidgets::LayoutSaver; friend class KDDockWidgets::MainWindowBase; friend class KDDockWidgets::FrameQuick; friend class KDDockWidgets::DockWidgetQuick; /** * @brief Constructs a dock widget from its serialized form. * @internal */ static DockWidgetBase *deserialize(const std::shared_ptr &); class Private; Private *const d; Private *dptr() const; }; } Q_DECLARE_METATYPE(KDDockWidgets::Location) #endif