Compare commits
124 Commits
fix-python
...
backup/lay
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b460237bd | ||
|
|
6b33e000f5 | ||
|
|
2dcc50aaad | ||
|
|
4f4f101b28 | ||
|
|
52c3827e5f | ||
|
|
3b02c6f7cd | ||
|
|
d1c7a039f1 | ||
|
|
bbbc54a704 | ||
|
|
f8dbd51817 | ||
|
|
477b42b4a9 | ||
|
|
f8c0c28717 | ||
|
|
fccb6f0cb2 | ||
|
|
32ff511b33 | ||
|
|
03572617d4 | ||
|
|
24e893dfd7 | ||
|
|
fcdf70ed60 | ||
|
|
948b736d95 | ||
|
|
6cffb6fbb4 | ||
|
|
f58d6285b8 | ||
|
|
16d337a648 | ||
|
|
3326429c05 | ||
|
|
4dc0da5e75 | ||
|
|
e09d66f20f | ||
|
|
ad87e73928 | ||
|
|
da35ac9f46 | ||
|
|
bba7ddc4a5 | ||
|
|
52e945c900 | ||
|
|
64c00375c9 | ||
|
|
03b3c4340d | ||
|
|
de60913a88 | ||
|
|
2e8db85d6b | ||
|
|
301711dcbd | ||
|
|
b2ff79997a | ||
|
|
54b5d7fb4f | ||
|
|
f86c45a94a | ||
|
|
b51d1d1812 | ||
|
|
4cc90af703 | ||
|
|
7c72d75ca0 | ||
|
|
0860957299 | ||
|
|
089ffae352 | ||
|
|
9111b6b525 | ||
|
|
7738513851 | ||
|
|
949e3756e9 | ||
|
|
9107a2f7fe | ||
|
|
2eebecb561 | ||
|
|
4de07d29b1 | ||
|
|
5ffac1cd92 | ||
|
|
7198bdf826 | ||
|
|
a355e1b8db | ||
|
|
1e02e6e965 | ||
|
|
69159a15a3 | ||
|
|
5bbc9f1d2a | ||
|
|
118cd1b30a | ||
|
|
4e047a25a3 | ||
|
|
20244a5747 | ||
|
|
b0bbd24661 | ||
|
|
2396a27906 | ||
|
|
713b2ed8c8 | ||
|
|
380b8bb9a9 | ||
|
|
e62f5b2afa | ||
|
|
033ca3ff39 | ||
|
|
178923f202 | ||
|
|
c96a5c858a | ||
|
|
5888ae7df9 | ||
|
|
37ab437f3f | ||
|
|
fec9a5ff4a | ||
|
|
d66e88b484 | ||
|
|
042d466a48 | ||
|
|
69fd55dfef | ||
|
|
1d553b5091 | ||
|
|
86e85d6f35 | ||
|
|
c39805704d | ||
|
|
1827a5b415 | ||
|
|
5df59016c7 | ||
|
|
eeec408879 | ||
|
|
5dea2c7afe | ||
|
|
7821708301 | ||
|
|
ab58d0ec2e | ||
|
|
ff1e2c1885 | ||
|
|
290592bbd6 | ||
|
|
7493cdc081 | ||
|
|
cf85c815f4 | ||
|
|
d5362dcf4d | ||
|
|
56127dbeaa | ||
|
|
adbcab2fb0 | ||
|
|
e0924bcfa1 | ||
|
|
2f03c11c1a | ||
|
|
5513964f31 | ||
|
|
6dd93c50c8 | ||
|
|
bc84e3ef0b | ||
|
|
da4a1e9302 | ||
|
|
93fec759ed | ||
|
|
d315473a54 | ||
|
|
8cd4bf1701 | ||
|
|
95feb27a97 | ||
|
|
8103857dbb | ||
|
|
37afe34a22 | ||
|
|
96d180e1c6 | ||
|
|
22dd3d37eb | ||
|
|
cb592e6749 | ||
|
|
94b47893d6 | ||
|
|
164ccd5cd7 | ||
|
|
2a0abe60f9 | ||
|
|
0a80629a50 | ||
|
|
8c4fbd4012 | ||
|
|
c03a786025 | ||
|
|
a5a7019d5a | ||
|
|
4be5b72208 | ||
|
|
f6200739ff | ||
|
|
42290b68ed | ||
|
|
4997970800 | ||
|
|
b455ecae74 | ||
|
|
5bd9a44324 | ||
|
|
d417837978 | ||
|
|
acf8705590 | ||
|
|
9a18276819 | ||
|
|
74a748936d | ||
|
|
1c16eff3e1 | ||
|
|
6a7291080f | ||
|
|
8539a9f1a4 | ||
|
|
4734f06046 | ||
|
|
993ca6e9c6 | ||
|
|
76eb54e86f | ||
|
|
1643f23612 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,3 +41,4 @@ kddockwidgets_basic_quick
|
||||
build-*
|
||||
/examples/dockwidgets/kddockwidgets_example
|
||||
.cmake
|
||||
mylayout.json
|
||||
|
||||
@@ -127,7 +127,7 @@ void MyMainWindow::createDockWidgets()
|
||||
addDockWidget(m_dockwidgets[3], KDDockWidgets::Location_OnBottom);
|
||||
addDockWidget(m_dockwidgets[4], KDDockWidgets::Location_OnBottom);
|
||||
|
||||
// Tab two dock widgets toghether
|
||||
// Tab two dock widgets together
|
||||
m_dockwidgets[3]->addDockWidgetAsTab(m_dockwidgets[5]);
|
||||
|
||||
// 6 is floating, as it wasn't added to the main window via MainWindow::addDockWidget().
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
|
||||
cmake_policy(SET CMP0043 NEW)
|
||||
|
||||
add_definitions(-DQT_NO_CAST_TO_ASCII
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_URL_CAST_FROM_STRING
|
||||
-DQT_NO_CAST_FROM_BYTEARRAY
|
||||
-DQT_NO_SIGNALS_SLOTS_KEYWORDS
|
||||
add_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS
|
||||
-DQT_USE_QSTRINGBUILDER
|
||||
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||
-DQT_STRICT_ITERATORS
|
||||
@@ -25,13 +21,9 @@ set(DOCKSLIBS_SRCS
|
||||
private/ObjectViewer.cpp
|
||||
private/DropIndicatorOverlayInterface.cpp
|
||||
private/indicators/ClassicIndicators.cpp
|
||||
private/indicators/AnimatedIndicators.cpp
|
||||
# private/indicators/AnimatedIndicators.cpp
|
||||
private/DropArea.cpp
|
||||
private/multisplitter/Item.cpp
|
||||
private/multisplitter/MultiSplitter.cpp
|
||||
private/multisplitter/Anchor.cpp
|
||||
private/multisplitter/AnchorGroup.cpp
|
||||
private/multisplitter/Separator.cpp
|
||||
private/multisplitter/MultiSplitterLayout.cpp
|
||||
private/TabWidget.cpp
|
||||
private/FloatingWindow.cpp
|
||||
@@ -72,7 +64,7 @@ set(DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES
|
||||
private/widgets/QWidgetAdapter_widgets_p.h
|
||||
private/widgets/TitleBarWidget_p.h
|
||||
private/widgets/SeparatorWidget_p.h
|
||||
private/widgets/FloatingWindowWidget_p.h
|
||||
private/widgets/FloatingWindowWidget_p.h
|
||||
private/widgets/FrameWidget_p.h
|
||||
private/widgets/TabBarWidget_p.h
|
||||
private/widgets/TabWidgetWidget_p.h
|
||||
@@ -119,6 +111,9 @@ else()
|
||||
set(IS_CLANG_BUILD FALSE)
|
||||
endif()
|
||||
|
||||
|
||||
add_subdirectory(private/multisplitter)
|
||||
|
||||
qt5_add_resources(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc)
|
||||
|
||||
add_library(kddockwidgets SHARED ${DOCKSLIBS_SRCS} ${DOCKS_INSTALLABLE_INCLUDES} ${RESOURCES} ${RESOURCES_QUICK})
|
||||
@@ -133,7 +128,13 @@ target_include_directories(kddockwidgets
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/private
|
||||
)
|
||||
|
||||
target_compile_definitions(kddockwidgets PRIVATE BUILDING_DOCKS_LIBRARY)
|
||||
target_compile_definitions(kddockwidgets PRIVATE BUILDING_DOCKS_LIBRARY
|
||||
QT_NO_CAST_TO_ASCII
|
||||
QT_NO_CAST_FROM_ASCII
|
||||
QT_NO_URL_CAST_FROM_STRING
|
||||
QT_NO_CAST_FROM_BYTEARRAY
|
||||
)
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCXX OR IS_CLANG_BUILD)
|
||||
target_compile_options(kddockwidgets PRIVATE -Wshadow -Wconversion -fvisibility=hidden)
|
||||
|
||||
@@ -145,13 +146,13 @@ endif()
|
||||
if (OPTION_QTQUICK)
|
||||
target_link_libraries(kddockwidgets Qt5::Widgets Qt5::Quick)
|
||||
else()
|
||||
target_link_libraries(kddockwidgets Qt5::Widgets)
|
||||
target_link_libraries(kddockwidgets PUBLIC Qt5::Widgets PRIVATE kddockwidgets_layouting)
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
find_package(Qt5X11Extras)
|
||||
target_link_libraries(kddockwidgets Qt5::X11Extras)
|
||||
target_link_libraries(kddockwidgets PUBLIC Qt5::X11Extras)
|
||||
endif()
|
||||
|
||||
install (TARGETS kddockwidgets
|
||||
@@ -161,6 +162,7 @@ install (TARGETS kddockwidgets
|
||||
ARCHIVE DESTINATION lib)
|
||||
install (FILES ${DOCKS_INSTALLABLE_INCLUDES} DESTINATION include/kddockwidgets)
|
||||
install (FILES ${DOCKS_INSTALLABLE_PRIVATE_INCLUDES} DESTINATION include/kddockwidgets/private)
|
||||
install (FILES private/multisplitter/Item_p.h DESTINATION include/kddockwidgets/multisplitter)
|
||||
install (FILES ${DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES} DESTINATION include/kddockwidgets/private/widgets)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "Config.h"
|
||||
#include "DockRegistry_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
#include "Anchor_p.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
@@ -67,6 +68,13 @@ Config::Config()
|
||||
: d(new Private())
|
||||
{
|
||||
d->fixFlags();
|
||||
|
||||
// stuff in multisplitter/ can't include the framework widget factory, so set it here
|
||||
auto separatorCreator = [](Layouting::Anchor *a, QWidget *parent){
|
||||
return Config::self().frameworkWidgetFactory()->createSeparator(a, parent);
|
||||
};
|
||||
|
||||
Layouting::Anchor::setSeparatorFactoryFunc(separatorCreator);
|
||||
}
|
||||
|
||||
Config& Config::self()
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
*/
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
class DockWidgetBase::Private
|
||||
{
|
||||
@@ -544,7 +545,9 @@ void DockWidgetBase::Private::restoreToPreviousPosition()
|
||||
return;
|
||||
}
|
||||
|
||||
m_lastPosition.layoutItem()->restorePlaceholder(q, m_lastPosition.m_tabIndex);
|
||||
MultiSplitterLayout *layout = DockRegistry::self()->layoutForItem(m_lastPosition.layoutItem());
|
||||
Q_ASSERT(layout);
|
||||
layout->restorePlaceholder(q, m_lastPosition.layoutItem(), m_lastPosition.m_tabIndex);
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::maybeRestoreToPreviousPosition()
|
||||
@@ -560,7 +563,7 @@ void DockWidgetBase::Private::maybeRestoreToPreviousPosition()
|
||||
|
||||
Frame *frame = q->frame();
|
||||
|
||||
if (frame && frame->parentWidget() == layoutItem->parentWidget()) {
|
||||
if (frame && frame->parentWidget() == DockRegistry::self()->layoutForItem(layoutItem)->multiSplitter()) {
|
||||
// There's a frame already. Means the DockWidget was hidden instead of closed.
|
||||
// Nothing to do, the dock widget will simply be shown
|
||||
qCDebug(placeholder) << Q_FUNC_INFO << "Already had frame.";
|
||||
|
||||
@@ -40,12 +40,15 @@ QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Layouting {
|
||||
class Item;
|
||||
}
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class Frame;
|
||||
class FloatingWindow;
|
||||
class DragController;
|
||||
class Item;
|
||||
class LastPosition;
|
||||
class DockRegistry;
|
||||
class LayoutSaver;
|
||||
@@ -333,7 +336,6 @@ private:
|
||||
friend class KDDockWidgets::TabWidget;
|
||||
friend class KDDockWidgets::TitleBar;
|
||||
friend class KDDockWidgets::DragController;
|
||||
friend class KDDockWidgets::Item;
|
||||
friend class KDDockWidgets::DockRegistry;
|
||||
friend class KDDockWidgets::LayoutSaver;
|
||||
|
||||
@@ -356,7 +358,7 @@ private:
|
||||
FloatingWindow *floatingWindow() const;
|
||||
|
||||
///@brief adds the current layout item containing this dock widget
|
||||
void addPlaceholderItem(Item*);
|
||||
void addPlaceholderItem(Layouting::Item*);
|
||||
|
||||
///@brief returns the last position, just for tests. TODO Make tests just use the d-pointer.
|
||||
LastPosition *lastPosition() const;
|
||||
@@ -366,5 +368,6 @@ private:
|
||||
};
|
||||
|
||||
}
|
||||
Q_DECLARE_METATYPE(KDDockWidgets::Location)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
#endif
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
|
||||
FrameworkWidgetFactory::~FrameworkWidgetFactory()
|
||||
{
|
||||
@@ -76,7 +78,7 @@ TabWidget *DefaultWidgetFactory::createTabWidget(Frame *parent) const
|
||||
return new TabWidgetWidget(parent);
|
||||
}
|
||||
|
||||
Separator *DefaultWidgetFactory::createSeparator(Anchor *anchor, QWidgetAdapter *parent) const
|
||||
Layouting::Separator *DefaultWidgetFactory::createSeparator(Anchor *anchor, QWidget *parent) const
|
||||
{
|
||||
return new SeparatorWidget(anchor, parent);
|
||||
}
|
||||
|
||||
@@ -32,17 +32,20 @@
|
||||
* @author Sérgio Martins \<sergio.martins@kdab.com\>
|
||||
*/
|
||||
|
||||
namespace Layouting {
|
||||
class Anchor;
|
||||
class Separator;
|
||||
}
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class MainWindowBase;
|
||||
class DropIndicatorOverlayInterface;
|
||||
class Separator;
|
||||
class FloatingWindow;
|
||||
class TabWidget;
|
||||
class TitleBar;
|
||||
class Frame;
|
||||
class DropArea;
|
||||
class Anchor;
|
||||
class TabBar;
|
||||
|
||||
/**
|
||||
@@ -103,7 +106,7 @@ public:
|
||||
/// the user to resize nested dock widgets.
|
||||
///@param anchor Just forward to Sepataror's constructor.
|
||||
///@param parent Just forward to Separator's constructor.
|
||||
virtual Separator* createSeparator(Anchor *anchor, QWidgetAdapter *parent = nullptr) const = 0;
|
||||
virtual Layouting::Separator* createSeparator(Layouting::Anchor *anchor, QWidget *parent = nullptr) const = 0;
|
||||
|
||||
///@brief Called internally by the framework to create a FloatingWindow
|
||||
/// Override to provide your own FloatingWindow sub-class. If overridden then
|
||||
@@ -135,7 +138,7 @@ public:
|
||||
TitleBar *createTitleBar(FloatingWindow *) const override;
|
||||
TabBar *createTabBar(TabWidget *parent) const override;
|
||||
TabWidget *createTabWidget(Frame *parent) const override;
|
||||
Separator *createSeparator(Anchor *anchor, QWidgetAdapter *parent = nullptr) const override;
|
||||
Layouting::Separator *createSeparator(Layouting::Anchor *anchor, QWidget *parent = nullptr) const override;
|
||||
FloatingWindow *createFloatingWindow(MainWindowBase *parent = nullptr) const override;
|
||||
FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const override;
|
||||
DropIndicatorOverlayInterface *createDropIndicatorOverlay(DropArea*) const override;
|
||||
|
||||
@@ -66,63 +66,26 @@ namespace KDDockWidgets
|
||||
};
|
||||
Q_DECLARE_FLAGS(RestoreOptions, RestoreOption)
|
||||
|
||||
///@internal
|
||||
inline Location oppositeLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case Location_OnLeft:
|
||||
return Location_OnRight;
|
||||
case Location_OnTop:
|
||||
return Location_OnBottom;
|
||||
case Location_OnRight:
|
||||
return Location_OnLeft;
|
||||
case Location_OnBottom:
|
||||
return Location_OnTop;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return Location_None;
|
||||
}
|
||||
}
|
||||
///@internal
|
||||
inline QString locationStr(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case KDDockWidgets::Location_None:
|
||||
return QStringLiteral("none");
|
||||
case KDDockWidgets::Location_OnLeft:
|
||||
return QStringLiteral("left");
|
||||
case KDDockWidgets::Location_OnTop:
|
||||
return QStringLiteral("top");
|
||||
case KDDockWidgets::Location_OnRight:
|
||||
return QStringLiteral("right");
|
||||
case KDDockWidgets::Location_OnBottom:
|
||||
return QStringLiteral("bottom");
|
||||
}
|
||||
|
||||
///@internal
|
||||
inline Location adjacentLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case Location_OnLeft:
|
||||
return Location_OnTop;
|
||||
case Location_OnTop:
|
||||
return Location_OnRight;
|
||||
case Location_OnRight:
|
||||
return Location_OnBottom;
|
||||
case Location_OnBottom:
|
||||
return Location_OnLeft;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return Location_None;
|
||||
}
|
||||
}
|
||||
|
||||
///@internal
|
||||
inline QString locationStr(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case KDDockWidgets::Location_None:
|
||||
return QStringLiteral("none");
|
||||
case KDDockWidgets::Location_OnLeft:
|
||||
return QStringLiteral("left");
|
||||
case KDDockWidgets::Location_OnTop:
|
||||
return QStringLiteral("top");
|
||||
case KDDockWidgets::Location_OnRight:
|
||||
return QStringLiteral("right");
|
||||
case KDDockWidgets::Location_OnBottom:
|
||||
return QStringLiteral("bottom");
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KDDockWidgets::Location)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KDDockWidgets::FrameOptions)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include "Logging_p.h"
|
||||
#include "Frame_p.h"
|
||||
#include "LastPosition_p.h"
|
||||
#include "multisplitter/Anchor_p.h"
|
||||
#include "multisplitter/Item_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
|
||||
@@ -47,44 +46,11 @@
|
||||
#include <memory>
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
QHash<QString, LayoutSaver::DockWidget::Ptr> LayoutSaver::DockWidget::s_dockWidgets;
|
||||
LayoutSaver::Layout* LayoutSaver::Layout::s_currentLayoutBeingRestored = nullptr;
|
||||
|
||||
static QVariantMap sizeToMap(QSize sz)
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("width"), sz.width());
|
||||
map.insert(QStringLiteral("height"), sz.height());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static QVariantMap rectToMap(QRect rect)
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("x"), rect.x());
|
||||
map.insert(QStringLiteral("y"), rect.y());
|
||||
map.insert(QStringLiteral("width"), rect.width());
|
||||
map.insert(QStringLiteral("height"), rect.height());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static QSize mapToSize(const QVariantMap &map)
|
||||
{
|
||||
return { map.value(QStringLiteral("width")).toInt(),
|
||||
map.value(QStringLiteral("height")).toInt() };
|
||||
}
|
||||
|
||||
static QRect mapToRect(const QVariantMap &map)
|
||||
{
|
||||
return QRect(map.value(QStringLiteral("x")).toInt(),
|
||||
map.value(QStringLiteral("y")).toInt(),
|
||||
map.value(QStringLiteral("width")).toInt(),
|
||||
map.value(QStringLiteral("height")).toInt());
|
||||
}
|
||||
|
||||
class KDDockWidgets::LayoutSaver::Private
|
||||
{
|
||||
public:
|
||||
@@ -221,31 +187,6 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
if (data.isEmpty())
|
||||
return true;
|
||||
|
||||
struct EnsureItemsAtCorrectPlace {
|
||||
|
||||
EnsureItemsAtCorrectPlace(LayoutSaver *ls)
|
||||
: layoutSaver(ls)
|
||||
{
|
||||
}
|
||||
|
||||
~EnsureItemsAtCorrectPlace()
|
||||
{
|
||||
// When using RestoreOption_RelativeToMainWindow we'll have many rounding errors so the layout won't be exact.
|
||||
// Make sure to run a relayout at the end
|
||||
// (Using RAII to make sure it runs after Private::RAIIIsRestoring went out of scope, since "isRestoring= true" inhibits relayout
|
||||
if (ensure) {
|
||||
for (auto layout : DockRegistry::self()->layouts()) {
|
||||
if (layoutSaver->d->matchesAffinity(layout->affinityName()))
|
||||
layout->redistributeSpace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ensure = false;
|
||||
LayoutSaver *const layoutSaver;
|
||||
};
|
||||
|
||||
EnsureItemsAtCorrectPlace ensureItemsAtCorrectPlace(this);
|
||||
Private::RAIIIsRestoring isRestoring;
|
||||
|
||||
struct FrameCleanup {
|
||||
@@ -273,7 +214,7 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
layout.scaleSizes();
|
||||
|
||||
// Hide all dockwidgets and unparent them from any layout before starting restore
|
||||
d->m_dockRegistry->clear(d->m_affinityNames, /*deleteStaticAnchors=*/true);
|
||||
d->m_dockRegistry->clear(d->m_affinityNames);
|
||||
|
||||
// 1. Restore main windows
|
||||
for (const LayoutSaver::MainWindow &mw : qAsConst(layout.mainWindows)) {
|
||||
@@ -327,9 +268,6 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
}
|
||||
}
|
||||
|
||||
// our raii class will run when
|
||||
ensureItemsAtCorrectPlace.ensure = d->m_restoreOptions & RestoreOption_RelativeToMainWindow;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -416,20 +354,6 @@ bool LayoutSaver::Layout::isValid() const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayoutSaver::Layout::fillFrom(const QByteArray &serialized)
|
||||
{
|
||||
QDataStream ds(serialized);
|
||||
ds >> this;
|
||||
|
||||
if (serializationVersion > KDDOCKWIDGETS_SERIALIZATION_VERSION) {
|
||||
qWarning() << "Unsupported serialization version. Got=" << serializationVersion
|
||||
<< "; expected equal or less than" << KDDOCKWIDGETS_SERIALIZATION_VERSION;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray LayoutSaver::Layout::toJson() const
|
||||
{
|
||||
QJsonDocument doc = QJsonDocument::fromVariant(toVariantMap());
|
||||
@@ -518,66 +442,9 @@ LayoutSaver::MainWindow LayoutSaver::Layout::mainWindowForIndex(int index) const
|
||||
return mainWindows.at(index);
|
||||
}
|
||||
|
||||
bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &layout) const
|
||||
{
|
||||
if (!frame.isValid())
|
||||
return false;
|
||||
|
||||
const int numAnchors = layout.anchors.size();
|
||||
|
||||
if (indexOfLeftAnchor < 0 || indexOfTopAnchor < 0 ||
|
||||
indexOfBottomAnchor < 0 || indexOfRightAnchor < 0 ||
|
||||
indexOfLeftAnchor >= numAnchors || indexOfTopAnchor >= numAnchors ||
|
||||
indexOfBottomAnchor >= numAnchors || indexOfRightAnchor >= numAnchors) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid anchor indexes"
|
||||
<< indexOfLeftAnchor << indexOfTopAnchor
|
||||
<< indexOfBottomAnchor << indexOfRightAnchor;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutSaver::Item::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
scalingInfo.applyFactorsTo(geometry);
|
||||
if (!frame.isNull)
|
||||
frame.scaleSizes(scalingInfo);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::Item::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("objectName"), objectName);
|
||||
map.insert(QStringLiteral("isPlaceholder"), isPlaceholder);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
map.insert(QStringLiteral("minSize"), sizeToMap(minSize));
|
||||
map.insert(QStringLiteral("indexOfLeftAnchor"), indexOfLeftAnchor);
|
||||
map.insert(QStringLiteral("indexOfTopAnchor"), indexOfTopAnchor);
|
||||
map.insert(QStringLiteral("indexOfRightAnchor"), indexOfRightAnchor);
|
||||
map.insert(QStringLiteral("indexOfBottomAnchor"), indexOfBottomAnchor);
|
||||
if (!frame.isNull)
|
||||
map.insert(QStringLiteral("frame"), frame.toVariantMap());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::Item::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
objectName = map.value(QStringLiteral("objectName")).toString();
|
||||
isPlaceholder = map.value(QStringLiteral("isPlaceholder")).toBool();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
minSize = mapToSize(map.value(QStringLiteral("minSize")).toMap());
|
||||
indexOfLeftAnchor = map.value(QStringLiteral("indexOfLeftAnchor")).toInt();
|
||||
indexOfTopAnchor = map.value(QStringLiteral("indexOfTopAnchor")).toInt();
|
||||
indexOfRightAnchor = map.value(QStringLiteral("indexOfRightAnchor")).toInt();
|
||||
indexOfBottomAnchor = map.value(QStringLiteral("indexOfBottomAnchor")).toInt();
|
||||
frame.fromVariantMap(map.value(QStringLiteral("frame"), QVariantMap()).toMap());
|
||||
}
|
||||
|
||||
bool LayoutSaver::Frame::isValid() const
|
||||
{
|
||||
if (!isNull)
|
||||
if (isNull)
|
||||
return true;
|
||||
|
||||
if (!geometry.isValid()) {
|
||||
@@ -585,14 +452,21 @@ bool LayoutSaver::Frame::isValid() const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (id.isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid id";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options > 3) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid options" << options;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentTabIndex >= dockWidgets.size() || currentTabIndex < 0) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid tab index" << currentTabIndex << dockWidgets.size();
|
||||
return false;
|
||||
if (!dockWidgets.isEmpty()) {
|
||||
if (currentTabIndex >= dockWidgets.size() || currentTabIndex < 0) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid tab index" << currentTabIndex << dockWidgets.size();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &dw : dockWidgets) {
|
||||
@@ -611,6 +485,7 @@ void LayoutSaver::Frame::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
QVariantMap LayoutSaver::Frame::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("id"), id);
|
||||
map.insert(QStringLiteral("isNull"), isNull);
|
||||
map.insert(QStringLiteral("objectName"), objectName);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
@@ -630,6 +505,7 @@ void LayoutSaver::Frame::fromVariantMap(const QVariantMap &map)
|
||||
return;
|
||||
}
|
||||
|
||||
id = map.value(QStringLiteral("id")).toString();
|
||||
isNull = map.value(QStringLiteral("isNull")).toBool();
|
||||
objectName = map.value(QStringLiteral("objectName")).toString();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
@@ -674,120 +550,6 @@ void LayoutSaver::DockWidget::fromVariantMap(const QVariantMap &map)
|
||||
lastPosition.fromVariantMap(map.value(QStringLiteral("lastPosition")).toMap());
|
||||
}
|
||||
|
||||
bool LayoutSaver::Anchor::isValid(const LayoutSaver::MultiSplitterLayout &layout) const
|
||||
{
|
||||
const bool isStatic = type != KDDockWidgets::Anchor::Type_None;
|
||||
const bool isFollowing = indexOfFollowee != -1;
|
||||
const int numAnchors = layout.anchors.size();
|
||||
|
||||
if (!geometry.isValid() && !isStatic && !isFollowing) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid geometry" << geometry;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (indexOfFrom < 0 || indexOfTo < 0 || indexOfFrom == indexOfTo ||
|
||||
indexOfTo >= numAnchors || indexOfFrom >= numAnchors) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid indexes" << indexOfFrom << indexOfTo;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &anchorTo = layout.anchors[indexOfTo];
|
||||
auto &anchorFrom = layout.anchors[indexOfFrom];
|
||||
if (anchorTo.orientation != anchorFrom.orientation || anchorTo.orientation == orientation) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid orientation" << anchorTo.orientation << anchorFrom.orientation
|
||||
<< orientation;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (orientation != Qt::Vertical && orientation != Qt::Horizontal) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid orientation" << orientation;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type != KDDockWidgets::Anchor::Type_None &&
|
||||
type != KDDockWidgets::Anchor::Type_LeftStatic &&
|
||||
type != KDDockWidgets::Anchor::Type_RightStatic &&
|
||||
type != KDDockWidgets::Anchor::Type_TopStatic &&
|
||||
type != KDDockWidgets::Anchor::Type_BottomStatic) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid type" << type;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isStatic && !isFollowing && (side1Items.isEmpty() || side2Items.isEmpty())) {
|
||||
qWarning() << Q_FUNC_INFO << "Anchor should have items on both sides";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::Anchor::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("objectName"), objectName);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
map.insert(QStringLiteral("orientation"), orientation);
|
||||
map.insert(QStringLiteral("type"), type);
|
||||
map.insert(QStringLiteral("indexOfFrom"), indexOfFrom);
|
||||
map.insert(QStringLiteral("indexOfTo"), indexOfTo);
|
||||
map.insert(QStringLiteral("indexOfFollowee"), indexOfFollowee);
|
||||
map.insert(QStringLiteral("positionPercentage"), positionPercentage);
|
||||
|
||||
QVariantList side1ItemsV;
|
||||
QVariantList side2ItemsV;
|
||||
side1ItemsV.reserve(side1Items.size());
|
||||
side2ItemsV.reserve(side2Items.size());
|
||||
for (int index : qAsConst(side1Items))
|
||||
side1ItemsV.push_back(index);
|
||||
for (int index : qAsConst(side2Items))
|
||||
side2ItemsV.push_back(index);
|
||||
|
||||
map.insert(QStringLiteral("side1Items"), side1ItemsV);
|
||||
map.insert(QStringLiteral("side2Items"), side2ItemsV);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::Anchor::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
objectName = map.value(QStringLiteral("objectName")).toString();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
|
||||
orientation = map.value(QStringLiteral("orientation")).toInt();
|
||||
type = map.value(QStringLiteral("type")).toInt();
|
||||
indexOfFrom = map.value(QStringLiteral("indexOfFrom")).toInt();
|
||||
indexOfTo = map.value(QStringLiteral("indexOfTo")).toInt();
|
||||
indexOfFollowee = map.value(QStringLiteral("indexOfFollowee")).toInt();
|
||||
positionPercentage = map.value(QStringLiteral("positionPercentage")).toDouble();
|
||||
|
||||
side1Items.clear();
|
||||
side2Items.clear();
|
||||
const QVariantList side1ItemsV = map.value(QStringLiteral("side1Items")).toList();
|
||||
const QVariantList side2ItemsV = map.value(QStringLiteral("side2Items")).toList();
|
||||
side1Items.reserve(side1ItemsV.size());
|
||||
side2Items.reserve(side2ItemsV.size());
|
||||
for (const QVariant &v : side1ItemsV)
|
||||
side1Items.push_back(v.toInt());
|
||||
for (const QVariant &v : side2ItemsV)
|
||||
side2Items.push_back(v.toInt());
|
||||
}
|
||||
|
||||
void LayoutSaver::Anchor::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
const QPoint pos = geometry.topLeft();
|
||||
|
||||
if (isVertical()) {
|
||||
geometry.moveLeft(int(pos.x() * scalingInfo.widthFactor));
|
||||
} else {
|
||||
geometry.moveTop(int(pos.y() * scalingInfo.heightFactor));
|
||||
}
|
||||
}
|
||||
|
||||
bool LayoutSaver::Anchor::isVertical() const
|
||||
{
|
||||
return orientation == Qt::Vertical;
|
||||
}
|
||||
|
||||
bool LayoutSaver::FloatingWindow::isValid() const
|
||||
{
|
||||
if (!multiSplitterLayout.isValid())
|
||||
@@ -893,51 +655,47 @@ void LayoutSaver::MainWindow::fromVariantMap(const QVariantMap &map)
|
||||
|
||||
bool LayoutSaver::MultiSplitterLayout::isValid() const
|
||||
{
|
||||
for (auto &item : items) {
|
||||
if (!item.isValid(*this))
|
||||
return false;
|
||||
}
|
||||
if (layout.isEmpty())
|
||||
return false;
|
||||
|
||||
for (auto &anchor : anchors) {
|
||||
if (!anchor.isValid(*this))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!size.isValid()) {
|
||||
/*if (!size.isValid()) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid size";
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutSaver::MultiSplitterLayout::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
void LayoutSaver::MultiSplitterLayout::scaleSizes(const ScalingInfo &)
|
||||
{
|
||||
scalingInfo.applyFactorsTo(/*by-ref*/size);
|
||||
for (LayoutSaver::Anchor &anchor : anchors)
|
||||
anchor.scaleSizes(scalingInfo);
|
||||
for (LayoutSaver::Item &item : items)
|
||||
item.scaleSizes(scalingInfo);
|
||||
// scalingInfo.applyFactorsTo(/*by-ref*/size);
|
||||
//for (LayoutSaver::Item &item : items) TODO
|
||||
// item.scaleSizes(scalingInfo);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::MultiSplitterLayout::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
QVariantMap result;
|
||||
result.insert(QStringLiteral("layout"), layout);
|
||||
|
||||
map.insert(QStringLiteral("anchors"), toVariantList<LayoutSaver::Anchor>(anchors));
|
||||
map.insert(QStringLiteral("items"), toVariantList<LayoutSaver::Item>(items));
|
||||
map.insert(QStringLiteral("minSize"), sizeToMap(minSize));
|
||||
map.insert(QStringLiteral("size"), sizeToMap(size));
|
||||
QVariantMap framesV;
|
||||
for (auto &frame : frames)
|
||||
framesV.insert(frame.id, frame.toVariantMap());
|
||||
|
||||
return map;
|
||||
result.insert(QStringLiteral("frames"), framesV);
|
||||
return result;
|
||||
}
|
||||
|
||||
void LayoutSaver::MultiSplitterLayout::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
anchors = fromVariantList<LayoutSaver::Anchor>(map.value(QStringLiteral("anchors")).toList());
|
||||
items = fromVariantList<LayoutSaver::Item>(map.value(QStringLiteral("items")).toList());
|
||||
minSize = mapToSize(map.value(QStringLiteral("minSize")).toMap());
|
||||
size = mapToSize(map.value(QStringLiteral("size")).toMap());
|
||||
layout = map.value(QStringLiteral("layout")).toMap();
|
||||
const QVariantMap framesV = map.value(QStringLiteral("frames")).toMap();
|
||||
frames.clear();
|
||||
for (const QVariant &frameV : framesV) {
|
||||
LayoutSaver::Frame frame;
|
||||
frame.fromVariantMap(frameV.toMap());
|
||||
frames.insert(frame.id, frame);
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutSaver::LastPosition::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
|
||||
@@ -108,8 +108,6 @@ public:
|
||||
struct DockWidget;
|
||||
struct LastPosition;
|
||||
struct MultiSplitterLayout;
|
||||
struct Item;
|
||||
struct Anchor;
|
||||
struct Frame;
|
||||
struct Placeholder;
|
||||
struct ScalingInfo;
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
#include "LayoutSaver.h"
|
||||
#include "KDDockWidgets.h"
|
||||
#include "multisplitter/Item_p.h"
|
||||
|
||||
#include <QRect>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QScreen>
|
||||
#include <QApplication>
|
||||
@@ -33,15 +33,15 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define ANCHOR_MAGIC_MARKER "e520c60e-cf5d-4a30-b1a7-588d2c569851"
|
||||
#define MULTISPLITTER_LAYOUT_MAGIC_MARKER "bac9948e-5f1b-4271-acc5-07f1708e2611"
|
||||
|
||||
/**
|
||||
* Bump whenever the format changes, so we can still load old layouts.
|
||||
* version 1: Initial version
|
||||
* version 2: Introduced MainWindow::screenSize and FloatingWindow::screenSize
|
||||
* version 3: New layouting engine
|
||||
*/
|
||||
#define KDDOCKWIDGETS_SERIALIZATION_VERSION 2
|
||||
#define KDDOCKWIDGETS_SERIALIZATION_VERSION 3
|
||||
|
||||
|
||||
namespace KDDockWidgets {
|
||||
@@ -194,56 +194,11 @@ struct LayoutSaver::Frame
|
||||
QRect geometry;
|
||||
unsigned int options;
|
||||
int currentTabIndex;
|
||||
QString id; // for coorelation purposes
|
||||
|
||||
LayoutSaver::DockWidget::List dockWidgets;
|
||||
};
|
||||
|
||||
struct LayoutSaver::Item
|
||||
{
|
||||
typedef QVector<LayoutSaver::Item> List;
|
||||
|
||||
bool isValid(const MultiSplitterLayout &) const;
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes(const ScalingInfo &scalingInfo);
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
QString objectName;
|
||||
bool isPlaceholder;
|
||||
QRect geometry;
|
||||
QSize minSize;
|
||||
int indexOfLeftAnchor;
|
||||
int indexOfTopAnchor;
|
||||
int indexOfRightAnchor;
|
||||
int indexOfBottomAnchor;
|
||||
LayoutSaver::Frame frame;
|
||||
};
|
||||
|
||||
struct LayoutSaver::Anchor
|
||||
{
|
||||
typedef QVector<LayoutSaver::Anchor> List;
|
||||
|
||||
bool isValid(const LayoutSaver::MultiSplitterLayout &layout) const;
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
void scaleSizes(const ScalingInfo &);
|
||||
|
||||
bool isVertical() const;
|
||||
|
||||
QString objectName;
|
||||
QRect geometry;
|
||||
double positionPercentage;
|
||||
int orientation;
|
||||
int type;
|
||||
int indexOfFrom;
|
||||
int indexOfTo;
|
||||
int indexOfFollowee;
|
||||
QVector<int> side1Items;
|
||||
QVector<int> side2Items;
|
||||
};
|
||||
|
||||
struct LayoutSaver::MultiSplitterLayout
|
||||
{
|
||||
bool isValid() const;
|
||||
@@ -253,10 +208,8 @@ struct LayoutSaver::MultiSplitterLayout
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
LayoutSaver::Anchor::List anchors;
|
||||
LayoutSaver::Item::List items;
|
||||
QSize minSize;
|
||||
QSize size;
|
||||
QVariantMap layout;
|
||||
QHash<QString, LayoutSaver::Frame> frames;
|
||||
};
|
||||
|
||||
struct LayoutSaver::FloatingWindow
|
||||
@@ -344,7 +297,6 @@ public:
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
bool fillFrom(const QByteArray &serialized);
|
||||
QByteArray toJson() const;
|
||||
bool fromJson(const QByteArray &jsonData);
|
||||
QVariantMap toVariantMap() const;
|
||||
@@ -353,7 +305,6 @@ public:
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes();
|
||||
|
||||
friend QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame);
|
||||
static LayoutSaver::Layout* s_currentLayoutBeingRestored;
|
||||
|
||||
LayoutSaver::MainWindow mainWindowForIndex(int index) const;
|
||||
@@ -366,244 +317,6 @@ public:
|
||||
ScreenInfo::List screenInfo;
|
||||
};
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::ScreenInfo *info)
|
||||
{
|
||||
ds >> info->index;
|
||||
ds >> info->geometry;
|
||||
ds >> info->name;
|
||||
ds >> info->devicePixelRatio;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Placeholder *p)
|
||||
{
|
||||
ds >> p->isFloatingWindow;
|
||||
if (p->isFloatingWindow)
|
||||
ds >> p->indexOfFloatingWindow;
|
||||
else
|
||||
ds >> p->mainWindowUniqueName;
|
||||
|
||||
ds >> p->itemIndex;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Anchor *a)
|
||||
{
|
||||
QString marker;
|
||||
|
||||
ds >> marker;
|
||||
if (marker != QLatin1String(ANCHOR_MAGIC_MARKER))
|
||||
qWarning() << Q_FUNC_INFO << "Corrupt stream";
|
||||
|
||||
ds >> a->objectName;
|
||||
ds >> a->geometry;
|
||||
ds >> a->orientation;
|
||||
ds >> a->type;
|
||||
ds >> a->indexOfFrom;
|
||||
ds >> a->indexOfTo;
|
||||
ds >> a->indexOfFollowee;
|
||||
ds >> a->side1Items;
|
||||
ds >> a->side2Items;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame)
|
||||
{
|
||||
int numDockWidgets;
|
||||
frame->dockWidgets.clear();
|
||||
frame->isNull = false;
|
||||
|
||||
ds >> frame->objectName;
|
||||
ds >> frame->geometry;
|
||||
|
||||
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
|
||||
QSize sz;
|
||||
ds >> sz; // deprecated field, just discard
|
||||
}
|
||||
|
||||
ds >> frame->options;
|
||||
ds >> frame->currentTabIndex;
|
||||
ds >> numDockWidgets;
|
||||
|
||||
for (int i = 0; i < numDockWidgets; ++i) {
|
||||
QString name;
|
||||
ds >> name;
|
||||
auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
|
||||
frame->dockWidgets.push_back(dw);
|
||||
}
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Item *item)
|
||||
{
|
||||
ds >> item->objectName;
|
||||
ds >> item->isPlaceholder;
|
||||
ds >> item->geometry;
|
||||
ds >> item->minSize;
|
||||
|
||||
ds >> item->indexOfLeftAnchor;
|
||||
ds >> item->indexOfTopAnchor;
|
||||
ds >> item->indexOfRightAnchor;
|
||||
ds >> item->indexOfBottomAnchor;
|
||||
|
||||
bool hasFrame;
|
||||
ds >> hasFrame;
|
||||
if (hasFrame) {
|
||||
ds >> &item->frame;
|
||||
item->frame.isNull = false;
|
||||
} else {
|
||||
item->frame.isNull = true;
|
||||
}
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout *l)
|
||||
{
|
||||
int numItems;
|
||||
int numAnchors;
|
||||
QString marker;
|
||||
ds >> marker;
|
||||
|
||||
if (marker != QLatin1String(MULTISPLITTER_LAYOUT_MAGIC_MARKER))
|
||||
qWarning() << Q_FUNC_INFO << "Corrupt stream, invalid magic";
|
||||
|
||||
ds >> l->size;
|
||||
ds >> l->minSize;
|
||||
ds >> numItems;
|
||||
ds >> numAnchors;
|
||||
|
||||
l->items.clear();
|
||||
l->anchors.clear();
|
||||
|
||||
for (int i = 0 ; i < numItems; ++i) {
|
||||
LayoutSaver::Item item;
|
||||
ds >> &item;
|
||||
l->items.push_back(item);
|
||||
}
|
||||
|
||||
for (int i = 0 ; i < numAnchors; ++i) {
|
||||
LayoutSaver::Anchor a;
|
||||
ds >> &a;
|
||||
l->anchors.push_back(a);
|
||||
}
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::LastPosition *lp)
|
||||
{
|
||||
int numPlaceholders;
|
||||
ds >> numPlaceholders;
|
||||
|
||||
lp->placeholders.clear();
|
||||
for (int i = 0 ; i < numPlaceholders; ++i) {
|
||||
LayoutSaver::Placeholder p;
|
||||
ds >> &p;
|
||||
lp->placeholders.push_back(p);
|
||||
}
|
||||
|
||||
ds >> lp->lastFloatingGeometry;
|
||||
ds >> lp->tabIndex;
|
||||
ds >> lp->wasFloating;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::FloatingWindow *fw)
|
||||
{
|
||||
ds >> fw->parentIndex;
|
||||
ds >> fw->geometry;
|
||||
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
|
||||
ds >> fw->screenIndex;
|
||||
ds >> fw->screenSize;
|
||||
}
|
||||
|
||||
ds >> fw->isVisible;
|
||||
ds >> &fw->multiSplitterLayout;
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MainWindow *m)
|
||||
{
|
||||
ds >> m->uniqueName;
|
||||
ds >> m->geometry;
|
||||
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
|
||||
ds >> m->screenIndex;
|
||||
ds >> m->screenSize;
|
||||
}
|
||||
ds >> m->isVisible;
|
||||
ds >> m->options;
|
||||
ds >> &m->multiSplitterLayout;
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Layout *l)
|
||||
{
|
||||
LayoutSaver::DockWidget::s_dockWidgets.clear();
|
||||
int numMainWindows;
|
||||
int numFloatingWindows;
|
||||
int numClosedDockWidgets;
|
||||
int numAllDockWidgets;
|
||||
int numScreenInfo;
|
||||
|
||||
ds >> l->serializationVersion;
|
||||
|
||||
ds >> numMainWindows;
|
||||
l->mainWindows.clear();
|
||||
for (int i = 0; i < numMainWindows; ++i) {
|
||||
LayoutSaver::MainWindow m;
|
||||
ds >> &m;
|
||||
l->mainWindows.push_back(m);
|
||||
}
|
||||
|
||||
ds >> numFloatingWindows;
|
||||
l->floatingWindows.clear();
|
||||
for (int i = 0; i < numFloatingWindows; ++i) {
|
||||
LayoutSaver::FloatingWindow m;
|
||||
ds >> &m;
|
||||
l->floatingWindows.push_back(m);
|
||||
}
|
||||
|
||||
ds >> numClosedDockWidgets;
|
||||
l->closedDockWidgets.clear();
|
||||
for (int i = 0; i < numClosedDockWidgets; ++i) {
|
||||
QString name;
|
||||
ds >> name;
|
||||
auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
|
||||
l->closedDockWidgets.push_back(dw);
|
||||
}
|
||||
|
||||
ds >> numAllDockWidgets;
|
||||
l->allDockWidgets.clear();
|
||||
for (int i = 0; i < numAllDockWidgets; ++i) {
|
||||
QString name;
|
||||
ds >> name;
|
||||
|
||||
auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
|
||||
ds >> &dw->lastPosition;
|
||||
l->allDockWidgets.push_back(dw);
|
||||
}
|
||||
|
||||
|
||||
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
|
||||
ds >> numScreenInfo;
|
||||
l->screenInfo.clear();
|
||||
l->screenInfo.reserve(numScreenInfo);
|
||||
for (int i = 0; i < numScreenInfo; ++i) {
|
||||
LayoutSaver::ScreenInfo info;
|
||||
ds >> &info;
|
||||
l->screenInfo.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -231,24 +231,6 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
repaintWidgetRecursive(w);
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("EnsureAnchorsBounded"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts)
|
||||
l->ensureAnchorsBounded();
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("RedistributeSpace"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts)
|
||||
l->redistributeSpace();
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("resize by 1x1"));
|
||||
layout->addWidget(button);
|
||||
@@ -260,24 +242,6 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
}
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("PositionStaticAnchors()"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts)
|
||||
l->positionStaticAnchors();
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("UpdateAnchorFollowing"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts)
|
||||
l->updateAnchorFollowing();
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("Raise #0 (after 3s timeout)"));
|
||||
layout->addWidget(button);
|
||||
@@ -289,33 +253,6 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
});
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("Convert old layout to JSON"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [this] {
|
||||
const QString filename = QFileDialog::getOpenFileName(this);
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
QFile f(filename);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Failed to open file" << filename;
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray oldData = f.readAll();
|
||||
LayoutSaver::Layout savedLayout;
|
||||
savedLayout.fillFrom(oldData);
|
||||
const QByteArray jsonData = savedLayout.toJson();
|
||||
QFile f2(QStringLiteral("%1.json").arg(filename));
|
||||
if (!f2.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Failed to open file for writing" << filename;
|
||||
return;
|
||||
}
|
||||
|
||||
f2.write(jsonData);
|
||||
});
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("Dump native windows"));
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "DebugWindow_p.h"
|
||||
#include "LastPosition_p.h"
|
||||
#include "multisplitter/MultiSplitterLayout_p.h"
|
||||
#include "multisplitter/MultiSplitter_p.h"
|
||||
#include "quick/QmlTypes.h"
|
||||
|
||||
#include <QPointer>
|
||||
@@ -32,6 +33,7 @@
|
||||
#include <QWindow>
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
DockRegistry::DockRegistry(QObject *parent)
|
||||
: QObject(parent)
|
||||
@@ -77,6 +79,22 @@ bool DockRegistry::isProcessingAppQuitEvent() const
|
||||
return m_isProcessingAppQuitEvent;
|
||||
}
|
||||
|
||||
MultiSplitterLayout *DockRegistry::layoutForItem(const Item *item) const
|
||||
{
|
||||
if (auto ms = qobject_cast<MultiSplitter*>(item->hostWidget()))
|
||||
return ms->multiSplitterLayout();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DockRegistry::itemIsInMainWindow(const Item *item) const
|
||||
{
|
||||
if (auto layout = layoutForItem(item))
|
||||
return layout->multiSplitter()->isInMainWindow();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DockRegistry *DockRegistry::self()
|
||||
{
|
||||
static QPointer<DockRegistry> s_dockRegistry;
|
||||
@@ -295,7 +313,7 @@ QVector<QWidget *> DockRegistry::topLevels(bool excludeFloatingDocks) const
|
||||
return windows;
|
||||
}
|
||||
|
||||
void DockRegistry::clear(bool deleteStaticAnchors)
|
||||
void DockRegistry::clear()
|
||||
{
|
||||
for (auto dw : qAsConst(m_dockWidgets)) {
|
||||
dw->forceClose();
|
||||
@@ -303,17 +321,17 @@ void DockRegistry::clear(bool deleteStaticAnchors)
|
||||
}
|
||||
|
||||
for (auto mw : qAsConst(m_mainWindows))
|
||||
mw->multiSplitterLayout()->clear(deleteStaticAnchors);
|
||||
mw->multiSplitterLayout()->clear();
|
||||
|
||||
qCDebug(restoring) << Q_FUNC_INFO << "; dockwidgets=" << m_dockWidgets.size()
|
||||
<< "; nestedwindows=" << m_nestedWindows.size();
|
||||
}
|
||||
|
||||
void DockRegistry::clear(QStringList affinities, bool deleteStaticAnchors)
|
||||
void DockRegistry::clear(QStringList affinities)
|
||||
{
|
||||
if (affinities.isEmpty()) {
|
||||
// Just clear everything
|
||||
clear(deleteStaticAnchors);
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -329,7 +347,7 @@ void DockRegistry::clear(QStringList affinities, bool deleteStaticAnchors)
|
||||
|
||||
for (auto mw : qAsConst(m_mainWindows)) {
|
||||
if (affinities.contains(mw->affinityName())) {
|
||||
mw->multiSplitterLayout()->clear(deleteStaticAnchors);
|
||||
mw->multiSplitterLayout()->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@ public:
|
||||
* @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors.
|
||||
* This is called before restoring a layout.
|
||||
*/
|
||||
void clear(bool deleteStaticAnchors = false);
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors with the specified affinities.
|
||||
*/
|
||||
void clear(QStringList affinities, bool deleteStaticAnchors = false);
|
||||
void clear(QStringList affinities);
|
||||
|
||||
/**
|
||||
* @brief Ensures that all floating DockWidgets have a FloatingWindow as a window.
|
||||
@@ -137,6 +137,12 @@ public:
|
||||
*/
|
||||
bool isProcessingAppQuitEvent() const;
|
||||
|
||||
// TODO: docs
|
||||
MultiSplitterLayout* layoutForItem(const Layouting::Item *) const;
|
||||
|
||||
// TODO: docs
|
||||
bool itemIsInMainWindow(const Layouting::Item *) const;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
private:
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "WindowBeingDragged_p.h"
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
/**
|
||||
* @file
|
||||
@@ -59,23 +60,11 @@ int DropArea::numFrames() const
|
||||
return m_layout->count();
|
||||
}
|
||||
|
||||
Anchor::List DropArea::nonStaticAnchors(bool includePlaceholders) const
|
||||
{
|
||||
auto anchors = m_layout->anchors();
|
||||
Anchor::List result;
|
||||
for (Anchor *anchor : anchors) {
|
||||
if (!anchor->isStatic() && !(!includePlaceholders && anchor->isFollowing()))
|
||||
result << anchor;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Frame *DropArea::frameContainingPos(QPoint globalPos) const
|
||||
{
|
||||
const ItemList &items = m_layout->items();
|
||||
const Item::List &items = m_layout->items();
|
||||
for (Item *item : items) {
|
||||
auto frame = item->frame();
|
||||
auto frame = static_cast<Frame*>(item->frame());
|
||||
if (!frame || !frame->isVisible()) {
|
||||
continue;
|
||||
}
|
||||
@@ -89,7 +78,7 @@ Frame *DropArea::frameContainingPos(QPoint globalPos) const
|
||||
Item *DropArea::centralFrame() const
|
||||
{
|
||||
for (Item *item : m_layout->items()) {
|
||||
if (auto f = item->frame()) {
|
||||
if (auto f = static_cast<Frame*>(item->frame())) {
|
||||
if (f->isCentralFrame())
|
||||
return item;
|
||||
}
|
||||
@@ -142,19 +131,16 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBa
|
||||
void DropArea::debug_updateItemNamesForGammaray()
|
||||
{
|
||||
for (Item *item : m_layout->items()) {
|
||||
if (auto frame = item->frame()) {
|
||||
if (auto frame = static_cast<Frame*>(item->frame())) {
|
||||
if (!frame->dockWidgets().isEmpty())
|
||||
frame->setObjectName(frame->dockWidgets().at(0)->uniqueName());
|
||||
}
|
||||
}
|
||||
|
||||
for (Anchor *a : m_layout->anchors())
|
||||
a->debug_updateItemNames();
|
||||
}
|
||||
|
||||
bool DropArea::checkSanity(MultiSplitterLayout::AnchorSanityOption o)
|
||||
bool DropArea::checkSanity()
|
||||
{
|
||||
return m_layout->checkSanity(o);
|
||||
return m_layout->checkSanity();
|
||||
}
|
||||
|
||||
bool DropArea::contains(DockWidgetBase *dw) const
|
||||
|
||||
@@ -38,6 +38,11 @@ DropAreaWithCentralFrame::~DropAreaWithCentralFrame()
|
||||
|
||||
Frame* DropAreaWithCentralFrame::createCentralFrame(MainWindowOptions options)
|
||||
{
|
||||
return (options & MainWindowOption_HasCentralFrame) ? Config::self().frameworkWidgetFactory()->createFrame(nullptr, FrameOptions() | FrameOption_IsCentralFrame | FrameOption_AlwaysShowsTabs)
|
||||
: nullptr;
|
||||
Frame *frame = nullptr;
|
||||
if (options & MainWindowOption_HasCentralFrame) {
|
||||
frame = Config::self().frameworkWidgetFactory()->createFrame(nullptr, FrameOptions() | FrameOption_IsCentralFrame | FrameOption_AlwaysShowsTabs);
|
||||
frame->setObjectName(QStringLiteral("central frame"));
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
@@ -58,15 +58,14 @@ public:
|
||||
bool drop(QWidgetOrQuick *droppedwindow, KDDockWidgets::Location location, Frame *relativeTo);
|
||||
int numFrames() const;
|
||||
|
||||
Anchor::List nonStaticAnchors(bool includePlaceholders = false) const;
|
||||
Frame *frameContainingPos(QPoint globalPos) const;
|
||||
Item *centralFrame() const;
|
||||
Layouting::Item *centralFrame() const;
|
||||
DropIndicatorOverlayInterface *dropIndicatorOverlay() const { return m_dropIndicatorOverlay; }
|
||||
void addDockWidget(DockWidgetBase *, KDDockWidgets::Location location, DockWidgetBase *relativeTo, AddingOption option = {});
|
||||
|
||||
void debug_updateItemNamesForGammaray();
|
||||
|
||||
bool checkSanity(MultiSplitterLayout::AnchorSanityOption o = MultiSplitterLayout::AnchorSanity_All);
|
||||
bool checkSanity();
|
||||
bool contains(DockWidgetBase *) const;
|
||||
|
||||
QString affinityName() const;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "QWidgetAdapter.h"
|
||||
#include "Frame_p.h"
|
||||
#include "KDDockWidgets.h"
|
||||
#include "multisplitter/Item_p.h"
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
|
||||
@@ -280,6 +280,7 @@ bool FloatingWindow::beingDeleted() const
|
||||
if (m_beingDeleted)
|
||||
return true;
|
||||
|
||||
// TODO: Confusing logic
|
||||
for (Frame *f : frames()) {
|
||||
if (!f->beingDeletedLater())
|
||||
return false;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
static int s_dbg_numFrames = 0;
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
namespace KDDockWidgets {
|
||||
static FrameOptions actualOptions(FrameOptions options)
|
||||
@@ -293,7 +294,7 @@ void Frame::restoreToPreviousPosition()
|
||||
return;
|
||||
}
|
||||
|
||||
m_layoutItem->restorePlaceholder(this);
|
||||
m_layoutItem->restore(this);
|
||||
}
|
||||
|
||||
int Frame::currentTabIndex() const
|
||||
@@ -420,6 +421,11 @@ QString Frame::affinityName() const
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *Frame::asWidget()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
DockWidgetBase *Frame::dockWidgetAt(int index) const
|
||||
{
|
||||
return qobject_cast<DockWidgetBase *>(m_tabWidget->dockwidgetAt(index));
|
||||
@@ -493,6 +499,9 @@ bool Frame::event(QEvent *e)
|
||||
|
||||
Frame *Frame::deserialize(const LayoutSaver::Frame &f)
|
||||
{
|
||||
if (!f.isValid())
|
||||
return nullptr;
|
||||
|
||||
auto frame = Config::self().frameworkWidgetFactory()->createFrame(/*parent=*/nullptr, FrameOptions(f.options));
|
||||
frame->setObjectName(f.objectName);
|
||||
|
||||
@@ -519,6 +528,7 @@ LayoutSaver::Frame Frame::serialize() const
|
||||
frame.geometry = geometry();
|
||||
frame.options = options();
|
||||
frame.currentTabIndex = currentTabIndex();
|
||||
frame.id = QString::number(qint64(this)); // for coorelation purposes
|
||||
|
||||
for (DockWidgetBase *dock : docks)
|
||||
frame.dockWidgets.push_back(dock->serialize());
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "docks_export.h"
|
||||
#include "QWidgetAdapter.h"
|
||||
#include "LayoutSaver_p.h"
|
||||
#include "multisplitter/Item_p.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
@@ -43,7 +44,6 @@ class TitleBar;
|
||||
class TabWidget;
|
||||
class DropArea;
|
||||
class DockWidgetBase;
|
||||
class Item;
|
||||
class FloatingWindow;
|
||||
|
||||
/**
|
||||
@@ -57,6 +57,7 @@ class FloatingWindow;
|
||||
* to a FloatingWindow.
|
||||
*/
|
||||
class DOCKS_EXPORT Frame : public QWidgetAdapter
|
||||
, public Layouting::GuestInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -178,11 +179,8 @@ public:
|
||||
///@brief Called when a dock widget child @p w is hidden
|
||||
void onDockWidgetHidden(DockWidgetBase *w);
|
||||
|
||||
///@brief sets the layout item that either contains this Frame in the layout or is a placeholder
|
||||
void setLayoutItem(Item *item);
|
||||
|
||||
///@brief returns the layout item that either contains this Frame in the layout or is a placeholder
|
||||
Item *layoutItem() const;
|
||||
Layouting::Item *layoutItem() const;
|
||||
|
||||
///@brief For tests-only. Returns the number of Frame instances in the whole application.
|
||||
static int dbg_numFrames();
|
||||
@@ -204,6 +202,12 @@ public:
|
||||
|
||||
QString affinityName() const;
|
||||
|
||||
///@brief sets the layout item that either contains this Frame in the layout or is a placeholder
|
||||
void setLayoutItem(Layouting::Item *item) override;
|
||||
|
||||
///@brief Overriden from GuestInterface
|
||||
QWidget *asWidget() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void currentDockWidgetChanged(KDDockWidgets::DockWidgetBase *);
|
||||
void numDockWidgetsChanged();
|
||||
@@ -223,7 +227,7 @@ private:
|
||||
TitleBar *const m_titleBar;
|
||||
DropArea *m_dropArea = nullptr;
|
||||
const FrameOptions m_options;
|
||||
QPointer<Item> m_layoutItem;
|
||||
QPointer<Layouting::Item> m_layoutItem;
|
||||
bool m_beingDeleted = false;
|
||||
QMetaObject::Connection m_visibleWidgetCountChangedConnection;
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
LastPosition::~LastPosition()
|
||||
{
|
||||
@@ -46,7 +47,7 @@ void LastPosition::addPlaceholderItem(Item *placeholder)
|
||||
if (containsPlaceholder(placeholder))
|
||||
return;
|
||||
|
||||
if (placeholder->isInMainWindow()) {
|
||||
if (DockRegistry::self()->itemIsInMainWindow(placeholder)) {
|
||||
// 2. If we have a MainWindow placeholder we don't need nothing else
|
||||
removePlaceholders();
|
||||
} else {
|
||||
@@ -82,7 +83,7 @@ Item *LastPosition::layoutItem() const
|
||||
// In the future we might want to restore it to FloatingWindows.
|
||||
|
||||
for (const auto &itemref : m_placeholders) {
|
||||
if (itemref->item->isInMainWindow())
|
||||
if (DockRegistry::self()->itemIsInMainWindow(itemref->item))
|
||||
return itemref->item;
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ bool LastPosition::containsPlaceholder(Item *item) const
|
||||
void LastPosition::removePlaceholders(const MultiSplitterLayout *layout)
|
||||
{
|
||||
m_placeholders.erase(std::remove_if(m_placeholders.begin(), m_placeholders.end(), [layout] (const std::unique_ptr<ItemRef> &itemref) {
|
||||
return itemref->item->layout() == layout;
|
||||
return DockRegistry::self()->layoutForItem(itemref->item) == layout;
|
||||
}), m_placeholders.end());
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ void LastPosition::removeNonMainWindowPlaceholders()
|
||||
auto it = m_placeholders.begin();
|
||||
while (it != m_placeholders.end()) {
|
||||
ItemRef *itemref = it->get();
|
||||
if (!itemref->item->isInMainWindow())
|
||||
if (!DockRegistry::self()->itemIsInMainWindow(itemref->item))
|
||||
it = m_placeholders.erase(it);
|
||||
else
|
||||
++it;
|
||||
@@ -154,7 +155,7 @@ void LastPosition::deserialize(const LayoutSaver::LastPosition &lp)
|
||||
layout = mainWindow->multiSplitterLayout();
|
||||
}
|
||||
|
||||
const ItemList &items = layout->items();
|
||||
const Item::List &items = layout->items();
|
||||
if (itemIndex < items.size()) {
|
||||
Item *item = items.at(itemIndex);
|
||||
addPlaceholderItem(item);
|
||||
@@ -179,7 +180,7 @@ LayoutSaver::LastPosition LastPosition::serialize() const
|
||||
LayoutSaver::Placeholder p;
|
||||
|
||||
Item *item = itemRef->item;
|
||||
MultiSplitterLayout *layout = item->layout();
|
||||
MultiSplitterLayout *layout = DockRegistry::self()->layoutForItem(item);
|
||||
const int itemIndex = layout->items().indexOf(item);
|
||||
|
||||
auto fw = layout->multiSplitter()->floatingWindow();
|
||||
|
||||
@@ -31,16 +31,19 @@
|
||||
#include "multisplitter/Item_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "LayoutSaver_p.h"
|
||||
#include "QWidgetAdapter.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <memory>
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class MultiSplitterLayout;
|
||||
|
||||
// Just a RAII class so we don't forget to unref
|
||||
struct ItemRef
|
||||
{
|
||||
ItemRef(const QMetaObject::Connection &conn, Item *it)
|
||||
ItemRef(const QMetaObject::Connection &conn, Layouting::Item *it)
|
||||
: item(it)
|
||||
, guard(it)
|
||||
, connection(conn)
|
||||
@@ -56,8 +59,8 @@ struct ItemRef
|
||||
}
|
||||
}
|
||||
|
||||
Item *const item;
|
||||
const QPointer<Item> guard;
|
||||
Layouting::Item *const item;
|
||||
const QPointer<Layouting::Item> guard;
|
||||
const QMetaObject::Connection connection;
|
||||
private:
|
||||
Q_DISABLE_COPY(ItemRef)
|
||||
@@ -105,13 +108,13 @@ public:
|
||||
bool m_wasFloating = false;
|
||||
|
||||
///@brief Adds the last layout item where the dock widget was (or is)
|
||||
void addPlaceholderItem(Item *placeholder);
|
||||
void addPlaceholderItem(Layouting::Item *placeholder);
|
||||
|
||||
|
||||
QWidgetOrQuick *window() const;
|
||||
Item* layoutItem() const;
|
||||
Layouting::Item* layoutItem() const;
|
||||
|
||||
bool containsPlaceholder(Item*) const;
|
||||
bool containsPlaceholder(Layouting::Item*) const;
|
||||
void removePlaceholders() { m_clearing = true; m_placeholders.clear(); m_clearing = false;}
|
||||
|
||||
const std::vector<std::unique_ptr<ItemRef>>& placeholders() const { return m_placeholders; }
|
||||
@@ -123,7 +126,7 @@ public:
|
||||
void removeNonMainWindowPlaceholders();
|
||||
|
||||
///@brief removes the Item @p placeholder
|
||||
void removePlaceholder(Item *placeholder);
|
||||
void removePlaceholder(Layouting::Item *placeholder);
|
||||
|
||||
void dumpDebug()
|
||||
{
|
||||
|
||||
@@ -19,13 +19,8 @@
|
||||
*/
|
||||
|
||||
#include "Anchor_p.h"
|
||||
#include "MultiSplitterLayout_p.h"
|
||||
#include "MultiSplitter_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "LayoutSaver.h"
|
||||
#include "Config.h"
|
||||
#include "Separator_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
|
||||
#include <QRubberBand>
|
||||
#include <QApplication>
|
||||
@@ -36,161 +31,63 @@
|
||||
#endif
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
bool Anchor::s_isResizing = false;
|
||||
const QString Anchor::s_magicMarker = QStringLiteral("e520c60e-cf5d-4a30-b1a7-588d2c569851");
|
||||
Anchor* Anchor::s_separatorBeingDragged = nullptr;
|
||||
|
||||
Anchor::Anchor(Qt::Orientation orientation, MultiSplitterLayout *multiSplitter, Type type)
|
||||
: QObject(multiSplitter->multiSplitter())
|
||||
, m_orientation(orientation)
|
||||
, m_type(type)
|
||||
, m_layout(multiSplitter)
|
||||
, m_separatorWidget(Config::self().frameworkWidgetFactory()->createSeparator(this, multiSplitter->multiSplitter()))
|
||||
, m_lazyResize(Config::self().flags() & Config::Flag_LazyResize)
|
||||
, m_lazyResizeRubberBand(m_lazyResize ? new QRubberBand(QRubberBand::Line, multiSplitter->multiSplitter()) : nullptr)
|
||||
static SeparatorFactoryFunc s_separatorFactoryFunc = nullptr;
|
||||
|
||||
static Separator* createSeparator(Anchor *a, QWidget *parent)
|
||||
{
|
||||
if (s_separatorFactoryFunc)
|
||||
return s_separatorFactoryFunc(a, parent);
|
||||
|
||||
return new Separator(a, parent);
|
||||
}
|
||||
|
||||
Anchor::Anchor(ItemContainer *parentContainer, Qt::Orientation orientation,
|
||||
Options options, QWidget *hostWidget)
|
||||
: QObject(hostWidget)
|
||||
, m_orientation(orientation)
|
||||
, m_hostWidget(hostWidget)
|
||||
, m_separatorWidget(createSeparator(this, m_hostWidget))
|
||||
, m_options(options)
|
||||
, m_lazyResizeRubberBand((options & Option::LazyResize) ? new QRubberBand(QRubberBand::Line, hostWidget) : nullptr)
|
||||
, m_parentContainer(parentContainer)
|
||||
{
|
||||
multiSplitter->insertAnchor(this);
|
||||
connect(this, &QObject::objectNameChanged, m_separatorWidget, &QObject::setObjectName);
|
||||
}
|
||||
|
||||
Anchor::~Anchor()
|
||||
{
|
||||
m_separatorWidget->setEnabled(false);
|
||||
m_separatorWidget->deleteLater();
|
||||
qCDebug(multisplittercreation) << "~Anchor; this=" << this << "; m_to=" << m_to << "; m_from=" << m_from;
|
||||
m_layout->removeAnchor(this);
|
||||
for (Item *item : items(Side1))
|
||||
item->anchorGroup().setAnchor(nullptr, m_orientation, Side1);
|
||||
for (Item *item : items(Side2))
|
||||
item->anchorGroup().setAnchor(nullptr, m_orientation, Side2);
|
||||
delete m_separatorWidget;
|
||||
if (s_separatorBeingDragged == this)
|
||||
s_separatorBeingDragged = nullptr;
|
||||
}
|
||||
|
||||
void Anchor::setFrom(Anchor *from)
|
||||
QWidget *Anchor::hostWidget() const
|
||||
{
|
||||
if (from->orientation() == orientation() || from == this) {
|
||||
qWarning() << "Anchor::setFrom: Invalid from" << from->orientation() << m_orientation
|
||||
<< from << this;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_from)
|
||||
disconnect(m_from, &Anchor::positionChanged, this, &Anchor::updateSize);
|
||||
m_from = from;
|
||||
connect(from, &Anchor::positionChanged, this, &Anchor::updateSize);
|
||||
updateSize();
|
||||
|
||||
Q_EMIT fromChanged();
|
||||
}
|
||||
|
||||
void Anchor::setTo(Anchor *to)
|
||||
{
|
||||
Q_ASSERT(to);
|
||||
if (to->orientation() == orientation() || to == this) {
|
||||
qWarning() << "Anchor::setFrom: Invalid to" << to->orientation() << m_orientation
|
||||
<< to << this;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_to)
|
||||
disconnect(m_to, &Anchor::positionChanged, this, &Anchor::updateSize);
|
||||
m_to = to;
|
||||
connect(to, &Anchor::positionChanged, this, &Anchor::updateSize);
|
||||
updateSize();
|
||||
|
||||
Q_EMIT toChanged();
|
||||
}
|
||||
|
||||
void Anchor::updateSize()
|
||||
{
|
||||
if (isValid()) {
|
||||
if (isVertical()) {
|
||||
setGeometry(QRect(position(), m_from->geometry().bottom() + 1, thickness(), length()));
|
||||
} else {
|
||||
setGeometry(QRect(m_from->geometry().right() + 1, position(), length(), thickness()));
|
||||
}
|
||||
}
|
||||
|
||||
qCDebug(anchors) << "Anchor::updateSize" << this << geometry();
|
||||
return m_hostWidget;
|
||||
}
|
||||
|
||||
void Anchor::setGeometry(QRect r)
|
||||
{
|
||||
if (r != m_geometry) {
|
||||
|
||||
if (position() < 0) {
|
||||
qCDebug(anchors) << Q_FUNC_INFO << "Old position was negative" << position() << "; new=" << r;
|
||||
qCDebug(separators) << Q_FUNC_INFO << "Old position was negative" << position() << "; new=" << r;
|
||||
}
|
||||
|
||||
m_geometry = r;
|
||||
m_separatorWidget->setGeometry(r);
|
||||
m_separatorWidget->setVisible(true);
|
||||
Q_EMIT geometryChanged(r);
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::updateItemSizes()
|
||||
bool Anchor::isVertical() const
|
||||
{
|
||||
if (!m_initialized) {
|
||||
// setPosition() hasn't been called yet, don't bother
|
||||
return;
|
||||
}
|
||||
|
||||
if (LayoutSaver::restoreInProgress()) {
|
||||
// Nothing to do. The LayoutSaver is setting up the whole layout.
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(anchors) << Q_FUNC_INFO << this << "; o=" << orientation();
|
||||
|
||||
int position = this->position() + m_positionOffset;
|
||||
for (Item *item : qAsConst(m_side2Items)) {
|
||||
QRect geo = item->geometry();
|
||||
|
||||
const QPoint topLeft = isVertical() ? QPoint(position + thickness(), item->y())
|
||||
: QPoint(item->x(), position + thickness());
|
||||
|
||||
geo.setTopLeft(topLeft);
|
||||
if (!item->isPlaceholder())
|
||||
item->setGeometry(geo);
|
||||
}
|
||||
|
||||
position = this->position() - m_positionOffset;
|
||||
|
||||
for (Item *item : qAsConst(m_side1Items)) {
|
||||
QRect geo = item->geometry();
|
||||
|
||||
// -1 as the widget is right next to the anchor, and not on top
|
||||
const QPoint bottomRight = isVertical() ? QPoint(position - 1, geo.bottom())
|
||||
: QPoint(geo.right(), position - 1);
|
||||
geo.setBottomRight(bottomRight);
|
||||
if (!item->isPlaceholder()) {
|
||||
item->setGeometry(geo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::debug_updateItemNames()
|
||||
{
|
||||
// I call this in the unit-tests, when running them on gammaray
|
||||
|
||||
m_debug_side1ItemNames.clear();
|
||||
m_debug_side2ItemNames.clear();
|
||||
|
||||
for (Item *item : qAsConst(m_side1Items))
|
||||
m_debug_side1ItemNames += item->objectName() + QStringLiteral("; ");
|
||||
|
||||
for (Item *item : qAsConst(m_side2Items))
|
||||
m_debug_side2ItemNames += item->objectName() + QStringLiteral("; ");
|
||||
|
||||
Q_EMIT debug_itemNamesChanged();
|
||||
}
|
||||
|
||||
QString Anchor::debug_side1ItemNames() const
|
||||
{
|
||||
return m_debug_side1ItemNames;
|
||||
}
|
||||
|
||||
QString Anchor::debug_side2ItemNames() const
|
||||
{
|
||||
return m_debug_side2ItemNames;
|
||||
return m_orientation == Qt::Vertical;
|
||||
}
|
||||
|
||||
Qt::Orientation Anchor::orientation() const
|
||||
@@ -198,484 +95,36 @@ Qt::Orientation Anchor::orientation() const
|
||||
return m_orientation;
|
||||
}
|
||||
|
||||
void Anchor::setPosition(int p, SetPositionOptions options)
|
||||
void Anchor::setGeometry(int pos, int pos2, int length)
|
||||
{
|
||||
qCDebug(anchors) << Q_FUNC_INFO << this << "; visible="
|
||||
<< m_separatorWidget->isVisible() << "; p=" << p;
|
||||
|
||||
const int max = m_layout->length(orientation()) - Anchor::thickness(true);
|
||||
const bool outOfBounds = max != -1 && (p < 0 || p > max);
|
||||
|
||||
if (outOfBounds) {
|
||||
if (m_layout->isRestoringPlaceholder() || m_layout->isAddingItem() || m_layout->isResizing()) {
|
||||
// Don't do anything here, it will call ensureAnchorsBounded() when finished
|
||||
return;
|
||||
} else if (!LayoutSaver::restoreInProgress()) {
|
||||
m_layout->dumpDebug();
|
||||
qWarning() << Q_FUNC_INFO << "Out of bounds position=" << p
|
||||
<< "; oldPosition=" << position()
|
||||
<< "; this=" << this
|
||||
<< "; size=" << m_layout->size()
|
||||
<< "; max=" << max
|
||||
<< m_layout->multiSplitter()->window();
|
||||
}
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
if (position() == p) {
|
||||
updateItemSizes();
|
||||
return;
|
||||
}
|
||||
|
||||
QRect newGeo = m_geometry;
|
||||
if (isVertical()) {
|
||||
m_geometry.moveLeft(p);
|
||||
// The separator itself is horizontal
|
||||
newGeo.setSize(QSize(length, Item::separatorThickness()));
|
||||
newGeo.moveTo(pos2, pos);
|
||||
} else {
|
||||
m_geometry.moveTop(p);
|
||||
// The separator itself is vertical
|
||||
newGeo.setSize(QSize(Item::separatorThickness(), length));
|
||||
newGeo.moveTo(pos, pos2);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're in the middle of a resize then remember the relative positions, so we can do
|
||||
* a redistribution so that relatively all widgets occupy the same amount
|
||||
*/
|
||||
const bool recalculatePercentage = !(options & SetPositionOption_DontRecalculatePercentage) && !m_layout->isResizing();
|
||||
|
||||
m_separatorWidget->move(p);
|
||||
if (recalculatePercentage) {
|
||||
// We keep the percentage, so we don't constantly recalculate it during a resize, which introduces rounding errors
|
||||
updatePositionPercentage();
|
||||
}
|
||||
|
||||
// Note: Position can be slightly negative if the main window isn't big enougn to host the new size.
|
||||
// In that case the window will be resized shortly after
|
||||
//Q_ASSERT(p >= 0); - commented out, as it's normal
|
||||
|
||||
Q_EMIT positionChanged(position());
|
||||
updateItemSizes();
|
||||
}
|
||||
|
||||
void Anchor::updatePositionPercentage()
|
||||
{
|
||||
const int layoutLength = m_layout->length(m_orientation);
|
||||
m_positionPercentage = (position() * 1.0) / layoutLength;
|
||||
|
||||
if (position() > layoutLength) {
|
||||
// This warning makes the unit-tests fail if some invalid m_positionPercentage ever appears.
|
||||
// Bug fixed now though.
|
||||
qWarning() << Q_FUNC_INFO << "Weird position percentage" << m_positionPercentage
|
||||
<< position() << layoutLength;
|
||||
}
|
||||
setGeometry(newGeo);
|
||||
}
|
||||
|
||||
int Anchor::position() const
|
||||
{
|
||||
const QPoint topLeft = m_geometry.topLeft();
|
||||
return isVertical() ? topLeft.x() : topLeft.y();
|
||||
}
|
||||
|
||||
void Anchor::setVisible(bool v)
|
||||
{
|
||||
m_separatorWidget->setVisible(v);
|
||||
if (v) {
|
||||
m_separatorWidget->setGeometry(m_geometry);
|
||||
}
|
||||
}
|
||||
|
||||
int Anchor::minPosition() const
|
||||
{
|
||||
const int smallestSqueeze = smallestAvailableItemSqueeze(Side1);
|
||||
return position() - smallestSqueeze;
|
||||
}
|
||||
|
||||
int Anchor::smallestAvailableItemSqueeze(Anchor::Side side) const
|
||||
{
|
||||
int smallest = 0;
|
||||
bool firstElement = true;
|
||||
for (Item *item : items(side)) {
|
||||
const int length = item->length(m_orientation);
|
||||
const int minLength = item->minLength(m_orientation);
|
||||
const int availableSqueeze = length - minLength;
|
||||
if (availableSqueeze < smallest || firstElement) {
|
||||
smallest = availableSqueeze;
|
||||
firstElement = false;
|
||||
}
|
||||
}
|
||||
return smallest;
|
||||
}
|
||||
|
||||
void Anchor::ensureBounded()
|
||||
{
|
||||
// TODO: Probably delete this unused method. It was used in the old days before discovering it
|
||||
// was flawed: Separators being in between bounds doesn't imply that all min sizes are being
|
||||
// Honoured. Use MultiSplitterLayout::ensureItemsMinSize() instead
|
||||
if (!isStatic() && !isFollowing()) {
|
||||
const QPair<int,int> bounds = m_layout->boundPositionsForAnchor(this);
|
||||
|
||||
if (position() < bounds.first) {
|
||||
setPosition(bounds.first);
|
||||
} else if (position() > bounds.second) {
|
||||
setPosition(bounds.second);
|
||||
}
|
||||
}
|
||||
|
||||
for (Item *item : items(Side2)) {
|
||||
item->anchorAtSide(Side2, orientation())->ensureBounded();
|
||||
}
|
||||
}
|
||||
|
||||
int Anchor::length() const
|
||||
{
|
||||
Q_ASSERT(m_to);
|
||||
Q_ASSERT(m_from);
|
||||
return m_to->position() - m_from->position();
|
||||
}
|
||||
|
||||
bool Anchor::isValid() const
|
||||
{
|
||||
return m_to && m_from && m_to != m_from && m_to != this && m_from != this;
|
||||
}
|
||||
|
||||
int Anchor::thickness() const
|
||||
{
|
||||
return isVertical() ? m_separatorWidget->width()
|
||||
: m_separatorWidget->height();
|
||||
}
|
||||
|
||||
bool Anchor::hasItems(Anchor::Side side) const
|
||||
{
|
||||
switch (side) {
|
||||
case Side1:
|
||||
return !m_side1Items.isEmpty();
|
||||
case Side2:
|
||||
return !m_side2Items.isEmpty();
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Anchor::onlyHasPlaceholderItems(Anchor::Side side) const
|
||||
{
|
||||
auto &items = side == Side1 ? m_side1Items
|
||||
: m_side2Items;
|
||||
|
||||
for (Item *item : items) {
|
||||
if (!item->isPlaceholder())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Anchor::hasNonPlaceholderItems(Anchor::Side side) const
|
||||
{
|
||||
auto &items = side == Side1 ? m_side1Items
|
||||
: m_side2Items;
|
||||
|
||||
for (Item *item : items) {
|
||||
if (!item->isPlaceholder())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Anchor::containsItem(const Item *item, Anchor::Side side) const
|
||||
{
|
||||
switch (side) {
|
||||
case Side1:
|
||||
return m_side1Items.contains(const_cast<Item *>(item));
|
||||
case Side2:
|
||||
return m_side2Items.contains(const_cast<Item *>(item));
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Anchor::isStaticOrFollowsStatic() const
|
||||
{
|
||||
if (isStatic())
|
||||
return true;
|
||||
|
||||
return m_followee && m_followee->isStaticOrFollowsStatic();
|
||||
}
|
||||
|
||||
const ItemList Anchor::items(Anchor::Side side) const
|
||||
{
|
||||
switch (side) {
|
||||
case Side1:
|
||||
return m_side1Items;
|
||||
case Side2:
|
||||
return m_side2Items;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::consume(Anchor *other)
|
||||
{
|
||||
QPointer<Anchor> otherp = other; // Just to check if it wasn't deleted meanwhile. Which doesn't happen, but we silence a clang-tidy warning this way.
|
||||
consume(other, Side1);
|
||||
if (otherp)
|
||||
consume(other, Side2);
|
||||
}
|
||||
|
||||
void Anchor::consume(Anchor *other, Side side)
|
||||
{
|
||||
auto items = other->items(side);
|
||||
other->removeItems(side);
|
||||
addItems(items, side);
|
||||
if (other->isUnneeded()) {
|
||||
// Before deleting an unneeded anchor, we must check if there's anchors following it, and make them follow us instead
|
||||
Anchor::List anchorsFollowingOther = m_layout->anchorsFollowing(other);
|
||||
for (Anchor *follower : anchorsFollowingOther) {
|
||||
if (follower != this)
|
||||
follower->setFollowee(this);
|
||||
}
|
||||
|
||||
delete other;
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::swapItems(Anchor *other)
|
||||
{
|
||||
auto other1 = other->m_side1Items;
|
||||
auto other2 = other->m_side2Items;
|
||||
auto my1 = m_side1Items;
|
||||
auto my2 = m_side2Items;
|
||||
|
||||
removeAllItems();
|
||||
other->removeAllItems();
|
||||
|
||||
other->addItems(my1, Side1);
|
||||
other->addItems(my2, Side2);
|
||||
addItems(other1, Side1);
|
||||
addItems(other2, Side1);
|
||||
}
|
||||
|
||||
void Anchor::removeAllItems()
|
||||
{
|
||||
removeItems(Side1);
|
||||
removeItems(Side2);
|
||||
}
|
||||
|
||||
/** static */
|
||||
Anchor *Anchor::createFrom(Anchor *other, Item *relativeTo)
|
||||
{
|
||||
Q_ASSERT(other);
|
||||
auto anchor = new Anchor(other->orientation(), other->m_layout);
|
||||
anchor->setFrom(other->m_from);
|
||||
anchor->setTo(other->m_to);
|
||||
|
||||
if (relativeTo) {
|
||||
if (other->containsItem(relativeTo, Side1)) {
|
||||
other->removeItem(relativeTo);
|
||||
anchor->addItem(relativeTo, Side1);
|
||||
} else if (other->containsItem(relativeTo, Side2)) {
|
||||
other->removeItem(relativeTo);
|
||||
anchor->addItem(relativeTo, Side2);
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
} else {
|
||||
auto other1 = other->m_side1Items;
|
||||
auto other2 = other->m_side2Items;
|
||||
other->removeAllItems();
|
||||
anchor->addItems(other1, Side1);
|
||||
anchor->addItems(other2, Side2);
|
||||
}
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
void Anchor::setPositionOffset(int value)
|
||||
{
|
||||
if (value != m_positionOffset) {
|
||||
m_positionOffset = value;
|
||||
updateItemSizes();
|
||||
}
|
||||
return isVertical() ? topLeft.y() : topLeft.x();
|
||||
}
|
||||
|
||||
bool Anchor::isBeingDragged() const
|
||||
{
|
||||
return m_layout->anchorBeingDragged() == this;
|
||||
return s_separatorBeingDragged == this;
|
||||
}
|
||||
|
||||
int Anchor::cumulativeMinLength(Anchor::Side side) const
|
||||
bool Anchor::lazyResizeEnabled() const
|
||||
{
|
||||
if (isStatic() && isEmpty()) {
|
||||
// There's no widget, but minimum is the space occupied by left+right anchors (or top+bottom).
|
||||
const int staticAnchorThickness = Anchor::thickness(/*static=*/true);
|
||||
if ((side == Side2 && (m_type & (Type_LeftStatic | Type_TopStatic))) ||
|
||||
(side == Side1 && (m_type & (Type_RightStatic | Type_BottomStatic))))
|
||||
return 2 * staticAnchorThickness;
|
||||
}
|
||||
const CumulativeMin result = cumulativeMinLength_recursive(side);
|
||||
|
||||
const int numNonStaticAnchors = result.numItems >= 2 ? result.numItems - 1
|
||||
: 0;
|
||||
|
||||
int r = Anchor::thickness(isStatic()) + Anchor::thickness(true)
|
||||
+ numNonStaticAnchors*Anchor::thickness(false)
|
||||
+ result.minLength;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Anchor::CumulativeMin Anchor::cumulativeMinLength_recursive(Anchor::Side side) const
|
||||
{
|
||||
const auto items = this->items(side);
|
||||
CumulativeMin result = { 0, 0 };
|
||||
|
||||
for (auto item : items) {
|
||||
Anchor *oppositeAnchor = item->anchorAtSide(side, orientation());
|
||||
if (!oppositeAnchor) {
|
||||
// Shouldn't happen. But don't assert as this might be being called from a dumpDebug()
|
||||
qWarning() << Q_FUNC_INFO << "Null opposite anchor";
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
CumulativeMin candidateMin = { 0, 0 };
|
||||
if (!item->isPlaceholder()) {
|
||||
candidateMin.numItems++;
|
||||
candidateMin.minLength = item->minLength(orientation());
|
||||
}
|
||||
|
||||
candidateMin += oppositeAnchor->cumulativeMinLength_recursive(side);
|
||||
|
||||
if (candidateMin.minLength >= result.minLength) {
|
||||
result = candidateMin;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Anchor::setFollowee(Anchor *followee)
|
||||
{
|
||||
Q_ASSERT(this != followee);
|
||||
if (m_followee == followee)
|
||||
return;
|
||||
|
||||
qCDebug(placeholder) << Q_FUNC_INFO << "follower="
|
||||
<< this << "; followee=" << followee;
|
||||
|
||||
if (m_followee) {
|
||||
disconnect(m_followee, &Anchor::positionChanged, this, &Anchor::onFolloweePositionChanged);
|
||||
disconnect(m_followee, &Anchor::thicknessChanged, this, &Anchor::setThickness);
|
||||
disconnect(m_followeeDestroyedConnection);
|
||||
}
|
||||
|
||||
m_followee = followee;
|
||||
setThickness();
|
||||
if (m_followee) {
|
||||
Q_ASSERT(orientation() == m_followee->orientation());
|
||||
setVisible(false);
|
||||
setPosition(m_followee->position());
|
||||
connect(m_followee, &Anchor::positionChanged, this, &Anchor::onFolloweePositionChanged);
|
||||
connect(m_followee, &Anchor::thicknessChanged, this, &Anchor::setThickness);
|
||||
m_followeeDestroyedConnection = connect(m_followee, &QObject::destroyed, this, [this] {
|
||||
setFollowee(nullptr);
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
Q_EMIT followeeChanged();
|
||||
}
|
||||
|
||||
const Anchor::List Anchor::followers() const
|
||||
{
|
||||
Anchor::List result;
|
||||
for (Anchor *a : m_layout->anchors()) {
|
||||
if (a->followee() == this)
|
||||
result.push_back(a);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Anchor *Anchor::endFollowee() const
|
||||
{
|
||||
Anchor *a = m_followee;
|
||||
while (a) {
|
||||
if (!a->followee())
|
||||
return a;
|
||||
|
||||
a = a->followee();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Anchor::findAnchor(Anchor *anchor, Anchor::Side side) const
|
||||
{
|
||||
if (!anchor)
|
||||
return false;
|
||||
|
||||
Q_ASSERT(anchor != this);
|
||||
Q_ASSERT(anchor->orientation() == orientation());
|
||||
|
||||
for (Item *item : items(side)) {
|
||||
Anchor *a = item->anchorAtSide(side, orientation());
|
||||
if (anchor == a)
|
||||
return true;
|
||||
|
||||
if (a->findAnchor(anchor, side))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Anchor *Anchor::findNearestAnchorWithItems(Anchor::Side side) const
|
||||
{
|
||||
Anchor *candidate = nullptr;
|
||||
for (Item *item : items(side)) {
|
||||
Anchor *a = item->anchorAtSide(side, orientation());
|
||||
if (!a->hasNonPlaceholderItems(side))
|
||||
a = a->findNearestAnchorWithItems(side);
|
||||
|
||||
if (!candidate || (side == Side1 && a->position() > candidate->position()) || (side == Side2 && a->position() < candidate->position()) ) {
|
||||
candidate = a;
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidate)
|
||||
candidate = m_layout->staticAnchor(side, orientation());
|
||||
|
||||
Q_ASSERT(candidate->isStatic() || candidate->hasNonPlaceholderItems(side));
|
||||
return candidate;
|
||||
}
|
||||
|
||||
void Anchor::clear()
|
||||
{
|
||||
m_side1Items.clear();
|
||||
m_side2Items.clear();
|
||||
}
|
||||
|
||||
void Anchor::onFolloweePositionChanged(int pos)
|
||||
{
|
||||
Q_ASSERT(isFollowing());
|
||||
setPosition(pos);
|
||||
}
|
||||
|
||||
int Anchor::thickness(bool staticAnchor)
|
||||
{
|
||||
return Config::self().separatorThickness(staticAnchor);
|
||||
}
|
||||
|
||||
void Anchor::setLayout(MultiSplitterLayout *layout)
|
||||
{
|
||||
m_layout->removeAnchor(this);
|
||||
m_layout = layout;
|
||||
setParent(layout->multiSplitter());
|
||||
m_separatorWidget->setParent(layout->multiSplitter());
|
||||
m_layout->insertAnchor(this);
|
||||
m_layout->setAnchorBeingDragged(nullptr);
|
||||
return m_options & Option::LazyResize;
|
||||
}
|
||||
|
||||
Separator *Anchor::separatorWidget() const
|
||||
@@ -683,26 +132,6 @@ Separator *Anchor::separatorWidget() const
|
||||
return m_separatorWidget;
|
||||
}
|
||||
|
||||
void Anchor::setThickness()
|
||||
{
|
||||
const int value = isFollowing() ? m_followee->thickness()
|
||||
: thickness(isStatic());
|
||||
|
||||
const int oldValue = thickness();
|
||||
|
||||
if (value != oldValue) {
|
||||
if (isVertical()) {
|
||||
m_separatorWidget->setFixedWidth(value);
|
||||
m_geometry.setWidth(value);
|
||||
} else {
|
||||
m_separatorWidget->setFixedHeight(value);
|
||||
m_geometry.setHeight(value);
|
||||
}
|
||||
|
||||
Q_EMIT thicknessChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::setLazyPosition(int pos)
|
||||
{
|
||||
if (m_lazyPosition != pos) {
|
||||
@@ -721,67 +150,29 @@ void Anchor::setLazyPosition(int pos)
|
||||
|
||||
int Anchor::position(QPoint p) const
|
||||
{
|
||||
return isVertical() ? p.x() : p.y();
|
||||
return isVertical() ? p.y() : p.x();
|
||||
}
|
||||
|
||||
void Anchor::addItem(Item *item, Anchor::Side side)
|
||||
void Anchor::setPosition(int p)
|
||||
{
|
||||
Q_ASSERT(side != Side_None);
|
||||
auto &items = (side == Side1) ? m_side1Items : m_side2Items;
|
||||
if (!items.contains(item)) {
|
||||
items << item;
|
||||
item->anchorGroup().setAnchor(this, orientation(), side);
|
||||
Q_EMIT itemsChanged(side);
|
||||
updateItemSizes();
|
||||
}
|
||||
}
|
||||
QRect geo = m_geometry;
|
||||
QPoint pt = geo.topLeft();
|
||||
if (isVertical())
|
||||
pt.setY(p);
|
||||
else
|
||||
pt.setX(p);
|
||||
|
||||
void Anchor::addItems(const ItemList &list, Side side)
|
||||
{
|
||||
for (Item *item : list)
|
||||
addItem(item, side);
|
||||
}
|
||||
|
||||
void Anchor::removeItem(Item *item)
|
||||
{
|
||||
if (m_side1Items.removeOne(item)) {
|
||||
item->anchorGroup().setAnchor(nullptr, orientation(), Side1);
|
||||
Q_EMIT itemsChanged(Side1);
|
||||
} else {
|
||||
if (m_side2Items.removeOne(item)) {
|
||||
item->anchorGroup().setAnchor(nullptr, orientation(), Side2);
|
||||
Q_EMIT itemsChanged(Side2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::removeItems(Side side)
|
||||
{
|
||||
const auto &items = this->items(side);
|
||||
for (Item *item : items)
|
||||
removeItem(item);
|
||||
}
|
||||
|
||||
Anchor::Side Anchor::oppositeSide(Side side)
|
||||
{
|
||||
switch (side) {
|
||||
case Side1:
|
||||
return Side2;
|
||||
case Side2:
|
||||
return Side1;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return Side_None;
|
||||
}
|
||||
geo.moveTopLeft(pt);
|
||||
setGeometry(geo);
|
||||
}
|
||||
|
||||
void Anchor::onMousePress()
|
||||
{
|
||||
s_isResizing = true;
|
||||
m_layout->setAnchorBeingDragged(this);
|
||||
qCDebug(anchors) << "Drag started";
|
||||
s_separatorBeingDragged = this;
|
||||
|
||||
if (m_lazyResize) {
|
||||
qCDebug(separators) << "Drag started";
|
||||
|
||||
if (lazyResizeEnabled()) {
|
||||
setLazyPosition(position());
|
||||
m_lazyResizeRubberBand->show();
|
||||
}
|
||||
@@ -789,22 +180,21 @@ void Anchor::onMousePress()
|
||||
|
||||
void Anchor::onMouseReleased()
|
||||
{
|
||||
if (m_lazyResize) {
|
||||
if (m_lazyResizeRubberBand) {
|
||||
m_lazyResizeRubberBand->hide();
|
||||
setPosition(m_lazyPosition);
|
||||
}
|
||||
|
||||
s_isResizing = false;
|
||||
m_layout->setAnchorBeingDragged(nullptr);
|
||||
s_separatorBeingDragged = nullptr;
|
||||
}
|
||||
|
||||
void Anchor::onMouseMoved(QPoint pt)
|
||||
{
|
||||
if (!isBeingDragged() || isStatic())
|
||||
if (!isBeingDragged())
|
||||
return;
|
||||
|
||||
if (!(qApp->mouseButtons() & Qt::LeftButton)) {
|
||||
qCDebug(mouseevents) << Q_FUNC_INFO << "Ignoring spurious mouse event. Someone ate our ReleaseEvent";
|
||||
qCDebug(separators) << Q_FUNC_INFO << "Ignoring spurious mouse event. Someone ate our ReleaseEvent";
|
||||
onMouseReleased();
|
||||
return;
|
||||
}
|
||||
@@ -820,18 +210,19 @@ void Anchor::onMouseMoved(QPoint pt)
|
||||
#endif
|
||||
|
||||
const int positionToGoTo = position(pt);
|
||||
auto bounds = m_layout->boundPositionsForAnchor(this);
|
||||
const int minPos = m_parentContainer->minPosForSeparator_global(this);
|
||||
const int maxPos = m_parentContainer->maxPosForSeparator_global(this);
|
||||
|
||||
if (positionToGoTo < bounds.first || positionToGoTo > bounds.second) {
|
||||
// qDebug() << "Out of bounds" << bounds.first << bounds.second << positionToGoTo << "; currentPos" << position() << "; window size" << window()->size();
|
||||
qDebug() << "foo" << minPos << positionToGoTo << maxPos;
|
||||
|
||||
if (positionToGoTo < minPos || positionToGoTo > maxPos)
|
||||
return;
|
||||
}
|
||||
|
||||
m_lastMoveDirection = positionToGoTo < position() ? Side1
|
||||
: (positionToGoTo > position() ? Side2
|
||||
: Side_None); // Side_None shouldn't happen though.
|
||||
: Side2); // Last case shouldn't happen though.
|
||||
|
||||
if (m_lazyResize)
|
||||
if (/*m_lazyResize*/ false) // TODO
|
||||
setLazyPosition(positionToGoTo);
|
||||
else
|
||||
setPosition(positionToGoTo);
|
||||
@@ -839,11 +230,15 @@ void Anchor::onMouseMoved(QPoint pt)
|
||||
|
||||
void Anchor::onWidgetMoved(int p)
|
||||
{
|
||||
if (m_layout->anchorBeingDragged() != this) // We only care if it's being dragged by mouse
|
||||
return;
|
||||
if (!isResizing()) // We only care if it's being dragged by mouse
|
||||
return;
|
||||
|
||||
setPosition(p);
|
||||
}
|
||||
|
||||
setPosition(p);
|
||||
QRect Anchor::geometry() const
|
||||
{
|
||||
return m_geometry;
|
||||
}
|
||||
|
||||
bool Anchor::isResizing()
|
||||
@@ -851,61 +246,7 @@ bool Anchor::isResizing()
|
||||
return s_isResizing;
|
||||
}
|
||||
|
||||
|
||||
Anchor *Anchor::deserialize(const LayoutSaver::Anchor &a, MultiSplitterLayout *layout)
|
||||
void Anchor::setSeparatorFactoryFunc(SeparatorFactoryFunc func)
|
||||
{
|
||||
auto anchor = new Anchor(Qt::Orientation(a.orientation), layout, Anchor::Type(a.type));
|
||||
anchor->setObjectName(a.objectName);
|
||||
anchor->setGeometry(a.geometry);
|
||||
anchor->m_positionPercentage = a.positionPercentage;
|
||||
|
||||
anchor->setProperty("indexFrom", a.indexOfFrom);
|
||||
anchor->setProperty("indexTo", a.indexOfTo);
|
||||
anchor->setProperty("indexFolowee", a.indexOfFollowee);
|
||||
|
||||
ItemList side1Items;
|
||||
ItemList side2Items;
|
||||
const ItemList allItems = layout->items();
|
||||
side1Items.reserve(a.side1Items.size());
|
||||
for (int index : qAsConst(a.side1Items)) {
|
||||
side1Items.push_back(allItems.at(index));
|
||||
}
|
||||
side2Items.reserve(a.side2Items.size());
|
||||
for (int index : qAsConst(a.side2Items)) {
|
||||
side2Items.push_back(allItems.at(index));
|
||||
}
|
||||
|
||||
anchor->m_side1Items = side1Items;
|
||||
anchor->m_side2Items = side2Items;
|
||||
anchor->m_initialized = true;
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
LayoutSaver::Anchor Anchor::serialize() const
|
||||
{
|
||||
LayoutSaver::Anchor a;
|
||||
const Anchor::List allAnchors = m_layout->anchors();
|
||||
const ItemList allItems = m_layout->items();
|
||||
|
||||
a.objectName = objectName();
|
||||
a.type = type();
|
||||
a.geometry = geometry();
|
||||
a.orientation = orientation();
|
||||
a.indexOfFrom = allAnchors.indexOf(from());
|
||||
a.indexOfTo = allAnchors.indexOf(to());
|
||||
a.indexOfFollowee = followee() ? allAnchors.indexOf(followee()) : -1;
|
||||
a.positionPercentage = m_positionPercentage;
|
||||
|
||||
a.side1Items.clear();
|
||||
a.side1Items.reserve(this->side1Items().size());
|
||||
for (Item *item : this->side1Items())
|
||||
a.side1Items.push_back(allItems.indexOf(item));
|
||||
|
||||
a.side2Items.clear();
|
||||
a.side2Items.reserve(this->side2Items().size());
|
||||
for (Item *item : this->side2Items())
|
||||
a.side2Items.push_back(allItems.indexOf(item));
|
||||
|
||||
return a;
|
||||
s_separatorFactoryFunc = func;
|
||||
}
|
||||
|
||||
@@ -1,468 +0,0 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "AnchorGroup_p.h"
|
||||
#include "Anchor_p.h"
|
||||
#include "MultiSplitterLayout_p.h"
|
||||
#include "MultiSplitter_p.h"
|
||||
#include "Logging_p.h"
|
||||
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
AnchorGroup::AnchorGroup(MultiSplitterLayout *l)
|
||||
: layout(l)
|
||||
{
|
||||
}
|
||||
|
||||
int AnchorGroup::width() const
|
||||
{
|
||||
return right->position() - left->position();
|
||||
}
|
||||
|
||||
int AnchorGroup::height() const
|
||||
{
|
||||
return bottom->position() - top->position();
|
||||
}
|
||||
|
||||
bool AnchorGroup::containsAnchor(Anchor *anchor) const
|
||||
{
|
||||
return anchor == left || anchor == top || anchor == right || anchor == bottom;
|
||||
}
|
||||
|
||||
bool AnchorGroup::containsAnchor(Anchor *anchor, Anchor::Side side) const
|
||||
{
|
||||
if (side == Anchor::Side1)
|
||||
return anchor == left || anchor == top;
|
||||
return anchor == right || anchor == bottom;
|
||||
}
|
||||
|
||||
QSize AnchorGroup::availableSize() const
|
||||
{
|
||||
const int leftBound = left->isStatic() ? left->position()
|
||||
: layout->boundPositionForAnchor(left, Anchor::Side1);
|
||||
|
||||
const int rightBound = right->isStatic() ? right->position()
|
||||
: layout->boundPositionForAnchor(right, Anchor::Side2);
|
||||
|
||||
const int topBound = top->isStatic() ? top->position()
|
||||
: layout->boundPositionForAnchor(top, Anchor::Side1);
|
||||
|
||||
const int bottomBound = bottom->isStatic() ? bottom->position()
|
||||
: layout->boundPositionForAnchor(bottom, Anchor::Side2);
|
||||
|
||||
return QSize(rightBound - leftBound - left->thickness(),
|
||||
bottomBound - topBound - top->thickness());
|
||||
}
|
||||
|
||||
QSize AnchorGroup::itemSize() const
|
||||
{
|
||||
return QSize(right->position() - left->position() - left->thickness(),
|
||||
bottom->position() - top->position() - top->thickness());
|
||||
}
|
||||
|
||||
int AnchorGroup::itemSize(Qt::Orientation o) const
|
||||
{
|
||||
return o == Qt::Vertical ? itemSize().width()
|
||||
: itemSize().height();
|
||||
}
|
||||
|
||||
bool AnchorGroup::hasAvailableSizeFor(QSize needed, Qt::Orientation orientation) const
|
||||
{
|
||||
const QSize available = availableSize();
|
||||
return orientation == Qt::Vertical ? available.width() >= needed.width()
|
||||
: available.height() >= needed.height();
|
||||
}
|
||||
|
||||
AnchorGroup AnchorGroup::outterGroup() const
|
||||
{
|
||||
AnchorGroup group(layout);
|
||||
|
||||
group.left = left->hasNonPlaceholderItems(Anchor::Side1) ? left
|
||||
: left->findNearestAnchorWithItems(Anchor::Side1);
|
||||
|
||||
group.top = top->hasNonPlaceholderItems(Anchor::Side1) ? top
|
||||
: top->findNearestAnchorWithItems(Anchor::Side1);
|
||||
|
||||
|
||||
group.right = right->hasNonPlaceholderItems(Anchor::Side2) ? right
|
||||
: right->findNearestAnchorWithItems(Anchor::Side2);
|
||||
|
||||
group.bottom = bottom->hasNonPlaceholderItems(Anchor::Side2) ? bottom
|
||||
: bottom->findNearestAnchorWithItems(Anchor::Side2);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
Anchor *AnchorGroup::oppositeAnchor(Anchor *a) const
|
||||
{
|
||||
if (a == left)
|
||||
return right;
|
||||
if (a == right)
|
||||
return left;
|
||||
if (a == top)
|
||||
return bottom;
|
||||
if (a == bottom)
|
||||
return top;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Anchor *AnchorGroup::createAnchorFrom(Location fromAnchorLocation, Item *relativeTo)
|
||||
{
|
||||
Anchor *other = anchor(fromAnchorLocation);
|
||||
Q_ASSERT(other);
|
||||
|
||||
auto anchor = new Anchor(other->orientation(), other->m_layout);
|
||||
if (anchor->isVertical()) {
|
||||
anchor->setFrom(top);
|
||||
anchor->setTo(bottom);
|
||||
} else {
|
||||
anchor->setFrom(left);
|
||||
anchor->setTo(right);
|
||||
}
|
||||
|
||||
if (relativeTo) {
|
||||
if (other->containsItem(relativeTo, Anchor::Side1)) {
|
||||
other->removeItem(relativeTo);
|
||||
anchor->addItem(relativeTo, Anchor::Side1);
|
||||
} else if (other->containsItem(relativeTo, Anchor::Side2)) {
|
||||
other->removeItem(relativeTo);
|
||||
anchor->addItem(relativeTo, Anchor::Side2);
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
} else {
|
||||
auto other1 = other->m_side1Items;
|
||||
auto other2 = other->m_side2Items;
|
||||
other->removeAllItems();
|
||||
anchor->addItems(other1, Anchor::Side1);
|
||||
anchor->addItems(other2, Anchor::Side2);
|
||||
}
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
Anchor *AnchorGroup::anchor(Location loc) const
|
||||
{
|
||||
switch (loc) {
|
||||
case KDDockWidgets::Location_OnLeft:
|
||||
return left;
|
||||
case KDDockWidgets::Location_OnTop:
|
||||
return top;
|
||||
case KDDockWidgets::Location_OnRight:
|
||||
return right;
|
||||
case KDDockWidgets::Location_OnBottom:
|
||||
return bottom;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Anchor *AnchorGroup::anchorAtDirection(Anchor::Side side, Qt::Orientation orientation) const
|
||||
{
|
||||
const bool isSide1 = side == Anchor::Side1;
|
||||
if (orientation == Qt::Vertical) {
|
||||
return isSide1 ? right : left;
|
||||
} else {
|
||||
return isSide1 ? bottom : top;
|
||||
}
|
||||
}
|
||||
|
||||
Anchor *AnchorGroup::anchorAtSide(Anchor::Side side, Qt::Orientation orientation) const
|
||||
{
|
||||
const bool isSide1 = side == Anchor::Side1;
|
||||
if (orientation == Qt::Vertical) {
|
||||
return isSide1 ? left: right;
|
||||
} else {
|
||||
return isSide1 ? top : bottom;
|
||||
}
|
||||
}
|
||||
|
||||
void AnchorGroup::setAnchor(Anchor *anchor, Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
|
||||
case KDDockWidgets::Location_OnLeft:
|
||||
left = anchor;
|
||||
break;
|
||||
case KDDockWidgets::Location_OnTop:
|
||||
top = anchor;
|
||||
break;
|
||||
case KDDockWidgets::Location_OnRight:
|
||||
right = anchor;
|
||||
break;
|
||||
case KDDockWidgets::Location_OnBottom:
|
||||
bottom = anchor;
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool AnchorGroup::anchorIsFollowingInwards(Anchor *anchor) const
|
||||
{
|
||||
if (!anchor)
|
||||
return false;
|
||||
|
||||
if (anchor == left && left->findAnchor(left->endFollowee(), Anchor::Side2))
|
||||
return true;
|
||||
|
||||
if (anchor == top && top->findAnchor(top->endFollowee(), Anchor::Side2))
|
||||
return true;
|
||||
|
||||
if (anchor == right && right->findAnchor(right->endFollowee(), Anchor::Side1))
|
||||
return true;
|
||||
|
||||
if (anchor == bottom && bottom->findAnchor(bottom->endFollowee(), Anchor::Side1))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QDebug AnchorGroup::debug(QDebug d) const
|
||||
{
|
||||
d << "AnchorGroup: this=" << ((void*)this) << "\n; top=" << top << "; left=" << left
|
||||
<< "\n ; right=" << right << "; bottom=" << bottom
|
||||
<< "\n ; valid=" << isValid()
|
||||
<< anchorIsFollowingInwards(left) << anchorIsFollowingInwards(top)
|
||||
<< anchorIsFollowingInwards(right) << anchorIsFollowingInwards(bottom)
|
||||
<< (left ? left->followee() : nullptr)
|
||||
<< "\n";
|
||||
return d;
|
||||
}
|
||||
|
||||
const Anchor::List AnchorGroup::anchorsFollowingInwards() const
|
||||
{
|
||||
Anchor::List result;
|
||||
if (anchorIsFollowingInwards(left))
|
||||
result.push_back(left);
|
||||
|
||||
if (anchorIsFollowingInwards(top))
|
||||
result.push_back(top);
|
||||
|
||||
if (anchorIsFollowingInwards(right)) {
|
||||
result.push_back(right);
|
||||
Q_ASSERT(!result.contains(left));
|
||||
}
|
||||
|
||||
if (anchorIsFollowingInwards(bottom)) {
|
||||
result.push_back(bottom);
|
||||
Q_ASSERT(!result.contains(top));
|
||||
}
|
||||
|
||||
Q_ASSERT(result.size() <= 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
const Anchor::List AnchorGroup::anchorsNotFollowingInwards() const
|
||||
{
|
||||
Anchor::List result = anchors();
|
||||
for (Anchor *a : anchorsFollowingInwards())
|
||||
result.removeOne(a);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const Anchor::List AnchorGroup::anchors() const
|
||||
{
|
||||
return { left, top, right, bottom };
|
||||
}
|
||||
|
||||
Anchor::Side AnchorGroup::sideForAnchor(Anchor *a) const
|
||||
{
|
||||
if (a == left || a == top)
|
||||
return Anchor::Side1;
|
||||
return Anchor::Side2;
|
||||
}
|
||||
|
||||
bool AnchorGroup::isStatic() const
|
||||
{
|
||||
return top->isStatic() && bottom->isStatic() && left->isStatic() && right->isStatic();
|
||||
}
|
||||
|
||||
bool AnchorGroup::isStaticOrFollowsStatic() const
|
||||
{
|
||||
return top->isStaticOrFollowsStatic() && bottom->isStaticOrFollowsStatic()
|
||||
&& left->isStaticOrFollowsStatic() && right->isStaticOrFollowsStatic();
|
||||
}
|
||||
|
||||
void AnchorGroup::updateItemSizes()
|
||||
{
|
||||
// Sets the geometry of the items that are inside this group
|
||||
left->updateItemSizes();
|
||||
top->updateItemSizes();
|
||||
right->updateItemSizes();
|
||||
bottom->updateItemSizes();
|
||||
}
|
||||
|
||||
void AnchorGroup::setAnchor(Anchor *a, Qt::Orientation orientation, Anchor::Side side)
|
||||
{
|
||||
const bool isSide1 = side == Anchor::Side1;
|
||||
if (orientation == Qt::Vertical) {
|
||||
if (isSide1)
|
||||
right = a;
|
||||
else
|
||||
left = a;
|
||||
} else {
|
||||
if (isSide1)
|
||||
bottom = a;
|
||||
else
|
||||
top = a;
|
||||
}
|
||||
}
|
||||
|
||||
Anchor *AnchorGroup::adjacentAnchor(Anchor *other) const
|
||||
{
|
||||
if (other == top)
|
||||
return right;
|
||||
if (other == right)
|
||||
return bottom;
|
||||
if (other == bottom)
|
||||
return left;
|
||||
if (other == left)
|
||||
return top;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QPair<Anchor *, Anchor *> AnchorGroup::adjacentAnchors(Anchor *anchor) const
|
||||
{
|
||||
if (anchor == left || anchor == right) {
|
||||
return { top, bottom };
|
||||
} else if (anchor == top || anchor == bottom) {
|
||||
return { left, right };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void AnchorGroup::addItem(Item *item)
|
||||
{
|
||||
// Dropping a single dockwidget, without any nesting
|
||||
left->addItem(item, Anchor::Side2);
|
||||
top->addItem(item, Anchor::Side2);
|
||||
right->addItem(item, Anchor::Side1);
|
||||
bottom->addItem(item, Anchor::Side1);
|
||||
}
|
||||
|
||||
void AnchorGroup::addItem(MultiSplitterLayout *sourceMultiSplitter)
|
||||
{
|
||||
// Here we rip all the widgets and anchors from the source multisplitter into the receiving multisplitter
|
||||
// preserving the layout between source widgets. Then we delete the source splitter, as all its
|
||||
// content has bene integrated into ours
|
||||
|
||||
// To prevent the source splitter from deleting the anchors once the widgets are reparented
|
||||
sourceMultiSplitter->m_beingMergedIntoAnotherMultiSplitter = true;
|
||||
|
||||
// Reparent the widgets:
|
||||
for (Item *sourceItem : sourceMultiSplitter->items()) {
|
||||
sourceItem->setLayout(layout);
|
||||
sourceItem->setVisible(true);
|
||||
}
|
||||
|
||||
// Reparent the inner anchors, they're ours now
|
||||
for (Anchor *anchor : sourceMultiSplitter->anchors()) {
|
||||
if (!anchor->isStatic()) {
|
||||
const qreal positionPercentage = anchor->positionPercentage();
|
||||
anchor->setLayout(layout);
|
||||
anchor->setVisible(true);
|
||||
|
||||
if (anchor->from()->isStatic()) {
|
||||
if (anchor->isVertical()) {
|
||||
anchor->setFrom(top);
|
||||
} else {
|
||||
anchor->setFrom(left);
|
||||
}
|
||||
}
|
||||
|
||||
if (anchor->to()->isStatic()) {
|
||||
if (anchor->isVertical()) {
|
||||
anchor->setTo(bottom);
|
||||
} else {
|
||||
anchor->setTo(right);
|
||||
}
|
||||
}
|
||||
|
||||
// And update their position
|
||||
|
||||
qreal newPos = 0;
|
||||
if (anchor->isVertical()) {
|
||||
newPos = left->position() + (width() * positionPercentage);
|
||||
} else {
|
||||
newPos = top->position() + (height() * positionPercentage);
|
||||
}
|
||||
|
||||
const QPair<int,int> bounds = layout->boundPositionsForAnchor(anchor);
|
||||
anchor->setPosition(qBound(bounds.first, static_cast<int>(newPos), bounds.second));
|
||||
}
|
||||
}
|
||||
|
||||
AnchorGroup sourceAnchorGroup = sourceMultiSplitter->staticAnchorGroup();
|
||||
|
||||
Q_ASSERT(sourceAnchorGroup.isValid());
|
||||
top->consume(sourceAnchorGroup.top);
|
||||
bottom->consume(sourceAnchorGroup.bottom);
|
||||
left->consume(sourceAnchorGroup.left);
|
||||
right->consume(sourceAnchorGroup.right);
|
||||
|
||||
delete sourceMultiSplitter->multiSplitter(); // Delete MultiSplitter and MultiSplitterLayout
|
||||
}
|
||||
|
||||
void AnchorGroup::removeItem(Item *item)
|
||||
{
|
||||
left->removeItem(item);
|
||||
right->removeItem(item);
|
||||
bottom->removeItem(item);
|
||||
top->removeItem(item);
|
||||
|
||||
if (left->isUnneeded()) {
|
||||
layout->updateAnchorsFromTo(left, right);
|
||||
const int leftPosition = left->position();
|
||||
right->consume(left, Anchor::Side1);
|
||||
|
||||
if (!right->isUnneeded() && !right->isStatic()) {
|
||||
// Make use of the extra space, so it's fair
|
||||
right->setPosition(right->position() - ((right->position() - leftPosition) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
if (right->isUnneeded()) {
|
||||
layout->updateAnchorsFromTo(right, left);
|
||||
left->consume(right, Anchor::Side2);
|
||||
}
|
||||
|
||||
if (top->isUnneeded()) {
|
||||
layout->updateAnchorsFromTo(top, bottom);
|
||||
const int topPosition = top->position();
|
||||
bottom->consume(top, Anchor::Side1);
|
||||
|
||||
if (!bottom->isUnneeded() && !bottom->isStatic()) {
|
||||
// Make use of the extra space, so it's fair
|
||||
bottom->setPosition(bottom->position() - ((bottom->position() - topPosition) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
if (bottom->isUnneeded()) {
|
||||
layout->updateAnchorsFromTo(bottom, top);
|
||||
top->consume(bottom, Anchor::Side2);
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KD_MULTISPLITTER_ANCHORGROUP_P_H
|
||||
#define KD_MULTISPLITTER_ANCHORGROUP_P_H
|
||||
|
||||
#include "docks_export.h"
|
||||
#include "KDDockWidgets.h"
|
||||
#include "Anchor_p.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class MultiSplitterLayout;
|
||||
class Anchor;
|
||||
class Item;
|
||||
|
||||
struct DOCKS_EXPORT_FOR_UNIT_TESTS AnchorGroup
|
||||
{
|
||||
///@brief contructs an invalid group
|
||||
AnchorGroup() = default;
|
||||
|
||||
explicit AnchorGroup(MultiSplitterLayout *);
|
||||
|
||||
void addItem(Item *item);
|
||||
void addItem(MultiSplitterLayout *);
|
||||
void removeItem(Item *item);
|
||||
bool isValid() const { return top && left && bottom && right; }
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
|
||||
///@brief returns whether this group contains @p anchor
|
||||
bool containsAnchor(Anchor *anchor) const;
|
||||
|
||||
///@brief returns whether this group contains @p anchor at Side @p side
|
||||
///If side is Side1, then anchor must be equal to left or top, otherwise top or bottom
|
||||
bool containsAnchor(Anchor *anchor, Anchor::Side side) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the max available size in this group
|
||||
* This is the size of the widget when you push all anchors outwards
|
||||
*/
|
||||
QSize availableSize() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the size of an item that would be inside these 4 anchors
|
||||
*/
|
||||
QSize itemSize() const;
|
||||
|
||||
/**
|
||||
* @brief Similar to @ref itemSize(), but returns the width if @p o is Qt::Vertical, otherwise
|
||||
* the height
|
||||
*/
|
||||
int itemSize(Qt::Orientation o) const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether @ref availableSize is bigger or equal than @ref needed
|
||||
*/
|
||||
bool hasAvailableSizeFor(QSize needed, Qt::Orientation orientation) const;
|
||||
|
||||
/// Returns the group formed by the Anchors that actually have items on their outter side
|
||||
AnchorGroup outterGroup() const;
|
||||
|
||||
Anchor *oppositeAnchor(Anchor*) const;
|
||||
Anchor *createAnchorFrom(KDDockWidgets::Location fromAnchorLocation, Item *relativeTo);
|
||||
void setAnchor(Anchor *a, Qt::Orientation orientation, Anchor::Side side);
|
||||
|
||||
Anchor *adjacentAnchor(Anchor*) const;
|
||||
|
||||
QPair<Anchor*,Anchor*> adjacentAnchors(Anchor*) const;
|
||||
|
||||
Anchor *anchor(KDDockWidgets::Location) const;
|
||||
Anchor *anchorAtDirection(Anchor::Side side, Qt::Orientation orientation) const;
|
||||
|
||||
Anchor *anchorAtSide(Anchor::Side side, Qt::Orientation orientation) const;
|
||||
|
||||
void setAnchor(Anchor *anchor, KDDockWidgets::Location);
|
||||
|
||||
bool anchorIsFollowingInwards(Anchor*) const;
|
||||
const Anchor::List anchorsFollowingInwards() const;
|
||||
const Anchor::List anchorsNotFollowingInwards() const;
|
||||
const Anchor::List anchors() const;
|
||||
|
||||
Anchor::Side sideForAnchor(Anchor*) const;
|
||||
bool isStatic() const;
|
||||
bool isStaticOrFollowsStatic() const;
|
||||
|
||||
void updateItemSizes();
|
||||
|
||||
|
||||
Anchor *top = nullptr;
|
||||
Anchor *left = nullptr;
|
||||
Anchor *bottom = nullptr;
|
||||
Anchor *right = nullptr;
|
||||
MultiSplitterLayout *layout;
|
||||
|
||||
QDebug debug(QDebug d) const;
|
||||
};
|
||||
}
|
||||
|
||||
inline QDebug operator<< (QDebug d, KDDockWidgets::AnchorGroup *group)
|
||||
{
|
||||
// out-of-line as it needs to include MultiSplitterLayout
|
||||
return group->debug(d);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -21,8 +21,7 @@
|
||||
#ifndef KD_MULTISPLITTER_ANCHOR_P_H
|
||||
#define KD_MULTISPLITTER_ANCHOR_P_H
|
||||
|
||||
#include "docks_export.h"
|
||||
#include "LayoutSaver_p.h"
|
||||
#include "Item_p.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
@@ -34,325 +33,83 @@ class QRubberBand;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class Item;
|
||||
class MultiSplitterLayout;
|
||||
class Separator;
|
||||
}
|
||||
|
||||
typedef QVector<Item*> ItemList;
|
||||
namespace Layouting {
|
||||
|
||||
/**
|
||||
* @brief An anchor is the vertical or horizontal (@ref orientation()) line that has an handle
|
||||
* so you can resize widgets with your mouse.
|
||||
*
|
||||
* A MultiSplitter comes with 4 static anchors (@ref isStatic()), that represent the top, left, right
|
||||
* and bottom borders. A static anchor means it can't change position, doesn't display the handle and
|
||||
* will have the same lifetime has the MultiSplitter.
|
||||
*
|
||||
* Each anchor has two properties indicating in which anchor it starts and where it ends, @ref from(), to().
|
||||
* For example, the top static horizontal anchor starts at the left anchor and ends at the right static anchor.
|
||||
* If this anchor is vertical, then from()/to() return horizontal anchors, and vice-versa.
|
||||
*
|
||||
* An anchor has a length, which is to()->pos() - from()->pos(). The length of a vertical anchor is,
|
||||
* thus, its vertical extent (Likewise for horizontal anchors).
|
||||
*
|
||||
* An anchor controls two groups of widgets: side1 and side2 widgets. When an anchor is dragged with mouse
|
||||
* it will resize those widgets. The widgets always start or end at the position where the anchor lives.
|
||||
* For vertical anchors, side1 means "the widgets at its left" and side2 means "the widgets at its right",
|
||||
* Same principle for horizontal anchors, but for top/bottom instead.
|
||||
* Static anchors only have 1 side with widgets. For example the left static anchor only has widgets at its
|
||||
* right, so side1Widgets is empty.
|
||||
* Non-static anchors, always have side1 and side2 widgets. If not then they are considered unneeded
|
||||
* and are deleted.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* +--------------------+
|
||||
* | | |
|
||||
* | | |
|
||||
* | | |
|
||||
* | Foo | Bar |
|
||||
* | | |
|
||||
* | | |
|
||||
* +--------------------+
|
||||
*
|
||||
* In the above example we have 5 anchors. 4 of them are static (left, right, top, bottom) and there's
|
||||
* a non-static one, in the middle. It's vertical, and can be dragged left and right, resizing its
|
||||
* side1Widgets (Foo) and side2Widgets (Bar). This non-static anchors has from=top anchor, and to=bottom anchor.
|
||||
*
|
||||
*/
|
||||
class DOCKS_EXPORT_FOR_UNIT_TESTS Anchor : public QObject // clazy:exclude=ctor-missing-parent-argument
|
||||
class Anchor : public QObject // clazy:exclude=ctor-missing-parent-argument
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
// properties for GammaRay
|
||||
Q_PROPERTY(KDDockWidgets::ItemList side1Items READ side1Items NOTIFY itemsChanged)
|
||||
Q_PROPERTY(KDDockWidgets::ItemList side2Items READ side2Items NOTIFY itemsChanged)
|
||||
|
||||
Q_PROPERTY(QString debug_side1ItemNames READ debug_side1ItemNames NOTIFY debug_itemNamesChanged)
|
||||
Q_PROPERTY(QString debug_side2ItemNames READ debug_side2ItemNames NOTIFY debug_itemNamesChanged)
|
||||
|
||||
Q_PROPERTY(Anchor* from READ from WRITE setFrom NOTIFY fromChanged)
|
||||
Q_PROPERTY(Anchor* to READ to WRITE setTo NOTIFY toChanged)
|
||||
Q_PROPERTY(int position READ position WRITE setPosition NOTIFY positionChanged)
|
||||
Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged)
|
||||
Q_PROPERTY(Qt::Orientation orientation READ orientation CONSTANT)
|
||||
Q_PROPERTY(Anchor *followee READ followee NOTIFY followeeChanged)
|
||||
public:
|
||||
///@brief represents the Anchor type
|
||||
///An anchor can be of 2 types:
|
||||
/// - Normal: Anchor that can be resized via mouse
|
||||
/// - static: this is the top, left, right, bottom borders of the main window. They are called static because they don't move.
|
||||
enum Type {
|
||||
Type_None = 0, ///< The anchor is normal, and can be resized.
|
||||
Type_LeftStatic = 1, ///< The anchor is static and represents the left mainwindow margin
|
||||
Type_RightStatic = 2, ///< The anchor is static and represents the right mainwindow margin
|
||||
Type_TopStatic = 4, ///< The anchor is static and represents the top mainwindow margin
|
||||
Type_BottomStatic = 8, ///< The anchor is static and represents the bottom mainwindow margin
|
||||
Type_Static = Type_TopStatic | Type_LeftStatic | Type_RightStatic | Type_BottomStatic ///< The anchor is static, one of the 4 previous ones
|
||||
};
|
||||
Q_ENUM(Type)
|
||||
|
||||
enum Side {
|
||||
Side_None = 0,
|
||||
Side1,
|
||||
Side2
|
||||
enum class Option {
|
||||
None = 0,
|
||||
LazyResize
|
||||
};
|
||||
Q_ENUM(Side)
|
||||
|
||||
enum SetPositionOption {
|
||||
SetPositionOption_None = 0,
|
||||
SetPositionOption_DontRecalculatePercentage = 1
|
||||
};
|
||||
Q_DECLARE_FLAGS(SetPositionOptions, SetPositionOption)
|
||||
Q_DECLARE_FLAGS(Options, Option);
|
||||
|
||||
typedef QVector<Anchor *> List;
|
||||
explicit Anchor(Qt::Orientation orientation, MultiSplitterLayout *multiSplitter, Type = Type_None);
|
||||
explicit Anchor(ItemContainer *parentContainer, Qt::Orientation orientation,
|
||||
Options options, QWidget *hostWidget);
|
||||
|
||||
~Anchor() override;
|
||||
static Anchor* deserialize(const LayoutSaver::Anchor &, MultiSplitterLayout *layout);
|
||||
LayoutSaver::Anchor serialize() const;
|
||||
|
||||
void setFrom(Anchor *);
|
||||
Anchor *from() const { return m_from; }
|
||||
Anchor *to() const { return m_to; }
|
||||
void setTo(Anchor *);
|
||||
QWidget *hostWidget() const;
|
||||
|
||||
Qt::Orientation orientation() const;
|
||||
void addItem(Item *, Side);
|
||||
void addItems(const ItemList &list, Side);
|
||||
void removeItem(Item *w);
|
||||
void removeItems(Side);
|
||||
bool isVertical() const { return m_orientation == Qt::Vertical; }
|
||||
void setPosition(int p, SetPositionOptions = SetPositionOption_None);
|
||||
void updatePositionPercentage();
|
||||
void setGeometry(int pos, int pos2, int length);
|
||||
int position() const;
|
||||
|
||||
void setVisible(bool);
|
||||
qreal positionPercentage() const { return m_positionPercentage; }
|
||||
|
||||
void ensureBounded();
|
||||
|
||||
/**
|
||||
* @brief Sets the new layout. Called when we're dropping a source layout into a target one.
|
||||
* The target one will steal the separators of the source one.
|
||||
*/
|
||||
void setLayout(MultiSplitterLayout *);
|
||||
|
||||
///@brief returns the separator widget
|
||||
Separator* separatorWidget() const;
|
||||
|
||||
/**
|
||||
* Returns how far left or top an anchor can go and still respecting it's Side1 widgets min-size.
|
||||
* This function doesn't count with shifting other anchors, for that use MultiSplitterLayout::boundPositionsForAnchor()
|
||||
* which is is recursive and returns the bounds after simulating that intermediary anchors to the left/top were
|
||||
* also resized (each still respecting widgets min sizes though).
|
||||
*/
|
||||
int minPosition() const;
|
||||
|
||||
/**
|
||||
* A squeeze is a widget's width (or height for horizontal anchors) minus its minimum width.
|
||||
* This function iterates through all widgets of the specified side and returns the minimum
|
||||
* available squeeze.
|
||||
*/
|
||||
int smallestAvailableItemSqueeze(Anchor::Side) const;
|
||||
|
||||
/**
|
||||
* @brief The length of this anchor. The distance between @ref from and @ref to.
|
||||
* @return the anchor's length
|
||||
*/
|
||||
int length() const;
|
||||
|
||||
/**
|
||||
* @brief Checks if this anchor is valid. It's valid if @ref from and @ref to are non-null, and not the same.
|
||||
* @return true if this anchor is valid.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/**
|
||||
* @brief The width of a vertical anchor, or height of an horizontal anchor.
|
||||
*/
|
||||
int thickness() const;
|
||||
|
||||
/**
|
||||
* @brief Checks if this Anchor is static.
|
||||
* @return true if this Anchor is static.
|
||||
*/
|
||||
bool isStatic() const { return m_type & Type_Static; }
|
||||
|
||||
bool isUnneeded() const { return !isStatic() && (!hasItems(Side1) || !hasItems(Side2)); }
|
||||
bool isEmpty() const { return !hasItems(Side1) && !hasItems(Side2); }
|
||||
bool hasItems(Side) const;
|
||||
bool hasNonPlaceholderItems(Side) const;
|
||||
bool onlyHasPlaceholderItems(Anchor::Side side) const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether this Anchor should follow another one. That happens if one of it's side is empty or only has placeholders
|
||||
* Also, it can't be a static anchor.
|
||||
*/
|
||||
bool shouldFollow() const { return !isStatic() && (onlyHasPlaceholderItems(Side1) || onlyHasPlaceholderItems(Side2)); }
|
||||
|
||||
bool containsItem(const Item *w, Side side) const;
|
||||
bool isStaticOrFollowsStatic() const;
|
||||
|
||||
const ItemList items(Side side) const;
|
||||
const ItemList side1Items() const { return m_side1Items; }
|
||||
const ItemList side2Items() const { return m_side2Items; }
|
||||
|
||||
void consume(Anchor *other);
|
||||
void consume(Anchor *other, Side);
|
||||
void swapItems(Anchor *other);
|
||||
void removeAllItems();
|
||||
|
||||
static Anchor *createFrom(Anchor *other, Item *relativeTo = nullptr);
|
||||
void setPositionOffset(int);
|
||||
bool isBeingDragged() const;
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
||||
int cumulativeMinLength(Anchor::Side side) const;
|
||||
|
||||
/**
|
||||
* @brief Makes this separator follow another one. This one will be made invisible.
|
||||
* Used when the item in the layout is just a placeholder remembering a previous dock widget position.
|
||||
* Pass nullptr do make it not follow and visible again.
|
||||
*/
|
||||
void setFollowee(Anchor *);
|
||||
|
||||
/**
|
||||
* @brief getter for the followee
|
||||
*/
|
||||
Anchor *followee() const { return m_followee; }
|
||||
|
||||
/**
|
||||
* @brief Returns the list of anchors following this one.
|
||||
*/
|
||||
const List followers() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the last followee in the chain.
|
||||
*/
|
||||
Anchor *endFollowee() const;
|
||||
|
||||
/**
|
||||
* @brief Recursively looks for an anchor in the whole layout but only looking at side @p side
|
||||
*
|
||||
* This allows us to know if there's an anchor on the top or left of us (side1) or right or bottom
|
||||
* (side2), in the whole layout.
|
||||
*
|
||||
* Returns false if @p anchor is nullptr
|
||||
*/
|
||||
bool findAnchor(Anchor *anchor, Side side) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the nearest Anchor with non-placeholder items on side @p side
|
||||
* If nothing is found then returns the static anchor on that side
|
||||
*/
|
||||
Anchor *findNearestAnchorWithItems(Side side) const;
|
||||
|
||||
///@brief removes the side1 and side2 items. Doesn't delete them
|
||||
void clear();
|
||||
|
||||
static int thickness(bool staticAnchor);
|
||||
static Anchor::Side oppositeSide(Side side);
|
||||
void onFolloweePositionChanged(int pos);
|
||||
bool isFollowing() const { return m_followee != nullptr; }
|
||||
bool lazyResizeEnabled() const;
|
||||
|
||||
void onMousePress();
|
||||
void onMouseReleased();
|
||||
void onMouseMoved(QPoint pt);
|
||||
void onWidgetMoved(int p);
|
||||
|
||||
QRect geometry() const;
|
||||
bool isVertical() const;
|
||||
|
||||
///@brief Returns whether we're dragging a separator. Can be useful for the app to stop other work while we're not in the final size
|
||||
static bool isResizing();
|
||||
|
||||
private:
|
||||
struct CumulativeMin {
|
||||
int minLength;
|
||||
int numItems;
|
||||
CumulativeMin& operator+=(CumulativeMin other) {
|
||||
minLength += other.minLength;
|
||||
numItems += other.numItems;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
CumulativeMin cumulativeMinLength_recursive(Anchor::Side side) const;
|
||||
|
||||
void setThickness();
|
||||
void setLazyPosition(int);
|
||||
static void setSeparatorFactoryFunc(SeparatorFactoryFunc);
|
||||
|
||||
Q_SIGNALS:
|
||||
void positionChanged(int pos);
|
||||
void itemsChanged(Anchor::Side);
|
||||
void fromChanged();
|
||||
void toChanged();
|
||||
void debug_itemNamesChanged();
|
||||
void followeeChanged();
|
||||
void thicknessChanged();
|
||||
void geometryChanged(QRect);
|
||||
|
||||
public:
|
||||
int position(QPoint) const;
|
||||
void updateSize();
|
||||
void updateItemSizes();
|
||||
void debug_updateItemNames();
|
||||
QString debug_side1ItemNames() const;
|
||||
QString debug_side2ItemNames() const;
|
||||
private:
|
||||
void setLazyPosition(int);
|
||||
void setGeometry(QRect);
|
||||
QRect geometry() const { return m_geometry; }
|
||||
int position(QPoint) const;
|
||||
void setPosition(int p);
|
||||
|
||||
const Qt::Orientation m_orientation;
|
||||
ItemList m_side1Items;
|
||||
ItemList m_side2Items;
|
||||
QPointer<Anchor> m_from;// QPointer just so we can assert. They should never be null.
|
||||
QPointer<Anchor> m_to;
|
||||
const Type m_type;
|
||||
qreal m_positionPercentage = 0.0; // Should be between 0 and 1
|
||||
|
||||
// Only set when anchor is moved through mouse. Side1 if going towards left or top, Side2 otherwise.
|
||||
Side m_lastMoveDirection = Side_None;
|
||||
Layouting::Side m_lastMoveDirection = Side1;
|
||||
|
||||
MultiSplitterLayout *m_layout = nullptr;
|
||||
bool m_showingSide1Rubberband = false;
|
||||
bool m_showingSide2Rubberband = false;
|
||||
bool m_initialized = false;
|
||||
QWidget *const m_hostWidget;
|
||||
static bool s_isResizing;
|
||||
static const QString s_magicMarker; // Just to validate serialize is symmetric to deserialize
|
||||
static Anchor* s_separatorBeingDragged;
|
||||
|
||||
// For when being animated. They are not displayed at their pos, but with an offset.
|
||||
int m_positionOffset = 0;
|
||||
|
||||
QString m_debug_side1ItemNames;
|
||||
QString m_debug_side2ItemNames;
|
||||
Separator *const m_separatorWidget;
|
||||
QRect m_geometry;
|
||||
Anchor *m_followee = nullptr;
|
||||
QMetaObject::Connection m_followeeDestroyedConnection;
|
||||
const bool m_lazyResize;
|
||||
int m_lazyPosition = 0;
|
||||
const Options m_options;
|
||||
QRubberBand *const m_lazyResizeRubberBand;
|
||||
ItemContainer *const m_parentContainer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KDDockWidgets::ItemList)
|
||||
Q_DECLARE_METATYPE(KDDockWidgets::Item*)
|
||||
|
||||
#endif
|
||||
|
||||
22
src/private/multisplitter/CMakeLists.txt
Normal file
22
src/private/multisplitter/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
set(MULTISPLITTER_SRCS
|
||||
Anchor.cpp
|
||||
Anchor_p.h
|
||||
Item.cpp
|
||||
Item_p.h
|
||||
Logging.cpp
|
||||
Logging_p.h
|
||||
Separator.cpp
|
||||
Separator_p.h
|
||||
)
|
||||
|
||||
add_library(kddockwidgets_layouting ${MULTISPLITTER_SRCS})
|
||||
target_link_libraries(kddockwidgets_layouting Qt5::Core Qt5::Widgets)
|
||||
|
||||
add_subdirectory(tests)
|
||||
|
||||
target_include_directories(kddockwidgets_layouting
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,177 +18,577 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KD_MULTISPLITTER_ITEM_P_H
|
||||
#define KD_MULTISPLITTER_ITEM_P_H
|
||||
#pragma once
|
||||
|
||||
#include "docks_export.h"
|
||||
#include "Anchor_p.h"
|
||||
#include "QWidgetAdapter.h"
|
||||
#include "LayoutSaver_p.h"
|
||||
|
||||
#include <QRect>
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include <QRect>
|
||||
#include <QVariant>
|
||||
#include <QWidget> // TODO: remove
|
||||
#include <QDebug>
|
||||
#include <QVariant>
|
||||
|
||||
/**
|
||||
* @brief Implements an item that you put into a multi-splitter.
|
||||
* For now it just wraps a KDDockWidgets::Frame, but could eventually be used in QML.
|
||||
*/
|
||||
namespace KDDockWidgets {
|
||||
#include <memory>
|
||||
|
||||
struct AnchorGroup;
|
||||
class MultiSplitterLayout;
|
||||
class Frame;
|
||||
class DockWidgetBase;
|
||||
class TestDocks;
|
||||
#define KDDOCKWIDGETS_MIN_WIDTH 80
|
||||
#define KDDOCKWIDGETS_MIN_HEIGHT 90
|
||||
|
||||
struct GeometryDiff
|
||||
{
|
||||
explicit GeometryDiff(QRect oldGeo, QRect newGeo)
|
||||
: leftDiff(newGeo.left() - oldGeo.left())
|
||||
, topDiff(newGeo.top() - oldGeo.top())
|
||||
, rightDiff(newGeo.right() - oldGeo.right())
|
||||
, bottomDiff(newGeo.bottom() - oldGeo.bottom())
|
||||
, onlyOneSideChanged([this]{
|
||||
int numChanged = 0;
|
||||
if (leftDiff != 0)
|
||||
numChanged++;
|
||||
if (topDiff != 0)
|
||||
numChanged++;
|
||||
if (rightDiff != 0)
|
||||
numChanged++;
|
||||
if (bottomDiff != 0)
|
||||
numChanged++;
|
||||
return numChanged == 1;
|
||||
}()) // Lambda just so we can have onlyOneChanged as const
|
||||
{
|
||||
}
|
||||
class TestMultiSplitter;
|
||||
|
||||
// Orientation of the Anchor that provoked the geometry diff
|
||||
Qt::Orientation orientation() const
|
||||
{
|
||||
if (leftDiff || rightDiff)
|
||||
return Qt::Vertical;
|
||||
namespace Layouting {
|
||||
|
||||
return Qt::Horizontal;
|
||||
}
|
||||
class ItemContainer;
|
||||
class Item;
|
||||
class Anchor;
|
||||
class Separator;
|
||||
|
||||
int delta() const
|
||||
{
|
||||
// Since we only use GeometryDiff when only 1 side changed, just sum them all
|
||||
return leftDiff + rightDiff + topDiff + bottomDiff;
|
||||
}
|
||||
typedef Separator* (*SeparatorFactoryFunc)(Layouting::Anchor*, QWidget *parent);
|
||||
|
||||
int signess() const
|
||||
{
|
||||
return delta() > 0 ? 1: -1;
|
||||
}
|
||||
|
||||
const int leftDiff;
|
||||
const int topDiff;
|
||||
const int rightDiff;
|
||||
const int bottomDiff;
|
||||
const bool onlyOneSideChanged;
|
||||
enum Location {
|
||||
Location_None,
|
||||
Location_OnLeft, ///> Left docking location
|
||||
Location_OnTop, ///> Top docking location
|
||||
Location_OnRight, ///> Right docking location
|
||||
Location_OnBottom ///> Bottom docking location
|
||||
};
|
||||
|
||||
class DOCKS_EXPORT_FOR_UNIT_TESTS Item : public QObject // clazy:exclude=ctor-missing-parent-argument
|
||||
enum AddingOption {
|
||||
AddingOption_None = 0, ///> No option set
|
||||
AddingOption_StartHidden ///< Don't show the dock widget when adding it
|
||||
};
|
||||
|
||||
///@internal
|
||||
inline Location oppositeLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case Location_OnLeft:
|
||||
return Location_OnRight;
|
||||
case Location_OnTop:
|
||||
return Location_OnBottom;
|
||||
case Location_OnRight:
|
||||
return Location_OnLeft;
|
||||
case Location_OnBottom:
|
||||
return Location_OnTop;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return Location_None;
|
||||
}
|
||||
}
|
||||
|
||||
///@internal
|
||||
inline Location adjacentLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case Location_OnLeft:
|
||||
return Location_OnTop;
|
||||
case Location_OnTop:
|
||||
return Location_OnRight;
|
||||
case Location_OnRight:
|
||||
return Location_OnBottom;
|
||||
case Location_OnBottom:
|
||||
return Location_OnLeft;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return Location_None;
|
||||
}
|
||||
}
|
||||
|
||||
enum Side {
|
||||
Side1,
|
||||
Side2
|
||||
};
|
||||
|
||||
enum class GrowthStrategy {
|
||||
BothSidesEqually
|
||||
};
|
||||
|
||||
inline Qt::Orientation oppositeOrientation(Qt::Orientation o) {
|
||||
return o == Qt::Vertical ? Qt::Horizontal
|
||||
: Qt::Vertical;
|
||||
}
|
||||
|
||||
inline int pos(QPoint p, Qt::Orientation o) {
|
||||
return o == Qt::Vertical ? p.y()
|
||||
: p.x();
|
||||
}
|
||||
|
||||
inline int length(QSize sz, Qt::Orientation o) {
|
||||
return o == Qt::Vertical ? sz.height()
|
||||
: sz.width();
|
||||
}
|
||||
|
||||
inline bool locationIsVertical(Location loc)
|
||||
{
|
||||
return loc == Location_OnTop || loc == Location_OnBottom;
|
||||
}
|
||||
|
||||
inline bool locationIsHorizontal(Location loc)
|
||||
{
|
||||
return !locationIsVertical(loc);
|
||||
}
|
||||
|
||||
inline bool locationIsSide1(Location loc)
|
||||
{
|
||||
return loc == Location_OnLeft || loc == Location_OnTop;
|
||||
}
|
||||
|
||||
inline bool locationIsSide2(Location loc)
|
||||
{
|
||||
return loc == Location_OnRight || loc == Location_OnBottom;
|
||||
}
|
||||
|
||||
inline QRect adjustedRect(QRect r, Qt::Orientation o, int p1, int p2)
|
||||
{
|
||||
if (o == Qt::Vertical) {
|
||||
r.adjust(0, p1, 0, p2);
|
||||
} else {
|
||||
r.adjust(p1, 0, p2, 0);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline QVariantMap sizeToMap(QSize sz)
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("width"), sz.width());
|
||||
map.insert(QStringLiteral("height"), sz.height());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
inline QVariantMap rectToMap(QRect rect)
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("x"), rect.x());
|
||||
map.insert(QStringLiteral("y"), rect.y());
|
||||
map.insert(QStringLiteral("width"), rect.width());
|
||||
map.insert(QStringLiteral("height"), rect.height());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
inline QSize mapToSize(const QVariantMap &map)
|
||||
{
|
||||
return { map.value(QStringLiteral("width")).toInt(),
|
||||
map.value(QStringLiteral("height")).toInt() };
|
||||
}
|
||||
|
||||
inline QRect mapToRect(const QVariantMap &map)
|
||||
{
|
||||
return QRect(map.value(QStringLiteral("x")).toInt(),
|
||||
map.value(QStringLiteral("y")).toInt(),
|
||||
map.value(QStringLiteral("width")).toInt(),
|
||||
map.value(QStringLiteral("height")).toInt());
|
||||
}
|
||||
|
||||
inline Qt::Orientation orientationForLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case Location_OnLeft:
|
||||
case Location_OnRight:
|
||||
return Qt::Horizontal;
|
||||
case Location_None:
|
||||
case Location_OnTop:
|
||||
case Location_OnBottom:
|
||||
return Qt::Vertical;
|
||||
}
|
||||
|
||||
return Qt::Vertical;
|
||||
}
|
||||
|
||||
inline Side sideForLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case Location_OnLeft:
|
||||
case Location_OnTop:
|
||||
return Side::Side1;
|
||||
case Location_OnRight:
|
||||
case Location_OnBottom:
|
||||
return Side::Side2;
|
||||
default:
|
||||
return Side::Side1;
|
||||
}
|
||||
}
|
||||
|
||||
struct SizingInfo {
|
||||
QSize size() const {
|
||||
return geometry.size();
|
||||
}
|
||||
|
||||
int length(Qt::Orientation o) const {
|
||||
return Layouting::length(size(), o);
|
||||
}
|
||||
|
||||
int minLength(Qt::Orientation o) const {
|
||||
return Layouting::length(minSize, o);
|
||||
}
|
||||
|
||||
int availableLength(Qt::Orientation o) const {
|
||||
return qMax(0, length(o) - minLength(o));
|
||||
}
|
||||
|
||||
int missingLength(Qt::Orientation o) const {
|
||||
return qMax(0, minLength(o) - length(o));
|
||||
}
|
||||
|
||||
QPoint pos() const {
|
||||
return geometry.topLeft();
|
||||
}
|
||||
|
||||
int position(Qt::Orientation o) const {
|
||||
return Layouting::pos(pos(), o);
|
||||
}
|
||||
|
||||
int edge(Qt::Orientation o) const {
|
||||
return o == Qt::Vertical ? geometry.bottom()
|
||||
: geometry.right();
|
||||
}
|
||||
|
||||
void setLength(int l, Qt::Orientation o) {
|
||||
if (o == Qt::Vertical) {
|
||||
geometry.setHeight(l);
|
||||
} else {
|
||||
geometry.setWidth(l);
|
||||
}
|
||||
}
|
||||
|
||||
void incrementLength(int byAmount, Qt::Orientation o) {
|
||||
setLength(length(o) + byAmount, o);
|
||||
}
|
||||
|
||||
void setOppositeLength(int l, Qt::Orientation o) {
|
||||
setLength(l, oppositeOrientation(o));
|
||||
}
|
||||
|
||||
void setPos(int p, Qt::Orientation o) {
|
||||
if (o == Qt::Vertical)
|
||||
geometry.moveTop(p);
|
||||
else
|
||||
geometry.moveLeft(p);
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return geometry.isNull();
|
||||
}
|
||||
|
||||
void setGeometry(QRect geo) {
|
||||
geometry = geo;
|
||||
}
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &);
|
||||
|
||||
typedef QVector<SizingInfo> List;
|
||||
QRect geometry;
|
||||
QSize minSize = QSize(40, 40); // TODO: Hardcoded
|
||||
QSize maxSize = QSize(16777215, 16777215); // TODO: Not supported yet
|
||||
double percentageWithinParent = 0.0;
|
||||
bool isBeingInserted = false;
|
||||
};
|
||||
|
||||
class GuestInterface
|
||||
{
|
||||
public:
|
||||
virtual void setLayoutItem(Item *) = 0;
|
||||
virtual QWidget *asWidget() = 0;
|
||||
};
|
||||
|
||||
class Item : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isPlaceholder READ isPlaceholder NOTIFY isPlaceholderChanged)
|
||||
Q_PROPERTY(int x READ x NOTIFY xChanged)
|
||||
Q_PROPERTY(int y READ y NOTIFY yChanged)
|
||||
Q_PROPERTY(int width READ width NOTIFY widthChanged)
|
||||
Q_PROPERTY(int height READ height NOTIFY heightChanged)
|
||||
Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged)
|
||||
Q_PROPERTY(QSize minimumSize READ minimumSize NOTIFY minimumSizeChanged)
|
||||
Q_PROPERTY(bool isContainer READ isContainer CONSTANT)
|
||||
public:
|
||||
typedef QVector<Item*> List;
|
||||
|
||||
/// @brief constructs a new layout item to show @p Frame in the layout @layout
|
||||
/// @param frame This is never nullptr.
|
||||
/// @param layout This is never nullptr.
|
||||
explicit Item(Frame *frame, MultiSplitterLayout *layout);
|
||||
|
||||
/// @brief Constructor overload used when restoring a layout and the Item is a placeholder (no frame)
|
||||
explicit Item(MultiSplitterLayout *layout);
|
||||
|
||||
/// @brief Destroys its frame too.
|
||||
explicit Item(QWidget *hostWidget, ItemContainer *parent = nullptr);
|
||||
~Item() override;
|
||||
|
||||
static Item* deserialize(const LayoutSaver::Item &, MultiSplitterLayout *layout);
|
||||
LayoutSaver::Item serialize() const;
|
||||
bool isRoot() const;
|
||||
virtual int visibleCount_recursive() const;
|
||||
|
||||
virtual void insertItem(Item *item, Location, AddingOption = AddingOption_None);
|
||||
|
||||
/**
|
||||
* @brief No widget can have a minimum size smaller than this, regardless of their minimum size.
|
||||
*/
|
||||
static QSize hardcodedMinimumSize();
|
||||
|
||||
int x() const;
|
||||
int y() const;
|
||||
QPoint pos() const;
|
||||
int position(Qt::Orientation) const;
|
||||
QSize size() const;
|
||||
int width() const;
|
||||
int height() const;
|
||||
bool isVisible() const;
|
||||
void setVisible(bool);
|
||||
|
||||
void setGeometry(QRect);
|
||||
void ensureMinSize(Qt::Orientation orientation, Anchor::Side);
|
||||
void ensureMinSize(Qt::Orientation orientation);
|
||||
|
||||
void beginBlockPropagateGeo();
|
||||
void endBlockPropagateGeo();
|
||||
|
||||
QSize size() const;
|
||||
void setSize(QSize);
|
||||
QPoint pos() const;
|
||||
int pos(Qt::Orientation) const;
|
||||
QRect geometry() const;
|
||||
bool eventFilter(QObject *, QEvent *) override;
|
||||
|
||||
Frame* frame() const;
|
||||
QWidgetOrQuick *window() const;
|
||||
QWidgetOrQuick *parentWidget() const;
|
||||
bool isContainer() const;
|
||||
bool isWidget() const { return !isContainer(); }
|
||||
|
||||
MultiSplitterLayout *layout() const;
|
||||
void setLayout(MultiSplitterLayout *w); // TODO: Make the widget children of this one?
|
||||
|
||||
/**
|
||||
* Returns the width of the widget if orientation is Vertical, the height otherwise.
|
||||
*/
|
||||
Qt::Orientation orientation() const;
|
||||
static int separatorThickness();
|
||||
[[nodiscard]] virtual bool checkSanity();
|
||||
void setParentContainer(ItemContainer *parent); // TODO: Make private
|
||||
ItemContainer *parentContainer() const;
|
||||
void setPos(QPoint); // TODO: Make private
|
||||
void setPos(int pos, Qt::Orientation);
|
||||
int position(Qt::Orientation) const;
|
||||
const ItemContainer *asContainer() const;
|
||||
ItemContainer *asContainer();
|
||||
void setMinSize(QSize);
|
||||
void setMaxSize(QSize);
|
||||
virtual QSize minSize() const;
|
||||
virtual QSize maxSize() const;
|
||||
virtual void resize(QSize newSize);
|
||||
int minLength(Qt::Orientation) const;
|
||||
void setLength(int length, Qt::Orientation);
|
||||
virtual void setLength_recursive(int length, Qt::Orientation);
|
||||
int length(Qt::Orientation) const;
|
||||
int minLength(Qt::Orientation orientation) const;
|
||||
|
||||
Anchor *anchorAtSide(Anchor::Side side, Qt::Orientation orientation) const;
|
||||
Anchor *anchor(const GeometryDiff &) const;
|
||||
AnchorGroup& anchorGroup();
|
||||
const AnchorGroup& anchorGroup() const;
|
||||
|
||||
QSize minimumSize() const;
|
||||
|
||||
int availableLength(Qt::Orientation) const;
|
||||
bool isPlaceholder() const;
|
||||
void setIsPlaceholder(bool);
|
||||
/**
|
||||
* @brief Returns whether this item lives in a @ref MainWindow, as opposed to a @ref FloatingWindow
|
||||
*/
|
||||
bool isInMainWindow() const;
|
||||
|
||||
///@brief turns the placeholder into a normal Item again showing @p dockWidget
|
||||
void restorePlaceholder(DockWidgetBase *dockWidget, int tabIndex);
|
||||
virtual bool isVisible() const;
|
||||
virtual void setIsVisible(bool);
|
||||
virtual void setGeometry_recursive(QRect rect);
|
||||
virtual void dumpLayout(int level = 0);
|
||||
void setGeometry(QRect rect);
|
||||
SizingInfo m_sizingInfo;
|
||||
QSize missingSize() const;
|
||||
int missingLength(Qt::Orientation) const;
|
||||
bool isBeingInserted() const;
|
||||
void setBeingInserted(bool);
|
||||
ItemContainer *root() const;
|
||||
QRect mapToRoot(QRect) const;
|
||||
QPoint mapToRoot(QPoint) const;
|
||||
int mapToRoot(int p, Qt::Orientation) const;
|
||||
QPoint mapFromRoot(QPoint) const;
|
||||
QRect mapFromRoot(QRect) const;
|
||||
QPoint mapFromParent(QPoint) const;
|
||||
int mapFromRoot(int p, Qt::Orientation) const;
|
||||
|
||||
///@brief turns the placeholder into a normal item again
|
||||
/// This overload is called when the Frame has more than 1 tab, otherwise we just use the DockWidget overload
|
||||
void restorePlaceholder(Frame *frame);
|
||||
|
||||
/**
|
||||
* @brief Checks if the minSize is correct.
|
||||
* The parent widget got a QEvent::LayoutRequest, so the Frame might have changed its constraints.
|
||||
*/
|
||||
void onLayoutRequest() const;
|
||||
QWidget *frame() const { return m_guest ? m_guest->asWidget() : nullptr; } // TODO: rename
|
||||
GuestInterface *guest() const { return m_guest; }
|
||||
void setFrame(GuestInterface *);
|
||||
QWidget *window() const {
|
||||
return m_guest ? frame()->window() : nullptr;
|
||||
}
|
||||
|
||||
void ref();
|
||||
void unref();
|
||||
int refCount() const; // for tests
|
||||
Q_SIGNALS:
|
||||
void frameChanged();
|
||||
void geometryChanged();
|
||||
void isPlaceholderChanged();
|
||||
void minimumSizeChanged();
|
||||
private:
|
||||
friend KDDockWidgets::TestDocks;
|
||||
QSize actualMinSize() const; // The min size, regardless if it's a placeholder or not, so we can save the actual value while LayoutSaver::saveLayout
|
||||
void restoreSizes(QSize minSize, QRect geometry); // Just for LayoutSaver::restore
|
||||
int refCount() const;
|
||||
|
||||
class Private;
|
||||
Private *const d;
|
||||
QWidget *hostWidget() const;
|
||||
void restore(GuestInterface *guest);
|
||||
virtual void setHostWidget(QWidget *);
|
||||
virtual void updateWidgetGeometries();
|
||||
virtual QVariantMap toVariantMap() const;
|
||||
virtual void fillFromVariantMap(const QVariantMap &map, const QHash<QString, GuestInterface*> &widgets);
|
||||
static Item* createFromVariantMap(QWidget *hostWidget, ItemContainer *parent,
|
||||
const QVariantMap &map, const QHash<QString, GuestInterface *> &widgets);
|
||||
|
||||
Q_SIGNALS:
|
||||
void geometryChanged();
|
||||
void xChanged();
|
||||
void yChanged();
|
||||
void widthChanged();
|
||||
void heightChanged();
|
||||
void visibleChanged(Item *thisItem, bool visible);
|
||||
void minSizeChanged(Item *thisItem);
|
||||
protected:
|
||||
friend class ::TestMultiSplitter;
|
||||
explicit Item(bool isContainer, QWidget *hostWidget, ItemContainer *parent);
|
||||
const bool m_isContainer;
|
||||
ItemContainer *m_parent = nullptr;
|
||||
private Q_SLOTS:
|
||||
void onWidgetLayoutRequested();
|
||||
|
||||
private:
|
||||
void turnIntoPlaceholder();
|
||||
bool eventFilter(QObject *o, QEvent *event) override;
|
||||
int m_refCount = 0;
|
||||
void updateObjectName();
|
||||
void onWidgetDestroyed();
|
||||
bool m_isVisible = false;
|
||||
bool m_destroying = false; // TODO: Remove and check if unit-tests pass
|
||||
QWidget * m_hostWidget = nullptr;
|
||||
GuestInterface *m_guest = nullptr;
|
||||
};
|
||||
|
||||
class ItemContainer : public Item {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QVariantList items READ items NOTIFY itemsChanged)
|
||||
public:
|
||||
|
||||
struct LengthOnSide {
|
||||
int length = 0;
|
||||
int minLength = 0;
|
||||
|
||||
int available() const {
|
||||
return qMax(0, length - minLength);
|
||||
}
|
||||
|
||||
int missing() const {
|
||||
return qMax(0, minLength - length);
|
||||
}
|
||||
};
|
||||
|
||||
explicit ItemContainer(QWidget *hostWidget, ItemContainer *parent);
|
||||
explicit ItemContainer(QWidget *parent);
|
||||
void insertItem(Item *item, int index);
|
||||
[[nodiscard]] bool checkSanity() override;
|
||||
void scheduleCheckSanity() const;
|
||||
bool hasOrientation() const;
|
||||
int numChildren() const;
|
||||
int numVisibleChildren() const;
|
||||
bool hasChildren() const;
|
||||
bool hasVisibleChildren() const;
|
||||
int indexOfVisibleChild(const Item *) const;
|
||||
void removeItem(Item *, bool hardRemove = true);
|
||||
bool isEmpty() const;
|
||||
void setGeometry_recursive(QRect rect) override;
|
||||
|
||||
ItemContainer *convertChildToContainer(Item *leaf);
|
||||
void insertItem(Item *item, Location, AddingOption = AddingOption_None) override;
|
||||
bool hasOrientationFor(Location) const;
|
||||
Item::List children() const;
|
||||
Item::List visibleChildren(bool includeBeingInserted = false) const;
|
||||
int usableLength() const;
|
||||
bool hasSingleVisibleItem() const;
|
||||
bool contains(const Item *item) const;
|
||||
bool contains_recursive(const Item *item) const;
|
||||
void setChildren(const Item::List children, Qt::Orientation o);
|
||||
void setOrientation(Qt::Orientation);
|
||||
QSize minSize() const override;
|
||||
QSize maxSize() const override;
|
||||
void resize(QSize newSize) override;
|
||||
int length() const;
|
||||
QRect rect() const;
|
||||
QVariantList items() const;
|
||||
void dumpLayout(int level = 0) override;
|
||||
void updateChildPercentages();
|
||||
void updateChildPercentages_recursive();
|
||||
void restoreChild(Item *);
|
||||
void updateWidgetGeometries() override;
|
||||
int oppositeLength() const;
|
||||
|
||||
///@brief Grows the side1Neighbour to the right and the side2Neighbour to the left
|
||||
///So they occupy the empty space that's between them (or bottom/top if Qt::Vertical).
|
||||
///This is useful when an Item is removed. Its neighbours will occupy its space.
|
||||
///side1Neighbour or side2Neighbour are allowed to be null, in which case the non-null one
|
||||
///will occupy the entire space.
|
||||
void growNeighbours(Item *side1Neighbour, Item *side2Neighbour);
|
||||
|
||||
///@brief grows an item by @p amount. It calculates how much to grow on side1 and on side2
|
||||
///Then calls growItem(item, side1Growth, side2Growth) which will effectively grow it,
|
||||
///and shrink the neighbours which are donating the size.
|
||||
void growItem(Item *, int amount, GrowthStrategy, bool accountForNewSeparator = false);
|
||||
void growItem(int index, SizingInfo::List &sizes, int missing, GrowthStrategy, bool accountForNewSeparator = false);
|
||||
|
||||
///@brief Shrinks the neighbours of the item at @p index
|
||||
///
|
||||
/// The neighbours at the left/top of the item, will be shrunk by @p side1Amount, while the items
|
||||
/// at right/bottom will be shrunk by @p side2Amount.
|
||||
/// Squeezes all the neighbours (not just the immediate ones).
|
||||
void shrinkNeighbours(int index, SizingInfo::List &sizes, int side1Amount, int side2Amount);
|
||||
|
||||
Item *visibleNeighbourFor(const Item *item, Side side) const;
|
||||
QSize availableSize() const;
|
||||
int availableLength() const;
|
||||
LengthOnSide lengthOnSide(const SizingInfo::List &sizes, int fromIndex, Side, Qt::Orientation) const;
|
||||
int minLength(int fromIndex, Side, Qt::Orientation) const;
|
||||
int neighboursLengthFor(const Item *item, Side, Qt::Orientation) const;
|
||||
int neighboursLengthFor_recursive(const Item *item, Side, Qt::Orientation) const;
|
||||
int neighboursMinLengthFor(const Item *item, Side, Qt::Orientation) const;
|
||||
int neighboursMinLengthFor_recursive(const Item *item, Side, Qt::Orientation) const;
|
||||
int neighbourSeparatorWaste(const Item *item, Side, Qt::Orientation) const;
|
||||
int neighbourSeparatorWaste_recursive(const Item *item, Side, Qt::Orientation) const;
|
||||
int availableOnSide(Item *child, Side) const;
|
||||
QSize missingSizeFor(Item *item, Qt::Orientation) const;
|
||||
void onChildMinSizeChanged(Item *child);
|
||||
void onChildVisibleChanged(Item *child, bool visible);
|
||||
void updateSizeConstraints();
|
||||
SizingInfo::List sizingInfosPerNeighbour(Item *item, Side) const;
|
||||
SizingInfo::List sizes(bool ignoreBeingInserted = false) const;
|
||||
QVector<int> calculateSqueezes(SizingInfo::List::ConstIterator begin, SizingInfo::List::ConstIterator end, int needed) const;
|
||||
QRect suggestedDropRect(QSize minSize, const Item *relativeTo, Location) const;
|
||||
void positionItems();
|
||||
void positionItems(SizingInfo::List &sizes);
|
||||
bool isResizing() const { return m_isResizing; }
|
||||
void clear();
|
||||
Item* itemForFrame(const QWidget *w) const; // TODO: Rename
|
||||
int visibleCount_recursive() const override;
|
||||
int count_recursive() const;
|
||||
Item *itemAt(QPoint p) const;
|
||||
Item *itemAt_recursive(QPoint p) const;
|
||||
Item::List items_recursive() const;
|
||||
void setHostWidget(QWidget *) override;
|
||||
void setIsVisible(bool) override;
|
||||
bool isVisible() const override;
|
||||
void setLength_recursive(int length, Qt::Orientation) override;
|
||||
void applyGeometries(const SizingInfo::List &sizes);
|
||||
void applyPositions(const SizingInfo::List &sizes);
|
||||
Qt::Orientation orientation() const;
|
||||
bool isVertical() const;
|
||||
bool isHorizontal() const;
|
||||
|
||||
int indexOf(Anchor *) const;
|
||||
int minPosForSeparator(Anchor *) const;
|
||||
int maxPosForSeparator(Anchor *) const;
|
||||
int minPosForSeparator_global(Anchor *) const;
|
||||
int maxPosForSeparator_global(Anchor *) const;
|
||||
|
||||
void deleteSeparators_recursive();
|
||||
void updateSeparators_recursive();
|
||||
|
||||
QVariantMap toVariantMap() const override;
|
||||
void fillFromVariantMap(const QVariantMap &map, const QHash<QString, GuestInterface *> &widgets) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void itemsChanged();
|
||||
void numVisibleItemsChanged(int);
|
||||
void numItemsChanged();
|
||||
public:
|
||||
Item::List m_children;
|
||||
bool m_isResizing = false;
|
||||
bool m_blockUpdatePercentages = false;
|
||||
QVector<Layouting::Anchor*> separators_recursive() const;
|
||||
Qt::Orientation m_orientation = Qt::Vertical;
|
||||
private:
|
||||
void updateWidgets_recursive();
|
||||
/// Returns the positions that each separator should have (x position if Qt::Horizontal, y otherwise)
|
||||
QVector<int> requiredSeparatorPositions() const;
|
||||
void updateSeparators();
|
||||
void deleteSeparators();
|
||||
Anchor* separatorAt(int p) const;
|
||||
QVector<double> childPercentages() const;
|
||||
mutable bool m_checkSanityScheduled = false;
|
||||
QVector<Layouting::Anchor*> m_separators;
|
||||
bool m_convertingItemToContainer = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the widget's min size
|
||||
*/
|
||||
inline QSize widgetMinSize(const QWidget *w)
|
||||
{
|
||||
const int minW = w->minimumWidth() > 0 ? w->minimumWidth()
|
||||
: w->minimumSizeHint().width();
|
||||
|
||||
const int minH = w->minimumHeight() > 0 ? w->minimumHeight()
|
||||
: w->minimumSizeHint().height();
|
||||
|
||||
return QSize(minW, minH).expandedTo(Item::hardcodedMinimumSize());
|
||||
}
|
||||
|
||||
#endif
|
||||
inline int widgetMinLength(const QWidget *w, Qt::Orientation o) {
|
||||
return length(widgetMinSize(w), o);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
23
src/private/multisplitter/Logging.cpp
Normal file
23
src/private/multisplitter/Logging.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Logging_p.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(separators, "kdab.docks.separators", QtWarningMsg)
|
||||
29
src/private/multisplitter/Logging_p.h
Normal file
29
src/private/multisplitter/Logging_p.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KD_DOCKWIDGETS_MULTISPLITTER_LOGGING_P_H
|
||||
#define KD_DOCKWIDGETS_MULTISPLITTER_LOGGING_P_H
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(separators)
|
||||
|
||||
|
||||
#endif
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "MultiSplitter_p.h"
|
||||
#include "MultiSplitterLayout_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "../Logging_p.h" // TODO
|
||||
#include "MainWindowBase.h"
|
||||
#include "FloatingWindow_p.h"
|
||||
#include "LayoutSaver.h"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,7 +34,6 @@
|
||||
|
||||
#include "../Frame_p.h"
|
||||
#include "Anchor_p.h"
|
||||
#include "AnchorGroup_p.h"
|
||||
#include "docks_export.h"
|
||||
#include "KDDockWidgets.h"
|
||||
#include "Item_p.h"
|
||||
@@ -45,59 +44,11 @@
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class MultiSplitter;
|
||||
class Length;
|
||||
|
||||
namespace Debug {
|
||||
class DebugWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the widget if orientation is Vertical, the height otherwise.
|
||||
*/
|
||||
template <typename T>
|
||||
inline int widgetLength(const T *w, Qt::Orientation orientation)
|
||||
{
|
||||
return (orientation == Qt::Vertical) ? w->width() : w->height();
|
||||
}
|
||||
|
||||
inline int lengthFromSize(QSize sz, Qt::Orientation orientation)
|
||||
{
|
||||
return orientation == Qt::Vertical ? sz.width()
|
||||
: sz.height();
|
||||
}
|
||||
|
||||
inline Anchor::Side sideForLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case KDDockWidgets::Location_OnLeft:
|
||||
case KDDockWidgets::Location_OnTop:
|
||||
return Anchor::Side1;
|
||||
case KDDockWidgets::Location_OnRight:
|
||||
case KDDockWidgets::Location_OnBottom:
|
||||
return Anchor::Side2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Anchor::Side_None;
|
||||
}
|
||||
|
||||
inline Qt::Orientation orientationForLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case KDDockWidgets::Location_OnLeft:
|
||||
case KDDockWidgets::Location_OnRight:
|
||||
return Qt::Vertical;
|
||||
case KDDockWidgets::Location_OnTop:
|
||||
case KDDockWidgets::Location_OnBottom:
|
||||
return Qt::Horizontal;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Qt::Vertical;
|
||||
}
|
||||
|
||||
/**
|
||||
* A MultiSplitter is like a QSplitter but supports mixing vertical and horizontal splitters in
|
||||
* any combination.
|
||||
@@ -124,11 +75,6 @@ public:
|
||||
explicit MultiSplitterLayout(MultiSplitter *parent);
|
||||
~MultiSplitterLayout() override;
|
||||
|
||||
/**
|
||||
* @brief No widget can have a minimum size smaller than this, regardless of their minimum size.s
|
||||
*/
|
||||
static QSize hardcodedMinimumSize();
|
||||
|
||||
/**
|
||||
* @brief returns the widget that this layout manages
|
||||
*/
|
||||
@@ -152,17 +98,17 @@ public:
|
||||
/**
|
||||
* @brief Adds the dockwidget but it stays hidden until an explicit show()
|
||||
*/
|
||||
void addAsPlaceholder(DockWidgetBase *dw, KDDockWidgets::Location location, Item *relativeTo = nullptr);
|
||||
void addAsPlaceholder(DockWidgetBase *dw, KDDockWidgets::Location location, Layouting::Item *relativeTo = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Removes an item from this MultiSplitter.
|
||||
*/
|
||||
void removeItem(Item *item);
|
||||
void removeItem(Layouting::Item *item);
|
||||
|
||||
/**
|
||||
* @brief Returns true if this layout contains the specified item.
|
||||
*/
|
||||
bool contains(const Item *) const;
|
||||
bool contains(const Layouting::Item *) const;
|
||||
|
||||
/**
|
||||
* @brief Returns true if this layout contains the specified frame.
|
||||
@@ -172,20 +118,20 @@ public:
|
||||
/**
|
||||
* @brief Returns the visible Item at pos @p p.
|
||||
*/
|
||||
Item *itemAt(QPoint p) const;
|
||||
Layouting::Item *itemAt(QPoint p) const;
|
||||
|
||||
/**
|
||||
* @brief Removes all Items, Anchors and Frames docked in this layout.
|
||||
* DockWidgets are closed but not deleted.
|
||||
*/
|
||||
void clear(bool alsoDeleteStaticAnchors = false);
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief Returns the number of Item objects in this layout.
|
||||
* This includes non-visible (placeholder) Items too.
|
||||
* @sa visibleCount
|
||||
*/
|
||||
int count() const { return m_items.size(); }
|
||||
int count() const { return m_rootItem->count_recursive(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the number of visible Items in this layout.
|
||||
@@ -201,11 +147,6 @@ public:
|
||||
*/
|
||||
int placeholderCount() const;
|
||||
|
||||
/**
|
||||
* @brief Returns true if count is 0.
|
||||
*/
|
||||
bool isEmpty() const { return m_items.isEmpty(); }
|
||||
|
||||
/**
|
||||
* @brief Returns whether there's non placeholder items.
|
||||
*/
|
||||
@@ -219,7 +160,9 @@ public:
|
||||
/**
|
||||
* @brief The list of items in this layout.
|
||||
*/
|
||||
const ItemList items() const;
|
||||
const Layouting::Item::List items() const;
|
||||
|
||||
Layouting::ItemContainer *rootItem() const;
|
||||
|
||||
/**
|
||||
* Called by the indicators, so they draw the drop rubber band at the correct place.
|
||||
@@ -227,38 +170,17 @@ public:
|
||||
* Excludes the Anchor thickness, result is actually smaller than what needed. In other words,
|
||||
* the result will be exactly the same as the geometry the widget will get.
|
||||
*/
|
||||
QRect rectForDrop(const QWidgetOrQuick *widget, KDDockWidgets::Location location, const Item *relativeTo) const;
|
||||
QRect rectForDrop(const QWidgetOrQuick *widget, KDDockWidgets::Location location, const Layouting::Item *relativeTo) const;
|
||||
|
||||
bool deserialize(const LayoutSaver::MultiSplitterLayout &);
|
||||
LayoutSaver::MultiSplitterLayout serialize() const;
|
||||
|
||||
void setAnchorBeingDragged(Anchor *);
|
||||
Anchor *anchorBeingDragged() const { return m_anchorBeingDragged; }
|
||||
void setAnchorBeingDragged(Layouting::Anchor *);
|
||||
Layouting::Anchor *anchorBeingDragged() const { return m_anchorBeingDragged; }
|
||||
bool anchorIsBeingDragged() const { return m_anchorBeingDragged != nullptr; }
|
||||
|
||||
///@brief returns list of separators
|
||||
const Anchor::List anchors() const { return m_anchors; }
|
||||
|
||||
/**
|
||||
* @brief Returns the list of anchors that are following @p followee
|
||||
*/
|
||||
Anchor::List anchorsFollowing(Anchor *followee) const;
|
||||
|
||||
///@brief returns the number of anchors that are following others, just for tests.
|
||||
int numAchorsFollowing() const;
|
||||
|
||||
///@brief returns the number of anchors that are following others, just for tests.
|
||||
int numVisibleAnchors() const;
|
||||
|
||||
///@brief returns either the left, top, right or bottom separator, depending on the @p type
|
||||
Anchor *staticAnchor(Anchor::Type type) const;
|
||||
|
||||
Anchor *staticAnchor(Anchor::Side side, Qt::Orientation orientation) const;
|
||||
|
||||
///@brief a function that all code paths adding Items will call.
|
||||
///It's mostly for code reuse, so we don't duplicate what's done here. But it's also nice to
|
||||
///have a central place that we know will be called
|
||||
void addItems_internal(const ItemList &, bool updateConstraints = true, bool emitSignal = true);
|
||||
Layouting::Anchor::List anchors() const;
|
||||
|
||||
/**
|
||||
* @brief Updates the min size of this layout.
|
||||
@@ -288,45 +210,30 @@ public:
|
||||
* @brief returns the contents width.
|
||||
* Usually it's the same width as the respective parent MultiSplitter.
|
||||
*/
|
||||
int width() const { return m_size.width(); }
|
||||
int width() const { return size().width(); }
|
||||
|
||||
/**
|
||||
* @brief returns the contents height.
|
||||
* Usually it's the same height as the respective parent MultiSplitter.
|
||||
*/
|
||||
int height() const { return m_size.height(); }
|
||||
int height() const { return size().height(); }
|
||||
|
||||
/**
|
||||
* @brief returns the layout's minimum size
|
||||
* @ref setMinimumSize
|
||||
*/
|
||||
QSize minimumSize() const { return m_minSize; }
|
||||
QSize minimumSize() const;
|
||||
|
||||
/**
|
||||
* @brief getter for the size
|
||||
*/
|
||||
QSize size() const { return m_size; }
|
||||
QSize size() const { return m_rootItem->size(); }
|
||||
|
||||
// For debug/hardening
|
||||
bool validateInputs(QWidgetOrQuick *widget, KDDockWidgets::Location location, const Frame *relativeToFrame, AddingOption option) const;
|
||||
// For debug/hardening
|
||||
|
||||
enum AnchorSanityOption {
|
||||
AnchorSanity_Normal = 0,
|
||||
AnchorSanity_Intersections = 1,
|
||||
AnchorSanity_WidgetMinSizes = 2,
|
||||
AnchorSanity_WidgetInvalidSizes = 4,
|
||||
AnchorSanity_Followers = 8,
|
||||
AnchorSanity_WidgetGeometry = 16,
|
||||
AnchorSanity_Visibility = 32,
|
||||
AnchorSanity_All = AnchorSanity_Intersections | AnchorSanity_WidgetMinSizes | AnchorSanity_WidgetInvalidSizes | AnchorSanity_Followers | AnchorSanity_WidgetGeometry | AnchorSanity_Visibility
|
||||
};
|
||||
Q_ENUM(AnchorSanityOption)
|
||||
|
||||
bool checkSanity(AnchorSanityOption o = AnchorSanity_All) const;
|
||||
void maybeCheckSanity();
|
||||
|
||||
void restorePlaceholder(Item *item);
|
||||
bool checkSanity() const;
|
||||
|
||||
/**
|
||||
* @brief Removes unneeded placeholder items when adding new frames.
|
||||
@@ -343,7 +250,7 @@ public:
|
||||
/**
|
||||
* @brief returns the Item that holds @p frame in this layout
|
||||
*/
|
||||
Item *itemForFrame(const Frame *frame) const;
|
||||
Layouting::Item *itemForFrame(const Frame *frame) const;
|
||||
|
||||
/**
|
||||
* @brief returns the frames contained in @p frameOrMultiSplitter
|
||||
@@ -362,58 +269,21 @@ public:
|
||||
*/
|
||||
QVector<DockWidgetBase*> dockWidgets() const;
|
||||
|
||||
/**
|
||||
* @brief Creates an AnchorGroup suited for adding a dockwidget to @location relative to @relativeToItem
|
||||
*
|
||||
* Returns the AnchorGroup and a new Anchor, if it was needed.
|
||||
* If relativeTo is null then it returns the static anchor group.
|
||||
*/
|
||||
QPair<AnchorGroup, Anchor *> createTargetAnchorGroup(Location location, Item *relativeToItem);
|
||||
|
||||
struct Length {
|
||||
Length() = default;
|
||||
Length(int side1, int side2)
|
||||
: side1Length(side1)
|
||||
, side2Length(side2)
|
||||
{}
|
||||
|
||||
int side1Length = 0;
|
||||
int side2Length = 0;
|
||||
int length() const { return side1Length + side2Length; }
|
||||
|
||||
void setLength(int newLength)
|
||||
{
|
||||
// Sets the new length, preserving proportion
|
||||
side1Length = int(side1Factor() * newLength);
|
||||
side2Length = newLength - side1Length;
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return length() <= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
qreal side1Factor() const
|
||||
{
|
||||
return (1.0 * side1Length) / length();
|
||||
}
|
||||
};
|
||||
void restorePlaceholder(DockWidgetBase *dw, Layouting::Item *, int tabIndex);
|
||||
|
||||
Q_SIGNALS:
|
||||
///@brief emitted when the number of widgets changes
|
||||
///@param count the new widget count
|
||||
void widgetCountChanged(int count);
|
||||
void widgetCountChanged();
|
||||
|
||||
void visibleWidgetCountChanged(int count);
|
||||
|
||||
///@brief emitted when a widget is added
|
||||
///@param item the item containing the new widget
|
||||
void widgetAdded(KDDockWidgets::Item *item);
|
||||
void widgetAdded(Layouting::Item *item);
|
||||
|
||||
///@brief emitted when a widget is removed
|
||||
///@param item the item containing the removed widget
|
||||
void widgetRemoved(KDDockWidgets::Item *item);
|
||||
void widgetRemoved(Layouting::Item *item);
|
||||
|
||||
///@brief emitted right before dumping debug
|
||||
///@sa dumpDebug
|
||||
@@ -428,35 +298,14 @@ Q_SIGNALS:
|
||||
void minimumSizeChanged(QSize);
|
||||
|
||||
public:
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
AnchorGroup anchorsForPos(QPoint pos) const;
|
||||
AnchorGroup staticAnchorGroup() const;
|
||||
Anchor::List anchors(Qt::Orientation, bool includeStatic = false, bool includePlaceholders = true) const;
|
||||
Anchor *newAnchor(AnchorGroup &group, KDDockWidgets::Location location);
|
||||
friend QDebug operator<<(QDebug d, const AnchorGroup &group);
|
||||
static const QString s_magicMarker;
|
||||
void ensureAnchorsBounded();
|
||||
Layouting::Anchor::List anchors(Qt::Orientation, bool includeStatic = false, bool includePlaceholders = true) const;
|
||||
private:
|
||||
friend struct AnchorGroup;
|
||||
friend class Item;
|
||||
friend class Anchor;
|
||||
friend class TestDocks;
|
||||
friend class KDDockWidgets::Debug::DebugWindow;
|
||||
friend class LayoutSaver;
|
||||
|
||||
struct AnchorBounds {
|
||||
Anchor *side1;
|
||||
Anchor *side2;
|
||||
};
|
||||
|
||||
std::pair<int,int> boundInterval(int newPos1, Anchor* anchor1, int newPos2, Anchor *anchor2) const;
|
||||
void blockItemPropagateGeo(bool block);
|
||||
|
||||
/**
|
||||
* @brief overload called by the first one. Split-out so it's easier to unit-test the math
|
||||
*/
|
||||
QRect rectForDrop(Length lengthForDrop, Location location, QRect relativeToRect) const;
|
||||
|
||||
/**
|
||||
* @brief setter for the minimum size
|
||||
* @ref minimumSize
|
||||
@@ -465,57 +314,10 @@ private:
|
||||
|
||||
void emitVisibleWidgetCountChanged();
|
||||
|
||||
/**
|
||||
* @brief Returns the size that the widget will get when dropped at this specific location.
|
||||
*
|
||||
* When location is Left or Right then the length represents a width, otherwise an height.
|
||||
* This function is also called to know the size of the rubberband when hovering over a location.
|
||||
*/
|
||||
MultiSplitterLayout::Length lengthForDrop(const QWidgetOrQuick *widget, KDDockWidgets::Location location,
|
||||
const Item *relativeTo) const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ensures that this layout's size is enough for dropping @p widget to @p location,
|
||||
* relative to @p relativeToItem.
|
||||
*
|
||||
* It may increase size or do notying, never decrease.
|
||||
*/
|
||||
void ensureEnoughSize(const QWidgetOrQuick *widget, KDDockWidgets::Location location,
|
||||
const Item *relativeToItem);
|
||||
|
||||
|
||||
void insertAnchor(Anchor *);
|
||||
void removeAnchor(Anchor *);
|
||||
|
||||
/**
|
||||
* Returns the min or max position that an anchor can go to (due to minimum size restriction on the widgets).
|
||||
* For example, if the anchor is vertical and direction is Side1 then it returns the minimum x
|
||||
* that the anchor can have. If direction is Side2 then it returns the maximum width. If horizontal
|
||||
* then the height.
|
||||
*/
|
||||
int boundPositionForAnchor(Anchor *, Anchor::Side direction) const;
|
||||
|
||||
/**
|
||||
* Similar to boundPositionForAnchor, but returns both the min and the max width (or height)
|
||||
*/
|
||||
QPair<int, int> boundPositionsForAnchor(Anchor *) const;
|
||||
|
||||
/**
|
||||
* @brief similar to @ref boundPositionsForAnchor but returns for all anchors
|
||||
*/
|
||||
QHash<Anchor*, QPair<int,int>> boundPositionsForAllAnchors() const;
|
||||
|
||||
/** Returns how much is available for the new drop. It already counts with the space for new anchor that will be created.
|
||||
* So it returns this layout's width() (or height), minus the minimum-sizes of all widgets, minus the thickness of all anchors
|
||||
* minus the thickness of the anchor that would be created.
|
||||
**/
|
||||
Length availableLengthForDrop(KDDockWidgets::Location location, const Item *relativeTo) const;
|
||||
|
||||
/**
|
||||
* @brief Like @ref availableLengthForDrop but just returns the total available width or height (depending on @p orientation)
|
||||
* So no need to receive any location.
|
||||
* @param orientation If Qt::Vertical then returns the available width. Height otherwise.
|
||||
* @param orientation If Qt::Vertical then returns the available height. Width otherwise.
|
||||
*/
|
||||
int availableLengthForOrientation(Qt::Orientation orientation) const;
|
||||
|
||||
@@ -525,126 +327,24 @@ private:
|
||||
*/
|
||||
QSize availableSize() const;
|
||||
|
||||
/**
|
||||
* @brief Increases the layout size if @ref availableSize is less than @needed
|
||||
*/
|
||||
void ensureHasAvailableSize(QSize needed);
|
||||
|
||||
/**
|
||||
* Removes the widgets associated with oldAnchor and gives them to newAnchor.
|
||||
* Called when removing a widget results in unneeded anchors.
|
||||
*/
|
||||
void updateAnchorsFromTo(Anchor *oldAnchor, Anchor *newAnchor);
|
||||
|
||||
void clearAnchorsFollowing();
|
||||
void updateAnchorFollowing(const AnchorGroup &groupBeingRemoved = {});
|
||||
QHash<Anchor *, Anchor *> anchorsShouldFollow() const;
|
||||
|
||||
/**
|
||||
* Positions the static anchors at their correct places. Called when the MultiSplitter is resized.
|
||||
* left and top anchor are at position 0, while right/bottom are at position= width/height.
|
||||
* (Approx, due to styling margins and whatnot)
|
||||
*/
|
||||
void positionStaticAnchors();
|
||||
|
||||
/**
|
||||
* When this MultiSplitter is resized, it gives or steals the less/extra space evenly through
|
||||
* all widgets.
|
||||
**/
|
||||
void redistributeSpace();
|
||||
void redistributeSpace(QSize oldSize, QSize newSize);
|
||||
void redistributeSpace_recursive(Anchor *fromAnchor, int minAnchorPos);
|
||||
|
||||
/**
|
||||
* Returns the width (if orientation = Horizontal), or height that is occupied by anchors.
|
||||
* For example, an horizontal anchor has 2 or 3 px of width, so that's space that can't be
|
||||
* occupied by child widgets.
|
||||
*/
|
||||
int wastedSpacing(Qt::Orientation) const;
|
||||
|
||||
/**
|
||||
* Called by addWidget().
|
||||
*
|
||||
* When adding a widget to a layout, it will steal space from the widgets on the left (or top) (@p direction being Anchor::Side1),
|
||||
* and from the widgets on the right (or bottom) (@p direction being Anchor::Side2).
|
||||
*
|
||||
* @param delta the amount of space we're stealing in the specified side
|
||||
* @param fromAnchor The anchor we're starting from
|
||||
* @param direction if we're going left/top (Side1) or right/bottom (Side2)
|
||||
*/
|
||||
void propagateResize(int delta, Anchor *fromAnchor, Anchor::Side direction);
|
||||
|
||||
// Helper function for propagateResize()
|
||||
void collectPaths(QVector<Anchor::List> &paths, Anchor *fromAnchor, Anchor::Side direction);
|
||||
|
||||
// convenience for the unit-tests
|
||||
// Moves the widget's bottom or right anchor, to resize it.
|
||||
void resizeItem(Frame *frame, int newSize, Qt::Orientation);
|
||||
|
||||
void ensureItemsMinSize();
|
||||
|
||||
///@brief returns whether we're inside setSize();
|
||||
bool isResizing() const { return m_resizing; }
|
||||
bool isRestoringPlaceholder() const { return m_restoringPlaceholder; }
|
||||
bool isAddingItem() const { return m_addingItem; }
|
||||
|
||||
QString affinityName() const;
|
||||
|
||||
MultiSplitter *const m_multiSplitter;
|
||||
Anchor::List m_anchors;
|
||||
|
||||
Anchor *m_leftAnchor = nullptr;
|
||||
Anchor *m_topAnchor = nullptr;
|
||||
Anchor *m_rightAnchor = nullptr;
|
||||
Anchor *m_bottomAnchor = nullptr;
|
||||
|
||||
ItemList m_items;
|
||||
bool m_inCtor = true;
|
||||
bool m_inDestructor = false;
|
||||
bool m_beingMergedIntoAnotherMultiSplitter = false;
|
||||
bool m_beingMergedIntoAnotherMultiSplitter = false; // TODO
|
||||
bool m_restoringPlaceholder = false;
|
||||
bool m_resizing = false;
|
||||
bool m_addingItem = false;
|
||||
|
||||
QSize m_minSize = QSize(0, 0);
|
||||
AnchorGroup m_staticAnchorGroup;
|
||||
QPointer<Anchor> m_anchorBeingDragged;
|
||||
QSize m_size;
|
||||
QPointer<Layouting::Anchor> m_anchorBeingDragged;
|
||||
Layouting::ItemContainer *m_rootItem = nullptr;
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug d, const AnchorGroup &group) {
|
||||
d << "AnchorGroup: top=" << group.top << "; left=" << group.left
|
||||
<< "; right=" << group.right << "; bottom=" << group.bottom;
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the widget's min-width if orientation is Vertical, the min-height otherwise.
|
||||
*/
|
||||
inline int widgetMinLength(const QWidgetOrQuick *w, Qt::Orientation orientation)
|
||||
{
|
||||
int min = 0;
|
||||
if (orientation == Qt::Vertical) {
|
||||
if (w->minimumWidth() > 0)
|
||||
min = w->minimumWidth();
|
||||
else
|
||||
min = w->minimumSizeHint().width();
|
||||
|
||||
min = qMax(MultiSplitterLayout::hardcodedMinimumSize().width(), min);
|
||||
} else {
|
||||
if (w->minimumHeight() > 0)
|
||||
min = w->minimumHeight();
|
||||
else
|
||||
min = w->minimumSizeHint().height();
|
||||
|
||||
min = qMax(MultiSplitterLayout::hardcodedMinimumSize().height(), min);
|
||||
}
|
||||
|
||||
return qMax(min, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KDDockWidgets::MultiSplitterLayout::Length)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,24 +19,20 @@
|
||||
*/
|
||||
|
||||
#include "Separator_p.h"
|
||||
#include "multisplitter/MultiSplitterLayout_p.h"
|
||||
#include "multisplitter/Anchor_p.h"
|
||||
#include "Anchor_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "Item_p.h"
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
#include <QMouseEvent>
|
||||
|
||||
Separator::Separator(KDDockWidgets::Anchor *anchor, QWidgetAdapter *parent)
|
||||
: QWidgetAdapter(parent)
|
||||
using namespace Layouting;
|
||||
|
||||
Separator::Separator(Layouting::Anchor *anchor, QWidget *hostWidget)
|
||||
: QWidget(hostWidget)
|
||||
, m_anchor(anchor)
|
||||
{
|
||||
Q_ASSERT(anchor);
|
||||
setVisible(true);
|
||||
|
||||
const int thickness = Anchor::thickness(isStatic());
|
||||
if (isVertical())
|
||||
setFixedWidth(thickness);
|
||||
else
|
||||
setFixedHeight(thickness);
|
||||
}
|
||||
|
||||
bool Separator::isVertical() const
|
||||
@@ -44,39 +40,26 @@ bool Separator::isVertical() const
|
||||
return m_anchor->isVertical();
|
||||
}
|
||||
|
||||
bool Separator::isStatic() const
|
||||
{
|
||||
return m_anchor->isStatic();
|
||||
}
|
||||
|
||||
int Separator::position() const
|
||||
{
|
||||
return isVertical() ? x() : y();
|
||||
}
|
||||
|
||||
void Separator::onMousePress()
|
||||
{
|
||||
Q_ASSERT(!m_anchor->isFollowing());
|
||||
m_anchor->onMousePress();
|
||||
}
|
||||
|
||||
void Separator::onMouseMove(QPoint globalPos)
|
||||
{
|
||||
Q_ASSERT(!m_anchor->isFollowing());
|
||||
m_anchor->onMouseMoved(parentWidget()->mapFromGlobal(globalPos));
|
||||
}
|
||||
|
||||
void Separator::onMouseRelease()
|
||||
{
|
||||
Q_ASSERT(!m_anchor->isFollowing());
|
||||
m_anchor->onMouseReleased();
|
||||
}
|
||||
|
||||
void Separator::move(int p)
|
||||
{
|
||||
if (isVertical()) {
|
||||
QWidgetAdapter::move(p, y());
|
||||
QWidget::move(x(), p);
|
||||
} else {
|
||||
QWidgetAdapter::move(x(), p);
|
||||
QWidget::move(p, y());
|
||||
}
|
||||
}
|
||||
|
||||
void Separator::mousePressEvent(QMouseEvent *)
|
||||
{
|
||||
m_anchor->onMousePress();
|
||||
}
|
||||
|
||||
void Separator::mouseMoveEvent(QMouseEvent *ev)
|
||||
{
|
||||
m_anchor->onMouseMoved(parentWidget()->mapFromGlobal(ev->globalPos()));
|
||||
}
|
||||
|
||||
void Separator::mouseReleaseEvent(QMouseEvent *)
|
||||
{
|
||||
m_anchor->onMouseReleased();
|
||||
}
|
||||
|
||||
@@ -21,35 +21,31 @@
|
||||
#ifndef KD_MULTISPLITTER_SEPARATOR_P_H
|
||||
#define KD_MULTISPLITTER_SEPARATOR_P_H
|
||||
|
||||
#include "docks_export.h"
|
||||
#include "QWidgetAdapter.h"
|
||||
//#include "docks_export.h" TODO
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPointer>
|
||||
|
||||
namespace KDDockWidgets {
|
||||
namespace Layouting {
|
||||
class Anchor;
|
||||
|
||||
class DOCKS_EXPORT Separator : public QWidgetAdapter
|
||||
class /*DOCKS_EXPORT*/ Separator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isVertical READ isVertical CONSTANT)
|
||||
Q_PROPERTY(bool isStatic READ isStatic CONSTANT)
|
||||
//Q_PROPERTY(int position READ position NOTIFY positionChanged)
|
||||
public:
|
||||
explicit Separator(Anchor *anchor, QWidgetAdapter *parent = nullptr);
|
||||
explicit Separator(Layouting::Anchor *anchor, QWidget *hostWidget);
|
||||
bool isVertical() const;
|
||||
bool isStatic() const;
|
||||
int position() const;
|
||||
void move(int p);
|
||||
const QPointer<Anchor> anchor() const { return m_anchor; }
|
||||
|
||||
protected:
|
||||
void onMousePress() override;
|
||||
void onMouseMove(QPoint globalPos) override;
|
||||
void onMouseRelease() override;
|
||||
const QPointer<Layouting::Anchor> anchor() const { return m_anchor; }
|
||||
void mousePressEvent(QMouseEvent *) override;
|
||||
void mouseMoveEvent(QMouseEvent *) override;
|
||||
void mouseReleaseEvent(QMouseEvent *) override;
|
||||
|
||||
private:
|
||||
const QPointer<Anchor> m_anchor; // QPointer so we don't dereference invalid point in paintEvent() when Anchor is deleted.
|
||||
const QPointer<Layouting::Anchor> m_anchor; // QPointer so we don't dereference invalid point in paintEvent() when Anchor is deleted.
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
5
src/private/multisplitter/tests/CMakeLists.txt
Normal file
5
src/private/multisplitter/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
find_package(Qt5Test)
|
||||
|
||||
|
||||
add_executable(tst_multisplitter tst_multisplitter.cpp)
|
||||
target_link_libraries(tst_multisplitter kddockwidgets_layouting Qt5::Test)
|
||||
1078
src/private/multisplitter/tests/tst_multisplitter.cpp
Normal file
1078
src/private/multisplitter/tests/tst_multisplitter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
|
||||
#include "SeparatorWidget_p.h"
|
||||
#include "multisplitter/MultiSplitterLayout_p.h"
|
||||
#include "multisplitter/Anchor_p.h"
|
||||
#include "Logging_p.h"
|
||||
|
||||
@@ -27,8 +26,9 @@
|
||||
#include <QStyleOption>
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
using namespace Layouting;
|
||||
|
||||
SeparatorWidget::SeparatorWidget(KDDockWidgets::Anchor *anchor, QWidgetAdapter *parent)
|
||||
SeparatorWidget::SeparatorWidget(Layouting::Anchor *anchor, QWidget *parent)
|
||||
: Separator(anchor, parent)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
@@ -45,7 +45,7 @@ void SeparatorWidget::paintEvent(QPaintEvent *)
|
||||
opt.palette = palette();
|
||||
opt.rect = rect();
|
||||
opt.state = QStyle::State_None;
|
||||
if (isVertical())
|
||||
if (!isVertical())
|
||||
opt.state |= QStyle::State_Horizontal;
|
||||
|
||||
if (isEnabled())
|
||||
@@ -56,16 +56,14 @@ void SeparatorWidget::paintEvent(QPaintEvent *)
|
||||
|
||||
void SeparatorWidget::enterEvent(QEvent *)
|
||||
{
|
||||
qCDebug(anchors) << Q_FUNC_INFO << anchor() << isEnabled() << this;
|
||||
qCDebug(anchors) << Q_FUNC_INFO << anchor() << this;
|
||||
if (!anchor())
|
||||
return;
|
||||
|
||||
if (!isStatic()) {
|
||||
if (isVertical())
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
else
|
||||
setCursor(Qt::SizeVerCursor);
|
||||
}
|
||||
if (isVertical())
|
||||
setCursor(Qt::SizeVerCursor);
|
||||
else
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
}
|
||||
|
||||
void SeparatorWidget::leaveEvent(QEvent *)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef KD_MULTISPLITTER_SEPARATORWIDGET_P_H
|
||||
#define KD_MULTISPLITTER_SEPARATORWIDGET_P_H
|
||||
|
||||
#include "docks_export.h"
|
||||
#include "multisplitter/Separator_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -29,11 +30,11 @@ QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class DOCKS_EXPORT SeparatorWidget : public Separator
|
||||
class DOCKS_EXPORT SeparatorWidget : public Layouting::Separator
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SeparatorWidget(Anchor *anchor, QWidgetAdapter *parent = nullptr);
|
||||
explicit SeparatorWidget(Layouting::Anchor *anchor, QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
||||
1133
tests/tst_docks.cpp
1133
tests/tst_docks.cpp
File diff suppressed because it is too large
Load Diff
@@ -97,7 +97,6 @@ DockWidgetBase *KDDockWidgets::Tests::createDockWidget(const QString &name, QWid
|
||||
dock->activateWindow();
|
||||
Q_ASSERT(dock->window());
|
||||
if (QTest::qWaitForWindowActive(dock->window()->windowHandle(), 200)) {
|
||||
qDebug() << dock->window();
|
||||
return dock;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
Reference in New Issue
Block a user