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

364 lines
12 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.
*/
#pragma once
#include "DockWidget.h"
#include "kddockwidgets/KDDockWidgets.h"
#include "kddockwidgets/LayoutSaver.h"
#include "kddockwidgets/QtCompat_p.h"
#include "kddockwidgets/core/Controller.h"
#include "kddockwidgets/core/FocusScope.h"
#include "kddockwidgets/docks_export.h"
class TestDocks;
namespace KDDockWidgets {
class WidgetResizeHandler;
}
namespace KDDockWidgets::Core {
class LayoutingGuest;
class DropArea;
class MDILayout;
class DockWidget;
class FloatingWindow;
class MainWindow;
class Stack;
class TabBar;
class TitleBar;
class Item;
class DOCKS_EXPORT Group : public Controller, public FocusScope
{
Q_OBJECT
public:
typedef Vector<Group *> List;
explicit Group(View *parent = nullptr, FrameOptions = FrameOption_None,
int userType = 0);
~Group() override;
static Group *deserialize(const LayoutSaver::Group &);
LayoutSaver::Group serialize() const;
///@brief Adds a widget into the Group's Stack
void addTab(DockWidget *, const InitialOption & = {});
///@overload
void addTab(Group *, const InitialOption & = {});
///@overload
void addTab(FloatingWindow *floatingWindow, const InitialOption & = {});
///@brief Inserts a widget into the Group's TabWidget at @p index
void insertWidget(DockWidget *, int index, const InitialOption & = {});
///@brief removes a dockwidget from the group
void removeWidget(DockWidget *);
///@brief detaches this dock widget
FloatingWindow *detachTab(DockWidget *);
///@brief returns the index of the specified dock widget
int indexOfDockWidget(const DockWidget *);
///@brief returns the index of the current tab
int currentIndex() const;
///@brief sets the current tab index
void setCurrentTabIndex(int index);
///@brief Sets the specified dock widget to be the current tab
void setCurrentDockWidget(DockWidget *);
///@brief Inserts a dock widget into the specified index
void insertDockWidget(DockWidget *, int index);
/// @brief Returns the dock widget at @p index
DockWidget *dockWidgetAt(int index) const;
///@brief Returns the current dock widget
DockWidget *currentDockWidget() const;
/// @brief returns the number of dock widgets inside the group
int dockWidgetCount() const;
/// @brief returns the tab widget
Core::Stack *stack() const;
Core::TabBar *tabBar() const;
void updateTitleAndIcon();
void onDockWidgetTitleChanged(DockWidget *);
void updateTitleBarVisibility();
void updateFloatingActions();
bool containsMouse(Point globalPos) const;
Core::TitleBar *titleBar() const;
Core::TitleBar *actualTitleBar() const;
QString title() const;
Icon icon() const;
Vector<DockWidget *> dockWidgets() const;
bool isTheOnlyGroup() const;
///@brief Returns whether this group is overlayed on top of the MainWindow (auto-hide feature);
bool isOverlayed() const;
///@brief clears the FrameOption_IsOverlayed flag.
/// For example, if you drag a side-bar overlay, then it becomes a normal floating window
void unoverlay();
/**
* @brief Returns whether this group is floating. A floating group isn't attached to any other
* MainWindow, and if it's attached to a FloatingWindow then it's considered floating if it's
* the only group in that Window. A floating group can have multiple dock widgets (tabbed), in
* which case each DockWidget::isFloating() returns false, in which case you can use
* isInFloatingWindow() which would still return true
*/
bool isFloating() const;
/**
* @brief Returns whether this group is in a FloatingWindow, as opposed to MainWindow.
*
* After setup it's equivalent to !isInMainWindow().
*/
bool isInFloatingWindow() const;
/**
* @brief Returns whether this group is docked inside a MainWindow.
*/
bool isInMainWindow() const;
/**
* @brief returns if this widget is the central group
* MainWindow supports a mode where the middle group is persistent even if no dock widget is
* there.
*
* @return whether this widget is the central group in a main window
*/
bool isCentralGroup() const;
/// @brief Returns whether you can DND dock widgets over this group and tab into it
bool isDockable() const;
/**
* @brief whether the tab widget will always show tabs, even if there's only 1 dock widget
*
* While technically a non-floating dock widget is always tabbed, the user won't see the tabs
* as in most cases there's only 1 widget tabbed. But for the main window central group it's
* often wanted to see tabs even if there's only 1 widget, where each widget represents a
* "document".
*
* @return whether the tab widget will always show tabs, even if there's only 1 dock widget
*/
bool alwaysShowsTabs() const;
/// @brief returns whether the dockwidget @p w is inside this group
bool containsDockWidget(DockWidget *w) const;
///@brief returns the FloatingWindow this group is in, if any
FloatingWindow *floatingWindow() const;
/**
* @brief Returns the main window this group is in.
* nullptr if not inside a main window.
*/
MainWindow *mainWindow() const;
/**
* @brief Puts the Group back in its previous main window position
*
* Usually DockWidget::Private::restoreToPreviousPosition() is used, but
* when we have a floating group with tabs we just reuse the group instead of
* moving the tabbed dock widgets one by one.
*/
void restoreToPreviousPosition();
int currentTabIndex() const;
FrameOptions options() const;
bool anyNonClosable() const;
bool anyNonDockable() const;
///@brief returns whether there's 0 dock widgets. If not persistent then the Frame will delete
/// itself.
bool isEmpty() const
{
return dockWidgetCount() == 0;
}
///@brief returns whether there's only 1 dock widget.
bool hasSingleDockWidget() const
{
return dockWidgetCount() == 1;
}
///@brief returns the layout item that either contains this Frame in the layout or is a
/// placeholder
Core::Item *layoutItem() const;
///@brief For tests-only. Returns the number of Frame instances in the whole application.
static int dbg_numFrames();
/**
* @brief Returns whether a deleteLater has already been issued
*/
bool beingDeletedLater() const;
/**
* @brief returns true if tabs are visible
*
* @sa hasTabsVisibleChanged()
**/
bool hasTabsVisible() const;
Vector<QString> affinities() const;
///@brief sets the layout item that either contains this Group in the layout or is a placeholder
void setLayoutItem(Core::Item *item);
/**
* Returns the drag rect in global coordinates. This is usually the title bar rect.
* However, when using Config::Flag_HideTitleBarWhenTabsVisible it will be the tab bar
* background. Returns global coordinates.
*/
virtual Rect dragRect() const;
///@brief Returns whether all dock widgets have the specified option set
bool allDockWidgetsHave(DockWidgetOption) const;
///@brief Returns whether at least one dock widget has the specified option set
bool anyDockWidgetsHas(DockWidgetOption) const;
///@brief Returns whether all dock widgets have the specified layout saver option set
bool allDockWidgetsHave(LayoutSaverOption) const;
///@brief Returns whether at least one dock widget has the specified layout saver option set
bool anyDockWidgetsHas(LayoutSaverOption) const;
/// @brief To allow resizing the overlayed dock widget (auto-hide feature)
void setAllowedResizeSides(CursorPositions sides);
/// @brief Returns whether this group is in a MDI layout
/// Usually no, unless you're using an MDI main window
bool isMDI() const;
/// @brief Returns whether this group was created automatically just for the purpose of
/// supporting DockWidgetOption_MDINestable
bool isMDIWrapper() const;
/// @brief If this is an MDI wrapper group, return the DockWidget MDI wrapper
/// @sa isMDIWrapper
DockWidget *mdiDockWidgetWrapper() const;
/// @brief If this is an MDI wrapper group, return the DropArea MDI wrapper
/// @sa isMDIWrapper
DropArea *mdiDropAreaWrapper() const;
/// @brief If this group is an MDI wrapper, returns the MDI group. That is the group you
/// actually drag inside the MDI area
Group *mdiFrame() const;
/// @brief Returns the MDI layout. Or nullptr if this group isn't in a MDI layout
MDILayout *mdiLayout() const;
/// @brief If this group is a MDI group (isMDI() == true), returns whether it contains nested
/// dock widgets (DockWidgetOption_MDINestable)
/// @sa isMDI()
bool hasNestedMDIDockWidgets() const;
/// @brief See Core::DockWidget::userType()
int userType() const;
/// @brief Returns the resize handler. Used mostly in MDI mode.
WidgetResizeHandler *resizeHandler() const;
void renameTab(int index, const QString &);
void changeTabIcon(int index, const Icon &);
/// @brief Sets the Layout which this group is in
void setLayout(Layout *);
void onDockWidgetCountChanged();
/// @brief User requested floating window flags for when this group floats
FloatingWindowFlags requestedFloatingWindowFlags() const;
LayoutingGuest *asLayoutingGuest() const;
/// Convenience that just calls close() on all its dock widgets
/// Returns whether all dock widgets accepted the close
bool close() const;
/// Returns the group that's in the specified item
static Core::Group *fromItem(const Core::Item *);
static Core::Item *s_inFloatHack;
protected:
void isFocusedChangedCallback() override;
void focusedWidgetChangedCallback() override;
void setParentView_impl(View *parent) override;
public:
/**
* @brief Returns the minimum size of the dock widgets.
* This might be slightly smaller than Frame::minSize() due to the QTabWidget having some
* margins and tab bar.
*/
Size dockWidgetsMinSize() const;
/**
* @brief Returns the biggest combined maxSize of all dock widgets.
*
* Example:
* dock 1, max=2000x1000
* dock 2, max=3000x400
* dock3, max=
* result=3000,1000
*
* Any widget having 16777215x16777215 is ignored (represents not having a max-size,
* QWIDGETSIZE_MAX)
*/
Size biggestDockWidgetMaxSize() const;
int nonContentsHeight() const;
class Private;
Private *dptr() const;
private:
Private *const d;
bool m_inCtor = true; // Needs to be initialized early, as pointed out by UBSAN
protected:
bool m_inDtor = false;
Core::Stack *const m_stack;
Core::TabBar *const m_tabBar;
Core::TitleBar *const m_titleBar;
#ifdef DOCKS_DEVELOPER_MODE
public:
#else
private:
#endif
KDDW_DELETE_COPY_CTOR(Group)
friend class ::TestDocks;
friend class KDDockWidgets::Core::Stack;
void scheduleDeleteLater();
void createMDIResizeHandler();
void onCloseEvent(CloseEvent *);
Layout *m_layout = nullptr;
WidgetResizeHandler *m_resizeHandler = nullptr;
bool m_updatingTitleBar = false;
bool m_beingDeleted = false;
};
}