While restoring a layout, we expect all widgets to exist already, but we allow the user to create them delayed, by providing us a factory function. What we're supporting in this commit is the ability of the user's factory function returning a dock widget with an ID different than the one that was requested. We then save that mapping so the rest of the layout restore works with the new ID.
7248 lines
248 KiB
C++
7248 lines
248 KiB
C++
/*
|
|
This file is part of KDDockWidgets.
|
|
|
|
SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
Author: Sérgio Martins <sergio.martins@kdab.com>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
|
|
|
|
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
|
*/
|
|
|
|
// We don't care about performance related checks in the tests
|
|
// clazy:excludeall=ctor-missing-parent-argument,missing-qobject-macro,range-loop,missing-typeinfo,detaching-member,function-args-by-ref,non-pod-global-static,reserve-candidates,qstring-allocations
|
|
|
|
#include "Config.h"
|
|
#include "DockWidgetBase.h"
|
|
#include "DockWidgetBase_p.h"
|
|
#include "DropAreaWithCentralFrame_p.h"
|
|
#include "MDILayoutWidget_p.h"
|
|
#include "MainWindowMDI.h"
|
|
#include "Position_p.h"
|
|
#include "SideBar_p.h"
|
|
#include "TabWidget_p.h"
|
|
#include "Testing.h"
|
|
#include "TitleBar_p.h"
|
|
#include "WindowBeingDragged_p.h"
|
|
#include "multisplitter/Separator_p.h"
|
|
#include "private/MultiSplitter_p.h"
|
|
#include "utils.h"
|
|
|
|
#include <QtTest/QtTest>
|
|
#include <QObject>
|
|
#include <QAction>
|
|
#include <QStyleFactory>
|
|
|
|
#ifdef KDDOCKWIDGETS_QTQUICK
|
|
# include "DockWidgetQuick.h"
|
|
# include "quick/MainWindowQuick_p.h"
|
|
|
|
# include <QQmlEngine>
|
|
# include <QQuickStyle>
|
|
# else
|
|
# include "DockWidget.h"
|
|
# include "MainWindow.h"
|
|
|
|
# include <QPushButton>
|
|
# include <QLineEdit>
|
|
# include <QTextEdit>
|
|
# include <QTabBar>
|
|
# include <QMenuBar>
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
using namespace KDDockWidgets;
|
|
using namespace Layouting;
|
|
using namespace KDDockWidgets::Tests;
|
|
|
|
static int osWindowMinWidth()
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
return GetSystemMetrics(SM_CXMIN);
|
|
#else
|
|
return 140; // Some random value for our windows. It's only important on Windows
|
|
#endif
|
|
}
|
|
|
|
class TestDocks : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public Q_SLOTS:
|
|
void initTestCase()
|
|
{
|
|
qputenv("KDDOCKWIDGETS_SHOW_DEBUG_WINDOW", "");
|
|
qApp->setOrganizationName("KDAB");
|
|
qApp->setApplicationName("dockwidgets-unit-tests");
|
|
|
|
qApp->setStyle(QStyleFactory::create("fusion"));
|
|
Testing::installFatalMessageHandler();
|
|
|
|
#ifdef KDDOCKWIDGETS_QTQUICK
|
|
QQuickStyle::setStyle("Material"); // so we don't load KDE plugins
|
|
KDDockWidgets::Config::self().setQmlEngine(new QQmlEngine(this));
|
|
#endif
|
|
}
|
|
|
|
void cleanupTestCase()
|
|
{
|
|
#ifdef KDDOCKWIDGETS_QTQUICK
|
|
delete KDDockWidgets::Config::Config::self().qmlEngine();
|
|
#endif
|
|
}
|
|
|
|
private Q_SLOTS:
|
|
void tst_simple1();
|
|
void tst_simple2();
|
|
void tst_doesntHaveNativeTitleBar();
|
|
void tst_resizeWindow2();
|
|
void tst_hasLastDockedLocation();
|
|
void tst_ghostSeparator();
|
|
void tst_detachFromMainWindow();
|
|
void tst_detachPos();
|
|
void tst_floatingWindowSize();
|
|
void tst_sizeAfterRedock();
|
|
void tst_tabbingWithAffinities();
|
|
void tst_honourUserGeometry();
|
|
void tst_floatingWindowTitleBug();
|
|
void tst_setFloatingSimple();
|
|
void tst_dragOverTitleBar();
|
|
void tst_setFloatingGeometry();
|
|
|
|
void tst_resizeWindow_data();
|
|
void tst_resizeWindow();
|
|
void tst_restoreEmpty();
|
|
void tst_restoreCentralFrame();
|
|
void tst_restoreMaximizedState();
|
|
void tst_shutdown();
|
|
void tst_closeDockWidgets();
|
|
void tst_layoutEqually();
|
|
void tst_doubleClose();
|
|
void tst_dockInternal();
|
|
void tst_maximizeAndRestore();
|
|
void tst_samePositionAfterHideRestore();
|
|
void tst_restoreTwice();
|
|
void tst_restoreAfterResize();
|
|
void tst_restoreWithNonClosableWidget();
|
|
void tst_restoreNestedAndTabbed();
|
|
void tst_restoreCrash();
|
|
void tst_restoreSideBySide();
|
|
void tst_restoreWithPlaceholder();
|
|
void tst_restoreWithAffinity();
|
|
void tst_marginsAfterRestore();
|
|
void tst_restoreWithNewDockWidgets();
|
|
void tst_restoreWithDockFactory();
|
|
void tst_restoreWithDockFactory2();
|
|
void tst_lastFloatingPositionIsRestored();
|
|
void tst_restoreSimple();
|
|
void tst_restoreSimplest();
|
|
void tst_restoreNonClosable();
|
|
void tst_invalidLayoutAfterRestore();
|
|
void tst_dontCloseDockWidgetBeforeRestore();
|
|
void tst_dontCloseDockWidgetBeforeRestore2();
|
|
void tst_dontCloseDockWidgetBeforeRestore3();
|
|
void tst_restoreWithNativeTitleBar();
|
|
|
|
void tst_closeOnlyCurrentTab();
|
|
void tst_tabWidgetCurrentIndex();
|
|
void tst_doubleClickTabToDetach();
|
|
void tst_propagateResize2();
|
|
void tst_negativeAnchorPosition();
|
|
void tst_negativeAnchorPosition2();
|
|
void tst_negativeAnchorPosition3();
|
|
void tst_negativeAnchorPosition4();
|
|
void tst_negativeAnchorPosition5();
|
|
void tst_negativeAnchorPosition6();
|
|
void tst_negativeAnchorPosition7();
|
|
void tst_startHidden();
|
|
void tst_startHidden2();
|
|
void tst_startClosed();
|
|
void tst_closeReparentsToNull();
|
|
void tst_invalidAnchorGroup();
|
|
void tst_addAsPlaceholder();
|
|
void tst_removeItem();
|
|
void tst_clear();
|
|
void tst_dockDockWidgetNested();
|
|
void tst_dockFloatingWindowNested();
|
|
void tst_crash();
|
|
void tst_refUnrefItem();
|
|
void tst_placeholderCount();
|
|
void tst_availableLengthForOrientation();
|
|
void tst_closeShowWhenNoCentralFrame();
|
|
void tst_setAsCurrentTab();
|
|
void tst_placeholderDisappearsOnReadd();
|
|
void tst_placeholdersAreRemovedProperly();
|
|
void tst_floatMaintainsSize();
|
|
void tst_preferredInitialSize();
|
|
|
|
void tst_crash2_data();
|
|
void tst_crash2();
|
|
void tst_closeAllDockWidgets();
|
|
void tst_toggleMiddleDockCrash();
|
|
void tst_stealFrame();
|
|
void tst_setFloatingWhenWasTabbed();
|
|
void tst_setFloatingWhenSideBySide();
|
|
void tst_dockWindowWithTwoSideBySideFramesIntoCenter();
|
|
void tst_tabTitleChanges();
|
|
void tst_dockWidgetGetsFocusWhenDocked();
|
|
void tst_setWidget();
|
|
void tst_isFocused();
|
|
void tst_floatingLastPosAfterDoubleClose();
|
|
void tst_registry();
|
|
void tst_honourGeometryOfHiddenWindow();
|
|
void tst_0_data();
|
|
void tst_0();
|
|
void tst_dockWindowWithTwoSideBySideFramesIntoRight();
|
|
void tst_dockWindowWithTwoSideBySideFramesIntoLeft();
|
|
void tst_posAfterLeftDetach();
|
|
void tst_preventClose();
|
|
void tst_propagateMinSize();
|
|
void tst_createFloatingWindow();
|
|
void tst_addAndReadd();
|
|
void tst_fairResizeAfterRemoveWidget();
|
|
void tst_invalidJSON_data();
|
|
void tst_invalidJSON();
|
|
|
|
void tst_invalidPlaceholderPosition_data();
|
|
void tst_invalidPlaceholderPosition();
|
|
void tst_setVisibleFalseWhenSideBySide_data();
|
|
void tst_setVisibleFalseWhenSideBySide();
|
|
void tst_resizeViaAnchorsAfterPlaceholderCreation();
|
|
void tst_rectForDropCrash();
|
|
void tst_addDockWidgetToMainWindow();
|
|
void tst_addDockWidgetToContainingWindow();
|
|
void tst_notClosable();
|
|
void tst_setFloatingAfterDraggedFromTabToSideBySide();
|
|
void tst_setFloatingAFrameWithTabs();
|
|
void tst_toggleDockWidgetWithHiddenTitleBar();
|
|
void tst_tabBarWithHiddenTitleBar_data();
|
|
void tst_tabBarWithHiddenTitleBar();
|
|
void tst_availableSizeWithPlaceholders();
|
|
void tst_anchorFollowingItselfAssert();
|
|
void tst_moreTitleBarCornerCases();
|
|
void tst_isInMainWindow();
|
|
void tst_sizeConstraintWarning();
|
|
void tst_stuckSeparator();
|
|
void tst_dockNotFillingSpace();
|
|
void tst_titlebar_getter();
|
|
void tst_raise();
|
|
void tst_addingOptionHiddenTabbed();
|
|
void tst_maxSizedFloatingWindow();
|
|
void tst_maxSizedHonouredAfterRemoved();
|
|
void tst_addDockWidgetAsTabToDockWidget();
|
|
void tst_close();
|
|
void tst_propagateSizeHonoursMinSize();
|
|
void tst_nonDockable();
|
|
void tst_floatingAction();
|
|
void tst_flagDoubleClick();
|
|
void tst_constraintsPropagateUp();
|
|
void tst_constraintsAfterPlaceholder();
|
|
void tst_addToSmallMainWindow1();
|
|
void tst_addToSmallMainWindow2();
|
|
void tst_addToSmallMainWindow3();
|
|
void tst_addToSmallMainWindow4();
|
|
void tst_addToSmallMainWindow5();
|
|
void tst_positionWhenShown();
|
|
void tst_28NestedWidgets();
|
|
void tst_28NestedWidgets_data();
|
|
void tst_dragBySingleTab();
|
|
void tst_dragByTabBar();
|
|
void tst_dragByTabBar_data();
|
|
void tst_titleBarFocusedWhenTabsChange();
|
|
void tst_dock2FloatingWidgetsTabbed();
|
|
void tst_deleteOnClose();
|
|
void tst_toggleAction();
|
|
void tst_redocksToPreviousTabIndex();
|
|
|
|
void tst_addMDIDockWidget();
|
|
void tst_redockToMDIRestoresPosition();
|
|
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
// TODO: Port these to QtQuick
|
|
|
|
void tst_mainWindowAlwaysHasCentralWidget();
|
|
void tst_dockableMainWindows();
|
|
|
|
// But these are fine to be widget only:
|
|
void tst_tabsNotClickable();
|
|
void tst_embeddedMainWindow();
|
|
void tst_restoreEmbeddedMainWindow();
|
|
void tst_negativeAnchorPositionWhenEmbedded();
|
|
void tst_negativeAnchorPositionWhenEmbedded_data();
|
|
void tst_closeRemovesFromSideBar();
|
|
void tst_restoreSideBar();
|
|
void tst_toggleActionOnSideBar();
|
|
void tst_deleteOnCloseWhenOnSideBar();
|
|
void tst_sidebarOverlayGetsHiddenOnClick();
|
|
void tst_floatRemovesFromSideBar();
|
|
void tst_overlayedGeometryIsSaved();
|
|
|
|
// And fix these
|
|
void tst_floatingWindowDeleted();
|
|
void tst_addToSmallMainWindow6();
|
|
void tst_minSizeChanges();
|
|
void tst_maxSizePropagates();
|
|
void tst_maxSizePropagates2();
|
|
void tst_restoreResizesLayout();
|
|
void tst_maxSizeHonouredWhenDropped();
|
|
void tst_fixedSizePolicy();
|
|
void tst_maxSizeHonouredWhenAnotherDropped();
|
|
void tst_addToHiddenMainWindow();
|
|
void tst_maximumSizePolicy();
|
|
void tst_complex();
|
|
#endif
|
|
};
|
|
|
|
static QPoint dragPointForWidget(Frame *frame, int index)
|
|
{
|
|
if (frame->hasSingleDockWidget()) {
|
|
Q_ASSERT(index == 0);
|
|
return frame->titleBar()->mapToGlobal(QPoint(5, 5));
|
|
} else {
|
|
QRect rect = frame->tabWidget()->tabBar()->rectForTab(index);
|
|
return frame->tabWidget()->tabBar()->asWidget()->mapToGlobal(rect.center());
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
inline int widgetMinLength(const T *w, Qt::Orientation o)
|
|
{
|
|
const QSize sz = Widget::widgetMinSize(w);
|
|
return o == Qt::Vertical ? sz.height() : sz.width();
|
|
}
|
|
|
|
static DockWidgetBase *createAndNestDockWidget(DropArea *dropArea, Frame *relativeTo,
|
|
KDDockWidgets::Location location)
|
|
{
|
|
static int count = 0;
|
|
count++;
|
|
const QString name = QString("dock%1").arg(count);
|
|
auto dock = createDockWidget(name, Qt::red);
|
|
dock->setObjectName(name);
|
|
nestDockWidget(dock, dropArea, relativeTo, location);
|
|
dropArea->checkSanity();
|
|
return dock;
|
|
}
|
|
|
|
static std::unique_ptr<MainWindowBase> createSimpleNestedMainWindow(DockWidgetBase * *centralDock,
|
|
DockWidgetBase * *leftDock,
|
|
DockWidgetBase * *rightDock)
|
|
{
|
|
auto window = createMainWindow({900, 500});
|
|
*centralDock = createDockWidget("centralDock", Qt::green);
|
|
window->addDockWidgetAsTab(*centralDock);
|
|
auto dropArea = window->dropArea();
|
|
|
|
*leftDock = createAndNestDockWidget(dropArea, nullptr, KDDockWidgets::Location_OnLeft);
|
|
*rightDock = createAndNestDockWidget(dropArea, nullptr, KDDockWidgets::Location_OnRight);
|
|
return window;
|
|
}
|
|
|
|
void TestDocks::tst_simple1()
|
|
{
|
|
// Simply create a MainWindow
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
m->layoutWidget()->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_simple2()
|
|
{
|
|
// Simply create a MainWindow, and dock something on top
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dw = createDockWidget("dw", new MyWidget("dw", Qt::blue));
|
|
auto fw = dw->floatingWindow();
|
|
m->addDockWidget(dw, KDDockWidgets::Location_OnTop);
|
|
m->layoutWidget()->checkSanity();
|
|
delete fw;
|
|
}
|
|
|
|
void TestDocks::tst_restoreSimple()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Tests restoring a very simple layout, composed of just 1 docked widget
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto layout = m->multiSplitter();
|
|
auto dock1 = createDockWidget("one", new QTextEdit());
|
|
auto dock2 = createDockWidget("two", new QTextEdit());
|
|
auto dock3 = createDockWidget("three", new QTextEdit());
|
|
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
// Dock2 floats at 150,150
|
|
const QPoint dock2FloatingPoint = QPoint(150, 150);
|
|
dock2->window()->move(dock2FloatingPoint);
|
|
QVERIFY(dock2->isVisible());
|
|
|
|
const QPoint dock3FloatingPoint = QPoint(200, 200);
|
|
dock3->window()->move(dock3FloatingPoint);
|
|
dock3->close();
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreSimple.json")));
|
|
auto f1 = dock1->dptr()->frame();
|
|
dock2->window()->move(QPoint(0, 0)); // Move *after* we saved.
|
|
dock3->window()->move(QPoint(0, 0)); // Move *after* we saved.
|
|
dock1->close();
|
|
dock2->close();
|
|
QVERIFY(!dock2->isVisible());
|
|
QCOMPARE(layout->count(), 1);
|
|
QVERIFY(Testing::waitForDeleted(f1));
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
QCOMPARE(DockRegistry::self()->floatingWindows().size(), 0);
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreSimple.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
QVERIFY(dock1->isVisible());
|
|
QCOMPARE(saver.restoredDockWidgets().size(), 3);
|
|
|
|
// Test a crash I got:
|
|
dock1->setFloating(true);
|
|
QVERIFY(layout->checkSanity());
|
|
dock1->setFloating(false);
|
|
|
|
auto fw2 = dock2->floatingWindow();
|
|
QVERIFY(fw2);
|
|
QVERIFY(fw2->isVisible());
|
|
QVERIFY(fw2->isTopLevel());
|
|
QCOMPARE(fw2->pos(), dock2FloatingPoint);
|
|
QCOMPARE(fw2->windowHandle()->transientParent(), m->windowHandle());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(dock2->isVisible());
|
|
|
|
QVERIFY(!dock3->isVisible()); // Remains closed
|
|
QVERIFY(dock3->parentWidget() == nullptr);
|
|
|
|
dock3->show();
|
|
dock3->dptr()->morphIntoFloatingWindow(); // as it would take 1 event loop. Do it now so we can
|
|
// compare already.
|
|
|
|
QCOMPARE(dock3->window()->pos(), dock3FloatingPoint);
|
|
}
|
|
|
|
void TestDocks::tst_doesntHaveNativeTitleBar()
|
|
{
|
|
// Tests that a floating window doesn't have a native title bar
|
|
// This test is mostly to test a bug that was happening with QtQuick, where the native title bar
|
|
// would appear on linux
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto dw1 = createDockWidget("dock1");
|
|
FloatingWindow *fw = dw1->floatingWindow();
|
|
QVERIFY(fw);
|
|
QVERIFY(fw->windowFlags() & Qt::Tool);
|
|
|
|
#if defined(Q_OS_LINUX)
|
|
QVERIFY(fw->windowFlags() & Qt::FramelessWindowHint);
|
|
#elif defined(Q_OS_WIN)
|
|
QVERIFY(!(fw->windowFlags() & Qt::FramelessWindowHint));
|
|
#endif
|
|
|
|
delete dw1->window();
|
|
}
|
|
|
|
void TestDocks::tst_resizeWindow2()
|
|
{
|
|
// Tests that resizing the width of the main window will never move horizontal anchors
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1");
|
|
auto dock2 = createDockWidget("2");
|
|
|
|
FloatingWindow *fw1 = dock1->floatingWindow();
|
|
FloatingWindow *fw2 = dock2->floatingWindow();
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
m->addDockWidget(dock2, Location_OnBottom);
|
|
|
|
auto layout = m->multiSplitter();
|
|
Separator *anchor = layout->separators().at(0);
|
|
const int oldPosY = anchor->position();
|
|
m->resize(QSize(m->width() + 10, m->height()));
|
|
QCOMPARE(anchor->position(), oldPosY);
|
|
layout->checkSanity();
|
|
|
|
delete fw1;
|
|
delete fw2;
|
|
}
|
|
|
|
void TestDocks::tst_hasLastDockedLocation()
|
|
{
|
|
// Tests DockWidgetBase::hasPreviousDockedLocation()
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1");
|
|
m->layoutWidget()->checkSanity();
|
|
m->multiSplitter()->setObjectName("mainWindow-dropArea");
|
|
dock1->floatingWindow()->layoutWidget()->setObjectName("first-dropArea1");
|
|
dock1->floatingWindow()->layoutWidget()->checkSanity();
|
|
auto window1 = dock1->window();
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(!dock1->hasPreviousDockedLocation());
|
|
QVERIFY(dock1->setFloating(true));
|
|
QVERIFY(!dock1->setFloating(false)); // No docking location, so it's not docked
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(!dock1->hasPreviousDockedLocation());
|
|
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
m->layoutWidget()->checkSanity();
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(dock1->setFloating(true));
|
|
|
|
auto ms1 = dock1->floatingWindow()->layoutWidget();
|
|
ms1->setObjectName("dropArea1");
|
|
ms1->checkSanity();
|
|
QVERIFY(dock1->hasPreviousDockedLocation());
|
|
auto window11 = dock1->window();
|
|
QVERIFY(dock1->setFloating(false));
|
|
|
|
delete window1;
|
|
delete window11;
|
|
}
|
|
|
|
void TestDocks::tst_ghostSeparator()
|
|
{
|
|
// Tests a situation where a separator wouldn't be removed after a widget had been removed
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1");
|
|
auto dock2 = createDockWidget("2");
|
|
auto dock3 = createDockWidget("3");
|
|
|
|
QPointer<FloatingWindow> fw1 = dock1->floatingWindow();
|
|
QPointer<FloatingWindow> fw2 = dock2->floatingWindow();
|
|
QPointer<FloatingWindow> fw3 = dock3->floatingWindow();
|
|
|
|
dock1->addDockWidgetToContainingWindow(dock2, Location_OnRight);
|
|
QCOMPARE(fw1->multiSplitter()->separators().size(), 1);
|
|
QCOMPARE(Layouting::Separator::numSeparators(), 1);
|
|
|
|
m->addDockWidget(dock3, Location_OnBottom);
|
|
QCOMPARE(m->multiSplitter()->separators().size(), 0);
|
|
QCOMPARE(Layouting::Separator::numSeparators(), 1);
|
|
|
|
m->multiSplitter()->addMultiSplitter(fw1->multiSplitter(), Location_OnRight);
|
|
QCOMPARE(m->multiSplitter()->separators().size(), 2);
|
|
QCOMPARE(Layouting::Separator::numSeparators(), 2);
|
|
|
|
delete fw1;
|
|
delete fw2;
|
|
delete fw3;
|
|
}
|
|
|
|
void TestDocks::tst_detachFromMainWindow()
|
|
{
|
|
// Tests a situation where clicking the float button wouldn't work on QtQuick
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1");
|
|
auto fw1 = dock1->window();
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
QVERIFY(m->layoutWidget()->mainWindow() != nullptr);
|
|
QVERIFY(!dock1->isFloating());
|
|
TitleBar *tb = dock1->titleBar();
|
|
QVERIFY(tb == dock1->dptr()->frame()->titleBar());
|
|
QVERIFY(tb->isVisible());
|
|
QVERIFY(!tb->isFloating());
|
|
|
|
delete fw1;
|
|
}
|
|
|
|
void TestDocks::tst_detachPos()
|
|
{
|
|
// Tests a situation where detaching a dock widget would send it to a bogus position
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new MyWidget(QStringLiteral("1"), Qt::black), {}, {}, /** show = */false); // we're creating the dock widgets without showing them as floating initially, so it doesn't record the previous floating position
|
|
auto dock2 = createDockWidget("2", new MyWidget(QStringLiteral("2"), Qt::black), {}, {}, /** show = */false);
|
|
|
|
QVERIFY(!dock1->isVisible());
|
|
QVERIFY(!dock2->isVisible());
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
|
|
QVERIFY(!dock1->dptr()->lastPositions().lastFloatingGeometry().isValid());
|
|
QVERIFY(!dock2->dptr()->lastPositions().lastFloatingGeometry().isValid());
|
|
|
|
const int previousWidth = dock1->width();
|
|
dock1->setFloating(true);
|
|
QTest::qWait(400); // Needed for QtQuick
|
|
|
|
QVERIFY(qAbs(previousWidth - dock1->width()) < 15); // 15px of difference when floating is fine, due to margins and what not.
|
|
delete dock1->window();
|
|
}
|
|
|
|
void TestDocks::tst_floatingWindowSize()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1");
|
|
auto fw1 = dock1->window();
|
|
|
|
QTest::qWait(100);
|
|
|
|
QVERIFY(!fw1->geometry().isNull());
|
|
QCOMPARE(fw1->size(), fw1->windowHandle()->size());
|
|
|
|
delete fw1;
|
|
}
|
|
|
|
void TestDocks::tst_tabbingWithAffinities()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Tests that dock widgets with different affinities should not tab together
|
|
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None);
|
|
m1->setAffinities({ "af1", "af2" });
|
|
|
|
auto dw1 = new DockWidgetType("1");
|
|
dw1->setAffinities({ "af1" });
|
|
dw1->show();
|
|
|
|
auto dw2 = new DockWidgetType("2");
|
|
dw2->setAffinities({ "af2" });
|
|
dw2->show();
|
|
|
|
FloatingWindow *fw1 = dw1->floatingWindow();
|
|
FloatingWindow *fw2 = dw2->floatingWindow();
|
|
|
|
{
|
|
SetExpectedWarning ignoreWarning("Refusing to dock widget with incompatible affinity");
|
|
dw1->addDockWidgetAsTab(dw2);
|
|
QVERIFY(dw1->window() != dw2->window());
|
|
}
|
|
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
QVERIFY(!dw1->isFloating());
|
|
|
|
{
|
|
SetExpectedWarning ignoreWarning("Refusing to dock widget with incompatible affinity");
|
|
auto dropArea = m1->dropArea();
|
|
WindowBeingDragged wbd(fw2, fw2);
|
|
QVERIFY(!dropArea->drop(&wbd, dw1->dptr()->frame(),
|
|
DropIndicatorOverlayInterface::DropLocation_Center));
|
|
QVERIFY(dw1->window() != dw2->window());
|
|
}
|
|
|
|
delete fw1;
|
|
delete fw2;
|
|
}
|
|
|
|
void TestDocks::tst_sizeAfterRedock()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
auto dw2 = new DockWidgetType(QStringLiteral("2"));
|
|
dw2->setWidget(new MyWidget("2", Qt::red));
|
|
|
|
dw1->addDockWidgetToContainingWindow(dw2, Location_OnBottom);
|
|
const int height2 = dw2->dptr()->frame()->height();
|
|
|
|
dw2->setFloating(true);
|
|
QTest::qWait(100);
|
|
|
|
QCOMPARE(height2, dw2->window()->height());
|
|
auto oldFw2 = dw2->floatingWindow();
|
|
|
|
// Redock
|
|
FloatingWindow *fw1 = dw1->floatingWindow();
|
|
DropArea *dropArea = fw1->dropArea();
|
|
|
|
MultiSplitter *ms1 = fw1->multiSplitter();
|
|
{
|
|
WindowBeingDragged wbd2(oldFw2);
|
|
const QRect suggestedDropRect = ms1->rectForDrop(&wbd2, Location_OnBottom, nullptr);
|
|
QCOMPARE(suggestedDropRect.height(), height2);
|
|
}
|
|
|
|
dropArea->drop(dw2->floatingWindow(), Location_OnBottom, nullptr);
|
|
|
|
QCOMPARE(dw2->dptr()->frame()->height(), height2);
|
|
|
|
delete dw1->window();
|
|
delete oldFw2;
|
|
}
|
|
|
|
void TestDocks::tst_honourUserGeometry()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None);
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
QVERIFY(!dw1->testAttribute(Qt::WA_PendingMoveEvent));
|
|
|
|
const QPoint pt(10, 10);
|
|
dw1->move(pt);
|
|
dw1->show();
|
|
FloatingWindow *fw1 = dw1->floatingWindow();
|
|
QCOMPARE(fw1->windowHandle()->geometry().topLeft(), pt);
|
|
|
|
delete dw1->window();
|
|
}
|
|
|
|
void TestDocks::tst_floatingWindowTitleBug()
|
|
{
|
|
// Test for #74
|
|
EnsureTopLevelsDeleted e;
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
auto dw2 = new DockWidgetType(QStringLiteral("2"));
|
|
auto dw3 = new DockWidgetType(QStringLiteral("3"));
|
|
|
|
dw1->setObjectName(QStringLiteral("1"));
|
|
dw2->setObjectName(QStringLiteral("2"));
|
|
dw3->setObjectName(QStringLiteral("3"));
|
|
|
|
dw1->show();
|
|
dw1->addDockWidgetAsTab(dw2);
|
|
dw1->addDockWidgetToContainingWindow(dw3, Location_OnBottom);
|
|
|
|
dw1->titleBar()->onFloatClicked();
|
|
|
|
QCOMPARE(dw3->titleBar()->title(), QLatin1String("3"));
|
|
|
|
delete dw1->window();
|
|
delete dw3->window();
|
|
}
|
|
|
|
void TestDocks::tst_resizeWindow_data()
|
|
{
|
|
QTest::addColumn<bool>("doASaveRestore");
|
|
QTest::newRow("false") << false;
|
|
QTest::newRow("true") << true;
|
|
}
|
|
|
|
void TestDocks::tst_resizeWindow()
|
|
{
|
|
QFETCH(bool, doASaveRestore);
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new MyWidget("1", Qt::red));
|
|
auto dock2 = createDockWidget("2", new MyWidget("2", Qt::blue));
|
|
QPointer<FloatingWindow> fw1 = dock1->floatingWindow();
|
|
QPointer<FloatingWindow> fw2 = dock2->floatingWindow();
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
|
|
auto layout = m->multiSplitter();
|
|
|
|
layout->checkSanity();
|
|
|
|
const int oldWidth1 = dock1->width();
|
|
const int oldWidth2 = dock2->width();
|
|
|
|
QVERIFY(oldWidth2 - oldWidth1 <= 1); // They're not equal if separator thickness if even
|
|
|
|
if (doASaveRestore) {
|
|
LayoutSaver saver;
|
|
saver.restoreLayout(saver.serializeLayout());
|
|
}
|
|
|
|
m->showMaximized();
|
|
Testing::waitForResize(m.get());
|
|
|
|
const int maximizedWidth1 = dock1->width();
|
|
const int maximizedWidth2 = dock2->width();
|
|
|
|
const double relativeDifference = qAbs((maximizedWidth1 - maximizedWidth2) / (1.0 * layout->width()));
|
|
|
|
QVERIFY(relativeDifference <= 0.01);
|
|
|
|
m->showNormal();
|
|
Testing::waitForResize(m.get());
|
|
|
|
const int newWidth1 = dock1->width();
|
|
const int newWidth2 = dock2->width();
|
|
|
|
QCOMPARE(oldWidth1, newWidth1);
|
|
QCOMPARE(oldWidth2, newWidth2);
|
|
layout->checkSanity();
|
|
|
|
delete fw1;
|
|
delete fw2;
|
|
}
|
|
|
|
void TestDocks::tst_restoreTwice()
|
|
{
|
|
// Tests that restoring multiple times doesn't hide the floating windows for some reason
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow(QSize(500, 500), MainWindowOption_HasCentralFrame, "tst_restoreTwice");
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidgetAsTab(dock1);
|
|
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
|
|
dock2->dptr()->morphIntoFloatingWindow();
|
|
dock3->dptr()->morphIntoFloatingWindow();
|
|
|
|
{
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreTwice.json")));
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreTwice.json")));
|
|
QVERIFY(dock2->isVisible());
|
|
QVERIFY(dock3->isVisible());
|
|
}
|
|
|
|
{
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreTwice.json")));
|
|
QVERIFY(dock2->isVisible());
|
|
QVERIFY(dock3->isVisible());
|
|
QVERIFY(dock2->window()->isVisible());
|
|
QVERIFY(dock3->window()->isVisible());
|
|
auto fw = dock2->floatingWindow();
|
|
QVERIFY(fw);
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_restoreEmpty()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
// Create an empty main window, save it to disk.
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto layout = m->multiSplitter();
|
|
LayoutSaver saver;
|
|
const QSize oldSize = m->size();
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreEmpty.json")));
|
|
saver.restoreFromFile(QStringLiteral("layout_tst_restoreEmpty.json"));
|
|
QVERIFY(m->layoutWidget()->checkSanity());
|
|
QCOMPARE(layout->separators().size(), 0);
|
|
QCOMPARE(layout->count(), 0);
|
|
QCOMPARE(m->size(), oldSize);
|
|
QVERIFY(layout->checkSanity());
|
|
}
|
|
|
|
void TestDocks::tst_restoreCentralFrame()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500));
|
|
auto layout = m->multiSplitter();
|
|
|
|
QCOMPARE(layout->count(), 1);
|
|
Item *item = m->dropArea()->centralFrame();
|
|
QVERIFY(item);
|
|
auto frame = static_cast<Frame *>(item->guestAsQObject());
|
|
QCOMPARE(frame->options(), FrameOption_IsCentralFrame | FrameOption_AlwaysShowsTabs);
|
|
QVERIFY(!frame->titleBar()->isVisible());
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreCentralFrame.json")));
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreCentralFrame.json")));
|
|
|
|
QCOMPARE(layout->count(), 1);
|
|
item = m->dropArea()->centralFrame();
|
|
QVERIFY(item);
|
|
frame = static_cast<Frame *>(item->guestAsQObject());
|
|
QCOMPARE(frame->options(), FrameOption_IsCentralFrame | FrameOption_AlwaysShowsTabs);
|
|
QVERIFY(!frame->titleBar()->isVisible());
|
|
}
|
|
|
|
void TestDocks::tst_restoreMaximizedState()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
|
|
m->showMaximized();
|
|
|
|
QCOMPARE(m->windowHandle()->windowState(), Qt::WindowMaximized);
|
|
LayoutSaver saver;
|
|
|
|
const QByteArray saved = saver.serializeLayout();
|
|
m->showNormal();
|
|
QVERIFY(m->windowHandle()->windowState() != Qt::WindowMaximized);
|
|
|
|
saver.restoreLayout(saved);
|
|
QCOMPARE(m->windowHandle()->windowState(), Qt::WindowMaximized);
|
|
}
|
|
|
|
void TestDocks::tst_setFloatingSimple()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new MyWidget("one"));
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
auto l = m->multiSplitter();
|
|
dock1->setFloating(true);
|
|
QVERIFY(l->checkSanity());
|
|
dock1->setFloating(false);
|
|
QVERIFY(l->checkSanity());
|
|
dock1->setFloating(true);
|
|
QVERIFY(l->checkSanity());
|
|
dock1->setFloating(false);
|
|
QVERIFY(l->checkSanity());
|
|
}
|
|
|
|
void TestDocks::tst_nonDockable()
|
|
{
|
|
{ // First test without Option_NotDockable
|
|
auto dock = new DockWidgetType("1");
|
|
dock->show();
|
|
|
|
TitleBar *tb = dock->titleBar();
|
|
QVERIFY(tb->isVisible());
|
|
QVERIFY(tb->isFloatButtonVisible());
|
|
|
|
delete dock->window();
|
|
}
|
|
|
|
{
|
|
// Test that when using Option_NotDockable we don't get a dock/undock icon
|
|
auto dock = new DockWidgetType("1", DockWidgetBase::Option_NotDockable);
|
|
dock->show();
|
|
|
|
TitleBar *tb = dock->titleBar();
|
|
QVERIFY(tb->isVisible());
|
|
QVERIFY(!tb->isFloatButtonVisible());
|
|
|
|
delete dock->window();
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if (!qpaPassedAsArgument(argc, argv)) {
|
|
// Use offscreen by default as it's less annoying, doesn't create visible windows
|
|
qputenv("QT_QPA_PLATFORM", "offscreen");
|
|
}
|
|
|
|
QApplication app(argc, argv);
|
|
if (shouldSkipTests())
|
|
return 0;
|
|
|
|
TestDocks test;
|
|
return QTest::qExec(&test, argc, argv);
|
|
}
|
|
|
|
void TestDocks::tst_closeDockWidgets()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = createDockWidget("hello1", Qt::green);
|
|
auto dock2 = createDockWidget("hello2", Qt::green);
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
m->addDockWidget(dock2, Location_OnBottom);
|
|
|
|
QVERIFY(m->closeDockWidgets(true));
|
|
QCOMPARE(m->layoutWidget()->visibleCount(), 0);
|
|
}
|
|
|
|
void TestDocks::tst_layoutEqually()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
const QString mainWindowId = "{7829427d-88e3-402e-9120-50c628dfd0bc}";
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None, mainWindowId);
|
|
m->setAffinities({ mainWindowId });
|
|
|
|
auto dock1 = createDockWidget("Favorite-481", new MyWidget2(QSize(536, 438)));
|
|
auto dock2 = createDockWidget("Favorite-482", new MyWidget2(QSize(229, 118)));
|
|
auto dock3 = createDockWidget("Favorite-483", new MyWidget2(QSize(356, 90)));
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
m->setContentsMargins(10, 0, 10, 0);
|
|
#endif
|
|
dock1->setAffinities({ mainWindowId });
|
|
dock2->setAffinities({ mainWindowId });
|
|
dock3->setAffinities({ mainWindowId });
|
|
|
|
LayoutSaver restorer;
|
|
restorer.restoreFromFile(":/layouts/layoutEquallyCrash.json");
|
|
|
|
m->layoutEqually();
|
|
}
|
|
|
|
void TestDocks::tst_doubleClose()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
{
|
|
// Via close()
|
|
auto dock1 = createDockWidget("hello", Qt::green);
|
|
dock1->close();
|
|
dock1->close();
|
|
|
|
delete dock1->window();
|
|
}
|
|
{
|
|
// Via the button
|
|
auto dock1 = createDockWidget("hello", Qt::green);
|
|
auto fw1 = dock1->floatingWindow();
|
|
|
|
auto t = dock1->dptr()->frame()->titleBar();
|
|
t->onCloseClicked();
|
|
t->onCloseClicked();
|
|
|
|
delete dock1;
|
|
delete fw1;
|
|
}
|
|
{
|
|
// Test for #141, double delete would ruin lastPositions()
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
|
|
QVERIFY(!dock1->dptr()->lastPositions().wasFloating());
|
|
dock1->close();
|
|
QVERIFY(!dock1->dptr()->lastPositions().wasFloating());
|
|
dock1->close();
|
|
QVERIFY(!dock1->dptr()->lastPositions().wasFloating());
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_dockInternal()
|
|
{
|
|
/**
|
|
* Here we dock relative to an existing widget, and not to the drop-area.
|
|
*/
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dropArea = m->dropArea();
|
|
|
|
auto centralWidget = static_cast<Frame*>(dropArea->items()[0]->guestAsQObject());
|
|
nestDockWidget(dock1, dropArea, centralWidget, KDDockWidgets::Location_OnRight);
|
|
|
|
QVERIFY(dock1->width() < dropArea->width() - centralWidget->width());
|
|
}
|
|
|
|
void TestDocks::tst_maximizeAndRestore()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight);
|
|
|
|
auto dropArea = m->dropArea();
|
|
QVERIFY(dropArea->checkSanity());
|
|
|
|
m->showMaximized();
|
|
Testing::waitForResize(m.get());
|
|
|
|
QVERIFY(dropArea->checkSanity());
|
|
m->showNormal();
|
|
Testing::waitForResize(m.get());
|
|
|
|
QVERIFY(dropArea->checkSanity());
|
|
}
|
|
|
|
void TestDocks::tst_propagateResize2()
|
|
{
|
|
// |5|1|2|
|
|
// | |3|4|
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnTop);
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight, dock1);
|
|
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
auto dock4 = createDockWidget("dock4", new QPushButton("four"));
|
|
|
|
m->addDockWidget(dock3, KDDockWidgets::Location_OnBottom);
|
|
m->addDockWidget(dock4, KDDockWidgets::Location_OnRight, dock3);
|
|
|
|
auto dock5 = createDockWidget("dock5", new QPushButton("five"));
|
|
m->addDockWidget(dock5, KDDockWidgets::Location_OnLeft);
|
|
|
|
auto dropArea = m->dropArea();
|
|
dropArea->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_shutdown()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock = createDockWidget("doc1", Qt::green);
|
|
|
|
auto m = createMainWindow();
|
|
m->show();
|
|
QVERIFY(QTest::qWaitForWindowActive(m->windowHandle()));
|
|
delete dock->window();
|
|
}
|
|
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
|
|
void TestDocks::tst_complex()
|
|
{
|
|
// Tests some anchors out of bounds I got
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(600, 500), MainWindowOption_None);
|
|
auto layout = m->multiSplitter();
|
|
m->resize(3266, 2239);
|
|
m->show(); // TODO: Remove and see if it crashes
|
|
|
|
DockWidgetBase::List docks;
|
|
|
|
QVector<KDDockWidgets::Location> locations = {Location_OnLeft, Location_OnLeft, Location_OnLeft,
|
|
Location_OnRight, Location_OnRight, Location_OnRight, Location_OnRight,
|
|
Location_OnBottom, Location_OnBottom, Location_OnBottom, Location_OnBottom, Location_OnBottom,
|
|
Location_OnBottom, Location_OnBottom, Location_OnBottom, Location_OnBottom, Location_OnBottom,
|
|
Location_OnBottom, Location_OnBottom, Location_OnBottom, Location_OnBottom
|
|
};
|
|
|
|
QVector<KDDockWidgets::InitialVisibilityOption> options = { InitialVisibilityOption::StartVisible, InitialVisibilityOption::StartVisible,
|
|
InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartHidden,
|
|
InitialVisibilityOption::StartVisible,
|
|
InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartHidden,InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartHidden,InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartHidden,
|
|
InitialVisibilityOption::StartVisible, InitialVisibilityOption::StartVisible,
|
|
InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartHidden,InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartHidden,InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartHidden,InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartHidden
|
|
};
|
|
|
|
QVector<bool> floatings = {true, false, true, false, false, false, false, false, false, false, false, false,
|
|
true, false, false, true, true, true, true, true, false };
|
|
|
|
QVector<QSize> minSizes= {
|
|
QSize(316, 219),
|
|
QSize(355, 237),
|
|
QSize(293, 66),
|
|
QSize(158, 72),
|
|
QSize(30, 141),
|
|
QSize(104, 143),
|
|
QSize(104, 105),
|
|
QSize(84, 341),
|
|
QSize(130, 130),
|
|
QSize(404, 205),
|
|
QSize(296, 177),
|
|
QSize(914, 474),
|
|
QSize(355, 237),
|
|
QSize(104, 104),
|
|
QSize(104, 138),
|
|
QSize(1061, 272),
|
|
QSize(165, 196),
|
|
QSize(296, 177),
|
|
QSize(104, 104),
|
|
QSize(355, 237),
|
|
QSize(104, 138)
|
|
};
|
|
|
|
const int num = 21;
|
|
for (int i = 0; i < num; ++i) {
|
|
auto widget = new MyWidget2(minSizes.at(i));
|
|
auto dw = new DockWidgetType(QString::number(i));
|
|
dw->setWidget(widget);
|
|
docks << dw;
|
|
}
|
|
|
|
for (int i = 0; i < num; ++i) {
|
|
m->addDockWidget(docks[i], locations[i], nullptr, options[i]);
|
|
layout->checkSanity();
|
|
docks[i]->setFloating(floatings[i]);
|
|
layout->checkSanity();
|
|
}
|
|
|
|
m->show();
|
|
|
|
// Cleanup
|
|
qDeleteAll(docks);
|
|
qDeleteAll(DockRegistry::self()->frames());
|
|
}
|
|
#endif
|
|
|
|
void TestDocks::tst_28NestedWidgets_data()
|
|
{
|
|
QTest::addColumn<QVector<DockDescriptor>>("docksToCreate");
|
|
QTest::addColumn<QVector<int>>("docksToHide");
|
|
|
|
QVector<DockDescriptor> docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible }
|
|
};
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
QTest::newRow("28") << docks << QVector<int>{11, 0};
|
|
#endif
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
|
|
};
|
|
|
|
QVector<int> docksToHide;
|
|
for (int i = 0; i < docks.size(); ++i) {
|
|
docksToHide << i;
|
|
}
|
|
|
|
QTest::newRow("anchor_intersection") << docks << docksToHide;
|
|
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
};
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
// 2. Produced valgrind invalid reads while adding
|
|
QTest::newRow("valgrind") << docks << QVector<int>{};
|
|
#endif
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
};
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
QTest::newRow("bug_when_closing") << docks << QVector<int>{}; // Q_ASSERT(!isSquashed())
|
|
#endif
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
};
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
QTest::newRow("bug_when_closing2") << docks << QVector<int>{}; // Tests for void KDDockWidgets::Anchor::setPosition(int, KDDockWidgets::Anchor::SetPositionOptions) Negative position -69
|
|
#endif
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, 0, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible }
|
|
};
|
|
|
|
docksToHide.clear();
|
|
for (int i = 0; i < 28; ++i) {
|
|
if (i != 16 && i != 17 && i != 18 && i != 27)
|
|
docksToHide << i;
|
|
}
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
QTest::newRow("bug_with_holes") << docks << docksToHide;
|
|
#endif
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnLeft, 17, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible } };
|
|
|
|
docksToHide.clear();
|
|
QTest::newRow("add_as_placeholder") << docks << docksToHide;
|
|
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden } };
|
|
|
|
QTest::newRow("add_as_placeholder_simple") << docks << docksToHide;
|
|
|
|
|
|
docks = {
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden } };
|
|
|
|
docksToHide.clear();
|
|
QTest::newRow("isSquashed_assert") << docks << docksToHide;
|
|
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden } };
|
|
|
|
docksToHide.clear();
|
|
QTest::newRow("negative_pos_warning") << docks << docksToHide;
|
|
|
|
docks = {
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible } };
|
|
|
|
docksToHide.clear();
|
|
QTest::newRow("bug") << docks << docksToHide;
|
|
|
|
docks = {
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible } };
|
|
|
|
docksToHide.clear();
|
|
QTest::newRow("bug2") << docks << docksToHide;
|
|
|
|
docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible } };
|
|
|
|
docksToHide.clear();
|
|
QTest::newRow("bug3") << docks << docksToHide;
|
|
}
|
|
|
|
void TestDocks::tst_28NestedWidgets()
|
|
{
|
|
QFETCH(QVector<DockDescriptor>, docksToCreate);
|
|
QFETCH(QVector<int>, docksToHide);
|
|
|
|
// Tests a case that used to cause negative anchor position when turning into placeholder
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
|
|
int i = 0;
|
|
for (DockDescriptor &desc : docksToCreate) {
|
|
desc.createdDock = createDockWidget(QString("%1").arg(i), new QPushButton(QString("%1").arg(i).toLatin1()), {}, {}, false);
|
|
|
|
DockWidgetBase *relativeTo = nullptr;
|
|
if (desc.relativeToIndex != -1)
|
|
relativeTo = docksToCreate.at(desc.relativeToIndex).createdDock;
|
|
m->addDockWidget(desc.createdDock, desc.loc, relativeTo, desc.option);
|
|
QVERIFY(layout->checkSanity());
|
|
++i;
|
|
}
|
|
|
|
layout->checkSanity();
|
|
|
|
// Run the saver in these complex scenarios:
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
QVERIFY(!saved.isEmpty());
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
|
|
layout->checkSanity();
|
|
|
|
for (int i : docksToHide) {
|
|
docksToCreate.at(i).createdDock->close();
|
|
layout->checkSanity();
|
|
QTest::qWait(200);
|
|
}
|
|
|
|
layout->checkSanity();
|
|
|
|
for (int i : docksToHide) {
|
|
docksToCreate.at(i).createdDock->deleteLater();
|
|
QVERIFY(Testing::waitForDeleted(docksToCreate.at(i).createdDock));
|
|
}
|
|
|
|
layout->checkSanity();
|
|
|
|
// And hide the remaining ones
|
|
i = 0;
|
|
for (auto dock : docksToCreate) {
|
|
if (dock.createdDock && dock.createdDock->isVisible()) {
|
|
dock.createdDock->close();
|
|
QTest::qWait(200); // Wait for the docks to be closed. TODO Replace with a global event filter and wait for any resize ?
|
|
}
|
|
++i;
|
|
}
|
|
|
|
layout->checkSanity();
|
|
|
|
// Cleanup
|
|
for (auto dock : DockRegistry::self()->dockwidgets()) {
|
|
dock->deleteLater();
|
|
QVERIFY(Testing::waitForDeleted(dock));
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_closeReparentsToNull()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto fw1 = dock1->window();
|
|
QVERIFY(dock1->parent() != nullptr);
|
|
dock1->close();
|
|
QVERIFY(dock1->parent() == nullptr);
|
|
delete fw1;
|
|
delete dock1;
|
|
}
|
|
|
|
void TestDocks::tst_startHidden()
|
|
{
|
|
// A really simple test for InitialVisibilityOption::StartHidden
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"), {}, {}, /*show=*/false);
|
|
m->addDockWidget(dock1, Location_OnRight, nullptr, InitialVisibilityOption::StartHidden);
|
|
delete dock1;
|
|
}
|
|
|
|
void TestDocks::tst_startHidden2()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, {}, false);
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, {}, false);
|
|
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
|
|
m->addDockWidget(dock1, Location_OnTop, nullptr, InitialVisibilityOption::StartHidden);
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
m->addDockWidget(dock2, Location_OnTop);
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
qDebug() << dock1->isVisible();
|
|
dock1->show();
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
Testing::waitForResize(dock2);
|
|
}
|
|
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, {}, false);
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, {}, false);
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"), {}, {}, false);
|
|
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
m->addDockWidget(dock1, Location_OnLeft, nullptr, InitialVisibilityOption::StartHidden);
|
|
|
|
m->addDockWidget(dock2, Location_OnBottom, nullptr, InitialVisibilityOption::StartHidden);
|
|
m->addDockWidget(dock3, Location_OnRight, nullptr, InitialVisibilityOption::StartHidden);
|
|
|
|
dock1->show();
|
|
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 2);
|
|
|
|
dock2->show();
|
|
dock3->show();
|
|
Testing::waitForResize(dock2);
|
|
layout->checkSanity();
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPosition()
|
|
{
|
|
// Tests that we don't hit:
|
|
// void KDDockWidgets::Anchor::setPosition(int, KDDockWidgets::Anchor::SetPositionOptions) Negative position
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(1002, 806));
|
|
|
|
auto w1 = new MyWidget2(QSize(104, 104));
|
|
w1->resize(994, 718);
|
|
auto w2 = new MyWidget2(QSize(133, 343));
|
|
w2->resize(392, 362);
|
|
auto w3 = new MyWidget2(QSize(133, 343));
|
|
w3->resize(392, 362);
|
|
|
|
MultiSplitter *layout = m->multiSplitter();
|
|
|
|
auto d1 = createDockWidget("1", w1);
|
|
auto d2 = createDockWidget("2", w2);
|
|
auto d3 = createDockWidget("3", w3);
|
|
|
|
m->addDockWidgetAsTab(d1);
|
|
|
|
m->addDockWidget(d2, Location_OnTop);
|
|
m->addDockWidget(d3, Location_OnTop);
|
|
|
|
d2->close();
|
|
|
|
Testing::waitForResize(d3);
|
|
d2->show(); // Should not result in negative anchor positions (Test will fail due to a qWarning)
|
|
Testing::waitForResize(d3);
|
|
layout->checkSanity();
|
|
|
|
d2->close();
|
|
Testing::waitForResize(d3);
|
|
layout->checkSanity();
|
|
|
|
// Now resize the Window, after removing middle one
|
|
const int availableToShrink = layout->size().height() - layout->minimumSize().height();
|
|
const QSize newSize = { layout->width(), layout->height() - availableToShrink };
|
|
if (layout->layoutMinimumSize().expandedTo(newSize) != newSize) {
|
|
qDebug() << "Size to set is too small=" << newSize
|
|
<< "; min=" << layout->layoutMinimumSize();
|
|
QFAIL("");
|
|
}
|
|
|
|
layout->setLayoutSize(newSize);
|
|
|
|
d2->deleteLater();
|
|
Testing::waitForDeleted(d2);
|
|
layout->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPosition2()
|
|
{
|
|
// Tests that the "Out of bounds position" warning doesn't appear. Test will abort if yes.
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"), {}, {}, /*show=*/false);
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"), {}, {}, /*show=*/false);
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"), {}, {}, /*show=*/false);
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight, nullptr, InitialVisibilityOption::StartHidden);
|
|
m->addDockWidget(dock3, Location_OnRight);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
QCOMPARE(layout->count(), 3);
|
|
|
|
dock1->setFloating(true);
|
|
dock1->setFloating(false);
|
|
dock2->deleteLater();
|
|
layout->checkSanity();
|
|
QVERIFY(Testing::waitForDeleted(dock2));
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPosition3()
|
|
{
|
|
// 1. Another case, when floating a dock:
|
|
EnsureTopLevelsDeleted e;
|
|
QVector<DockDescriptor> docks = { {Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible } };
|
|
auto m = createMainWindow(docks);
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
layout->checkSanity();
|
|
|
|
auto dock1 = docks.at(1).createdDock;
|
|
auto dock3 = docks.at(3).createdDock;
|
|
|
|
dock1->setFloating(true);
|
|
delete dock1->window();
|
|
delete dock3->window();
|
|
|
|
layout->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPosition4()
|
|
{
|
|
// 1. Tests that we don't get a warning
|
|
// Out of bounds position= -5 ; oldPosition= 0 KDDockWidgets::Anchor(0x55e726be9090, name = "left") KDDockWidgets::MainWindow(0x55e726beb8d0)
|
|
EnsureTopLevelsDeleted e;
|
|
QVector<DockDescriptor> docks = { { Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{ Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{ Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{ Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{ Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible } };
|
|
|
|
auto m = createMainWindow(docks);
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
layout->checkSanity();
|
|
|
|
auto dock1 = docks.at(1).createdDock;
|
|
auto dock2 = docks.at(2).createdDock;
|
|
dock2->setFloating(true);
|
|
auto fw2 = dock2->floatingWindow();
|
|
dropArea->addWidget(fw2->dropArea(), Location_OnLeft, dock1->dptr()->frame());
|
|
dock2->setFloating(true);
|
|
fw2 = dock2->floatingWindow();
|
|
|
|
dropArea->addWidget(fw2->dropArea(), Location_OnRight, dock1->dptr()->frame());
|
|
|
|
layout->checkSanity();
|
|
docks.at(0).createdDock->deleteLater();
|
|
docks.at(4).createdDock->deleteLater();
|
|
Testing::waitForDeleted(docks.at(4).createdDock);
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPosition5()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
QVector<DockDescriptor> docks = {
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
};
|
|
|
|
auto m = createMainWindow(docks);
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
layout->checkSanity();
|
|
|
|
auto dock0 = docks.at(0).createdDock;
|
|
auto dock1 = docks.at(1).createdDock;
|
|
|
|
dock1->show();
|
|
QVERIFY(layout->checkSanity());
|
|
dock0->show();
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
// Cleanup
|
|
for (auto dock : DockRegistry::self()->dockwidgets())
|
|
dock->deleteLater();
|
|
|
|
QVERIFY(Testing::waitForDeleted(dock0));
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPosition6()
|
|
{
|
|
// Tests a case when we add a widget to left/right but the layout doesn't have enough height (or vice-versa)
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
m->resize(QSize(100, 100));
|
|
m->show();
|
|
|
|
auto layout = m->multiSplitter();
|
|
|
|
auto w1 = new MyWidget2(QSize(400,100));
|
|
auto w2 = new MyWidget2(QSize(400,100));
|
|
auto w3 = new MyWidget2(QSize(400,100));
|
|
auto w4 = new MyWidget2(QSize(400,900));
|
|
auto d1 = createDockWidget("1", w1);
|
|
auto d2 = createDockWidget("2", w2);
|
|
auto d3 = createDockWidget("3", w3);
|
|
auto d4 = createDockWidget("4", w4);
|
|
|
|
m->addDockWidget(d1, Location_OnBottom);
|
|
m->addDockWidget(d2, Location_OnBottom);
|
|
m->addDockWidget(d3, Location_OnBottom);
|
|
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
m->addDockWidget(d4, Location_OnRight, d3);
|
|
|
|
layout->checkSanity();
|
|
|
|
Item *centralItem = m->dropArea()->centralFrame();
|
|
layout->rectForDrop(nullptr, Location_OnTop, centralItem);
|
|
layout->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPosition7()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
m->show();
|
|
auto w1 = new MyWidget2(QSize(400,400));
|
|
auto w2 = new MyWidget2(QSize(400,400));
|
|
|
|
auto d1 = new DockWidgetType("1");
|
|
d1->setWidget(w1);
|
|
auto d2 = new DockWidgetType("2");
|
|
d2->setWidget(w2);
|
|
|
|
auto w3 = new MyWidget2(QSize(100,100));
|
|
auto d3 = new DockWidgetType("3");
|
|
d3->setWidget(w3);
|
|
|
|
// Stack 1, 2
|
|
m->addDockWidget(d2, Location_OnTop);
|
|
m->addDockWidget(d1, Location_OnTop);
|
|
|
|
// add a small one to the middle
|
|
|
|
// Stack: 1, 3, 2
|
|
m->addDockWidget(d3, Location_OnTop, d2);
|
|
m->layoutWidget()->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_invalidAnchorGroup()
|
|
{
|
|
// Tests a bug I got. Should not warn.
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
{
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
QPointer<FloatingWindow> fw = dock2->dptr()->morphIntoFloatingWindow();
|
|
nestDockWidget(dock1, fw->dropArea(), nullptr, KDDockWidgets::Location_OnTop);
|
|
|
|
dock1->close();
|
|
Testing::waitForResize(dock2);
|
|
auto layout = fw->dropArea();
|
|
layout->checkSanity();
|
|
|
|
dock2->close();
|
|
dock1->deleteLater();
|
|
dock2->deleteLater();
|
|
Testing::waitForDeleted(dock1);
|
|
}
|
|
|
|
{
|
|
// Stack 1, 2, 3, close 2, close 1
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
|
|
m->addDockWidget(dock3, Location_OnTop);
|
|
m->addDockWidget(dock2, Location_OnTop);
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
dock2->close();
|
|
dock1->close();
|
|
|
|
dock1->deleteLater();
|
|
dock2->deleteLater();
|
|
Testing::waitForDeleted(dock1);
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_addAsPlaceholder()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, {}, false);
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, {}, false);
|
|
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
m->addDockWidget(dock2, Location_OnTop, nullptr, InitialVisibilityOption::StartHidden);
|
|
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
QVERIFY(!dock2->isVisible());
|
|
|
|
dock2->show();
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
layout->checkSanity();
|
|
|
|
// Cleanup
|
|
dock2->deleteLater();
|
|
Testing::waitForDeleted(dock2);
|
|
}
|
|
|
|
void TestDocks::tst_removeItem()
|
|
{
|
|
// Tests that MultiSplitterLayout::removeItem() works
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, {}, false);
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
m->addDockWidget(dock2, Location_OnTop, nullptr, InitialVisibilityOption::StartHidden);
|
|
Item *item2 = dock2->dptr()->lastPositions().lastItem();
|
|
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
// 1. Remove an item that's a placeholder
|
|
|
|
layout->removeItem(item2);
|
|
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// 2. Remove an item that has an actual widget
|
|
Item *item1 = dock1->dptr()->lastPositions().lastItem();
|
|
layout->removeItem(item1);
|
|
QCOMPARE(layout->count(), 0);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// 3. Remove an item that has anchors following one of its other anchors (Tests that anchors stop following)
|
|
// Stack 1, 2, 3
|
|
m->addDockWidget(dock3, Location_OnBottom);
|
|
m->addDockWidget(dock2, Location_OnBottom);
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
dock2->close();
|
|
auto frame1 = dock1->dptr()->frame();
|
|
dock1->close();
|
|
QVERIFY(Testing::waitForDeleted(frame1));
|
|
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 2);
|
|
|
|
// Now remove the items
|
|
layout->removeItem(dock2->dptr()->lastPositions().lastItem());
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
layout->checkSanity();
|
|
layout->removeItem(dock1->dptr()->lastPositions().lastItem());
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// Add again
|
|
m->addDockWidget(dock2, Location_OnBottom);
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
dock2->close();
|
|
frame1 = dock1->dptr()->frame();
|
|
dock1->close();
|
|
QVERIFY(Testing::waitForDeleted(frame1));
|
|
|
|
// Now remove the items, but first dock1
|
|
layout->removeItem(dock1->dptr()->lastPositions().lastItem());
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
layout->checkSanity();
|
|
layout->removeItem(dock2->dptr()->lastPositions().lastItem());
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
layout->checkSanity();
|
|
|
|
// Add again, stacked as 1, 2, 3, then close 2 and 3.
|
|
m->addDockWidget(dock2, Location_OnTop);
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
auto frame2 = dock2->dptr()->frame();
|
|
dock2->close();
|
|
Testing::waitForDeleted(frame2);
|
|
|
|
auto frame3 = dock3->dptr()->frame();
|
|
dock3->close();
|
|
Testing::waitForDeleted(frame3);
|
|
|
|
// The second anchor is now following the 3rd, while the 3rd is following 'bottom'
|
|
layout->removeItem(dock3->dptr()->lastPositions().lastItem()); // will trigger the 3rd anchor to
|
|
// be removed
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
layout->checkSanity();
|
|
|
|
dock1->deleteLater();
|
|
dock2->deleteLater();
|
|
dock3->deleteLater();
|
|
Testing::waitForDeleted(dock3);
|
|
}
|
|
|
|
void TestDocks::tst_clear()
|
|
{
|
|
// Tests MultiSplitterLayout::clear()
|
|
EnsureTopLevelsDeleted e;
|
|
QCOMPARE(Frame::dbg_numFrames(), 0);
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
auto fw3 = dock3->floatingWindow();
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
m->addDockWidget(dock3, Location_OnRight);
|
|
QVERIFY(Testing::waitForDeleted(fw3));
|
|
dock3->close();
|
|
|
|
QCOMPARE(Frame::dbg_numFrames(), 3);
|
|
|
|
auto layout = m->multiSplitter();
|
|
layout->clearLayout();
|
|
|
|
QCOMPARE(layout->count(), 0);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
layout->checkSanity();
|
|
|
|
// Cleanup
|
|
dock3->deleteLater();
|
|
QVERIFY(Testing::waitForDeleted(dock3));
|
|
}
|
|
|
|
void TestDocks::tst_samePositionAfterHideRestore()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
m->addDockWidget(dock3, Location_OnRight);
|
|
QRect geo2 = dock2->dptr()->frame()->QWidgetAdapter::geometry();
|
|
dock2->setFloating(true);
|
|
|
|
auto fw2 = dock2->floatingWindow();
|
|
dock2->setFloating(false);
|
|
QVERIFY(Testing::waitForDeleted(fw2));
|
|
QCOMPARE(geo2, dock2->dptr()->frame()->QWidgetAdapter::geometry());
|
|
m->layoutWidget()->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_startClosed()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
Frame *frame1 = dock1->dptr()->frame();
|
|
dock1->close();
|
|
Testing::waitForDeleted(frame1);
|
|
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
m->addDockWidget(dock2, Location_OnTop);
|
|
|
|
layout->checkSanity();
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
dock1->show();
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
}
|
|
|
|
void TestDocks::tst_dockDockWidgetNested()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Test detaching too, and check if the window size is correct
|
|
// TODO
|
|
}
|
|
|
|
void TestDocks::tst_dockFloatingWindowNested()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// TODO
|
|
}
|
|
|
|
void TestDocks::tst_crash()
|
|
{
|
|
// tests some crash I got
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto layout = m->multiSplitter();
|
|
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
Item *item1 = layout->itemForFrame(dock1->dptr()->frame());
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
dock1->setFloating(true);
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(!dock1->isInMainWindow());
|
|
|
|
Item *layoutItem = dock1->dptr()->lastPositions().lastItem();
|
|
QVERIFY(layoutItem && DockRegistry::self()->itemIsInMainWindow(layoutItem));
|
|
QCOMPARE(layoutItem, item1);
|
|
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
QCOMPARE(layout->count(), 1);
|
|
|
|
// Move from tab to bottom
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnBottom);
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
delete dock1->window();
|
|
}
|
|
|
|
void TestDocks::tst_refUnrefItem()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("2"));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight);
|
|
auto dropArea = m->dropArea();
|
|
auto layout = dropArea;
|
|
QPointer<Frame> frame1 = dock1->dptr()->frame();
|
|
QPointer<Frame> frame2 = dock2->dptr()->frame();
|
|
QPointer<Item> item1 = layout->itemForFrame(frame1);
|
|
QPointer<Item> item2 = layout->itemForFrame(frame2);
|
|
QVERIFY(item1.data());
|
|
QVERIFY(item2.data());
|
|
QCOMPARE(item1->refCount(), 2); // 2 - the item and its frame, which can be persistent
|
|
QCOMPARE(item2->refCount(), 2);
|
|
|
|
// 1. Delete a dock widget directly. It should delete its frame and also the Item
|
|
delete dock1;
|
|
Testing::waitForDeleted(frame1);
|
|
QVERIFY(!frame1.data());
|
|
QVERIFY(!item1.data());
|
|
|
|
// 2. Delete dock3, but neither the frame or the item is deleted, since there were two tabs to begin with
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("3"));
|
|
QCOMPARE(item2->refCount(), 2);
|
|
dock2->addDockWidgetAsTab(dock3);
|
|
QCOMPARE(item2->refCount(), 3);
|
|
delete dock3;
|
|
QVERIFY(item2.data());
|
|
QCOMPARE(frame2->dockWidgets().size(), 1);
|
|
|
|
// 3. Close dock2. frame2 should be deleted, but item2 preserved.
|
|
QCOMPARE(item2->refCount(), 2);
|
|
dock2->close();
|
|
Testing::waitForDeleted(frame2);
|
|
QVERIFY(dock2);
|
|
QVERIFY(item2.data());
|
|
QCOMPARE(item2->refCount(), 1);
|
|
QCOMPARE(dock2->dptr()->lastPositions().lastItem(), item2.data());
|
|
delete dock2;
|
|
|
|
QVERIFY(!item2.data());
|
|
QCOMPARE(layout->count(), 1);
|
|
|
|
// 4. Move a closed dock widget from one mainwindow to another
|
|
// It should delete its old placeholder
|
|
auto dock4 = createDockWidget("dock4", new QPushButton("4"));
|
|
m->addDockWidget(dock4, KDDockWidgets::Location_OnLeft);
|
|
|
|
QPointer<Frame> frame4 = dock4->dptr()->frame();
|
|
QPointer<Item> item4 = layout->itemForFrame(frame4);
|
|
dock4->close();
|
|
Testing::waitForDeleted(frame4);
|
|
QCOMPARE(item4->refCount(), 1);
|
|
QVERIFY(item4->isPlaceholder());
|
|
layout->checkSanity();
|
|
|
|
auto m2 = createMainWindow();
|
|
m2->addDockWidget(dock4, KDDockWidgets::Location_OnLeft);
|
|
m2->layoutWidget()->checkSanity();
|
|
QVERIFY(!item4.data());
|
|
}
|
|
|
|
void TestDocks::tst_placeholderCount()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Tests MultiSplitterLayout::count(),visibleCount() and placeholdercount()
|
|
|
|
// 1. MainWindow with just the initial frame.
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dropArea = m->dropArea();
|
|
auto layout = dropArea;
|
|
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->visibleCount(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// 2. MainWindow with central frame and left widget
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->visibleCount(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// 3. Add another dockwidget, this time tabbed in the center. It won't increase count, as it reuses an existing frame.
|
|
m->addDockWidgetAsTab(dock2);
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->visibleCount(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// 4. Float dock1. It should create a placeholder
|
|
dock1->setFloating(true);
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->visibleCount(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
// 5. Re-dock dock1. It should reuse the placeholder
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->visibleCount(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// 6. Again
|
|
dock1->setFloating(true);
|
|
fw = dock1->floatingWindow();
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->visibleCount(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
layout->checkSanity();
|
|
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
|
|
void TestDocks::tst_availableLengthForOrientation()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
// 1. Test a completely empty window, it's available space is its size minus the static separators thickness
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
|
|
int availableWidth = layout->availableLengthForOrientation(Qt::Horizontal);
|
|
int availableHeight = layout->availableLengthForOrientation(Qt::Vertical);
|
|
QCOMPARE(availableWidth, layout->width());
|
|
QCOMPARE(availableHeight, layout->height());
|
|
|
|
//2. Now do the same, but we have some widget docked
|
|
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
|
|
const int dock1MinWidth =
|
|
layout->itemForFrame(dock1->dptr()->frame())->minLength(Qt::Horizontal);
|
|
const int dock1MinHeight =
|
|
layout->itemForFrame(dock1->dptr()->frame())->minLength(Qt::Vertical);
|
|
|
|
availableWidth = layout->availableLengthForOrientation(Qt::Horizontal);
|
|
availableHeight = layout->availableLengthForOrientation(Qt::Vertical);
|
|
QCOMPARE(availableWidth, layout->width() - dock1MinWidth);
|
|
QCOMPARE(availableHeight, layout->height() - dock1MinHeight);
|
|
m->layoutWidget()->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_closeShowWhenNoCentralFrame()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Tests a crash I got when hidding and showing and no central frame
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
|
|
QPointer<DockWidgetBase> dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
dock1->close();
|
|
m->layoutWidget()->checkSanity();
|
|
|
|
QVERIFY(!dock1->dptr()->frame());
|
|
QVERIFY(!Testing::waitForDeleted(dock1)); // It was being deleted due to a bug
|
|
QVERIFY(dock1);
|
|
dock1->show();
|
|
m->layoutWidget()->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_setAsCurrentTab()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
// Tests DockWidget::setAsCurrentTab() and DockWidget::isCurrentTab()
|
|
// 1. a single dock widget is current, by definition
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
QVERIFY(dock1->isCurrentTab());
|
|
|
|
// 2. Tab dock2 to the group, dock2 is current now
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
QVERIFY(!dock1->isCurrentTab());
|
|
QVERIFY(dock2->isCurrentTab());
|
|
|
|
// 3. Set dock1 as current
|
|
dock1->setAsCurrentTab();
|
|
QVERIFY(dock1->isCurrentTab());
|
|
QVERIFY(!dock2->isCurrentTab());
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
fw->layoutWidget()->checkSanity();
|
|
|
|
delete dock1;
|
|
delete dock2;
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
|
|
void TestDocks::tst_placeholderDisappearsOnReadd()
|
|
{
|
|
// This tests that addMultiSplitter also updates refcount of placeholders
|
|
|
|
// 1. Detach a widget and dock it on the opposite side. Placeholder
|
|
// should have been deleted and anchors properly positioned
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
|
|
MultiSplitter *layout = m->multiSplitter();
|
|
|
|
QPointer<DockWidgetBase> dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
dock1->setFloating(true);
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
dock1->dptr()->morphIntoFloatingWindow();
|
|
auto fw = dock1->floatingWindow();
|
|
layout->addMultiSplitter(fw->dropArea(), Location_OnRight );
|
|
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
QCOMPARE(layout->count(), 1);
|
|
|
|
layout->checkSanity();
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// The dock1 should occupy the entire width
|
|
QCOMPARE(dock1->dptr()->frame()->width(), layout->width());
|
|
|
|
QVERIFY(Testing::waitForDeleted(fw));
|
|
}
|
|
|
|
void TestDocks::tst_placeholdersAreRemovedProperly()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
|
|
MultiSplitter *layout = m->multiSplitter();
|
|
QPointer<DockWidgetBase> dock1 = createDockWidget("1", new QPushButton("1"));
|
|
QPointer<DockWidgetBase> dock2 = createDockWidget("2", new QPushButton("2"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
Item *item = layout->items().constFirst();
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
QVERIFY(!item->isPlaceholder());
|
|
dock1->setFloating(true);
|
|
QVERIFY(item->isPlaceholder());
|
|
|
|
QCOMPARE(layout->separators().size(), 0);
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
layout->removeItem(item);
|
|
QCOMPARE(layout->separators().size(), 0);
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// 2. Recreate the placeholder. This time delete the dock widget to see if placeholder is deleted too.
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
dock1->setFloating(true);
|
|
auto window1 = Tests::make_qpointer(dock1->window());
|
|
delete dock1;
|
|
QCOMPARE(layout->separators().size(), 0);
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
layout->checkSanity();
|
|
|
|
delete window1;
|
|
}
|
|
|
|
void TestDocks::tst_floatMaintainsSize()
|
|
{
|
|
// Tests that when we make a window float by pressing the float button, it will popup with
|
|
// the same size it had when docked
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto dw1 = new DockWidgetType("1");
|
|
auto dw2 = new DockWidgetType("2");
|
|
|
|
dw1->addDockWidgetToContainingWindow(dw2, Location_OnRight);
|
|
const int oldWidth2 = dw2->width();
|
|
dw1->show();
|
|
|
|
dw2->setFloating(true);
|
|
QTest::qWait(100);
|
|
|
|
QVERIFY(qAbs(dw2->width() - oldWidth2) < 16); // 15px for margins
|
|
|
|
delete dw1->window();
|
|
delete dw2->window();
|
|
}
|
|
|
|
void TestDocks::tst_preferredInitialSize()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dw1 = new DockWidgetType("1");
|
|
auto dw2 = new DockWidgetType("2");
|
|
auto m = createMainWindow(QSize(1200, 1200), MainWindowOption_None);
|
|
|
|
m->addDockWidget(dw1, Location_OnTop);
|
|
m->addDockWidget(dw2, Location_OnBottom, nullptr, QSize(0, 200));
|
|
|
|
QCOMPARE(dw2->dptr()->frame()->height(), 200);
|
|
}
|
|
|
|
void TestDocks::tst_crash2_data()
|
|
{
|
|
QTest::addColumn<bool>("show");
|
|
QTest::newRow("true") << true;
|
|
QTest::newRow("false") << false;
|
|
}
|
|
|
|
void TestDocks::tst_crash2()
|
|
{
|
|
QFETCH(bool, show);
|
|
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto layout = m->multiSplitter();
|
|
m->setVisible(show);
|
|
|
|
DockWidgetBase::List docks;
|
|
const int num = 4;
|
|
for (int i = 0; i < num; ++i)
|
|
docks << new DockWidgetType(QString::number(i));
|
|
|
|
QVector<KDDockWidgets::Location> locations = {Location_OnLeft,
|
|
Location_OnRight, Location_OnRight, Location_OnRight};
|
|
|
|
QVector<KDDockWidgets::InitialVisibilityOption> options = { InitialVisibilityOption::StartHidden,
|
|
InitialVisibilityOption::StartHidden, InitialVisibilityOption::StartVisible, InitialVisibilityOption::StartHidden};
|
|
|
|
QVector<bool> floatings = {true, false, false, false};
|
|
|
|
for (int i = 0; i < num; ++i) {
|
|
|
|
m->addDockWidget(docks[i], locations[i], nullptr, options[i]);
|
|
layout->checkSanity();
|
|
docks[i]->setFloating(floatings[i]);
|
|
}
|
|
|
|
qDeleteAll(docks);
|
|
qDeleteAll(DockRegistry::self()->frames());
|
|
}
|
|
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto layout = m->multiSplitter();
|
|
m->show();
|
|
|
|
const int num = 3;
|
|
DockWidgetBase::List docks;
|
|
for (int i = 0; i < num; ++i)
|
|
docks << new DockWidgetType(QString::number(i));
|
|
|
|
QVector<KDDockWidgets::Location> locations = {Location_OnLeft, Location_OnLeft,
|
|
Location_OnRight};
|
|
|
|
QVector<KDDockWidgets::InitialVisibilityOption> options = { InitialVisibilityOption::StartVisible, InitialVisibilityOption::StartVisible,
|
|
InitialVisibilityOption::StartHidden};
|
|
|
|
QVector<bool> floatings = {true, false, false};
|
|
|
|
for (int i = 0; i < num; ++i) {
|
|
m->addDockWidget(docks[i], locations[i], nullptr, options[i]);
|
|
layout->checkSanity();
|
|
if (i == 2) {
|
|
// Wait for the resizes. This used to make the app crash.
|
|
QTest::qWait(1000);
|
|
}
|
|
|
|
docks[i]->setFloating(floatings[i]);
|
|
}
|
|
layout->checkSanity();
|
|
|
|
qDeleteAll(docks);
|
|
qDeleteAll(DockRegistry::self()->frames());
|
|
}
|
|
|
|
}
|
|
|
|
void TestDocks::tst_closeAllDockWidgets()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow();
|
|
auto dropArea = m->dropArea();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("one"));
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("one"));
|
|
auto dock4 = createDockWidget("dock4", new QPushButton("one"));
|
|
auto dock5 = createDockWidget("dock5", new QPushButton("one"));
|
|
auto dock6 = createDockWidget("dock6", new QPushButton("one"));
|
|
|
|
QPointer<FloatingWindow> fw = dock3->dptr()->morphIntoFloatingWindow();
|
|
|
|
nestDockWidget(dock4, dropArea, nullptr, KDDockWidgets::Location_OnRight);
|
|
nestDockWidget(dock5, dropArea, nullptr, KDDockWidgets::Location_OnTop);
|
|
|
|
const int oldFWHeight = fw->height();
|
|
nestDockWidget(dock6, fw->dropArea(), nullptr, KDDockWidgets::Location_OnTop);
|
|
|
|
QVERIFY(oldFWHeight <= fw->height());
|
|
QCOMPARE(fw->frames().size(), 2);
|
|
|
|
QCOMPARE(dock3->window(), fw.data());
|
|
QCOMPARE(dock4->window(), m.get());
|
|
QCOMPARE(dock5->window(), m.get());
|
|
QCOMPARE(dock6->window(), fw.data());
|
|
auto layout = m->multiSplitter();
|
|
layout->checkSanity();
|
|
DockRegistry::self()->clear();
|
|
layout->checkSanity();
|
|
|
|
Testing::waitForDeleted(fw);
|
|
QVERIFY(!fw);
|
|
|
|
QCOMPARE(dock1->window(), dock1);
|
|
QCOMPARE(dock2->window(), dock2);
|
|
QCOMPARE(dock3->window(), dock3);
|
|
QCOMPARE(dock4->window(), dock4);
|
|
QCOMPARE(dock5->window(), dock5);
|
|
QCOMPARE(dock6->window(), dock6);
|
|
|
|
QVERIFY(!dock1->isVisible());
|
|
QVERIFY(!dock2->isVisible());
|
|
QVERIFY(!dock3->isVisible());
|
|
QVERIFY(!dock4->isVisible());
|
|
QVERIFY(!dock5->isVisible());
|
|
QVERIFY(!dock6->isVisible());
|
|
|
|
delete dock1;
|
|
delete dock2;
|
|
delete dock3;
|
|
delete dock4;
|
|
delete dock5;
|
|
delete dock6;
|
|
}
|
|
|
|
void TestDocks::tst_toggleMiddleDockCrash()
|
|
{
|
|
// tests some crash I got
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
|
|
MultiSplitter *layout = m->multiSplitter();
|
|
QPointer<DockWidgetBase> dock1 = createDockWidget("1", new QPushButton("1"));
|
|
QPointer<DockWidgetBase> dock2 = createDockWidget("2", new QPushButton("2"));
|
|
QPointer<DockWidgetBase> dock3 = createDockWidget("3", new QPushButton("3"));
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
m->addDockWidget(dock3, Location_OnRight);
|
|
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
auto frame = dock2->dptr()->frame();
|
|
dock2->close();
|
|
QVERIFY(Testing::waitForDeleted(frame));
|
|
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
dock2->show();
|
|
layout->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_stealFrame()
|
|
{
|
|
// Tests using addWidget() with dock widgets which are already in a layout
|
|
EnsureTopLevelsDeleted e;
|
|
auto m1 = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
auto m2 = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
auto dock4 = createDockWidget("dock4", new QPushButton("four"));
|
|
|
|
auto dropArea1 = m1->dropArea();
|
|
auto dropArea2 = m2->dropArea();
|
|
|
|
m1->addDockWidget(dock1, Location_OnRight);
|
|
m1->addDockWidget(dock2, Location_OnRight);
|
|
m2->addDockWidget(dock3, Location_OnRight);
|
|
m2->addDockWidget(dock4, Location_OnRight);
|
|
|
|
// 1. MainWindow #1 steals a widget from MainWindow2 and vice-versa
|
|
m1->addDockWidget(dock3, Location_OnRight);
|
|
m1->addDockWidget(dock4, Location_OnRight);
|
|
m2->addDockWidget(dock1, Location_OnRight);
|
|
QPointer<Item> item2 = dropArea1->itemForFrame(dock2->dptr()->frame());
|
|
m2->addDockWidget(dock2, Location_OnRight);
|
|
QVERIFY(!item2.data());
|
|
|
|
QCOMPARE(dropArea1->count(), 2);
|
|
QCOMPARE(dropArea2->count(), 2);
|
|
QCOMPARE(dropArea1->placeholderCount(), 0);
|
|
QCOMPARE(dropArea2->placeholderCount(), 0);
|
|
|
|
// 2. MainWindow #1 steals a widget from MainWindow2 and vice-versa, but adds as tabs
|
|
dock1->addDockWidgetAsTab(dock3);
|
|
QPointer<Frame> f2 = dock2->dptr()->frame();
|
|
dock4->addDockWidgetAsTab(dock2);
|
|
QVERIFY(Testing::waitForDeleted(f2.data()));
|
|
QVERIFY(!f2.data());
|
|
|
|
QCOMPARE(dropArea1->count(), 1);
|
|
QCOMPARE(dropArea2->count(), 1);
|
|
QCOMPARE(dropArea1->placeholderCount(), 0);
|
|
QCOMPARE(dropArea2->placeholderCount(), 0);
|
|
|
|
// 3. Test stealing a tab from the same tab-widget we're in. Nothing happens
|
|
{
|
|
SetExpectedWarning sew("Already contains KDDockWidgets::DockWidget"); // Suppress the qFatal this time
|
|
dock1->addDockWidgetAsTab(dock3);
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 2);
|
|
}
|
|
|
|
// 4. Steal from another tab which resides in another Frame, which resides in the same main window
|
|
m1->addDockWidget(dock1, Location_OnTop);
|
|
f2 = dock2->dptr()->frame();
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 2);
|
|
QCOMPARE(dock4->dptr()->frame()->dockWidgetCount(), 1);
|
|
|
|
QCOMPARE(dropArea1->count(), 2);
|
|
QCOMPARE(dropArea1->placeholderCount(), 0);
|
|
|
|
// 5. And also steal a side-by-side one into the tab
|
|
QPointer<Frame> f4 = dock4->dptr()->frame();
|
|
dock1->addDockWidgetAsTab(dock4);
|
|
QVERIFY(Testing::waitForDeleted(f4.data()));
|
|
QCOMPARE(dropArea1->count(), 1);
|
|
QCOMPARE(dropArea1->placeholderCount(), 0);
|
|
|
|
// 6. Steal from tab to side-by-side within the same MainWindow
|
|
m1->addDockWidget(dock1, Location_OnLeft);
|
|
QCOMPARE(dropArea1->count(), 2);
|
|
QCOMPARE(dropArea1->placeholderCount(), 0);
|
|
|
|
// 6. side-by-side to side-by-side within same MainWindow
|
|
m2->addDockWidget(dock1, Location_OnRight);
|
|
QCOMPARE(dropArea2->count(), 2);
|
|
QCOMPARE(dropArea2->placeholderCount(), 0);
|
|
|
|
{
|
|
SetExpectedWarning sew("Invalid parameters KDDockWidgets::DockWidget"); // Suppress the qFatal this time
|
|
m2->addDockWidget(dock1, Location_OnLeft, dock1);
|
|
QCOMPARE(dropArea2->count(), 2); // Nothing happened
|
|
QCOMPARE(dropArea2->placeholderCount(), 0);
|
|
QVERIFY(dock1->isVisible());
|
|
}
|
|
|
|
QVERIFY(dock1->isVisible());
|
|
m2->addDockWidget(dock1, Location_OnLeft, nullptr); // Should not warn
|
|
|
|
QVERIFY(dock1->isVisible());
|
|
QCOMPARE(dropArea2->count(), 2); // Nothing happened
|
|
QCOMPARE(dropArea2->placeholderCount(), 0);
|
|
|
|
m2->addDockWidget(dock1, Location_OnLeft, nullptr);
|
|
QVERIFY(dock1->isVisible());
|
|
QCOMPARE(dropArea2->count(), 2); // Nothing happened
|
|
QCOMPARE(dropArea2->placeholderCount(), 0);
|
|
dropArea1->checkSanity();
|
|
dropArea2->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_setFloatingWhenWasTabbed()
|
|
{
|
|
// Tests DockWidget::isTabbed() and DockWidget::setFloating(false|true) when tabbed (it should redock)
|
|
// setFloating(false) for side-by-side is tested in another function
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
// 1. Two floating dock widgets. They are floating, not tabbed.
|
|
QVERIFY(!dock1->isTabbed());
|
|
QVERIFY(!dock2->isTabbed());
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
|
|
// 2. Dock a floating dock into another floating dock. They're not floating anymore, just tabbed.
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
QVERIFY(dock1->isTabbed());
|
|
QVERIFY(dock2->isTabbed());
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!dock2->isFloating());
|
|
|
|
// 2.1 Set one of them invisible. // Not much will happen, the tab will be still there, just showing an empty space.
|
|
// Users should use close() instead. Tabwidgets control visibility, they hide the widget when it's not the current tab.
|
|
dock2->setVisible(false);
|
|
QVERIFY(dock2->isTabbed());
|
|
QVERIFY(!dock1->isFloating());
|
|
QCOMPARE(dock2->dptr()->frame()->dockWidgetCount(), 2);
|
|
// 3. Set one floating. Now both cease to be tabbed, and both are floating.
|
|
dock1->setFloating(true);
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(!dock1->isTabbed());
|
|
QVERIFY(!dock2->isTabbed());
|
|
|
|
// 4. Dock one floating dock into another, side-by-side. They're neither docking or tabbed now.
|
|
dock1->addDockWidgetToContainingWindow(dock2, KDDockWidgets::Location_OnLeft);
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!dock2->isFloating());
|
|
QVERIFY(!dock1->isTabbed());
|
|
QVERIFY(!dock2->isTabbed());
|
|
|
|
// 5. float one of them, now both are floating, not tabbed anymore.
|
|
dock2->setFloating(true);
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(!dock1->isTabbed());
|
|
QVERIFY(!dock2->isTabbed());
|
|
|
|
// 6. With two dock widgets tabbed, detach 1, and reattach it, via DockWidget::setFloating(false)
|
|
m->addDockWidgetAsTab(dock1);
|
|
m->addDockWidgetAsTab(dock2);
|
|
|
|
qDebug() << "6.";
|
|
dock2->setFloating(true);
|
|
QVERIFY(dock1->isTabbed());
|
|
QVERIFY(!dock2->isTabbed());
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
|
|
QCOMPARE(dock2->dptr()->lastPositions().lastTabIndex(), 1);
|
|
QVERIFY(dock2->dptr()->lastPositions().isValid());
|
|
dock2->setFloating(false);
|
|
|
|
QVERIFY(dock1->isTabbed());
|
|
QVERIFY(dock2->isTabbed());
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!dock2->isFloating());
|
|
|
|
// 7. Call setFloating(true) on an already docked widget
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
dock3->setFloating(true);
|
|
dock3->setFloating(true);
|
|
|
|
// 8. Tab 3 together, detach the middle one, reattach the middle one, it should go to the middle.
|
|
m->addDockWidgetAsTab(dock3);
|
|
dock2->setFloating(true);
|
|
QVERIFY(dock2->isFloating());
|
|
dock2->setFloating(false);
|
|
QVERIFY(!dock2->isFloating());
|
|
QVERIFY(dock2->isTabbed());
|
|
QCOMPARE(dock2->dptr()->frame()->indexOfDockWidget(dock2), 1);
|
|
|
|
// 10. Float dock1, and dock it to main window as tab. This tests Option_AlwaysShowsTabs.
|
|
dock1->setFloating(true);
|
|
dock2->setFloating(true);
|
|
dock3->setFloating(true);
|
|
|
|
m->addDockWidgetAsTab(dock1);
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(dock1->isTabbed());
|
|
dock1->setFloating(true);
|
|
dock1->setFloating(false);
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 1);
|
|
// Cleanup
|
|
m->addDockWidgetAsTab(dock2);
|
|
m->addDockWidgetAsTab(dock3);
|
|
m->deleteLater();
|
|
auto window = m.release();
|
|
Testing::waitForDeleted(window);
|
|
}
|
|
|
|
void TestDocks::tst_setFloatingWhenSideBySide()
|
|
{
|
|
// Tests DockWidget::setFloating(false|true) when side-by-side (it should put it where it was)
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
{
|
|
// 1. Create a MainWindow with two docked dock-widgets, then float the first one.
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight);
|
|
|
|
QPointer<Frame> frame1 = dock1->dptr()->frame();
|
|
dock1->setFloating(true);
|
|
QVERIFY(dock1->isFloating());
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
|
|
//2. Put it back, via setFloating(). It should return to its place.
|
|
dock1->setFloating(false);
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!dock1->isTabbed());
|
|
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
|
|
{
|
|
// 2. Tests a case where restoring a dock widget wouldn't make it use all its available space
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight);
|
|
m->addDockWidget(dock3, KDDockWidgets::Location_OnRight);
|
|
auto f2 = dock2->dptr()->frame();
|
|
Item *item2 = layout->itemForFrame(f2);
|
|
QVERIFY(item2);
|
|
dock2->close();
|
|
dock3->close();
|
|
Testing::waitForDeleted(f2);
|
|
dock2->show();
|
|
Testing::waitForResize(dock2);
|
|
|
|
QCOMPARE(item2->geometry(), dock2->dptr()->frame()->QWidgetAdapter::geometry());
|
|
layout->checkSanity();
|
|
|
|
// Cleanup
|
|
dock3->deleteLater();
|
|
Testing::waitForDeleted(dock3);
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_dockWindowWithTwoSideBySideFramesIntoCenter()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow();
|
|
auto fw = createFloatingWindow();
|
|
auto dock2 = createDockWidget("doc2", Qt::red);
|
|
nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnLeft);
|
|
QCOMPARE(fw->frames().size(), 2);
|
|
QVERIFY(fw->dropArea()->checkSanity());
|
|
|
|
auto fw2 = createFloatingWindow();
|
|
fw2->move(fw->x() + fw->width() + 100, fw->y());
|
|
|
|
dragFloatingWindowTo(fw, fw2->geometry().center());
|
|
QVERIFY(fw2->dropArea()->checkSanity());
|
|
|
|
QCOMPARE(fw2->frames().size(), 1);
|
|
auto f2 = fw2->frames().constFirst();
|
|
QCOMPARE(f2->dockWidgetCount(), 3);
|
|
QVERIFY(Testing::waitForDeleted(fw));
|
|
delete fw2;
|
|
}
|
|
|
|
void TestDocks::tst_tabTitleChanges()
|
|
{
|
|
// Tests that the tab's title changes if the dock widget's title changes
|
|
EnsureTopLevelsDeleted e;
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
auto dw2 = new DockWidgetType(QStringLiteral("2"));
|
|
|
|
dw1->addDockWidgetAsTab(dw2);
|
|
|
|
TabBar *tb = dw1->dptr()->frame()->tabWidget()->tabBar();
|
|
QCOMPARE(tb->text(0), QStringLiteral("1"));
|
|
dw1->setTitle(QStringLiteral("other"));
|
|
QCOMPARE(tb->text(0), QStringLiteral("other"));
|
|
|
|
delete dw1->window();
|
|
}
|
|
|
|
void TestDocks::tst_dockWidgetGetsFocusWhenDocked()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_TitleBarIsFocusable);
|
|
|
|
// We drag dw2 onto dw2 and drop it
|
|
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
auto dw2 = new DockWidgetType(QStringLiteral("2"));
|
|
auto le1 = new FocusableWidget();
|
|
auto le2 = new FocusableWidget();
|
|
dw1->setWidget(le1);
|
|
dw2->setWidget(le2);
|
|
dw2->show();
|
|
dw1->show();
|
|
QTest::qWait(200);
|
|
|
|
auto fw1 = dw1->floatingWindow();
|
|
QPointer<FloatingWindow> fw2 = dw2->floatingWindow();
|
|
|
|
// Focus dock widget 1 first
|
|
QVERIFY(!dw1->isFocused());
|
|
dw1->window()->activateWindow();
|
|
le1->setFocus(Qt::MouseFocusReason);
|
|
QTest::qWait(200);
|
|
QVERIFY(dw1->isFocused());
|
|
|
|
QVERIFY(fw1->isActiveWindow());
|
|
dragFloatingWindowTo(fw2, fw1->dropArea(), DropIndicatorOverlayInterface::DropLocation_Left);
|
|
Testing::waitForEvent(fw1, QEvent::WindowActivate);
|
|
|
|
/// We dropped into floating window 1, it should still be active
|
|
QVERIFY(fw1->isActiveWindow());
|
|
|
|
// DockWidget 2 was dropped, it should now be focused
|
|
QVERIFY(!dw1->isFocused());
|
|
QVERIFY(dw2->isFocused());
|
|
|
|
delete fw1;
|
|
delete fw2;
|
|
}
|
|
|
|
void TestDocks::tst_isFocused()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
// 1. Create 2 floating windows
|
|
auto dock1 = createDockWidget(QStringLiteral("dock1"), new FocusableWidget());
|
|
auto dock2 = createDockWidget(QStringLiteral("dock2"), new FocusableWidget());
|
|
|
|
QTest::qWait(200); // macOS is flaky here, needs dock2 to be shown first before focusing dock1, otherwise dock1 looses again
|
|
|
|
dock1->window()->move(400, 200);
|
|
|
|
// 2. Raise dock1 and focus its line edit
|
|
dock1->raise();
|
|
dock1->widget()->setFocus(Qt::OtherFocusReason);
|
|
Testing::waitForEvent(dock1->widget(), QEvent::FocusIn);
|
|
|
|
QVERIFY(dock1->isFocused());
|
|
QVERIFY(!dock2->isFocused());
|
|
|
|
// 3. Raise dock2 and focus its line edit
|
|
dock2->raiseAndActivate();
|
|
if (!dock2->window()->windowHandle()->isActive())
|
|
Testing::waitForEvent(dock2->window()->windowHandle(), QEvent::WindowActivate);
|
|
|
|
dock2->widget()->setFocus(Qt::OtherFocusReason);
|
|
Testing::waitForEvent(dock2->widget(), QEvent::FocusIn);
|
|
|
|
QVERIFY(!dock1->isFocused());
|
|
QVERIFY(dock2->widget()->hasFocus());
|
|
QVERIFY(dock2->isFocused());
|
|
|
|
// 4. Tab dock1, it's current tab now
|
|
auto oldFw1 = dock1->window();
|
|
dock2->addDockWidgetAsTab(dock1);
|
|
delete oldFw1;
|
|
QVERIFY(dock1->isFocused());
|
|
QVERIFY(!dock2->isFocused());
|
|
|
|
// 5. Set dock2 as current tab again
|
|
dock2->raise();
|
|
QVERIFY(!dock1->isFocused());
|
|
QVERIFY(dock2->isFocused());
|
|
|
|
// 6. Create dock3, focus it
|
|
auto dock3 = createDockWidget(QStringLiteral("dock3"), new FocusableWidget());
|
|
auto oldFw3 = dock3->window();
|
|
dock3->raise();
|
|
dock3->widget()->setFocus(Qt::OtherFocusReason);
|
|
Testing::waitForEvent(dock2->widget(), QEvent::FocusIn);
|
|
QVERIFY(!dock1->isFocused());
|
|
QVERIFY(!dock2->isFocused());
|
|
QVERIFY(dock3->isFocused());
|
|
|
|
// 4. Add dock3 to the 1st window, nested, focus 2 again
|
|
dock2->addDockWidgetToContainingWindow(dock3, Location_OnLeft);
|
|
delete oldFw3;
|
|
dock2->raise();
|
|
dock2->widget()->setFocus(Qt::OtherFocusReason);
|
|
Testing::waitForEvent(dock2->widget(), QEvent::FocusIn);
|
|
QVERIFY(!dock1->isFocused());
|
|
QVERIFY(dock2->isFocused());
|
|
QVERIFY(!dock3->isFocused());
|
|
delete dock2->window();
|
|
}
|
|
|
|
void TestDocks::tst_setWidget()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dw = new DockWidgetType(QStringLiteral("FOO"));
|
|
auto button1 = new QPushButton("button1");
|
|
auto button2 = new QPushButton("button2");
|
|
dw->setWidget(button1);
|
|
dw->setWidget(button2);
|
|
delete button1;
|
|
delete dw;
|
|
}
|
|
|
|
void TestDocks::tst_floatingLastPosAfterDoubleClose()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto d1 = new DockWidgetType(QStringLiteral("a"));
|
|
QVERIFY(d1->dptr()->lastPositions().lastFloatingGeometry().isNull());
|
|
QVERIFY(!d1->isVisible());
|
|
d1->close();
|
|
QVERIFY(d1->dptr()->lastPositions().lastFloatingGeometry().isNull());
|
|
delete d1;
|
|
}
|
|
|
|
void TestDocks::tst_0_data()
|
|
{
|
|
QTest::addColumn<int>("thickness");
|
|
QTest::newRow("2") << 2;
|
|
QTest::newRow("1") << 1;
|
|
QTest::newRow("0") << 0;
|
|
}
|
|
|
|
void TestDocks::tst_0()
|
|
{
|
|
QFETCH(int, thickness);
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setSeparatorThickness(thickness);
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
m->resize(QSize(502, 500));
|
|
m->show();
|
|
|
|
auto d1 = createDockWidget("1", new QTextEdit());
|
|
auto d2 = createDockWidget("2", new QTextEdit());
|
|
|
|
m->addDockWidget(d1, Location_OnLeft);
|
|
m->addDockWidget(d2, Location_OnRight);
|
|
}
|
|
|
|
void TestDocks::tst_honourGeometryOfHiddenWindow()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto d1 = new DockWidgetType("1");
|
|
d1->setWidget(new QTextEdit());
|
|
|
|
QVERIFY(!d1->isVisible());
|
|
|
|
// Clear had a bug where it saved the position of all dock widgets being closed
|
|
DockRegistry::self()->clear();
|
|
|
|
const QRect suggestedGeo(150, 150, 250, 250);
|
|
d1->setGeometry(suggestedGeo);
|
|
|
|
d1->show();
|
|
Testing::waitForEvent(d1, QEvent::Show);
|
|
|
|
QCOMPARE(d1->window()->windowHandle()->geometry(), suggestedGeo);
|
|
delete d1->window();
|
|
}
|
|
|
|
void TestDocks::tst_registry()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dr = DockRegistry::self();
|
|
|
|
QCOMPARE(dr->dockwidgets().size(), 0);
|
|
auto dw = new DockWidgetType(QStringLiteral("dw1"));
|
|
auto guest = new QWidgetOrQuick();
|
|
dw->setWidget(guest);
|
|
QCOMPARE(dr->dockWidgetForGuest(nullptr), nullptr);
|
|
QCOMPARE(dr->dockWidgetForGuest(guest), dw);
|
|
delete dw;
|
|
}
|
|
|
|
void TestDocks::tst_dockWindowWithTwoSideBySideFramesIntoRight()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto fw = createFloatingWindow();
|
|
auto dock2 = createDockWidget("doc2", Qt::red);
|
|
nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnTop); // No we stack on top, unlike in previous test
|
|
QCOMPARE(fw->frames().size(), 2);
|
|
|
|
auto fw2 = createFloatingWindow();
|
|
fw2->move(fw->x() + fw->width() + 100, fw->y());
|
|
|
|
dragFloatingWindowTo(fw, fw2->dropArea(), DropIndicatorOverlayInterface::DropLocation_Right); // Outter right instead of Left
|
|
QCOMPARE(fw2->frames().size(), 3);
|
|
QVERIFY(fw2->dropArea()->checkSanity());
|
|
|
|
fw2->deleteLater();
|
|
Testing::waitForDeleted(fw2);
|
|
}
|
|
|
|
void TestDocks::tst_dockWindowWithTwoSideBySideFramesIntoLeft()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto fw = createFloatingWindow();
|
|
fw->setObjectName("fw1");
|
|
|
|
auto dock2 = createDockWidget("doc2", Qt::red);
|
|
nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnLeft);
|
|
QCOMPARE(fw->frames().size(), 2);
|
|
|
|
auto fw2 = createFloatingWindow();
|
|
fw2->setObjectName("fw2");
|
|
fw2->move(fw->x() + fw->width() + 100, fw->y());
|
|
|
|
QVERIFY(fw2->dropArea()->checkSanity());
|
|
dragFloatingWindowTo(fw, fw2->dropArea(), DropIndicatorOverlayInterface::DropLocation_Left);
|
|
QCOMPARE(fw2->frames().size(), 3);
|
|
|
|
QVERIFY(fw2->dropArea()->checkSanity());
|
|
|
|
///Cleanup
|
|
fw2->deleteLater();
|
|
Testing::waitForDeleted(fw2);
|
|
}
|
|
|
|
void TestDocks::tst_posAfterLeftDetach()
|
|
{
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto fw = createFloatingWindow();
|
|
auto dock2 = createDockWidget("doc2", Qt::red);
|
|
nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnRight);
|
|
QVERIFY(fw->dropArea()->checkSanity());
|
|
// When dragging the right one there was a bug where it jumped
|
|
const QPoint globalSrc = dock2->mapToGlobal(QPoint(0, 0));
|
|
const int offset = 10;
|
|
const QPoint globalDest = globalSrc + QPoint(offset, 0);
|
|
drag(dock2, globalDest);
|
|
QVERIFY(fw->dropArea()->checkSanity());
|
|
const QPoint actualEndPos = dock2->mapToGlobal(QPoint(0, 0));
|
|
QVERIFY(actualEndPos.x() - globalSrc.x() < offset + 5); // 5px so we have margin for window system fluctuations. The actual bug was a very big jump like 50px, so a 5 margin is fine to test that the bug doesn't happen
|
|
|
|
delete dock2;
|
|
fw->deleteLater();
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto fw = createFloatingWindow();
|
|
auto dock2 = createDockWidget("doc2", Qt::red);
|
|
nestDockWidget(dock2, fw->dropArea(), nullptr, KDDockWidgets::Location_OnRight);
|
|
QVERIFY(fw->dropArea()->checkSanity());
|
|
|
|
const int originalX = dock2->mapToGlobal(QPoint(0, 0)).x();
|
|
dock2->dptr()->frame()->titleBar()->makeWindow();
|
|
const int finalX = dock2->mapToGlobal(QPoint(0, 0)).x();
|
|
|
|
QVERIFY(finalX - originalX < 10); // 10 or some other small number that is less than say 200
|
|
|
|
delete dock2;
|
|
fw->deleteLater();
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_preventClose()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto nonClosableWidget = new NonClosableWidget();
|
|
auto dock1 = new DockWidgetType("1");
|
|
dock1->setWidget(nonClosableWidget);
|
|
|
|
// 1. Test a floating dock widget
|
|
dock1->resize(200, 200);
|
|
dock1->show();
|
|
QVERIFY(dock1->isVisible());
|
|
dock1->close();
|
|
QVERIFY(dock1->isVisible());
|
|
|
|
// 2. Morph it into a FlatingWindow
|
|
dock1->dptr()->morphIntoFloatingWindow();
|
|
dock1->close();
|
|
QVERIFY(dock1->isVisible());
|
|
dock1->dptr()->frame()->titleBar()->onCloseClicked();
|
|
QVERIFY(dock1->isVisible());
|
|
auto fw = dock1->floatingWindow();
|
|
fw->close();
|
|
QVERIFY(dock1->isVisible());
|
|
|
|
dock1->deleteLater();
|
|
QVERIFY(Testing::waitForDeleted(dock1));
|
|
}
|
|
|
|
void TestDocks::tst_propagateMinSize()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dropArea = m->dropArea();
|
|
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
|
|
nestDockWidget(dock1, dropArea, nullptr, KDDockWidgets::Location_OnRight);
|
|
nestDockWidget(dock2, dropArea, nullptr, KDDockWidgets::Location_OnRight);
|
|
nestDockWidget(dock3, dropArea, nullptr, KDDockWidgets::Location_OnRight);
|
|
|
|
// TODO finish this when the 3 dock widgets have proper sizes
|
|
//QTest::qWait(50000);
|
|
|
|
}
|
|
|
|
void TestDocks::tst_createFloatingWindow()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto dock = createDockWidget("doc1", Qt::green);
|
|
QVERIFY(dock);
|
|
QVERIFY(dock->isFloating());
|
|
|
|
QCOMPARE(dock->uniqueName(), QLatin1String("doc1")); // 1.0 objectName() is inherited
|
|
|
|
QPointer<FloatingWindow> window = dock->floatingWindow();
|
|
QVERIFY(window); // 1.1 DockWidget creates a FloatingWindow and is reparented
|
|
QVERIFY(window->dropArea()->checkSanity());
|
|
dock->deleteLater();
|
|
QVERIFY(Testing::waitForDeleted(dock));
|
|
QVERIFY(Testing::waitForDeleted(window)); // 1.2 Floating Window is destroyed when DockWidget is destroyed
|
|
QVERIFY(!window);
|
|
}
|
|
|
|
void TestDocks::tst_addAndReadd()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
// 1. This just tests some crash I got.
|
|
// Make a dock widget float and immediately reattach it
|
|
auto m = createMainWindow();
|
|
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
dock1->setFloating(true);
|
|
m->layoutWidget()->checkSanity();
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
dock1->dptr()->frame()->titleBar()->makeWindow();
|
|
m->layoutWidget()->checkSanity();
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
dock1->dptr()->frame()->titleBar()->makeWindow();
|
|
m->layoutWidget()->checkSanity();
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
auto dropArea = m->dropArea();
|
|
dragFloatingWindowTo(fw, dropArea, DropIndicatorOverlayInterface::DropLocation_Right);
|
|
QVERIFY(dock1->dptr()->frame()->titleBar()->isVisible());
|
|
fw->titleBar()->makeWindow();
|
|
m->layoutWidget()->checkSanity();
|
|
|
|
//Cleanup
|
|
delete dock1;
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
|
|
void TestDocks::tst_addToSmallMainWindow1()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2());
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2());
|
|
auto dock3 = createDockWidget("dock3", new MyWidget2());
|
|
auto dock4 = createDockWidget("dock4", new MyWidget2());
|
|
|
|
const int mainWindowLength = 400;
|
|
|
|
m->windowHandle()->resize(mainWindowLength, mainWindowLength);
|
|
QTest::qWait(100);
|
|
|
|
dock1->resize(800, 800);
|
|
dock2->resize(800, 800);
|
|
dock3->resize(800, 800);
|
|
|
|
// Add as tabbed:
|
|
m->addDockWidgetAsTab(dock1);
|
|
|
|
QCOMPARE(m->height(), mainWindowLength);
|
|
|
|
QTest::qWait(100);
|
|
QVERIFY(dock1->height() <= mainWindowLength);
|
|
QVERIFY(dock1->width() <= mainWindowLength);
|
|
|
|
//Add in area:
|
|
m->addDockWidget(dock2, Location_OnLeft);
|
|
m->addDockWidget(dock3, Location_OnTop, dock2);
|
|
m->addDockWidget(dock4, Location_OnBottom);
|
|
|
|
auto dropArea = m->dropArea();
|
|
|
|
QVERIFY(dropArea->checkSanity());
|
|
QVERIFY(dock2->width() < mainWindowLength);
|
|
QVERIFY(dock3->height() < m->height());
|
|
QVERIFY(dock4->height() < m->height());
|
|
}
|
|
|
|
void TestDocks::tst_addToSmallMainWindow2()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dropArea = m->dropArea();
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(100, 100)));
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(100, 100)));
|
|
m->addDockWidgetAsTab(dock1);
|
|
m->windowHandle()->resize(osWindowMinWidth(), 200);
|
|
|
|
Testing::waitForResize(m.get());
|
|
|
|
QVERIFY(qAbs(m->width() - osWindowMinWidth()) < 15); // Not very important verification. Anyway, using 15 to account for margins and what not.
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight);
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
QVERIFY(Testing::waitForResize(m.get()));
|
|
#else
|
|
QTest::qWait(100);
|
|
#endif
|
|
|
|
QVERIFY(dropArea->width() > osWindowMinWidth());
|
|
QMargins margins = m->centerWidgetMargins();
|
|
QCOMPARE(dropArea->width(), m->width() - margins.left() - margins.right());
|
|
QVERIFY(m->dropArea()->checkSanity());
|
|
}
|
|
|
|
void TestDocks::tst_addToSmallMainWindow3()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dropArea = m->dropArea();
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2());
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2());
|
|
m->addDockWidgetAsTab(dock1);
|
|
m->windowHandle()->resize(osWindowMinWidth(), 200);
|
|
QTest::qWait(200);
|
|
QVERIFY(qAbs(m->width() - osWindowMinWidth()) < 15); // Not very important verification. Anyway, using 15 to account for margins and what not.
|
|
|
|
auto fw = dock2->dptr()->morphIntoFloatingWindow();
|
|
QVERIFY(fw->isVisible());
|
|
QVERIFY(dropArea->checkSanity());
|
|
dragFloatingWindowTo(fw, dropArea, DropIndicatorOverlayInterface::DropLocation_Right);
|
|
QVERIFY(m->dropArea()->checkSanity());
|
|
delete fw;
|
|
}
|
|
|
|
void TestDocks::tst_addToSmallMainWindow4()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(100, 100), MainWindowOption_None);
|
|
|
|
QTest::qWait(100);
|
|
QCOMPARE(m->height(), 100);
|
|
|
|
auto dropArea = m->dropArea();
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(50, 50)));
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(50, 50)));
|
|
MultiSplitter *layout = dropArea;
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnBottom);
|
|
Testing::waitForResize(m.get());
|
|
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnBottom);
|
|
Testing::waitForResize(m.get());
|
|
QVERIFY(m->dropArea()->checkSanity());
|
|
|
|
const int item2MinHeight =
|
|
layout->itemForFrame(dock2->dptr()->frame())->minLength(Qt::Vertical);
|
|
QCOMPARE(dropArea->height(),
|
|
dock1->dptr()->frame()->height() + item2MinHeight + Item::separatorThickness);
|
|
}
|
|
|
|
void TestDocks::tst_addToSmallMainWindow5()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Test test shouldn't spit any warnings
|
|
|
|
auto m = createMainWindow(QSize(100, 100), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(50, 240)));
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(50, 240)));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnBottom);
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnBottom);
|
|
QVERIFY(m->dropArea()->checkSanity());
|
|
}
|
|
|
|
void TestDocks::tst_fairResizeAfterRemoveWidget()
|
|
{
|
|
// 1. Add 3 dock widgets horizontally, remove the middle one, make sure
|
|
// both left and right widgets get a share of the new available space
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
DockWidgetBase *dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
DockWidgetBase *dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
DockWidgetBase *dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
|
|
dock1->addDockWidgetToContainingWindow(dock2, Location_OnRight);
|
|
dock1->addDockWidgetToContainingWindow(dock3, Location_OnRight, dock2);
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
|
|
QPointer<Frame> frame2 = dock2->dptr()->frame();
|
|
|
|
const int oldWidth1 = dock1->dptr()->frame()->width();
|
|
const int oldWidth2 = dock2->dptr()->frame()->width();
|
|
const int oldWidth3 = dock3->dptr()->frame()->width();
|
|
MultiSplitter *layout = fw->dropArea();
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->visibleCount(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
delete dock2;
|
|
QVERIFY(Testing::waitForResize(dock1));
|
|
QVERIFY(!frame2);
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->visibleCount(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
const int delta1 = (dock1->dptr()->frame()->width() - oldWidth1);
|
|
const int delta3 = (dock3->dptr()->frame()->width() - oldWidth3);
|
|
|
|
qDebug() << "old1=" << oldWidth1
|
|
<< "; old3=" << oldWidth3
|
|
<< "; to spread=" << oldWidth2
|
|
<< "; Delta1=" << delta1
|
|
<< "; Delta3=" << delta3;
|
|
|
|
QVERIFY(delta1 > 0);
|
|
QVERIFY(delta3 > 0);
|
|
QVERIFY(qAbs(delta3 - delta1) <= 1); // Both dock1 and dock3 should have increased by the same amount
|
|
|
|
delete dock1->window();
|
|
}
|
|
|
|
void TestDocks::tst_invalidJSON_data()
|
|
{
|
|
// Be sure that the main windows in the json are called "MyMainWindow1" and the dock widgets
|
|
// dock-x where x starts at 0
|
|
QTest::addColumn<QString>("layoutFileName");
|
|
QTest::addColumn<int>("numDockWidgets");
|
|
QTest::addColumn<QString>("expectedWarning");
|
|
QTest::addColumn<bool>("expectedResult");
|
|
QTest::newRow("unsupported-serialization-version") << "unsupported-serialization-version.json"
|
|
<< 10
|
|
<< "Serialization format is too old"
|
|
<< false;
|
|
QTest::newRow("invalid") << "invalid.json" << 29 << "" << false;
|
|
QTest::newRow("overlapping-item") << "overlapping-item.json" << 2 << "Unexpected pos" << true;
|
|
}
|
|
|
|
void TestDocks::tst_invalidJSON()
|
|
{
|
|
QFETCH(QString, layoutFileName);
|
|
QFETCH(int, numDockWidgets);
|
|
QFETCH(QString, expectedWarning);
|
|
QFETCH(bool, expectedResult);
|
|
|
|
const QString absoluteLayoutFileName = QStringLiteral(":/layouts/%1").arg(layoutFileName);
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m1 = createMainWindow(QSize(800, 500), MainWindowOption_None, "MyMainWindow1");
|
|
for (int i = 0; i < numDockWidgets; ++i) {
|
|
createDockWidget(QStringLiteral("dock-%1").arg(i), new QPushButton("one"));
|
|
}
|
|
|
|
SetExpectedWarning sew(expectedWarning);
|
|
|
|
LayoutSaver restorer;
|
|
QCOMPARE(restorer.restoreFromFile(absoluteLayoutFileName), expectedResult);
|
|
}
|
|
|
|
void TestDocks::tst_invalidPlaceholderPosition_data()
|
|
{
|
|
QTest::addColumn<bool>("restore1First");
|
|
QTest::newRow("restore1First") << true;
|
|
QTest::newRow("restore2First") << false;
|
|
}
|
|
|
|
void TestDocks::tst_invalidPlaceholderPosition()
|
|
{
|
|
QFETCH(bool, restore1First);
|
|
|
|
// Tests a bug I saw: 3 widgets stacked, close the top one, then the second top one
|
|
// result: the bottom most one didn't have it's top separator at y=0
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
|
|
MultiSplitter *layout = m->multiSplitter();
|
|
|
|
// Stack: 1, 2, 3 vertically
|
|
m->addDockWidget(dock3, Location_OnTop);
|
|
m->addDockWidget(dock2, Location_OnTop);
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
auto frame1 = dock1->dptr()->frame();
|
|
auto frame2 = dock2->dptr()->frame();
|
|
auto frame3 = dock3->dptr()->frame();
|
|
QCOMPARE(frame1->QWidgetAdapter::y(), 0);
|
|
|
|
// Close 1
|
|
dock1->close();
|
|
Testing::waitForResize(frame2);
|
|
|
|
// Check that frame2 moved up to y=1
|
|
QCOMPARE(frame2->QWidgetAdapter::y(), 0);
|
|
|
|
// Close 2
|
|
dock2->close();
|
|
Testing::waitForResize(dock3);
|
|
|
|
QVERIFY(layout->checkSanity());
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 2);
|
|
|
|
// Check that frame3 moved up to y=1
|
|
QCOMPARE(frame3->QWidgetAdapter::y(), 0);
|
|
|
|
// Now restore:
|
|
auto toRestore1 = restore1First ? dock1 : dock2;
|
|
auto toRestore2 = restore1First ? dock2 : dock1;
|
|
|
|
toRestore1->show();
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
QVERIFY(dock3->isVisible());
|
|
QVERIFY(!dock3->size().isNull());
|
|
|
|
toRestore2->show();
|
|
|
|
Testing::waitForResize(frame3);
|
|
QVERIFY(layout->checkSanity());
|
|
QCOMPARE(layout->count(), 3);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
layout->checkSanity();
|
|
|
|
dock1->deleteLater();
|
|
dock2->deleteLater();
|
|
QVERIFY(Testing::waitForDeleted(dock2));
|
|
}
|
|
|
|
void TestDocks::tst_setVisibleFalseWhenSideBySide_data()
|
|
{
|
|
QTest::addColumn<bool>("useSetVisible");
|
|
QTest::newRow("false") << false;
|
|
// QTest::newRow("true") << true; // We don't support closing dock widgets via setVisible(false). (Yet ? Maybe never).
|
|
}
|
|
|
|
void TestDocks::tst_setVisibleFalseWhenSideBySide()
|
|
{
|
|
QFETCH(bool, useSetVisible);
|
|
|
|
auto setVisible = [useSetVisible] (DockWidgetBase *dw, bool visible) {
|
|
if (useSetVisible)
|
|
dw->setVisible(visible);
|
|
else if (visible)
|
|
dw->show();
|
|
else
|
|
dw->close();
|
|
};
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight);
|
|
|
|
const QRect oldGeo = dock1->geometry();
|
|
auto oldParent = dock1->parentWidget();
|
|
|
|
// 1. Just toggle visibility and check that stuff remained sane
|
|
QVERIFY(dock1->titleBar()->isVisible());
|
|
setVisible(dock1, false);
|
|
|
|
QVERIFY(!dock1->titleBar());
|
|
QVERIFY(!dock1->isTabbed());
|
|
QVERIFY(dock1->isFloating());
|
|
|
|
setVisible(dock1, true);
|
|
QVERIFY(dock1->titleBar()->isVisible());
|
|
QVERIFY(!dock1->isTabbed());
|
|
QVERIFY(!dock1->isFloating());
|
|
QCOMPARE(dock1->geometry(), oldGeo);
|
|
QCOMPARE(dock1->parentWidget(), oldParent);
|
|
|
|
// 2. Check that the parent frame also is hidden now
|
|
//auto fw1 = dock1->window();
|
|
setVisible(dock1, false);
|
|
QVERIFY(!dock1->dptr()->frame());
|
|
delete dock1;
|
|
}
|
|
|
|
void TestDocks::tst_restoreSimplest()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Tests restoring a very simple layout, composed of just 1 docked widget
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto layout = m->multiSplitter();
|
|
auto dock1 = createDockWidget("one", new QTextEdit());
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreSimplest.json")));
|
|
QTest::qWait(200);
|
|
QVERIFY(layout->checkSanity());
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreSimplest.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
}
|
|
|
|
void TestDocks::tst_restoreNonClosable()
|
|
{
|
|
// Tests that restoring state also restores the Option_NotClosable option
|
|
|
|
{
|
|
// Basic case:
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("one", new QTextEdit(), DockWidgetBase::Option_NotClosable);
|
|
QCOMPARE(dock1->options(), DockWidgetBase::Option_NotClosable);
|
|
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QCOMPARE(dock1->options(), DockWidgetBase::Option_NotClosable);
|
|
}
|
|
|
|
{
|
|
// Case from issue #137
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
|
|
auto dock1 = createDockWidget("1", new QTextEdit());
|
|
auto dock2 = createDockWidget("2", new QTextEdit(), DockWidgetBase::Option_NotClosable);
|
|
auto dock3 = createDockWidget("3", new QTextEdit());
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnLeft);
|
|
m->addDockWidget(dock3, Location_OnLeft);
|
|
|
|
QCOMPARE(dock2->options(), DockWidgetBase::Option_NotClosable);
|
|
dock2->setFloating(true);
|
|
QCOMPARE(dock2->options(), DockWidgetBase::Option_NotClosable);
|
|
|
|
TitleBar *tb = dock2->dptr()->frame()->actualTitleBar();
|
|
QVERIFY(tb->isVisible());
|
|
QVERIFY(!tb->closeButtonEnabled());
|
|
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QCOMPARE(dock2->options(), DockWidgetBase::Option_NotClosable);
|
|
|
|
tb = dock2->dptr()->frame()->actualTitleBar();
|
|
QVERIFY(tb->isVisible());
|
|
|
|
QVERIFY(!tb->closeButtonEnabled());
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
// Stack 1, 2, 3, close 2, close 2
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
MultiSplitter *layout = m->multiSplitter();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
m->addDockWidget(dock3, Location_OnTop);
|
|
m->addDockWidget(dock2, Location_OnTop);
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
QCOMPARE(layout->separators().size(), 2);
|
|
dock2->close();
|
|
Testing::waitForResize(dock3);
|
|
QCOMPARE(layout->separators().size(), 1);
|
|
layout->checkSanity();
|
|
|
|
// Cleanup:
|
|
dock2->deleteLater();
|
|
Testing::waitForDeleted(dock2);
|
|
}
|
|
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
auto dock4 = createDockWidget("dock4", new QPushButton("four"));
|
|
m->addDockWidget(dock1, Location_OnRight);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
m->addDockWidget(dock3, Location_OnRight);
|
|
m->addDockWidget(dock4, Location_OnRight);
|
|
|
|
MultiSplitter *layout = m->multiSplitter();
|
|
|
|
Item *item1 = layout->itemForFrame(dock1->dptr()->frame());
|
|
Item *item2 = layout->itemForFrame(dock2->dptr()->frame());
|
|
Item *item3 = layout->itemForFrame(dock3->dptr()->frame());
|
|
Item *item4 = layout->itemForFrame(dock4->dptr()->frame());
|
|
|
|
const auto separators = layout->separators();
|
|
QCOMPARE(separators.size(), 3);
|
|
|
|
Separator *anchor1 = separators[0];
|
|
int boundToTheRight = layout->rootItem()->maxPosForSeparator(anchor1);
|
|
int expectedBoundToTheRight = layout->size().width() -
|
|
3*Item::separatorThickness -
|
|
item2->minLength(Qt::Horizontal) -
|
|
item3->minLength(Qt::Horizontal) -
|
|
item4->minLength(Qt::Horizontal);
|
|
|
|
QCOMPARE(boundToTheRight, expectedBoundToTheRight);
|
|
|
|
dock3->close();
|
|
Testing::waitForResize(dock2);
|
|
|
|
QVERIFY(!item1->isPlaceholder());
|
|
QVERIFY(!item2->isPlaceholder());
|
|
QVERIFY(item3->isPlaceholder());
|
|
QVERIFY(!item4->isPlaceholder());
|
|
|
|
boundToTheRight = layout->rootItem()->maxPosForSeparator(anchor1);
|
|
expectedBoundToTheRight = layout->size().width() -
|
|
2*Item::separatorThickness -
|
|
item2->minLength(Qt::Horizontal) -
|
|
item4->minLength(Qt::Horizontal) ;
|
|
|
|
QCOMPARE(boundToTheRight, expectedBoundToTheRight);
|
|
dock3->deleteLater();
|
|
Testing::waitForDeleted(dock3);
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_rectForDropCrash()
|
|
{
|
|
// Tests a crash I got in MultiSplitterLayout::rectForDrop() (asserts being hit)
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow();
|
|
m->resize(QSize(500, 500));
|
|
m->show();
|
|
|
|
auto layout = m->multiSplitter();
|
|
|
|
auto w1 = new MyWidget2(QSize(400,400));
|
|
auto w2 = new MyWidget2(QSize(400,400));
|
|
|
|
auto d1 = createDockWidget("1", w1);
|
|
auto d2 = createDockWidget("2", w2);
|
|
|
|
m->addDockWidget(d1, Location_OnTop);
|
|
Item *centralItem = m->dropArea()->centralFrame();
|
|
{
|
|
WindowBeingDragged wbd2(d2->floatingWindow());
|
|
layout->rectForDrop(&wbd2, Location_OnTop, centralItem);
|
|
}
|
|
layout->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_restoreAfterResize()
|
|
{
|
|
// Tests a crash I got when the layout received a resize event *while* restoring
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(500, 500), {}, "tst_restoreAfterResize");
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
auto layout = m->multiSplitter();
|
|
const QSize oldContentsSize = layout->size();
|
|
const QSize oldWindowSize = m->size();
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreAfterResize.json")));
|
|
m->resize(1000, 1000);
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreAfterResize.json")));
|
|
QCOMPARE(oldContentsSize, layout->size());
|
|
QCOMPARE(oldWindowSize, m->size());
|
|
}
|
|
|
|
void TestDocks::tst_restoreWithNonClosableWidget()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(500, 500), {}, "tst_restoreWithNonClosableWidget");
|
|
auto dock1 = createDockWidget("1", new NonClosableWidget(), DockWidgetBase::Option_NotClosable);
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
auto layout = m->multiSplitter();
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreWithNonClosableWidget.json")));
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreWithNonClosableWidget.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
}
|
|
|
|
void TestDocks::tst_restoreNestedAndTabbed()
|
|
{
|
|
// Just a more involved test
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
QPoint oldFW4Pos;
|
|
QRect oldGeo;
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None, "tst_restoreNestedAndTabbed");
|
|
m->move(500, 500);
|
|
oldGeo = m->geometry();
|
|
auto layout = m->multiSplitter();
|
|
auto dock1 = createDockWidget("1", new QTextEdit());
|
|
auto dock2 = createDockWidget("2", new QTextEdit());
|
|
auto dock3 = createDockWidget("3", new QTextEdit());
|
|
auto dock4 = createDockWidget("4", new QTextEdit());
|
|
auto dock5 = createDockWidget("5", new QTextEdit());
|
|
dock4->addDockWidgetAsTab(dock5);
|
|
oldFW4Pos = dock4->window()->pos();
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
dock2->addDockWidgetAsTab(dock3);
|
|
dock2->setAsCurrentTab();
|
|
QCOMPARE(dock2->dptr()->frame()->currentTabIndex(), 0);
|
|
QCOMPARE(dock4->dptr()->frame()->currentTabIndex(), 1);
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreNestedAndTabbed.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
delete dock4->window();
|
|
// Let it be destroyed, we'll restore a new one
|
|
}
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None, "tst_restoreNestedAndTabbed");
|
|
auto layout = m->multiSplitter();
|
|
auto dock1 = createDockWidget("1", new QTextEdit());
|
|
auto dock2 = createDockWidget("2", new QTextEdit());
|
|
auto dock3 = createDockWidget("3", new QTextEdit());
|
|
auto dock4 = createDockWidget("4", new QTextEdit());
|
|
auto dock5 = createDockWidget("5", new QTextEdit());
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreNestedAndTabbed.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
auto fw4 = dock4->floatingWindow();
|
|
QVERIFY(fw4);
|
|
QCOMPARE(dock4->window(), dock5->window());
|
|
QCOMPARE(fw4->pos(), oldFW4Pos);
|
|
|
|
QCOMPARE(dock1->window(), m.get());
|
|
QCOMPARE(dock2->window(), m.get());
|
|
QCOMPARE(dock3->window(), m.get());
|
|
|
|
QCOMPARE(dock2->dptr()->frame()->currentTabIndex(), 0);
|
|
QCOMPARE(dock4->dptr()->frame()->currentTabIndex(), 1);
|
|
|
|
QCOMPARE(m->geometry(), oldGeo);
|
|
}
|
|
|
|
void TestDocks::tst_restoreCrash()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
{
|
|
// Create a main window, with a left dock, save it to disk.
|
|
auto m = createMainWindow({}, {}, "tst_restoreCrash");
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreCrash.json")));
|
|
}
|
|
|
|
// Restore
|
|
auto m = createMainWindow({}, {}, "tst_restoreCrash");
|
|
auto layout = m->multiSplitter();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreCrash.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
QVERIFY(!dock1->isFloating());
|
|
}
|
|
|
|
void TestDocks::tst_restoreSideBySide()
|
|
{
|
|
// Save a layout that has a floating window with nesting
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
QSize item2MinSize;
|
|
{
|
|
EnsureTopLevelsDeleted e1;
|
|
// MainWindow:
|
|
auto m = createMainWindow(QSize(500, 500), MainWindowOption_HasCentralFrame, "tst_restoreTwice");
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidgetAsTab(dock1);
|
|
auto layout = m->multiSplitter();
|
|
|
|
// FloatingWindow:
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
dock2->addDockWidgetToContainingWindow(dock3, Location_OnRight);
|
|
auto fw2 = dock2->floatingWindow();
|
|
item2MinSize = fw2->layoutWidget()->itemForFrame(dock2->dptr()->frame())->minSize();
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreSideBySide.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
}
|
|
|
|
{
|
|
auto m = createMainWindow(QSize(500, 500), MainWindowOption_HasCentralFrame, "tst_restoreTwice");
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
|
|
LayoutSaver restorer;
|
|
QVERIFY(restorer.restoreFromFile(QStringLiteral("layout_tst_restoreSideBySide.json")));
|
|
|
|
DockRegistry::self()->checkSanityAll();
|
|
|
|
QCOMPARE(dock1->window(), m.get());
|
|
QCOMPARE(dock2->window(), dock3->window());
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_restoreWithPlaceholder()
|
|
{
|
|
// Float dock1, save and restore, then unfloat and see if dock2 goes back to where it was
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
{
|
|
auto m = createMainWindow(QSize(500, 500), {}, "tst_restoreWithPlaceholder");
|
|
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
auto layout = m->multiSplitter();
|
|
dock1->setFloating(true);
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_restoreWithPlaceholder.json")));
|
|
|
|
dock1->close();
|
|
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreWithPlaceholder.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock1->isVisible());
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
dock1->setFloating(false); // Put it back. Should go back because the placeholder was restored.
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(dock1->isVisible());
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
}
|
|
|
|
// Try again, but on a different main window
|
|
auto m = createMainWindow(QSize(500, 500), {}, "tst_restoreWithPlaceholder");
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto layout = m->multiSplitter();
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_restoreWithPlaceholder.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock1->isVisible());
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
dock1->setFloating(false); // Put it back. Should go back because the placeholder was restored.
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(dock1->isVisible());
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
}
|
|
|
|
void TestDocks::tst_restoreWithAffinity()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m1 = createMainWindow(QSize(500, 500));
|
|
m1->setAffinities({ "a1" });
|
|
auto m2 = createMainWindow(QSize(500, 500));
|
|
m2->setAffinities({ "a2" });
|
|
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"), {}, {}, true, "a1");
|
|
m1->addDockWidget(dock1, Location_OnLeft);
|
|
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"), {}, {}, true, "a2");
|
|
dock2->setFloating(true);
|
|
dock2->show();
|
|
|
|
LayoutSaver saver;
|
|
saver.setAffinityNames({"a1"});
|
|
const QByteArray saved1 = saver.serializeLayout();
|
|
|
|
QPointer<FloatingWindow> fw2 = dock2->floatingWindow();
|
|
saver.restoreLayout(saved1);
|
|
|
|
// Restoring affinity 1 shouldn't close affinity 2
|
|
QVERIFY(!fw2.isNull());
|
|
QVERIFY(dock2->isVisible());
|
|
|
|
// Close all and restore again
|
|
DockRegistry::self()->clear();
|
|
saver.restoreLayout(saved1);
|
|
|
|
// dock2 continues closed
|
|
QVERIFY(!dock2->isVisible());
|
|
|
|
// dock1 was restored
|
|
QVERIFY(dock1->isVisible());
|
|
QVERIFY(!dock1->isFloating());
|
|
QCOMPARE(dock1->window(), m1.get());
|
|
|
|
delete dock2->window();
|
|
}
|
|
|
|
void TestDocks::tst_marginsAfterRestore()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
{
|
|
EnsureTopLevelsDeleted e1;
|
|
// MainWindow:
|
|
auto m = createMainWindow(QSize(500, 500), {}, "tst_marginsAfterRestore");
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
auto layout = m->multiSplitter();
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile(QStringLiteral("layout_tst_marginsAfterRestore.json")));
|
|
QVERIFY(saver.restoreFromFile(QStringLiteral("layout_tst_marginsAfterRestore.json")));
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
dock1->setFloating(true);
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
layout->addWidget(fw->dropArea(), Location_OnRight);
|
|
|
|
layout->checkSanity();
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_restoreWithNewDockWidgets()
|
|
{
|
|
// Tests that if the LayoutSaver doesn't know about some dock widget
|
|
// when it saves the layout, then it won't close it when restoring layout
|
|
// it will just be ignored.
|
|
EnsureTopLevelsDeleted e;
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
QVERIFY(!saved.isEmpty());
|
|
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("dock1"));
|
|
dock1->show();
|
|
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QVERIFY(dock1->isVisible());
|
|
|
|
delete dock1->window();
|
|
}
|
|
|
|
void TestDocks::tst_restoreWithDockFactory()
|
|
{
|
|
// Tests that restore the layout with a missing dock widget will recreate the dock widget using a factory
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
auto layout = m->multiSplitter();
|
|
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
QCOMPARE(layout->visibleCount(), 1);
|
|
|
|
LayoutSaver saver;
|
|
QByteArray saved = saver.serializeLayout();
|
|
QVERIFY(!saved.isEmpty());
|
|
QPointer<Frame> f1 = dock1->dptr()->frame();
|
|
delete dock1;
|
|
Testing::waitForDeleted(f1);
|
|
QVERIFY(!f1);
|
|
|
|
// Directly deleted don't leave placeolders. We could though.
|
|
QCOMPARE(layout->count(), 0);
|
|
|
|
{
|
|
// We don't know how to create the dock widget
|
|
SetExpectedWarning expectedWarning("Couldn't find dock widget");
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QCOMPARE(layout->count(), 0);
|
|
}
|
|
|
|
// Now try with a factory func
|
|
DockWidgetFactoryFunc func = [] (const QString &) {
|
|
return createDockWidget("1", new QPushButton("1"), {}, {}, /*show=*/ false);
|
|
};
|
|
|
|
KDDockWidgets::Config::self().setDockWidgetFactoryFunc(func);
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QCOMPARE(layout->count(), 1);
|
|
QCOMPARE(layout->visibleCount(), 1);
|
|
layout->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_restoreWithDockFactory2()
|
|
{
|
|
// Teste that the factory function can do id remapping.
|
|
// For example, if id "foo" is missing, the factory can return a
|
|
// dock widget with id "bar" if it feels like it
|
|
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
|
|
auto dock1 = createDockWidget("dw1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
dock1->setFloating(true);
|
|
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
delete dock1;
|
|
|
|
DockWidgetFactoryFunc func = [] (const QString &) {
|
|
// A factory func which does id remapping
|
|
return createDockWidget("dw2", new QPushButton("w"), {}, {}, /*show=*/ false);
|
|
};
|
|
|
|
KDDockWidgets::Config::self().setDockWidgetFactoryFunc(func);
|
|
saver.restoreLayout(saved);
|
|
}
|
|
|
|
void TestDocks::tst_addDockWidgetToMainWindow()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
m->addDockWidget(dock1, Location_OnRight, nullptr);
|
|
m->addDockWidget(dock2, Location_OnTop, dock1);
|
|
QVERIFY(m->dropArea()->checkSanity());
|
|
|
|
QCOMPARE(dock1->window(), m.get());
|
|
QCOMPARE(dock2->window(), m.get());
|
|
QVERIFY(dock1->dptr()->frame()->QWidgetAdapter::y()
|
|
> dock2->dptr()->frame()->QWidgetAdapter::y());
|
|
QCOMPARE(dock1->dptr()->frame()->QWidgetAdapter::x(),
|
|
dock2->dptr()->frame()->QWidgetAdapter::x());
|
|
}
|
|
|
|
void TestDocks::tst_addDockWidgetToContainingWindow()
|
|
{
|
|
{ // Test with a floating window
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
|
|
dock1->addDockWidgetToContainingWindow(dock2, Location_OnRight);
|
|
dock1->addDockWidgetToContainingWindow(dock3, Location_OnTop, dock2);
|
|
|
|
QCOMPARE(dock1->window(), dock2->window());
|
|
QCOMPARE(dock2->window(), dock3->window());
|
|
|
|
QVERIFY(dock3->dptr()->frame()->QWidgetAdapter::y()
|
|
< dock2->dptr()->frame()->QWidgetAdapter::y());
|
|
QVERIFY(dock1->dptr()->frame()->QWidgetAdapter::x()
|
|
< dock2->dptr()->frame()->QWidgetAdapter::x());
|
|
QCOMPARE(dock2->dptr()->frame()->QWidgetAdapter::x(),
|
|
dock3->dptr()->frame()->QWidgetAdapter::x());
|
|
}
|
|
|
|
{ // Also test with a main window
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
m->addDockWidget(dock1, Location_OnRight, nullptr);
|
|
dock1->addDockWidgetToContainingWindow(dock2, Location_OnRight);
|
|
|
|
QCOMPARE(dock1->window(), dock2->window());
|
|
QCOMPARE(m.get(), dock2->window());
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_notClosable()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
{
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"), DockWidgetBase::Option_NotClosable);
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
TitleBar *titlebarFW = fw->titleBar();
|
|
TitleBar *titleBarFrame = fw->frames().at(0)->titleBar();
|
|
QVERIFY(titlebarFW->isCloseButtonVisible());
|
|
QVERIFY(!titlebarFW->isCloseButtonEnabled());
|
|
QVERIFY(!titleBarFrame->isCloseButtonVisible());
|
|
QVERIFY(!titleBarFrame->isCloseButtonEnabled());
|
|
|
|
dock1->setOptions(DockWidgetBase::Option_None);
|
|
QVERIFY(titlebarFW->isCloseButtonVisible());
|
|
QVERIFY(titlebarFW->isCloseButtonEnabled());
|
|
QVERIFY(!titleBarFrame->isCloseButtonVisible());
|
|
QVERIFY(!titleBarFrame->isCloseButtonEnabled());
|
|
|
|
dock1->setOptions(DockWidgetBase::Option_NotClosable);
|
|
QVERIFY(titlebarFW->isCloseButtonVisible());
|
|
QVERIFY(!titlebarFW->isCloseButtonEnabled());
|
|
QVERIFY(!titleBarFrame->isCloseButtonVisible());
|
|
QVERIFY(!titleBarFrame->isCloseButtonEnabled());
|
|
|
|
auto window = dock1->window();
|
|
window->deleteLater();
|
|
Testing::waitForDeleted(window);
|
|
}
|
|
|
|
{
|
|
// Now dock dock1 into dock1 instead
|
|
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"), DockWidgetBase::Option_NotClosable);
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
dock2->dptr()->morphIntoFloatingWindow();
|
|
dock2->addDockWidgetAsTab(dock1);
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
TitleBar *titlebarFW = fw->titleBar();
|
|
TitleBar *titleBarFrame = fw->frames().at(0)->titleBar();
|
|
|
|
QVERIFY(titlebarFW->isCloseButtonVisible());
|
|
QVERIFY(!titleBarFrame->isCloseButtonVisible());
|
|
QVERIFY(!titleBarFrame->isCloseButtonEnabled());
|
|
|
|
auto window = dock2->window();
|
|
window->deleteLater();
|
|
Testing::waitForDeleted(window);
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_dragOverTitleBar()
|
|
{
|
|
// Tests that dragging over the title bar is returning DropLocation_None
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("Two"));
|
|
|
|
DropArea *da = dock1->floatingWindow()->dropArea();
|
|
FloatingWindow *fw1 = dock1->floatingWindow();
|
|
FloatingWindow *fw2 = dock2->floatingWindow();
|
|
WindowBeingDragged wbd(fw2, fw2);
|
|
|
|
const QPoint titleBarPoint = fw1->titleBar()->mapToGlobal(QPoint(5, 5));
|
|
|
|
auto loc = da->hover(&wbd, titleBarPoint);
|
|
QCOMPARE(loc, DropIndicatorOverlayInterface::DropLocation_None);
|
|
|
|
delete fw1;
|
|
delete fw2;
|
|
}
|
|
|
|
void TestDocks::tst_setFloatingGeometry()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = createDockWidget("dock1", new MyWidget("one"));
|
|
|
|
QVERIFY(dock1->isVisible());
|
|
const QRect requestedGeo = QRect(70, 70, 400, 400);
|
|
dock1->setFloatingGeometry(requestedGeo);
|
|
QCOMPARE(dock1->window()->geometry(), requestedGeo);
|
|
|
|
dock1->close();
|
|
QVERIFY(!dock1->isVisible());
|
|
|
|
const QRect requestedGeo2 = QRect(80, 80, 400, 400);
|
|
dock1->setFloatingGeometry(requestedGeo2);
|
|
dock1->show();
|
|
|
|
QCOMPARE(dock1->window()->geometry(), requestedGeo2);
|
|
}
|
|
|
|
void TestDocks::tst_setFloatingAfterDraggedFromTabToSideBySide()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dropArea = m->dropArea();
|
|
auto layout = dropArea;
|
|
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
// Move from tab to bottom
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnBottom);
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
dock2->setFloating(true);
|
|
dock2->setFloating(false);
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
QVERIFY(!dock2->isFloating());
|
|
}
|
|
|
|
{
|
|
// 2. Try again, but now detach from tab before putting it on the bottom. What was happening was that MultiSplitterLayout::addWidget()
|
|
// called with a MultiSplitter as widget wasn't setting the layout items for the dock widgets
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dropArea = m->dropArea();
|
|
auto layout = dropArea;
|
|
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
Item *oldItem2 = dock2->dptr()->lastPositions().lastItem();
|
|
QCOMPARE(oldItem2, layout->itemForFrame(dock2->dptr()->frame()));
|
|
|
|
|
|
// Detach tab
|
|
dock1->dptr()->frame()->detachTab(dock2);
|
|
QVERIFY(layout->checkSanity());
|
|
auto fw2 = dock2->floatingWindow();
|
|
QVERIFY(fw2);
|
|
QCOMPARE(dock2->dptr()->lastPositions().lastItem(), oldItem2);
|
|
Item *item2 = fw2->dropArea()->itemForFrame(dock2->dptr()->frame());
|
|
QVERIFY(item2);
|
|
QCOMPARE(item2->hostWidget()->asQObject(), fw2->dropArea());
|
|
QVERIFY(!layout->itemForFrame(dock2->dptr()->frame()));
|
|
|
|
// Move from tab to bottom
|
|
layout->addWidget(fw2->dropArea(), KDDockWidgets::Location_OnRight, nullptr);
|
|
QVERIFY(layout->checkSanity());
|
|
QVERIFY(dock2->dptr()->lastPositions().lastItem());
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
dock2->setFloating(true);
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
dock2->setFloating(false);
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
QVERIFY(!dock2->isFloating());
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
Testing::waitForDeleted(fw2);
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_setFloatingAFrameWithTabs()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dropArea = m->dropArea();
|
|
auto layout = dropArea;
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
// Make it float
|
|
dock1->dptr()->frame()->titleBar()->onFloatClicked();
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 1);
|
|
|
|
auto frame1 = dock1->dptr()->frame();
|
|
QVERIFY(frame1->layoutItem());
|
|
|
|
// Attach it again
|
|
dock1->dptr()->frame()->titleBar()->onFloatClicked();
|
|
|
|
QCOMPARE(layout->count(), 2);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
QCOMPARE(dock1->window(), m.get());
|
|
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
|
|
void TestDocks::tst_tabBarWithHiddenTitleBar_data()
|
|
{
|
|
QTest::addColumn<bool>("hiddenTitleBar");
|
|
QTest::addColumn<bool>("tabsAlwaysVisible");
|
|
|
|
QTest::newRow("false-false") << false << false;
|
|
QTest::newRow("true-false") << true << false;
|
|
|
|
QTest::newRow("false-true") << false << true;
|
|
QTest::newRow("true-true") << true << true;
|
|
|
|
}
|
|
|
|
void TestDocks::tst_tabBarWithHiddenTitleBar()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
QFETCH(bool, hiddenTitleBar);
|
|
QFETCH(bool, tabsAlwaysVisible);
|
|
|
|
const auto originalFlags = KDDockWidgets::Config::self().flags();
|
|
|
|
auto newFlags = originalFlags;
|
|
|
|
if (hiddenTitleBar)
|
|
newFlags = newFlags | KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible;
|
|
|
|
if (tabsAlwaysVisible)
|
|
newFlags = newFlags | KDDockWidgets::Config::Flag_AlwaysShowTabs;
|
|
|
|
KDDockWidgets::Config::self().setFlags(newFlags);
|
|
|
|
auto m = createMainWindow();
|
|
|
|
auto d1 = createDockWidget("1", new QTextEdit());
|
|
auto d2 = createDockWidget("2", new QTextEdit());
|
|
m->addDockWidget(d1, Location_OnTop);
|
|
|
|
if (tabsAlwaysVisible) {
|
|
if (hiddenTitleBar)
|
|
QVERIFY(!d1->dptr()->frame()->titleBar()->isVisible());
|
|
else
|
|
QVERIFY(d1->dptr()->frame()->titleBar()->isVisible());
|
|
} else {
|
|
QVERIFY(d1->dptr()->frame()->titleBar()->isVisible());
|
|
}
|
|
|
|
d1->addDockWidgetAsTab(d2);
|
|
|
|
QVERIFY(d2->dptr()->frame()->titleBar()->isVisible() ^ hiddenTitleBar);
|
|
|
|
d2->close();
|
|
m->layoutWidget()->checkSanity();
|
|
delete d2;
|
|
if (tabsAlwaysVisible) {
|
|
if (hiddenTitleBar)
|
|
QVERIFY(!d1->dptr()->frame()->titleBar()->isVisible());
|
|
else
|
|
QVERIFY(d1->dptr()->frame()->titleBar()->isVisible());
|
|
} else {
|
|
QVERIFY(d1->dptr()->frame()->titleBar()->isVisible());
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_toggleDockWidgetWithHiddenTitleBar()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible | KDDockWidgets::Config::Flag_AlwaysShowTabs);
|
|
auto m = createMainWindow();
|
|
|
|
auto d1 = createDockWidget("1", new QTextEdit());
|
|
m->addDockWidget(d1, Location_OnTop);
|
|
|
|
QVERIFY(!d1->dptr()->frame()->titleBar()->isVisible());
|
|
|
|
d1->toggleAction()->setChecked(false);
|
|
auto f1 = d1->dptr()->frame();
|
|
Testing::waitForDeleted(f1);
|
|
d1->toggleAction()->setChecked(true);
|
|
QVERIFY(d1->dptr()->frame());
|
|
QVERIFY(!d1->dptr()->frame()->titleBar()->isVisible());
|
|
}
|
|
|
|
void TestDocks::tst_availableSizeWithPlaceholders()
|
|
{
|
|
// Tests MultiSplitterLayout::available() with and without placeholders. The result should be the same.
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
QVector<DockDescriptor> docks1 = {
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
};
|
|
|
|
QVector<DockDescriptor> docks2 = {
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnBottom, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
};
|
|
|
|
QVector<DockDescriptor> empty;
|
|
|
|
auto m1 = createMainWindow(docks1);
|
|
auto m2 = createMainWindow(docks2);
|
|
auto m3 = createMainWindow(empty);
|
|
|
|
auto layout1 = m1->multiSplitter();
|
|
auto layout2 = m2->multiSplitter();
|
|
auto layout3 = m3->multiSplitter();
|
|
|
|
auto f20 = docks2.at(0).createdDock->dptr()->frame();
|
|
|
|
docks2.at(0).createdDock->close();
|
|
docks2.at(1).createdDock->close();
|
|
docks2.at(2).createdDock->close();
|
|
QVERIFY(Testing::waitForDeleted(f20));
|
|
|
|
QCOMPARE(layout1->size(), layout2->size());
|
|
QCOMPARE(layout1->size(), layout3->size());
|
|
|
|
QCOMPARE(layout1->availableSize(), layout2->availableSize());
|
|
QCOMPARE(layout1->availableSize(), layout3->availableSize());
|
|
|
|
// Now show 1 widget in m1 and m3
|
|
docks1.at(0).createdDock->show();
|
|
m3->addDockWidget(docks2.at(0).createdDock, Location_OnBottom); // just steal from m2
|
|
|
|
QCOMPARE(layout1->size(), layout3->size());
|
|
|
|
Frame *f10 = docks1.at(0).createdDock->dptr()->frame();
|
|
Item *item10 = layout1->itemForFrame(f10);
|
|
Item *item30 = layout3->itemForFrame(docks2.at(0).createdDock->dptr()->frame());
|
|
|
|
QCOMPARE(item10->geometry(), item30->geometry());
|
|
QCOMPARE(item10->guestWidget()->minSize(), item10->guestWidget()->minSize());
|
|
QCOMPARE(item10->minSize(), item30->minSize());
|
|
QCOMPARE(layout1->availableSize(), layout3->availableSize());
|
|
|
|
layout1->checkSanity();
|
|
layout2->checkSanity();
|
|
layout3->checkSanity();
|
|
|
|
// Cleanup
|
|
docks1.at(0).createdDock->deleteLater();
|
|
docks1.at(1).createdDock->deleteLater();
|
|
docks1.at(2).createdDock->deleteLater();
|
|
docks2.at(0).createdDock->deleteLater();
|
|
docks2.at(1).createdDock->deleteLater();
|
|
docks2.at(2).createdDock->deleteLater();
|
|
QVERIFY(Testing::waitForDeleted(docks2.at(2).createdDock));
|
|
}
|
|
|
|
void TestDocks::tst_anchorFollowingItselfAssert()
|
|
{
|
|
// 1. Tests that we don't assert in Anchor::setFollowee()
|
|
// ASSERT: "this != m_followee" in file ../src/multisplitter/Anchor.cpp
|
|
EnsureTopLevelsDeleted e;
|
|
QVector<DockDescriptor> docks = {
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnTop, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnLeft, -1, nullptr, InitialVisibilityOption::StartVisible },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartHidden },
|
|
{Location_OnRight, -1, nullptr, InitialVisibilityOption::StartVisible } };
|
|
|
|
auto m = createMainWindow(docks);
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
layout->checkSanity();
|
|
|
|
auto dock1 = docks.at(1).createdDock;
|
|
auto dock2 = docks.at(2).createdDock;
|
|
dock2->setFloating(true);
|
|
auto fw2 = dock2->floatingWindow();
|
|
dropArea->addWidget(fw2->dropArea(), Location_OnLeft, dock1->dptr()->frame());
|
|
dock2->setFloating(true);
|
|
fw2 = dock2->floatingWindow();
|
|
dropArea->addWidget(fw2->dropArea(), Location_OnRight, dock1->dptr()->frame());
|
|
|
|
docks.at(0).createdDock->deleteLater();
|
|
docks.at(4).createdDock->deleteLater();
|
|
Testing::waitForDeleted(docks.at(4).createdDock);
|
|
}
|
|
|
|
void TestDocks::tst_positionWhenShown()
|
|
{
|
|
// Tests that when showing a dockwidget it shows in the same position as before
|
|
EnsureTopLevelsDeleted e;
|
|
auto window = createMainWindow();
|
|
auto dock1 = new DockWidgetType("1");
|
|
dock1->show();
|
|
dock1->window()->move(100, 100);
|
|
QCOMPARE(dock1->window()->windowHandle()->frameGeometry().topLeft(), QPoint(100, 100));
|
|
|
|
dock1->close();
|
|
dock1->show();
|
|
QCOMPARE(dock1->window()->windowHandle()->frameGeometry().topLeft(), QPoint(100, 100));
|
|
|
|
// Cleanup
|
|
window->layoutWidget()->checkSanity();
|
|
dock1->deleteLater();
|
|
QVERIFY(Testing::waitForDeleted(dock1));
|
|
}
|
|
|
|
void TestDocks::tst_moreTitleBarCornerCases()
|
|
{
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("foo1"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("foo2"));
|
|
dock1->show();
|
|
dock2->show();
|
|
auto fw2 = dock2->window();
|
|
dock1->addDockWidgetToContainingWindow(dock2, Location_OnLeft);
|
|
QVERIFY(dock1->dptr()->frame()->titleBar()->isVisible());
|
|
QVERIFY(dock2->dptr()->frame()->titleBar()->isVisible());
|
|
QVERIFY(dock1->dptr()->frame()->titleBar() != dock2->dptr()->frame()->titleBar());
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw->titleBar()->isVisible());
|
|
QVERIFY(fw->titleBar() != dock1->dptr()->frame()->titleBar());
|
|
QVERIFY(fw->titleBar() != dock2->dptr()->frame()->titleBar());
|
|
delete fw;
|
|
delete fw2;
|
|
}
|
|
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("foo1"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("foo2"));
|
|
dock1->show();
|
|
dock2->show();
|
|
auto fw1 = dock1->floatingWindow();
|
|
auto fw2 = dock2->floatingWindow();
|
|
fw1->dropArea()->drop(fw2, Location_OnRight, nullptr);
|
|
QVERIFY(fw1->titleBar()->isVisible());
|
|
QVERIFY(dock1->dptr()->frame()->titleBar()->isVisible());
|
|
QVERIFY(dock2->dptr()->frame()->titleBar()->isVisible());
|
|
QVERIFY(dock1->dptr()->frame()->titleBar() != dock2->dptr()->frame()->titleBar());
|
|
QVERIFY(fw1->titleBar() != dock1->dptr()->frame()->titleBar());
|
|
QVERIFY(fw1->titleBar() != dock2->dptr()->frame()->titleBar());
|
|
delete fw1;
|
|
delete fw2;
|
|
}
|
|
|
|
{
|
|
// Tests that restoring a single floating dock widget doesn't make it show two title-bars
|
|
// As reproduced myself... and fixed in this commit
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("foo1"));
|
|
dock1->show();
|
|
|
|
auto fw1 = dock1->floatingWindow();
|
|
QVERIFY(!dock1->dptr()->frame()->titleBar()->isVisible());
|
|
QVERIFY(fw1->titleBar()->isVisible());
|
|
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
saver.restoreLayout(saved);
|
|
|
|
delete fw1; // the old window
|
|
|
|
fw1 = dock1->floatingWindow();
|
|
QVERIFY(dock1->isVisible());
|
|
QVERIFY(!dock1->dptr()->frame()->titleBar()->isVisible());
|
|
QVERIFY(fw1->titleBar()->isVisible());
|
|
delete dock1->window();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void TestDocks::tst_isInMainWindow()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto dw = new DockWidgetType(QStringLiteral("FOO"));
|
|
dw->show();
|
|
auto fw = dw->window();
|
|
QVERIFY(!dw->isInMainWindow());
|
|
auto m1 = createMainWindow(QSize(2560, 809), MainWindowOption_None, "MainWindow1");
|
|
m1->addDockWidget(dw, KDDockWidgets::Location_OnLeft);
|
|
QVERIFY(dw->isInMainWindow());
|
|
delete fw;
|
|
|
|
// Also test after creating the MainWindow, as the FloatingWindow will get parented to it
|
|
auto dw2 = new DockWidgetType(QStringLiteral("2"));
|
|
dw2->show();
|
|
QVERIFY(!dw2->isInMainWindow());
|
|
delete dw2->window();
|
|
}
|
|
|
|
void TestDocks::tst_sizeConstraintWarning()
|
|
{
|
|
// Tests that we don't get the warning: MultiSplitterLayout::checkSanity: Widget has height= 122 but minimum is 144 KDDockWidgets::Item
|
|
// Code autogenerated by the fuzzer:
|
|
EnsureTopLevelsDeleted e;
|
|
SetExpectedWarning sew("Dock widget already exists in the layout");
|
|
|
|
auto window = createMainWindow();
|
|
QList<DockWidgetBase *> listDockWidget;
|
|
{
|
|
auto dock = new DockWidgetType("foo-0");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-1");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-2");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-3");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-4");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-5");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-6");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-7");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-8");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-9");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-10");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-11");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-12");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-13");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-14");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-15");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-16");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-17");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
{
|
|
auto dock = new DockWidgetType("foo-18");
|
|
dock->setWidget(new QTextEdit());
|
|
listDockWidget.append(dock);
|
|
}
|
|
|
|
auto dropArea = window->dropArea();
|
|
window->addDockWidget(listDockWidget.at(0), static_cast<Location>(2));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(1), static_cast<Location>(1));
|
|
dropArea->checkSanity();
|
|
|
|
listDockWidget.at(2 - 1)->addDockWidgetAsTab(listDockWidget.at(2));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(3-1), static_cast<Location>(2), listDockWidget.at(3), static_cast<InitialVisibilityOption>(1));
|
|
dropArea->checkSanity();
|
|
|
|
listDockWidget.at(4 - 1)->addDockWidgetAsTab(listDockWidget.at(4));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(5), static_cast<Location>(1));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(6), static_cast<Location>(1));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(7), static_cast<Location>(4));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(8-1), static_cast<Location>(1), listDockWidget.at(8), static_cast<InitialVisibilityOption>(1));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(9), static_cast<Location>(2));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(10-1), static_cast<Location>(2), listDockWidget.at(10), static_cast<InitialVisibilityOption>(1));
|
|
dropArea->checkSanity();
|
|
|
|
listDockWidget.at(11 - 1)->addDockWidgetAsTab(listDockWidget.at(11));
|
|
dropArea->checkSanity();
|
|
|
|
listDockWidget.at(12 - 1)->addDockWidgetAsTab(listDockWidget.at(12));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(13), static_cast<Location>(4));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(14), static_cast<Location>(2));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(15), static_cast<Location>(3));
|
|
dropArea->checkSanity();
|
|
|
|
window->addDockWidget(listDockWidget.at(16), static_cast<Location>(4));
|
|
dropArea->checkSanity();
|
|
|
|
listDockWidget.at(17 - 1)->addDockWidgetAsTab(listDockWidget.at(17));
|
|
dropArea->checkSanity();
|
|
listDockWidget.at(18 - 1)->addDockWidgetAsTab(listDockWidget.at(18));
|
|
dropArea->checkSanity();
|
|
|
|
auto docks = DockRegistry::self()->dockwidgets();
|
|
auto lastDock = docks.last();
|
|
for (auto dock: docks)
|
|
dock->deleteLater();
|
|
|
|
Testing::waitForDeleted(lastDock);
|
|
}
|
|
|
|
void TestDocks::tst_stuckSeparator()
|
|
{
|
|
const QString absoluteLayoutFileName = QStringLiteral(":/layouts/stuck-separator.json");
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m1 = createMainWindow(QSize(2560, 809), MainWindowOption_None, "MainWindow1");
|
|
const int numDockWidgets = 26;
|
|
DockWidgetBase *dw25 = nullptr;
|
|
for (int i = 0; i < numDockWidgets; ++i) {
|
|
auto createdDw = createDockWidget(QStringLiteral("dock-%1").arg(i));
|
|
if (i == 25)
|
|
dw25 = createdDw;
|
|
}
|
|
|
|
LayoutSaver restorer;
|
|
QVERIFY(restorer.restoreFromFile(absoluteLayoutFileName));
|
|
|
|
Frame *frame25 = dw25->dptr()->frame();
|
|
ItemBoxContainer *root = m1->multiSplitter()->rootItem();
|
|
Item *item25 = root->itemForWidget(frame25);
|
|
ItemBoxContainer *container25 = item25->parentBoxContainer();
|
|
Separator::List separators = container25->separators();
|
|
QCOMPARE(separators.size(), 1);
|
|
|
|
Separator *separator25 = separators.constFirst();
|
|
const int sepMin = container25->minPosForSeparator_global(separator25);
|
|
const int sepMax = container25->maxPosForSeparator_global(separator25);
|
|
|
|
QVERIFY(sepMin <= sepMax);
|
|
|
|
for (auto dw : DockRegistry::self()->dockwidgets()) {
|
|
delete dw;
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_titlebar_getter()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(1000, 1000), MainWindowOption_HasCentralFrame);
|
|
m->resize(QSize(500, 500));
|
|
m->show();
|
|
|
|
auto w1 = new MyWidget2(QSize(400, 400));
|
|
auto d1 = createDockWidget("1", w1);
|
|
|
|
m->addDockWidget(d1, Location_OnTop);
|
|
|
|
QVERIFY(d1->titleBar()->isVisible());
|
|
d1->setFloating(true);
|
|
QVERIFY(d1->floatingWindow());
|
|
QVERIFY(d1->floatingWindow()->isVisible());
|
|
QVERIFY(d1->titleBar()->isVisible());
|
|
}
|
|
|
|
void TestDocks::tst_dockNotFillingSpace()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(1000, 1000));
|
|
m->resize(QSize(500, 500));
|
|
m->show();
|
|
|
|
auto d1 = createDockWidget("1", new QTextEdit());
|
|
auto d2 = createDockWidget("2", new QTextEdit());
|
|
auto d3 = createDockWidget("3", new QTextEdit());
|
|
|
|
m->addDockWidget(d1, Location_OnTop);
|
|
m->addDockWidget(d2, Location_OnBottom);
|
|
m->addDockWidget(d3, Location_OnBottom);
|
|
|
|
Frame *frame2 = d2->dptr()->frame();
|
|
d1->close();
|
|
d2->close();
|
|
Testing::waitForDeleted(frame2);
|
|
|
|
auto layout = m->multiSplitter();
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
delete d1;
|
|
delete d2;
|
|
}
|
|
|
|
void TestDocks::tst_lastFloatingPositionIsRestored()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m1 = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1");
|
|
dock1->show();
|
|
QPoint targetPos = QPoint(340, 340);
|
|
dock1->window()->move(targetPos);
|
|
auto oldFw = dock1->window();
|
|
|
|
LayoutSaver saver;
|
|
QByteArray saved = saver.serializeLayout();
|
|
|
|
dock1->window()->move(0, 0);
|
|
dock1->close();
|
|
delete oldFw;
|
|
|
|
saver.restoreLayout(saved);
|
|
QCOMPARE(dock1->window()->pos(), targetPos);
|
|
QCOMPARE(dock1->window()->frameGeometry().topLeft(), targetPos);
|
|
|
|
// Adjsut to what we got without the frame
|
|
targetPos = dock1->window()->geometry().topLeft();
|
|
|
|
// Now dock it:
|
|
m1->addDockWidget(dock1, Location_OnTop);
|
|
QCOMPARE(dock1->dptr()->lastPositions().lastFloatingGeometry().topLeft(), targetPos);
|
|
|
|
dock1->setFloating(true);
|
|
QCOMPARE(dock1->window()->geometry().topLeft(), targetPos);
|
|
|
|
saver.restoreLayout(saved);
|
|
QCOMPARE(dock1->window()->geometry().topLeft(), targetPos);
|
|
|
|
// Dock again and save:
|
|
m1->addDockWidget(dock1, Location_OnTop);
|
|
saved = saver.serializeLayout();
|
|
dock1->setFloating(true);
|
|
dock1->window()->move(0, 0);
|
|
saver.restoreLayout(saved);
|
|
QVERIFY(!dock1->isFloating());
|
|
dock1->setFloating(true);
|
|
QCOMPARE(dock1->window()->geometry().topLeft(), targetPos);
|
|
delete dock1->window();
|
|
}
|
|
|
|
void TestDocks::tst_titleBarFocusedWhenTabsChange()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_TitleBarIsFocusable);
|
|
|
|
auto le1 = new FocusableWidget();
|
|
le1->setObjectName("le1");
|
|
auto dock1 = createDockWidget(QStringLiteral("dock1"), le1);
|
|
auto dock2 = createDockWidget(QStringLiteral("dock2"), new FocusableWidget());
|
|
auto dock3 = createDockWidget(QStringLiteral("dock3"), new FocusableWidget());
|
|
auto oldFw1 = dock1->window();
|
|
auto oldFw2 = dock2->window();
|
|
auto oldFw3 = dock3->window();
|
|
|
|
auto m1 = createMainWindow(QSize(2560, 809), MainWindowOption_None, "MainWindow1");
|
|
|
|
m1->addDockWidget(dock1, Location_OnLeft);
|
|
m1->addDockWidget(dock2, Location_OnRight);
|
|
delete oldFw1;
|
|
delete oldFw2;
|
|
dock2->addDockWidgetAsTab(dock3);
|
|
delete oldFw3;
|
|
|
|
TitleBar *titleBar1 = dock1->titleBar();
|
|
dock1->widget()->setFocus(Qt::MouseFocusReason);
|
|
|
|
QVERIFY(dock1->isFocused() || Testing::waitForEvent(dock1->widget(), QEvent::FocusIn));
|
|
QVERIFY(titleBar1->isFocused());
|
|
|
|
auto frame2 = dock2->dptr()->frame();
|
|
|
|
TabWidget *tb2 = frame2->tabWidget();
|
|
QCOMPARE(tb2->currentIndex(), 1); // Was the last to be added
|
|
|
|
auto tabBar2 = tb2->tabBar();
|
|
const QRect rect0 = tabBar2->rectForTab(0);
|
|
const QPoint globalPos = tabBar2->asWidget()->mapToGlobal(rect0.topLeft()) + QPoint(5, 5);
|
|
Tests::clickOn(globalPos, tabBar2->asWidget());
|
|
|
|
QVERIFY(!titleBar1->isFocused());
|
|
QVERIFY(dock2->titleBar()->isFocused());
|
|
|
|
// Test that clicking on a tab that is already current will also set focus
|
|
dock1->setFocus(Qt::MouseFocusReason);
|
|
QVERIFY(dock1->titleBar()->isFocused());
|
|
QVERIFY(!dock2->titleBar()->isFocused());
|
|
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
// TODO: Not yet ready for QtQuick. The TitleBar.qml is clicked, but we check the C++ TitleBar for focus
|
|
Tests::clickOn(globalPos, tabBar2->asWidget());
|
|
QVERIFY(!dock1->titleBar()->isFocused());
|
|
QVERIFY(dock2->titleBar()->isFocused());
|
|
#endif
|
|
}
|
|
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
|
|
void TestDocks::tst_tabsNotClickable()
|
|
{
|
|
// Well, not a great unit-test, as it's only repro when it's Windows sending the native event
|
|
// Can't repro with fabricated events. Uncomment the WAIT and test different configs manually
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_Default | KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible);
|
|
|
|
auto dock1 = createDockWidget("dock1", new QWidget());
|
|
auto dock2 = createDockWidget("dock2", new QWidget());
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
auto frame = qobject_cast<FrameWidget *>(dock1->dptr()->frame());
|
|
QCOMPARE(frame->currentIndex(), 1);
|
|
|
|
QTest::qWait(500); // wait for window to get proper geometry
|
|
|
|
const QPoint clickPoint = frame->tabBar()->mapToGlobal(frame->tabBar()->tabRect(0).center());
|
|
QCursor::setPos(clickPoint); // Just for visual debug when needed
|
|
|
|
pressOn(clickPoint, frame->tabBar());
|
|
releaseOn(clickPoint, frame->tabBar());
|
|
|
|
// WAIT // Uncomment for MANUAL test. Also test by adding Flag_AlwaysShowTabs
|
|
|
|
QCOMPARE(frame->currentIndex(), 0);
|
|
|
|
delete frame->window();
|
|
}
|
|
|
|
void TestDocks::tst_mainWindowAlwaysHasCentralWidget()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow();
|
|
|
|
QWidget *central = m->centralWidget();
|
|
auto dropArea = m->dropArea();
|
|
QVERIFY(dropArea);
|
|
|
|
QPointer<Frame> centralFrame = static_cast<Frame*>(dropArea->centralFrame()->guestAsQObject());
|
|
QVERIFY(central);
|
|
QVERIFY(dropArea);
|
|
QCOMPARE(dropArea->count(), 1);
|
|
QVERIFY(centralFrame);
|
|
QCOMPARE(centralFrame->dockWidgetCount(), 0);
|
|
|
|
// Add a tab
|
|
auto dock = createDockWidget("doc1", Qt::green);
|
|
m->addDockWidgetAsTab(dock);
|
|
QCOMPARE(dropArea->count(), 1);
|
|
QCOMPARE(centralFrame->dockWidgetCount(), 1);
|
|
|
|
qDebug() << "Central widget width=" << central->size() << "; mainwindow="
|
|
<< m->size();
|
|
|
|
// Detach tab
|
|
QPoint globalPressPos = dragPointForWidget(centralFrame.data(), 0);
|
|
QTabBar *tabBar = static_cast<FrameWidget*>(centralFrame.data())->tabBar();
|
|
QVERIFY(tabBar);
|
|
qDebug() << "Detaching tab from dropArea->size=" << dropArea->QWidget::size() << "; dropArea=" << dropArea;
|
|
drag(tabBar, globalPressPos, m->geometry().bottomRight() + QPoint(30, 30));
|
|
|
|
QVERIFY(centralFrame);
|
|
QCOMPARE(dropArea->count(), 1);
|
|
QCOMPARE(centralFrame->dockWidgetCount(), 0);
|
|
QVERIFY(dropArea->checkSanity());
|
|
|
|
delete dock->window();
|
|
}
|
|
|
|
void TestDocks::tst_dockableMainWindows()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m1 = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("foo"));
|
|
m1->addDockWidget(dock1, Location_OnTop);
|
|
|
|
auto m2 = new KDDockWidgets::MainWindow("mainwindow-dockable");
|
|
auto m2Container = createDockWidget("mainwindow-dw", m2);
|
|
auto menubar = m2->menuBar();
|
|
menubar->addMenu("File");
|
|
menubar->addMenu("View");
|
|
menubar->addMenu("Help");
|
|
m2Container->show();
|
|
|
|
auto dock21 = createDockWidget("dock21", new QPushButton("foo"));
|
|
auto dock22 = createDockWidget("dock22", new QPushButton("foo"));
|
|
m2->addDockWidget(dock21, Location_OnLeft);
|
|
m2->addDockWidget(dock22, Location_OnRight);
|
|
|
|
auto fw = m2Container->floatingWindow();
|
|
TitleBar *fwTitleBar = fw->titleBar();
|
|
|
|
QVERIFY(fw->hasSingleFrame());
|
|
QVERIFY(fw->hasSingleDockWidget());
|
|
|
|
// Check that the inner-inner dock widgets have a visible title-bar
|
|
QVERIFY(dock21->titleBar()->isVisible());
|
|
QVERIFY(dock22->titleBar()->isVisible());
|
|
QVERIFY(dock21->titleBar() != fwTitleBar);
|
|
QVERIFY(dock22->titleBar() != fwTitleBar);
|
|
|
|
const QPoint startPoint = fwTitleBar->mapToGlobal(QPoint(5, 5));
|
|
const QPoint destination = startPoint + QPoint(20, 20);
|
|
|
|
// Check that we don't get the "Refusing to itself" warning. not actually dropping anywhere
|
|
drag(fwTitleBar, startPoint, destination);
|
|
|
|
// The FloatingWindow has a single DockWidget, so it shows the title bar, while the Frame doesn't
|
|
QVERIFY(fwTitleBar->isVisible());
|
|
QVERIFY(!m2Container->dptr()->frame()->titleBar()->isVisible());
|
|
|
|
fw->dropArea()->addDockWidget(dock1, Location::Location_OnLeft, nullptr);
|
|
// Now the FloatingWindow has two dock widgets, so our main window dock widget also shows the title bar
|
|
QVERIFY(fwTitleBar->isVisible());
|
|
QVERIFY(m2Container->dptr()->frame()->titleBar()->isVisible());
|
|
|
|
// Put it how it was, FloatingWindow is single dock again
|
|
auto frame1 = dock1->dptr()->frame();
|
|
dock1->close();
|
|
Testing::waitForDeleted(frame1);
|
|
QVERIFY(fwTitleBar->isVisible());
|
|
QVERIFY(!m2Container->dptr()->frame()->titleBar()->isVisible());
|
|
|
|
// Repeat, but instead of closing dock1, we float it
|
|
fw->dropArea()->addDockWidget(dock1, Location::Location_OnLeft, nullptr);
|
|
QVERIFY(fwTitleBar->isVisible());
|
|
QVERIFY(m2Container->dptr()->frame()->titleBar()->isVisible());
|
|
frame1 = dock1->dptr()->frame();
|
|
frame1->titleBar()->onFloatClicked();
|
|
QVERIFY(fwTitleBar->isVisible());
|
|
|
|
QVERIFY(!m2Container->dptr()->frame()->titleBar()->isVisible());
|
|
|
|
fw->dropArea()->addDockWidget(dock1, Location::Location_OnLeft, nullptr);
|
|
}
|
|
|
|
// No need to port to QtQuick
|
|
void TestDocks::tst_floatingWindowDeleted()
|
|
{
|
|
// Tests a case where the empty floating dock widget wouldn't be deleted
|
|
// Doesn't repro QTBUG-83030 unfortunately, as we already have an event loop running
|
|
// but let's leave this here nontheless
|
|
class MyMainWindow : public KDDockWidgets::MainWindow {
|
|
public:
|
|
|
|
MyMainWindow()
|
|
: KDDockWidgets::MainWindow("tst_floatingWindowDeleted", MainWindowOption_None)
|
|
{
|
|
auto dock1 = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #1"));
|
|
auto myWidget = new QWidget();
|
|
dock1->setWidget(myWidget);
|
|
dock1->resize(600, 600);
|
|
dock1->show();
|
|
|
|
auto dock2 = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #2"));
|
|
myWidget = new QWidget();
|
|
dock2->setWidget(myWidget);
|
|
dock2->resize(600, 600);
|
|
dock2->show();
|
|
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
}
|
|
};
|
|
|
|
MyMainWindow m;
|
|
}
|
|
|
|
void TestDocks::tst_addToSmallMainWindow6()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Test test shouldn't spit any warnings
|
|
|
|
QWidget container;
|
|
auto lay = new QVBoxLayout(&container);
|
|
MainWindow m("MyMainWindow_tst_addToSmallMainWindow8", MainWindowOption_None);
|
|
lay->addWidget(&m);
|
|
container.resize(100, 100);
|
|
Testing::waitForResize(&container);
|
|
container.show();
|
|
Testing::waitForResize(&m);
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(50, 240)));
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(50, 240)));
|
|
m.addDockWidget(dock1, KDDockWidgets::Location_OnBottom);
|
|
m.addDockWidget(dock2, KDDockWidgets::Location_OnBottom);
|
|
Testing::waitForResize(&m);
|
|
QVERIFY(m.dropArea()->checkSanity());
|
|
}
|
|
|
|
|
|
void TestDocks::tst_closeRemovesFromSideBar()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None);
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
auto fw1 = dw1->window();
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
m1->moveToSideBar(dw1);
|
|
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(!dw1->isVisible());
|
|
QVERIFY(dw1->isInSideBar());
|
|
|
|
SideBar *sb = m1->sideBarForDockWidget(dw1);
|
|
QVERIFY(sb);
|
|
|
|
// Overlay it:
|
|
sb->toggleOverlay(dw1);
|
|
QVERIFY(dw1->isOverlayed());
|
|
QVERIFY(dw1->isVisible());
|
|
QCOMPARE(dw1->sideBarLocation(), sb->location());
|
|
QVERIFY(dw1->isInMainWindow());
|
|
QVERIFY(!dw1->isFloating());
|
|
|
|
// Close it while it's overlayed:
|
|
dw1->close();
|
|
QVERIFY(!dw1->isInMainWindow());
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(!dw1->isVisible());
|
|
QCOMPARE(dw1->sideBarLocation(), SideBarLocation::None);
|
|
|
|
delete fw1;
|
|
}
|
|
|
|
void TestDocks::tst_restoreSideBar()
|
|
{
|
|
SideBarLocation loc;
|
|
QByteArray serialized; // serialization after having 1 sidebar visible
|
|
QByteArray beforeSideBarSerialized; // serialization without any sidebar visible
|
|
|
|
{
|
|
LayoutSaver saver;
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1");
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
auto fw1 = dw1->window();
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
beforeSideBarSerialized = saver.serializeLayout();
|
|
QVERIFY(!m1->anySideBarIsVisible());
|
|
m1->moveToSideBar(dw1);
|
|
QVERIFY(m1->anySideBarIsVisible());
|
|
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(!dw1->isVisible());
|
|
loc = dw1->sideBarLocation();
|
|
QVERIFY(loc != SideBarLocation::None);
|
|
|
|
serialized = saver.serializeLayout();
|
|
|
|
delete fw1;
|
|
}
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1");
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
auto fw1 = dw1->window();
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
QVERIFY(!m1->anySideBarIsVisible());
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(dw1->isVisible());
|
|
QVERIFY(!dw1->isFloating());
|
|
QVERIFY(dw1->isInMainWindow());
|
|
|
|
LayoutSaver restorer;
|
|
restorer.restoreLayout(serialized);
|
|
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(!dw1->isVisible());
|
|
QVERIFY(!dw1->isInMainWindow());
|
|
QVERIFY(m1->anySideBarIsVisible());
|
|
|
|
QCOMPARE(loc, dw1->sideBarLocation());
|
|
|
|
restorer.restoreLayout(beforeSideBarSerialized);
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(dw1->isVisible());
|
|
QVERIFY(!dw1->isFloating());
|
|
QVERIFY(dw1->isInMainWindow());
|
|
QVERIFY(!m1->anySideBarIsVisible());
|
|
|
|
delete fw1;
|
|
}
|
|
|
|
void TestDocks::tst_toggleActionOnSideBar()
|
|
{
|
|
// When a dock widget is in the sidebar and we use DockWidget::toggleAction() then it should
|
|
// toggle visibility without removing it from the sidebar
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1");
|
|
auto dw1 = new DockWidgetType("1");
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
dw1->moveToSideBar();
|
|
|
|
QVERIFY(!dw1->isVisible());
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(dw1->isInSideBar());
|
|
QVERIFY(!dw1->isInMainWindow());
|
|
|
|
QAction *action = dw1->toggleAction();
|
|
action->trigger();
|
|
|
|
QVERIFY(dw1->isVisible());
|
|
QVERIFY(dw1->isOverlayed());
|
|
QVERIFY(dw1->isInMainWindow());
|
|
|
|
QVERIFY(dw1->isInSideBar());
|
|
action->trigger();
|
|
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(!dw1->isInMainWindow());
|
|
|
|
QVERIFY(dw1->isInSideBar());
|
|
QVERIFY(!dw1->isInMainWindow());
|
|
}
|
|
|
|
void TestDocks::tst_deleteOnCloseWhenOnSideBar()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
QPointer<DockWidgetBase> dock1 = createDockWidget("dock1", new MyWidget2(QSize(400, 400)), DockWidgetBase::Option_DeleteOnClose);
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
|
|
dock1->moveToSideBar();
|
|
QVERIFY(dock1);
|
|
QVERIFY(dock1->isInSideBar());
|
|
|
|
QTest::qWait(500);
|
|
QVERIFY(dock1);
|
|
}
|
|
|
|
void TestDocks::tst_sidebarOverlayGetsHiddenOnClick()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
|
|
|
|
{
|
|
// Case #1 click on another dockwidget should hide the overlay
|
|
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1");
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
auto dw2 = new DockWidgetType(QStringLiteral("2"));
|
|
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
m1->addDockWidget(dw2, Location_OnBottom);
|
|
|
|
m1->moveToSideBar(dw1);
|
|
m1->overlayOnSideBar(dw1);
|
|
|
|
QVERIFY(dw1->isOverlayed());
|
|
|
|
Tests::clickOn(dw2->mapToGlobal(dw2->rect().bottomLeft() + QPoint(5, -5)), dw2);
|
|
QVERIFY(!dw1->isOverlayed());
|
|
|
|
auto widget2 = new MyWidget("foo");
|
|
dw2->setWidget(widget2);
|
|
m1->overlayOnSideBar(dw1);
|
|
QVERIFY(dw1->isOverlayed());
|
|
|
|
Tests::clickOn(widget2->mapToGlobal(widget2->rect().bottomLeft() + QPoint(5, -5)), widget2);
|
|
QVERIFY(!dw1->isOverlayed());
|
|
|
|
delete dw1;
|
|
}
|
|
|
|
{
|
|
// Case #1 click on empty main window space, should hide the overlay
|
|
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1");
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
|
|
m1->moveToSideBar(dw1);
|
|
m1->overlayOnSideBar(dw1);
|
|
|
|
QVERIFY(dw1->isOverlayed());
|
|
|
|
const QPoint localPt(100, 250);
|
|
Tests::clickOn(m1->mapToGlobal(m1->rect().topLeft() + localPt), m1->childAt(localPt));
|
|
QVERIFY(!dw1->isOverlayed());
|
|
|
|
delete dw1;
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_floatRemovesFromSideBar()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
|
|
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1");
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
|
|
m1->moveToSideBar(dw1);
|
|
m1->overlayOnSideBar(dw1);
|
|
|
|
QVERIFY(dw1->isOverlayed());
|
|
QVERIFY(!dw1->isFloating());
|
|
QVERIFY(dw1->isInMainWindow());
|
|
|
|
dw1->setFloating(true);
|
|
QVERIFY(!dw1->isOverlayed());
|
|
QVERIFY(dw1->isFloating());
|
|
QVERIFY(!dw1->isInMainWindow());
|
|
|
|
QCOMPARE(dw1->sideBarLocation(), SideBarLocation::None);
|
|
|
|
// Also test a crash I got
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
|
|
auto tb = dw1->titleBar();
|
|
QVERIFY(tb->isVisible());
|
|
|
|
tb->onFloatClicked();
|
|
}
|
|
|
|
void TestDocks::tst_overlayedGeometryIsSaved()
|
|
{
|
|
// Tests that after resizing an overlayed widget, and then hide+show, its size is preserved
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
|
|
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1");
|
|
auto dw1 = new DockWidgetType(QStringLiteral("1"));
|
|
m1->addDockWidget(dw1, Location_OnBottom);
|
|
|
|
m1->moveToSideBar(dw1, SideBarLocation::North);
|
|
m1->overlayOnSideBar(dw1);
|
|
|
|
Frame *frame = dw1->dptr()->frame();
|
|
QVERIFY(frame->isOverlayed());
|
|
QCOMPARE(dw1->sideBarLocation(), SideBarLocation::North);
|
|
QVERIFY(frame->height() > 0);
|
|
|
|
const int newHeight = frame->height() + 300;
|
|
frame->setHeight(newHeight);
|
|
|
|
m1->toggleOverlayOnSideBar(dw1);
|
|
m1->toggleOverlayOnSideBar(dw1);
|
|
|
|
frame = dw1->dptr()->frame();
|
|
QCOMPARE(frame->height(), newHeight);
|
|
}
|
|
|
|
void TestDocks::tst_embeddedMainWindow()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Tests a MainWindow which isn't a top-level window, but is embedded in another window
|
|
EmbeddedWindow *window = createEmbeddedMainWindow(QSize(800, 800));
|
|
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
window->mainWindow->addDockWidget(dock1, Location_OnTop);
|
|
dock1->setFloating(true);
|
|
auto dropArea = window->mainWindow->dropArea();
|
|
auto fw = dock1->floatingWindow();
|
|
|
|
dragFloatingWindowTo(fw, dropArea, DropIndicatorOverlayInterface::DropLocation_Left);
|
|
|
|
auto layout = dropArea;
|
|
QVERIFY(Testing::waitForDeleted(fw));
|
|
QCOMPARE(layout->count(), 2); // 2, as it has the central frame
|
|
QCOMPARE(layout->visibleCount(), 2);
|
|
layout->checkSanity();
|
|
|
|
delete window;
|
|
}
|
|
|
|
void TestDocks::tst_restoreEmbeddedMainWindow()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Tests a MainWindow which isn't a top-level window, but is embedded in another window
|
|
EmbeddedWindow *window = createEmbeddedMainWindow(QSize(800, 800));
|
|
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
window->mainWindow->addDockWidget(dock1, Location_OnTop);
|
|
|
|
const QPoint originalPos(250, 250);
|
|
const QSize originalSize = window->size();
|
|
window->move(originalPos);
|
|
|
|
LayoutSaver saver;
|
|
QByteArray saved = saver.serializeLayout();
|
|
QVERIFY(!saved.isEmpty());
|
|
|
|
window->resize(555, 555);
|
|
const QPoint newPos(500, 500);
|
|
window->move(newPos);
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
|
|
QCOMPARE(window->pos(), originalPos);
|
|
QCOMPARE(window->size(), originalSize);
|
|
window->mainWindow->layoutWidget()->checkSanity();
|
|
|
|
delete window;
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPositionWhenEmbedded_data()
|
|
{
|
|
QTest::addColumn<bool>("embedded");
|
|
|
|
QTest::newRow("false") << false;
|
|
QTest::newRow("true") << true;
|
|
}
|
|
|
|
void TestDocks::tst_negativeAnchorPositionWhenEmbedded()
|
|
{
|
|
QFETCH(bool, embedded);
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
MainWindowBase *m = nullptr;
|
|
if (embedded) {
|
|
auto em = createEmbeddedMainWindow(QSize(500, 500));
|
|
m = em->mainWindow;
|
|
} else {
|
|
m = createMainWindow(QSize(500, 500), MainWindowOption_HasCentralFrame).release();
|
|
m->resize(QSize(500, 500));
|
|
m->show();
|
|
}
|
|
auto layout = m->multiSplitter();
|
|
|
|
auto w1 = new MyWidget2(QSize(400,400));
|
|
auto w2 = new MyWidget2(QSize(400,400));
|
|
auto d1 = createDockWidget("1", w1);
|
|
auto d2 = createDockWidget("2", w2);
|
|
auto d3 = createDockWidget("3", w2);
|
|
|
|
m->addDockWidget(d1, Location_OnLeft);
|
|
m->addDockWidget(d2, Location_OnLeft);
|
|
m->addDockWidget(d3, Location_OnLeft);
|
|
|
|
layout->checkSanity();
|
|
|
|
delete m->window();
|
|
}
|
|
|
|
void TestDocks::tst_restoreResizesLayout()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(500, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
|
|
LayoutSaver saver;
|
|
QVERIFY(saver.saveToFile("layout_tst_restoreResizesLayout.json"));
|
|
|
|
// Now resize the window, and then restore. The layout should have the new size
|
|
|
|
auto layout = m->multiSplitter();
|
|
m->resize(1050, 1050);
|
|
QCOMPARE(m->size(), QSize(1050, 1050));
|
|
|
|
LayoutSaver restorer(RestoreOption_RelativeToMainWindow);
|
|
QVERIFY(restorer.restoreFromFile("layout_tst_restoreResizesLayout.json"));
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
QCOMPARE(m->dropArea()->QWidgetAdapter::size(), layout->size());
|
|
QVERIFY(layout->checkSanity());
|
|
}
|
|
|
|
void TestDocks::tst_maximumSizePolicy()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto widget = new MyWidget2();
|
|
const int maxHeight = 250;
|
|
widget->setMinimumSize(QSize(200, 200));
|
|
widget->setSizeHint(QSize(250, maxHeight));
|
|
widget->setSizePolicy({QSizePolicy::Preferred, QSizePolicy::Maximum});
|
|
|
|
auto dock1 = createDockWidget("dock1", widget);
|
|
dock1->show();
|
|
dock1->window()->resize(QSize(500, 500));
|
|
auto oldFw = Tests::make_qpointer(dock1->window());
|
|
dock1->close();
|
|
dock1->show();
|
|
auto oldFw2 = dock1->window();
|
|
|
|
|
|
const int tollerance = 50;
|
|
QVERIFY(dock1->window()->height() <= maxHeight + tollerance); // +tollerance as the floating window is a bit bigger, due to margins etc.
|
|
QVERIFY(dock1->height() <= maxHeight);
|
|
|
|
auto m1 = createMainWindow();
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("foo"));
|
|
m1->addDockWidget(dock2, Location_OnTop);
|
|
m1->resize(2000, 3000);
|
|
|
|
// Make the floating window big, and see if the suggested highlight is still small
|
|
dock1->window()->resize(QSize(dock1->width(), 800));
|
|
|
|
{
|
|
WindowBeingDragged wbd1(dock1->floatingWindow());
|
|
const QRect highlightRect = m1->multiSplitter()->rectForDrop(&wbd1, Location_OnBottom, nullptr);
|
|
QVERIFY(highlightRect.height() <= maxHeight + tollerance);
|
|
}
|
|
|
|
// Now drop it, and check too
|
|
m1->addDockWidget(dock1, Location_OnBottom);
|
|
QVERIFY(dock1->height() <= maxHeight);
|
|
|
|
delete oldFw.data();
|
|
delete oldFw2;
|
|
}
|
|
|
|
void TestDocks::tst_minSizeChanges()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(600, 600), MainWindowOption_None);
|
|
m->show();
|
|
auto w1 = new MyWidget2(QSize(400,400));
|
|
auto w2 = new MyWidget2(QSize(400,400));
|
|
|
|
auto d1 = new DockWidgetType("1");
|
|
d1->setWidget(w1);
|
|
auto d2 = new DockWidgetType("2");
|
|
d2->setWidget(w2);
|
|
|
|
m->addDockWidget(d1, Location_OnTop);
|
|
m->addDockWidget(d2, Location_OnTop, nullptr, InitialVisibilityOption::StartHidden);
|
|
auto layout = m->multiSplitter();
|
|
|
|
// 1. d2 is a placeholder, let's change its min size before showing it
|
|
w2->setMinimumSize(QSize(800, 800));
|
|
d2->show();
|
|
|
|
Item *item2 = layout->itemForFrame(d2->dptr()->frame());
|
|
|
|
QVERIFY(layout->checkSanity());
|
|
|
|
Testing::waitForResize(m.get());
|
|
|
|
QVERIFY(item2->width() >= 800);
|
|
QVERIFY(item2->height() >= 800);
|
|
QVERIFY(m->height() >= 1200);
|
|
|
|
// 2. d1 is visible, let's change its min size
|
|
w1->setMinimumSize(QSize(800, 800));
|
|
|
|
Testing::waitForResize(m.get());
|
|
layout->checkSanity();
|
|
|
|
QVERIFY(m->height() >= 1600);
|
|
|
|
// add a small one to the middle
|
|
auto w3 = new MyWidget2(QSize(100,100));
|
|
auto d3 = new DockWidgetType("3");
|
|
d3->setWidget(w3);
|
|
m->addDockWidget(d3, Location_OnTop, d1);
|
|
}
|
|
|
|
void TestDocks::tst_maxSizePropagates()
|
|
{
|
|
// Tests that the DockWidget gets the min and max size of its guest widget
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = new DockWidgetType("dock1");
|
|
|
|
auto w = new MyWidget2(QSize(200, 200));
|
|
w->setMinimumSize(120, 120);
|
|
w->setMaximumSize(500, 500);
|
|
dock1->setWidget(w);
|
|
dock1->show();
|
|
|
|
QCOMPARE(Widget_qwidget::widgetMinSize(dock1), Widget_qwidget::widgetMinSize(w));
|
|
QCOMPARE(dock1->maximumSize(), w->maximumSize());
|
|
|
|
w->setMinimumSize(121, 121);
|
|
w->setMaximumSize(501, 501);
|
|
|
|
Testing::waitForEvent(w, QEvent::LayoutRequest);
|
|
|
|
QCOMPARE(Widget_qwidget::widgetMinSize(dock1), Widget_qwidget::widgetMinSize(w));
|
|
QCOMPARE(dock1->maximumSize(), w->maximumSize());
|
|
|
|
// Now let's see if our Frame also has proper size-constraints
|
|
Frame *frame = dock1->dptr()->frame();
|
|
QCOMPARE(frame->maximumSize().expandedTo(w->maximumSize()), frame->maximumSize());
|
|
|
|
delete dock1->window();
|
|
}
|
|
|
|
void TestDocks::tst_maxSizeHonouredWhenAnotherDropped()
|
|
{
|
|
// dock1 is docked, and has small max-height.
|
|
// When dropping dock2, which is small too, dock2 should occupy all the height except dock1's max-height
|
|
// i.e. dock2 should expand and eat all available space
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None);
|
|
auto dock1 = new DockWidgetType("dock1");
|
|
|
|
auto w = new MyWidget2(QSize(400,400));
|
|
w->setMinimumSize(120, 100);
|
|
w->setMaximumSize(300, 150);
|
|
dock1->setWidget(w);
|
|
m1->addDockWidget(dock1, Location_OnLeft);
|
|
|
|
auto dock2 = new DockWidgetType("dock2");
|
|
m1->addDockWidget(dock2, Location_OnBottom);
|
|
|
|
auto root = m1->multiSplitter()->rootItem();
|
|
Separator *separator = root->separators().constFirst();
|
|
const int min1 = root->minPosForSeparator_global(separator);
|
|
const int max2 = root->maxPosForSeparator_global(separator);
|
|
|
|
QVERIFY(separator->position() >= min1);
|
|
QVERIFY(separator->position() <= max2);
|
|
const int item1MaxHeight = dock1->dptr()->frame()->maxSizeHint().height();
|
|
QVERIFY(dock1->dptr()->frame()->height() <= item1MaxHeight);
|
|
root->dumpLayout();
|
|
QCOMPARE(dock2->dptr()->frame()->height(),
|
|
root->height() - item1MaxHeight - Item::separatorThickness);
|
|
}
|
|
|
|
void TestDocks::tst_addToHiddenMainWindow()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(1000, 1000), MainWindowOption_HasCentralFrame, {}, false);
|
|
auto w1 = new MyWidget2(QSize(400,400));
|
|
auto w2 = new MyWidget2(QSize(400,400));
|
|
auto d1 = createDockWidget("1", w1);
|
|
auto d2 = createDockWidget("2", w2);
|
|
|
|
m->addDockWidget(d1, Location_OnTop);
|
|
m->addDockWidget(d2, Location_OnTop);
|
|
|
|
QVERIFY(!m->isVisible());
|
|
d1->setFloating(true);
|
|
d2->setFloating(false);
|
|
m->layoutWidget()->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_maxSizePropagates2()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None);
|
|
auto dock1 = new DockWidgetType("dock1");
|
|
|
|
auto w = new MyWidget2(QSize(200, 200));
|
|
w->setMinimumSize(120, 120);
|
|
w->setMaximumSize(300, 500);
|
|
dock1->setWidget(w);
|
|
dock1->show();
|
|
|
|
auto dock2 = new DockWidgetType("dock2");
|
|
auto dock3 = new DockWidgetType("dock3");
|
|
auto dock4 = new DockWidgetType("dock4");
|
|
m1->addDockWidget(dock2, Location_OnLeft);
|
|
m1->addDockWidget(dock3, Location_OnRight);
|
|
m1->addDockWidget(dock4, Location_OnBottom, dock3);
|
|
m1->addDockWidget(dock1, Location_OnLeft, dock4);
|
|
|
|
Frame *frame1 = dock1->dptr()->frame();
|
|
|
|
Layouting::ItemBoxContainer *root = m1->multiSplitter()->rootItem();
|
|
Item *item1 = root->itemForWidget(frame1);
|
|
auto vertSep1 = root->separators().constFirst();
|
|
const int min1 = root->minPosForSeparator_global(vertSep1);
|
|
|
|
ItemBoxContainer *container1 = item1->parentBoxContainer();
|
|
auto innerVertSep1 = container1->separators().constFirst();
|
|
const int minInnerSep = container1->minPosForSeparator_global(innerVertSep1);
|
|
const int maxInnerSep = container1->maxPosForSeparator_global(innerVertSep1);
|
|
|
|
root->requestSeparatorMove(vertSep1, -(vertSep1->position() - min1));
|
|
QVERIFY(frame1->width() <= frame1->maxSizeHint().width());
|
|
|
|
container1->requestSeparatorMove(innerVertSep1, -(innerVertSep1->position() - minInnerSep));
|
|
QVERIFY(frame1->width() <= frame1->maxSizeHint().width());
|
|
|
|
container1->requestSeparatorMove(innerVertSep1, maxInnerSep - innerVertSep1->position());
|
|
QVERIFY(frame1->width() <= frame1->maxSizeHint().width());
|
|
}
|
|
|
|
void TestDocks::tst_maxSizeHonouredWhenDropped()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m1 = createMainWindow();
|
|
auto dock1 = new DockWidgetType("dock1");
|
|
auto dock2 = new DockWidgetType("dock2");
|
|
m1->addDockWidget(dock1, Location_OnTop);
|
|
m1->resize(2000, 2000);
|
|
|
|
auto w2 = new MyWidget2(QSize(400,400));
|
|
dock2->setWidget(w2);
|
|
const int maxWidth = 200;
|
|
w2->setMaximumSize(maxWidth, 200);
|
|
m1->addDockWidget(dock2, Location_OnLeft);
|
|
const int droppedWidth = dock2->dptr()->frame()->width();
|
|
QVERIFY(droppedWidth < maxWidth + 50); // +50 to cover any margins and waste by QTabWidget
|
|
|
|
// Try again, but now dropping a multisplitter
|
|
dock2->setFloating(true);
|
|
auto fw = dock2->floatingWindow();
|
|
|
|
m1->dropArea()->drop(fw, Location_OnLeft, nullptr);
|
|
QCOMPARE(dock2->dptr()->frame()->width(), droppedWidth);
|
|
}
|
|
|
|
void TestDocks::tst_fixedSizePolicy()
|
|
{
|
|
// tests that KDDW also takes into account QSizePolicy::Fixed for calculating the max size hint.
|
|
// Since QPushButton for example doesn't set QWidget::maximumSize(), but instead uses sizeHint()
|
|
// + QSizePolicy::Fixed.
|
|
EnsureTopLevelsDeleted e;
|
|
auto button = new QPushButton("one");
|
|
auto dock1 = createDockWidget("dock1", button);
|
|
Frame *frame = dock1->dptr()->frame();
|
|
|
|
// Just a precondition from the test. If QPushButton ever changes, replace with a QWidget and set fixed size policy
|
|
QCOMPARE(button->sizePolicy().verticalPolicy(), QSizePolicy::Fixed);
|
|
|
|
const int buttonMaxHeight = button->sizeHint().height();
|
|
|
|
QCOMPARE(dock1->sizeHint(), button->sizeHint());
|
|
QCOMPARE(dock1->sizePolicy().verticalPolicy(), button->sizePolicy().verticalPolicy());
|
|
QCOMPARE(dock1->sizePolicy().horizontalPolicy(), button->sizePolicy().horizontalPolicy());
|
|
|
|
QCOMPARE(frame->maxSizeHint().height(), qMax(buttonMaxHeight, Layouting::Item::hardcodedMinimumSize.height()));
|
|
|
|
delete dock1->window();
|
|
}
|
|
|
|
#endif
|
|
|
|
void TestDocks::tst_floatingAction()
|
|
{
|
|
// Tests DockWidget::floatAction()
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
{
|
|
// 1. Create a MainWindow with two docked dock-widgets, then float the first one.
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight);
|
|
|
|
auto action = dock1->floatAction();
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!action->isChecked());
|
|
QVERIFY(action->isEnabled());
|
|
QCOMPARE(action->toolTip(), tr("Detach"));
|
|
|
|
action->toggle();
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(action->isChecked());
|
|
QVERIFY(action->isEnabled());
|
|
QCOMPARE(action->toolTip(), tr("Dock"));
|
|
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
|
|
//2. Put it back, via setFloating(). It should return to its place.
|
|
action->toggle();
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!action->isChecked());
|
|
QVERIFY(action->isEnabled());
|
|
QVERIFY(!dock1->isTabbed());
|
|
QCOMPARE(action->toolTip(), tr("Detach"));;
|
|
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
|
|
{
|
|
// 1. Create a MainWindow with one docked dock-widgets, and one floating.
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
m->addDockWidget(dock1, KDDockWidgets::Location_OnLeft);
|
|
|
|
//The floating window action should be disabled as it has no previous place
|
|
auto action = dock2->floatAction();
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(action->isChecked());
|
|
QVERIFY(!action->isEnabled());
|
|
QCOMPARE(action->toolTip(), tr("Dock"));
|
|
|
|
m->addDockWidget(dock2, KDDockWidgets::Location_OnRight);
|
|
|
|
QVERIFY(!dock2->isFloating());
|
|
QVERIFY(!action->isChecked());
|
|
QVERIFY(action->isEnabled());
|
|
QCOMPARE(action->toolTip(), tr("Detach"));
|
|
|
|
action->toggle();
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(action->isChecked());
|
|
QVERIFY(action->isEnabled());
|
|
QCOMPARE(action->toolTip(), tr("Dock"));
|
|
|
|
auto fw = dock2->floatingWindow();
|
|
QVERIFY(fw);
|
|
|
|
//2. Put it back, via setFloating(). It should return to its place.
|
|
action->toggle();
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!action->isChecked());
|
|
QVERIFY(action->isEnabled());
|
|
QVERIFY(!dock1->isTabbed());
|
|
QCOMPARE(action->toolTip(), tr("Detach"));
|
|
|
|
Testing::waitForDeleted(fw);
|
|
}
|
|
{
|
|
// 3. A floating window with two tabs
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
bool dock1IsFloating = dock1->floatAction()->isChecked();
|
|
bool dock2IsFloating = dock2->floatAction()->isChecked();
|
|
|
|
connect(dock1->floatAction(), &QAction::toggled, [&dock1IsFloating] (bool t) {
|
|
Q_ASSERT(dock1IsFloating != t);
|
|
dock1IsFloating = t;
|
|
});
|
|
|
|
connect(dock2->floatAction(), &QAction::toggled, [&dock2IsFloating] (bool t) {
|
|
Q_ASSERT(dock2IsFloating != t);
|
|
dock2IsFloating = t;
|
|
});
|
|
|
|
auto fw2 = dock2->floatingWindow();
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(dock1->floatAction()->isChecked());
|
|
QVERIFY(dock2->floatAction()->isChecked());
|
|
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!dock2->isFloating());
|
|
QVERIFY(!dock1->floatAction()->isChecked());
|
|
QVERIFY(!dock2->floatAction()->isChecked());
|
|
|
|
dock2->setFloating(true);
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock1->floatAction()->isChecked());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(dock2->floatAction()->isChecked());
|
|
|
|
QVERIFY(dock1IsFloating);
|
|
QVERIFY(dock2IsFloating);
|
|
|
|
delete fw2;
|
|
delete dock1->window();
|
|
delete dock2->window();
|
|
}
|
|
|
|
{
|
|
// If the dock widget is alone then it's floating, but we suddenly dock a widget side-by-side
|
|
// to it, then both aren't floating anymore. This test tests if the signal was emitted
|
|
|
|
auto dock1 = createDockWidget("one", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("two", new QPushButton("two"));
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(dock1->floatAction()->isChecked());
|
|
QVERIFY(dock2->floatAction()->isChecked());
|
|
auto oldFw2 = dock2->window();
|
|
|
|
QSignalSpy spy1(dock1->floatAction(), &QAction::toggled);
|
|
QSignalSpy spy2(dock2->floatAction(), &QAction::toggled);
|
|
|
|
QSignalSpy spy11(dock1, &DockWidgetBase::isFloatingChanged);
|
|
QSignalSpy spy21(dock2, &DockWidgetBase::isFloatingChanged);
|
|
|
|
dock1->addDockWidgetToContainingWindow(dock2, Location_OnRight);
|
|
|
|
QCOMPARE(spy1.count(), 1);
|
|
QCOMPARE(spy2.count(), 1);
|
|
QCOMPARE(spy11.count(), 1);
|
|
QCOMPARE(spy21.count(), 1);
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!dock2->isFloating());
|
|
|
|
QVERIFY(!dock2->floatAction()->isChecked());
|
|
QVERIFY(!dock1->floatAction()->isChecked());
|
|
|
|
delete dock1->window();
|
|
delete oldFw2->window();
|
|
}
|
|
|
|
{
|
|
// Like before, but now we use addMultiSplitter()
|
|
|
|
auto dock1 = createDockWidget("one", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("two", new QPushButton("two"));
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(dock1->floatAction()->isChecked());
|
|
QVERIFY(dock2->floatAction()->isChecked());
|
|
auto oldFw2 = dock2->floatingWindow();
|
|
|
|
QSignalSpy spy1(dock1->floatAction(), &QAction::toggled);
|
|
QSignalSpy spy2(dock2->floatAction(), &QAction::toggled);
|
|
|
|
QSignalSpy spy11(dock1, &DockWidgetBase::isFloatingChanged);
|
|
QSignalSpy spy21(dock2, &DockWidgetBase::isFloatingChanged);
|
|
|
|
auto dropArea1 = dock1->floatingWindow()->dropArea();
|
|
dropArea1->drop(oldFw2, Location_OnRight, nullptr);
|
|
|
|
QCOMPARE(spy1.count(), 1);
|
|
QCOMPARE(spy2.count(), 1);
|
|
QCOMPARE(spy11.count(), 1);
|
|
QCOMPARE(spy21.count(), 1);
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!dock2->isFloating());
|
|
QVERIFY(!dock2->floatAction()->isChecked());
|
|
QVERIFY(!dock1->floatAction()->isChecked());
|
|
|
|
// Let's now remove dock1, dock2 should be floating
|
|
dock1->setFloating(true);
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(dock2->floatAction()->isChecked());
|
|
QVERIFY(dock1->floatAction()->isChecked());
|
|
|
|
delete dock1->window();
|
|
delete dock2->window();
|
|
delete oldFw2->window();
|
|
}
|
|
|
|
{
|
|
// Same test as before, but now tab instead of side-by-side
|
|
auto dock1 = createDockWidget("one", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("two", new QPushButton("two"));
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(dock1->floatAction()->isChecked());
|
|
QVERIFY(dock2->floatAction()->isChecked());
|
|
auto oldFw2 = dock2->window();
|
|
|
|
QSignalSpy spy1(dock1->floatAction(), &QAction::toggled);
|
|
QSignalSpy spy2(dock2->floatAction(), &QAction::toggled);
|
|
QSignalSpy spy11(dock1, &DockWidgetBase::isFloatingChanged);
|
|
QSignalSpy spy21(dock2, &DockWidgetBase::isFloatingChanged);
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
QCOMPARE(spy1.count(), 1);
|
|
|
|
// On earlier Qt versions this is flaky, but technically correct.
|
|
// Windows can get hidden while being reparented and floating changes momentarily.
|
|
QVERIFY(spy2.count() == 1 || spy2.count() == 3);
|
|
QVERIFY(spy21.count() == 1 || spy21.count() == 3);
|
|
QCOMPARE(spy11.count(), 1);
|
|
|
|
QVERIFY(!dock1->isFloating());
|
|
QVERIFY(!dock2->isFloating());
|
|
|
|
QVERIFY(!dock2->floatAction()->isChecked());
|
|
QVERIFY(!dock1->floatAction()->isChecked());
|
|
|
|
delete dock1->window();
|
|
delete oldFw2->window();
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_raise()
|
|
{
|
|
// Tests DockWidget::raise();
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto dock1 = createDockWidget("1");
|
|
auto dock2 = createDockWidget("2");
|
|
auto fw2 = Tests::make_qpointer(dock2->window());
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
dock1->setAsCurrentTab();
|
|
QVERIFY(dock1->isCurrentTab());
|
|
QVERIFY(!dock2->isCurrentTab());
|
|
dock2->raise();
|
|
QVERIFY(!dock1->isCurrentTab());
|
|
QVERIFY(dock2->isCurrentTab());
|
|
|
|
if (qApp->platformName() != QLatin1String("offscreen")) { // offscreen qpa doesn't seem to keep Window Z.
|
|
auto dock3 = createDockWidget("3");
|
|
dock3->window()->setGeometry(dock1->window()->geometry());
|
|
dock3->window()->setObjectName("3");
|
|
dock1->window()->setObjectName("1");
|
|
dock3->raise();
|
|
QTest::qWait(200);
|
|
|
|
if (qApp->QGuiApplication::topLevelAt(dock3->window()->geometry().topLeft() + QPoint(50, 50)) != dock3->windowHandle()) {
|
|
qDebug() << "Failing before raise" << qApp->widgetAt(dock3->window()->geometry().topLeft() + QPoint(50, 50))->window() << dock3->window()
|
|
<< dock1->window()->geometry() << dock3->window()->geometry();
|
|
QVERIFY(false);
|
|
}
|
|
|
|
dock1->raise();
|
|
QTest::qWait(200);
|
|
QVERIFY(dock1->isCurrentTab());
|
|
|
|
if (qApp->QGuiApplication::topLevelAt(dock3->window()->geometry().topLeft() + QPoint(50, 50)) != dock1->windowHandle()) {
|
|
qDebug() << "Failing after raise" << qApp->widgetAt(dock3->window()->geometry().topLeft() + QPoint(50, 50))->window() << dock1->window()
|
|
<< dock1->window()->geometry() << dock3->window()->geometry();
|
|
QVERIFY(false);
|
|
}
|
|
|
|
delete dock3->window();
|
|
}
|
|
|
|
delete fw2;
|
|
delete dock1->window();
|
|
}
|
|
|
|
void TestDocks::tst_invalidLayoutAfterRestore()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"));
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
// Stack 1, 2, 3
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
m->addDockWidget(dock3, Location_OnRight);
|
|
|
|
const int oldContentsWidth = layout->width();
|
|
|
|
auto f1 = dock1->dptr()->frame();
|
|
dock3->close();
|
|
dock2->close();
|
|
dock1->close();
|
|
QVERIFY(Testing::waitForDeleted(f1));
|
|
|
|
dock3->show();
|
|
dock2->show();
|
|
dock1->show();
|
|
Testing::waitForEvent(m.get(), QEvent::LayoutRequest); // So MainWindow min size is updated
|
|
|
|
Item *item1 = layout->itemForFrame(dock1->dptr()->frame());
|
|
Item *item3 = layout->itemForFrame(dock3->dptr()->frame());
|
|
Item *item4 = dropArea->centralFrame();
|
|
|
|
QCOMPARE(layout->count(), 4);
|
|
QCOMPARE(layout->placeholderCount(), 0);
|
|
|
|
// Detach dock2
|
|
QPointer<Frame> f2 = dock2->dptr()->frame();
|
|
f2->detachTab(dock2);
|
|
QVERIFY(!f2.data());
|
|
QTest::qWait(200); // Not sure why. Some event we're waiting for. TODO: Investigate
|
|
auto fw2 = dock2->floatingWindow();
|
|
QCOMPARE(layout->minimumSize().width(), 2*Item::separatorThickness + item1->minSize().width() + item3->minSize().width() + item4->minSize().width());
|
|
|
|
// Drop left of dock3
|
|
layout->addWidget(fw2->dropArea(), Location_OnLeft, dock3->dptr()->frame());
|
|
|
|
QVERIFY(Testing::waitForDeleted(fw2));
|
|
QCOMPARE(layout->width(), oldContentsWidth);
|
|
layout->checkSanity();
|
|
}
|
|
|
|
void TestDocks::tst_dontCloseDockWidgetBeforeRestore()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, DockWidgetBase::LayoutSaverOption::Skip);
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"), {}, DockWidgetBase::LayoutSaverOption::Skip);
|
|
auto dock4 = createDockWidget("4", new QPushButton("4"), {}, {}, /*show=*/ false);
|
|
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
m->addDockWidget(dock2, Location_OnBottom);
|
|
|
|
// Dock #3 floats, while #1 and #2 are docked.
|
|
dock3->setFloating(true);
|
|
QVERIFY(dock3->isOpen());
|
|
QVERIFY(dock3->isFloating());
|
|
QVERIFY(!dock3->isInMainWindow());
|
|
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
dock3->close();
|
|
|
|
// Not open anymore
|
|
QVERIFY(!dock3->isOpen());
|
|
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
|
|
// #3 is still closed, the restore will skip it
|
|
QVERIFY(!dock3->isOpen());
|
|
QVERIFY(!dock3->isInMainWindow());
|
|
|
|
auto dock5 = createDockWidget("5", new QPushButton("5"), {}, DockWidgetBase::LayoutSaverOption::Skip);
|
|
|
|
dock4->show();
|
|
dock5->show();
|
|
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QVERIFY(!dock4->isOpen());
|
|
QVERIFY(dock5->isOpen()); // #5 is still open, it ignored restore
|
|
}
|
|
|
|
void TestDocks::tst_dontCloseDockWidgetBeforeRestore2()
|
|
{
|
|
// In this case we have a floating window with two dock widgets tabbed, both having LayoutSaverOption::Skip
|
|
// Meaning the whole window should be skipped
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, DockWidgetBase::LayoutSaverOption::Skip);
|
|
auto dock3 = createDockWidget("dock3", new QPushButton("three"), {}, DockWidgetBase::LayoutSaverOption::Skip);
|
|
|
|
dock2->close();
|
|
dock3->close();
|
|
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout(); // This layout has 0 docks visible
|
|
|
|
dock2->show();
|
|
dock3->show();
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QVERIFY(dock2->isVisible()); // They're still visible
|
|
QVERIFY(dock3->isVisible());
|
|
|
|
// Now tab and restore again
|
|
dock2->addDockWidgetAsTab(dock3);
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QVERIFY(dock2->isOpen());
|
|
QVERIFY(dock3->isOpen());
|
|
QVERIFY(dock3->isVisible());
|
|
QCOMPARE(dock3->dptr()->frame(), dock2->dptr()->frame());
|
|
}
|
|
|
|
void TestDocks::tst_dontCloseDockWidgetBeforeRestore3()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, DockWidgetBase::LayoutSaverOption::Skip);
|
|
dock1->close();
|
|
dock2->close();
|
|
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout(); // This layout has 0 docks visible
|
|
|
|
m->addDockWidget(dock1, Location_OnBottom);
|
|
m->addDockWidget(dock2, Location_OnBottom);
|
|
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
|
|
QVERIFY(!dock1->isOpen()); // Gets closed by the restore
|
|
QVERIFY(dock2->isOpen()); // Dock2 remains open, it ignores restore
|
|
QVERIFY(dock2->isFloating());
|
|
}
|
|
|
|
void TestDocks::tst_closeOnlyCurrentTab()
|
|
{
|
|
{
|
|
// Case of a floating window with tabs
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_CloseOnlyCurrentTab);
|
|
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
|
|
/// Floating window with 3 tabs
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
dock1->addDockWidgetAsTab(dock3);
|
|
|
|
TitleBar *tb = dock1->titleBar();
|
|
QVERIFY(tb->isVisible());
|
|
dock1->setAsCurrentTab();
|
|
Frame *frame = dock1->dptr()->frame();
|
|
QCOMPARE(frame->currentIndex(), 0);
|
|
|
|
tb->onCloseClicked();
|
|
|
|
QVERIFY(!dock1->isOpen());
|
|
QVERIFY(dock2->isOpen());
|
|
QVERIFY(dock3->isOpen());
|
|
}
|
|
|
|
{
|
|
// Case of a floating window with tabs
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_CloseOnlyCurrentTab);
|
|
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
|
|
dock2->addDockWidgetAsTab(dock3);
|
|
Frame *frame = dock2->dptr()->frame();
|
|
QCOMPARE(frame->currentIndex(), 1);
|
|
TitleBar *tb = frame->titleBar();
|
|
QVERIFY(tb->isVisible());
|
|
tb->onCloseClicked();
|
|
|
|
QVERIFY(!dock3->isOpen());
|
|
QVERIFY(dock2->isOpen());
|
|
QVERIFY(dock1->isOpen());
|
|
QCOMPARE(frame->dockWidgetCount(), 1);
|
|
}
|
|
|
|
}
|
|
|
|
void TestDocks::tst_tabWidgetCurrentIndex()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
auto dock3 = createDockWidget("3", new QPushButton("3"));
|
|
auto fw2 = dock2->window();
|
|
auto fw3 = dock3->window();
|
|
|
|
DockWidgetBase *currentDw = nullptr;
|
|
auto frame = dock1->dptr()->frame();
|
|
connect(frame, &Frame::currentDockWidgetChanged, this, [¤tDw] (DockWidgetBase *dw){
|
|
currentDw = dw;
|
|
});
|
|
|
|
QCOMPARE(frame->tabWidget()->currentIndex(), 0);
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
QCOMPARE(frame->tabWidget()->currentIndex(), 1);
|
|
QCOMPARE(frame->currentDockWidget(), currentDw);
|
|
QCOMPARE(dock2, currentDw);
|
|
|
|
dock2->close();
|
|
|
|
QCOMPARE(frame->tabWidget()->currentIndex(), 0);
|
|
QCOMPARE(dock1, currentDw);
|
|
|
|
delete fw2;
|
|
delete fw3;
|
|
delete dock2;
|
|
delete dock1->window();
|
|
}
|
|
|
|
void TestDocks::tst_doubleClickTabToDetach()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
|
|
auto fw2 = dock2->window();
|
|
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
auto frame = dock1->dptr()->frame();
|
|
QCOMPARE(frame->currentIndex(), 1);
|
|
|
|
auto tb = frame->tabWidget()->asWidget();
|
|
|
|
Tests::doubleClickOn(tb->mapToGlobal(QPoint(20, 20)), frame->window()->windowHandle());
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
QVERIFY(dock1->floatingWindow() != dock2->floatingWindow());
|
|
|
|
delete fw2;
|
|
delete dock1->window();
|
|
delete dock2->window();
|
|
}
|
|
|
|
void TestDocks::tst_addingOptionHiddenTabbed()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 1);
|
|
dock1->addDockWidgetAsTab(dock2, InitialVisibilityOption::StartHidden);
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 1);
|
|
dock2->show();
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 2);
|
|
|
|
QVERIFY(dock1->dptr()->frame() == dock2->dptr()->frame());
|
|
}
|
|
|
|
void TestDocks::tst_flagDoubleClick()
|
|
{
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_DoubleClickMaximizes);
|
|
auto m = createMainWindow(QSize(500, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
auto dock2 = createDockWidget("2", new QPushButton("2"));
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
FloatingWindow *fw2 = dock2->floatingWindow();
|
|
QVERIFY(!fw2->isMaximized());
|
|
TitleBar *t2 = dock2->titleBar();
|
|
QPoint pos = t2->mapToGlobal(QPoint(5, 5));
|
|
Tests::doubleClickOn(pos, t2);
|
|
QVERIFY(fw2->isMaximized());
|
|
delete fw2;
|
|
|
|
TitleBar *t1 = dock1->titleBar();
|
|
QVERIFY(!t1->isFloating());
|
|
pos = t1->mapToGlobal(QPoint(5, 5));
|
|
Tests::doubleClickOn(pos, t1);
|
|
QVERIFY(t1->isFloating());
|
|
QVERIFY(!dock1->window()->isMaximized());
|
|
delete dock1->window();
|
|
}
|
|
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(500, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("1", new QPushButton("1"));
|
|
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
|
|
TitleBar *t1 = dock1->titleBar();
|
|
QVERIFY(!t1->isFloating());
|
|
QPoint pos = t1->mapToGlobal(QPoint(5, 5));
|
|
Tests::doubleClickOn(pos, t1);
|
|
QVERIFY(t1->isFloating());
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(!dock1->window()->isMaximized());
|
|
|
|
pos = t1->mapToGlobal(QPoint(5, 5));
|
|
Tests::doubleClickOn(pos, t1);
|
|
QVERIFY(!dock1->isFloating());
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_maxSizedFloatingWindow()
|
|
{
|
|
// Tests that FloatingWindows get a proper max-size, if its dock widget has one
|
|
EnsureTopLevelsDeleted e;
|
|
auto dock1 = new DockWidgetType("dock1");
|
|
auto dock2 = new DockWidgetType("dock2");
|
|
auto w = new MyWidget("foo");
|
|
w->setMinimumSize(120, 100);
|
|
w->setMaximumSize(300, 300);
|
|
dock1->setWidget(w);
|
|
|
|
dock1->show();
|
|
dock2->show();
|
|
|
|
auto window1 = dock1->window();
|
|
auto window2 = dock2->window();
|
|
Testing::waitForEvent(window1, QEvent::LayoutRequest);
|
|
|
|
QVERIFY(window1->maximumSize().width() < 500);
|
|
QVERIFY(window1->maximumSize().height() < 500);
|
|
QVERIFY(window2->maximumSize().width() > 500);
|
|
QVERIFY(window2->maximumSize().height() > 500);
|
|
|
|
auto hasMax = [window1] {
|
|
const QSize max = window1->maximumSize();
|
|
return max.width() < 500 && max.height() < 500;
|
|
};
|
|
|
|
// Adding side-by-side, we don't honour max size (yet)
|
|
dock1->addDockWidgetToContainingWindow(dock2, Location_OnBottom);
|
|
Testing::waitForEvent(window1, QEvent::LayoutRequest);
|
|
QVERIFY(window1->maximumSize().width() > 500);
|
|
QVERIFY(window1->maximumSize().height() > 500);
|
|
|
|
// Close dw2, we have a single dock widget again, we honour max-size
|
|
dock2->close();
|
|
Testing::waitForEvent(window1, QEvent::LayoutRequest);
|
|
QVERIFY(hasMax());
|
|
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
Testing::waitForEvent(window1, QEvent::LayoutRequest);
|
|
QVERIFY(!hasMax());
|
|
|
|
dock2->close();
|
|
Testing::waitForEvent(window1, QEvent::LayoutRequest);
|
|
QVERIFY(hasMax());
|
|
}
|
|
|
|
void TestDocks::tst_maxSizedHonouredAfterRemoved()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None);
|
|
auto dock1 = new DockWidgetType("dock1");
|
|
dock1->show();
|
|
|
|
auto w = new MyWidget("foo");
|
|
w->setMinimumSize(120, 100);
|
|
w->setMaximumSize(300, 150);
|
|
dock1->setWidget(w);
|
|
m1->dropArea()->addMultiSplitter(dock1->floatingWindow()->multiSplitter(), Location_OnLeft);
|
|
|
|
auto dock2 = new DockWidgetType("dock2");
|
|
dock2->show();
|
|
m1->dropArea()->addMultiSplitter(dock2->floatingWindow()->multiSplitter(), Location_OnTop);
|
|
|
|
auto root = m1->multiSplitter()->rootItem();
|
|
|
|
// Wait 1 event loop so we get layout invalidated and get max-size constraints
|
|
QTest::qWait(10);
|
|
|
|
auto sep = root->separators().constFirst();
|
|
root->requestEqualSize(sep); // Since we're not calling honourMaxSizes() after a widget changes its max size afterwards yet
|
|
const int sepMin = root->minPosForSeparator_global(sep);
|
|
const int sepMax = root->maxPosForSeparator_global(sep);
|
|
|
|
QVERIFY(sep->position() >= sepMin);
|
|
QVERIFY(sep->position() <= sepMax);
|
|
|
|
auto dock3 = new DockWidgetType("dock3");
|
|
dock3->show();
|
|
m1->dropArea()->addMultiSplitter(dock3->floatingWindow()->multiSplitter(), Location_OnBottom);
|
|
|
|
dock1->setFloating(true);
|
|
m1->dropArea()->addMultiSplitter(dock1->floatingWindow()->multiSplitter(), Location_OnBottom,
|
|
dock2->dptr()->frame());
|
|
|
|
// Close dock2 and check if dock1's max-size is still honoured
|
|
dock2->close();
|
|
QTest::qWait(100); // wait for the resize, so dock1 gets taller"
|
|
|
|
QVERIFY(dock1->dptr()->frame()->height() <= dock1->dptr()->frame()->maxSizeHint().height());
|
|
delete dock2;
|
|
}
|
|
|
|
void TestDocks::tst_addDockWidgetAsTabToDockWidget()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
{
|
|
// Dock into a non-morphed floating dock widget
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
auto window1 = dock1->window();
|
|
auto window2 = dock2->window();
|
|
QCOMPARE(window1, window2);
|
|
QCOMPARE(dock1->dptr()->frame(), dock2->dptr()->frame());
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 2);
|
|
dock1->deleteLater();
|
|
dock2->deleteLater();
|
|
Testing::waitForDeleted(dock2);
|
|
}
|
|
{
|
|
// Dock into a morphed dock widget
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
dock1->dptr()->morphIntoFloatingWindow();
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
auto window1 = dock1->window();
|
|
auto window2 = dock2->window();
|
|
QCOMPARE(window1, window2);
|
|
QCOMPARE(dock1->dptr()->frame(), dock2->dptr()->frame());
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 2);
|
|
dock1->deleteLater();
|
|
dock2->deleteLater();
|
|
Testing::waitForDeleted(dock2);
|
|
}
|
|
{
|
|
// Dock a morphed dock widget into a morphed dock widget
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
dock1->dptr()->morphIntoFloatingWindow();
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
dock2->dptr()->morphIntoFloatingWindow();
|
|
auto originalWindow2 = Tests::make_qpointer(dock2->window());
|
|
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
|
|
auto window1 = dock1->window();
|
|
auto window2 = dock2->window();
|
|
QCOMPARE(window1, window2);
|
|
QCOMPARE(dock1->dptr()->frame(), dock2->dptr()->frame());
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 2);
|
|
Testing::waitForDeleted(originalWindow2);
|
|
QVERIFY(!originalWindow2);
|
|
dock1->deleteLater();
|
|
dock2->deleteLater();
|
|
Testing::waitForDeleted(dock2);
|
|
}
|
|
{
|
|
// Dock to an already docked widget
|
|
auto m = createMainWindow();
|
|
auto dropArea = m->dropArea();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
nestDockWidget(dock1, dropArea, nullptr, KDDockWidgets::Location_OnLeft);
|
|
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
dock1->addDockWidgetAsTab(dock2);
|
|
QCOMPARE(dock1->window(), m.get());
|
|
QCOMPARE(dock2->window(), m.get());
|
|
QCOMPARE(dock1->dptr()->frame(), dock2->dptr()->frame());
|
|
QCOMPARE(dock1->dptr()->frame()->dockWidgetCount(), 2);
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_close()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
// 1.0 Call QWidget::close() on QDockWidget
|
|
auto dock1 = createDockWidget("doc1", Qt::green);
|
|
QAction *toggleAction = dock1->toggleAction();
|
|
QVERIFY(toggleAction->isChecked());
|
|
|
|
QVERIFY(dock1->close());
|
|
|
|
QVERIFY(!dock1->isVisible());
|
|
QVERIFY(!dock1->window()->isVisible());
|
|
QCOMPARE(dock1->window(), dock1);
|
|
QVERIFY(!toggleAction->isChecked());
|
|
|
|
// 1.1 Reshow with show()
|
|
dock1->show();
|
|
auto fw = dock1->floatingWindow();
|
|
QVERIFY(fw);
|
|
QVERIFY(toggleAction->isChecked());
|
|
QVERIFY(dock1->isVisible());
|
|
QCOMPARE(dock1->window(), fw);
|
|
QVERIFY(toggleAction->isChecked());
|
|
|
|
// 1.2 Reshow with toggleAction instead
|
|
QVERIFY(dock1->close());
|
|
QVERIFY(!toggleAction->isChecked());
|
|
QVERIFY(!dock1->isVisible());
|
|
toggleAction->setChecked(true);
|
|
QVERIFY(dock1->isVisible());
|
|
|
|
// 1.3 Use hide() instead
|
|
auto fw1 = dock1->floatingWindow();
|
|
QVERIFY(fw1);
|
|
|
|
dock1->close(); // TODO: Hide doesn't delete the FloatingWindow
|
|
|
|
QVERIFY(Testing::waitForDeleted(fw1));
|
|
QVERIFY(!dock1->isVisible());
|
|
QVERIFY(!dock1->window()->isVisible());
|
|
QCOMPARE(dock1->window(), dock1);
|
|
QVERIFY(!toggleAction->isChecked());
|
|
|
|
// 1.4 close a FloatingWindow, via DockWidget::close
|
|
QPointer<FloatingWindow> window = dock1->dptr()->morphIntoFloatingWindow();
|
|
QPointer<Frame> frame1 = dock1->dptr()->frame();
|
|
QVERIFY(dock1->isVisible());
|
|
QVERIFY(dock1->window()->isVisible());
|
|
QVERIFY(frame1->QWidgetAdapter::isVisible());
|
|
QCOMPARE(dock1->window(), window.data());
|
|
|
|
QVERIFY(dock1->close());
|
|
QVERIFY(!dock1->dptr()->frame());
|
|
QVERIFY(Testing::waitForDeleted(frame1));
|
|
QVERIFY(Testing::waitForDeleted(window));
|
|
|
|
// 1.5 close a FloatingWindow, via FloatingWindow::close
|
|
dock1->show();
|
|
|
|
window = dock1->dptr()->morphIntoFloatingWindow();
|
|
frame1 = dock1->dptr()->frame();
|
|
QVERIFY(dock1->isVisible());
|
|
QVERIFY(dock1->window()->isVisible());
|
|
QVERIFY(frame1->QWidgetAdapter::isVisible());
|
|
QCOMPARE(dock1->window(), window.data());
|
|
|
|
QVERIFY(window->close());
|
|
|
|
QVERIFY(!dock1->dptr()->frame());
|
|
QVERIFY(Testing::waitForDeleted(frame1));
|
|
QVERIFY(Testing::waitForDeleted(window));
|
|
|
|
// TODO: 1.6 Test FloatingWindow with two frames
|
|
// TODO: 1.7 Test Frame with two tabs
|
|
|
|
// 1.8 Check if space is reclaimed after closing left dock
|
|
DockWidgetBase *centralDock;
|
|
DockWidgetBase *leftDock;
|
|
DockWidgetBase *rightDock;
|
|
|
|
auto mainwindow = createSimpleNestedMainWindow(¢ralDock, &leftDock, &rightDock);
|
|
auto da = mainwindow->dropArea();
|
|
|
|
QVERIFY(da->checkSanity());
|
|
QCOMPARE(leftDock->dptr()->frame()->QWidgetAdapter::x(), 0);
|
|
|
|
QCOMPARE(centralDock->dptr()->frame()->QWidgetAdapter::x(),
|
|
leftDock->dptr()->frame()->QWidgetAdapter::geometry().right()
|
|
+ Item::separatorThickness + 1);
|
|
QCOMPARE(rightDock->dptr()->frame()->QWidgetAdapter::x(),
|
|
centralDock->dptr()->frame()->QWidgetAdapter::geometry().right()
|
|
+ Item::separatorThickness + 1);
|
|
leftDock->close();
|
|
QTest::qWait(250); // TODO: wait for some signal
|
|
QCOMPARE(centralDock->dptr()->frame()->QWidgetAdapter::x(), 0);
|
|
QCOMPARE(rightDock->dptr()->frame()->QWidgetAdapter::x(),
|
|
centralDock->dptr()->frame()->QWidgetAdapter::geometry().right()
|
|
+ Item::separatorThickness + 1);
|
|
|
|
rightDock->close();
|
|
QTest::qWait(250); // TODO: wait for some signal
|
|
QMargins margins = mainwindow->centerWidgetMargins();
|
|
QCOMPARE(centralDock->dptr()->frame()->width(),
|
|
mainwindow->width() - 0 * 2 - margins.left() - margins.right());
|
|
delete leftDock; delete rightDock; delete centralDock;
|
|
|
|
// 1.9 Close tabbed dock, side docks will maintain their position
|
|
mainwindow = createSimpleNestedMainWindow(¢ralDock, &leftDock, &rightDock);
|
|
const int leftX = leftDock->dptr()->frame()->QWidgetAdapter::x();
|
|
const int rightX = rightDock->dptr()->frame()->QWidgetAdapter::x();
|
|
|
|
centralDock->close();
|
|
|
|
QCOMPARE(leftDock->dptr()->frame()->QWidgetAdapter::x(), leftX);
|
|
QCOMPARE(rightDock->dptr()->frame()->QWidgetAdapter::x(), rightX);
|
|
delete leftDock; delete rightDock; delete centralDock;
|
|
delete dock1;
|
|
|
|
|
|
// 2. Test that closing the single frame of a main window doesn't close the main window itself
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
|
|
QPointer<MainWindowBase> mainWindowPtr = m.get();
|
|
dock1 = createDockWidget("hello", Qt::green);
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
|
|
// 2.2 Closing should not close the main window
|
|
dock1->close();
|
|
QVERIFY(mainWindowPtr.data());
|
|
delete dock1;
|
|
}
|
|
|
|
// 2.1 Test closing the frame instead
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
|
|
QPointer<MainWindowBase> mainWindowPtr = m.get();
|
|
dock1 = createDockWidget("hello", Qt::green);
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
|
|
// 2.2 Closing should not close the main window
|
|
dock1->dptr()->frame()->titleBar()->onCloseClicked();
|
|
QVERIFY(mainWindowPtr.data());
|
|
QVERIFY(mainWindowPtr->isVisible());
|
|
delete dock1;
|
|
}
|
|
|
|
// 2.2 Repeat, but with a central frame
|
|
{
|
|
auto m = createMainWindow(QSize(800, 500));
|
|
QPointer<MainWindowBase> mainWindowPtr = m.get();
|
|
dock1 = createDockWidget("hello", Qt::green);
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
|
|
// 2.2 Closing should not close the main window
|
|
dock1->dptr()->frame()->titleBar()->onCloseClicked();
|
|
QVERIFY(mainWindowPtr.data());
|
|
QVERIFY(mainWindowPtr->isVisible());
|
|
delete dock1;
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_propagateSizeHonoursMinSize()
|
|
{
|
|
// Here we dock a widget on the left size, and on the right side.
|
|
// When docking the second one, the 1st one shouldn't be squeezed too much, as it has a min size
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
auto m = createMainWindow();
|
|
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
|
|
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
|
|
auto dropArea = m->dropArea();
|
|
int min1 = widgetMinLength(dock1, Qt::Horizontal);
|
|
int min2 = widgetMinLength(dock2, Qt::Horizontal);
|
|
|
|
QVERIFY(dock1->width() >= min1);
|
|
QVERIFY(dock2->width() >= min2);
|
|
|
|
nestDockWidget(dock1, dropArea, nullptr, KDDockWidgets::Location_OnRight);
|
|
nestDockWidget(dock2, dropArea, nullptr, KDDockWidgets::Location_OnLeft);
|
|
|
|
// Calculate again, as the window frame has disappeared
|
|
min1 = widgetMinLength(dock1, Qt::Horizontal);
|
|
min2 = widgetMinLength(dock2, Qt::Horizontal);
|
|
|
|
auto l = m->dropArea();
|
|
l->checkSanity();
|
|
|
|
if (dock1->width() < min1) {
|
|
qDebug() << "\ndock1->width()=" << dock1->width() << "\nmin1=" << min1
|
|
<< "\ndock min sizes=" << dock1->minimumWidth() << dock1->minimumSizeHint().width()
|
|
<< "\nframe1->width()=" << dock1->dptr()->frame()->width()
|
|
<< "\nframe1->min=" << widgetMinLength(dock1->dptr()->frame(), Qt::Horizontal);
|
|
l->dumpLayout();
|
|
QVERIFY(false);
|
|
}
|
|
|
|
QVERIFY(dock2->width() >= min2);
|
|
|
|
// Dock on top of center widget:
|
|
m = createMainWindow();
|
|
|
|
dock1 = createDockWidget("one", new QTextEdit());
|
|
m->addDockWidgetAsTab(dock1);
|
|
auto dock3 = createDockWidget("three", new QTextEdit());
|
|
m->addDockWidget(dock3, Location_OnTop);
|
|
QVERIFY(m->dropArea()->checkSanity());
|
|
|
|
min1 = widgetMinLength(dock1, Qt::Vertical);
|
|
QVERIFY(dock1->height() >= min1);
|
|
}
|
|
|
|
void TestDocks::tst_constraintsPropagateUp()
|
|
{
|
|
// Mostly for QtQuick, which doesn't have any layouts, so we need to make the propagation
|
|
// Manually in DockWidgetQuick::minimumSize(), in FrameQuick, etc.
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
const int minWidth = 500;
|
|
const int minHeight = 400;
|
|
const QSize minSz = { minWidth, minHeight };
|
|
auto guestWidget = new MyWidget2(QSize(minWidth, minHeight));
|
|
auto dock1 = createDockWidget("dock1", guestWidget);
|
|
auto dock2= createDockWidget("dock2", new MyWidget2(QSize(minWidth, minHeight)));
|
|
|
|
QCOMPARE(widgetMinLength(guestWidget, Qt::Vertical), minHeight);
|
|
QCOMPARE(widgetMinLength(guestWidget, Qt::Horizontal), minWidth);
|
|
QCOMPARE(dock1->minimumWidth(), minWidth);
|
|
QCOMPARE(dock1->minimumHeight(), minHeight);
|
|
QCOMPARE(dock1->minimumSize(), minSz);
|
|
|
|
auto frame1 = dock1->dptr()->frame();
|
|
|
|
QVERIFY(qAbs(widgetMinLength(frame1, Qt::Horizontal) - minWidth) < 10); //10px for styling differences
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
|
// Flaky with 5.9
|
|
QVERIFY(qAbs(widgetMinLength(frame1, Qt::Vertical) - (minHeight + frame1->nonContentsHeight())) < 10); //10px for styling differences
|
|
#endif
|
|
|
|
// Add dock2 side-by side, so the Frame now has a title bar.
|
|
auto oldFw2 = dock2->window();
|
|
dock1->addDockWidgetToContainingWindow(dock2, Location_OnLeft);
|
|
TitleBar *tb = dock1->titleBar();
|
|
QVERIFY(tb->isVisible());
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
|
// Flaky with 5.9
|
|
QVERIFY(qAbs(widgetMinLength(frame1, Qt::Vertical) - (minHeight + frame1->nonContentsHeight())) < 10);
|
|
#endif
|
|
delete dock1->window();
|
|
delete oldFw2;
|
|
}
|
|
|
|
void TestDocks::tst_constraintsAfterPlaceholder()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(500, 500), MainWindowOption_None);
|
|
const int minHeight = 400;
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(400, minHeight)));
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(400, minHeight)));
|
|
auto dock3 = createDockWidget("dock3", new MyWidget2(QSize(400, minHeight)));
|
|
auto dropArea = m->dropArea();
|
|
MultiSplitter *layout = dropArea;
|
|
|
|
// Stack 3, 2, 1
|
|
m->addDockWidget(dock1, Location_OnTop);
|
|
m->addDockWidget(dock2, Location_OnTop);
|
|
m->addDockWidget(dock3, Location_OnTop);
|
|
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
QVERIFY(Testing::waitForResize(m.get()));
|
|
#endif
|
|
|
|
QVERIFY(widgetMinLength(m.get(), Qt::Vertical) > minHeight * 3); // > since some vertical space is occupied by the separators
|
|
|
|
// Now close dock1 and check again
|
|
dock1->close();
|
|
Testing::waitForResize(dock2);
|
|
|
|
Item *item2 = layout->itemForFrame(dock2->dptr()->frame());
|
|
Item *item3 = layout->itemForFrame(dock3->dptr()->frame());
|
|
|
|
QMargins margins = m->centerWidgetMargins();
|
|
const int expectedMinHeight = item2->minLength(Qt::Vertical) +
|
|
item3->minLength(Qt::Vertical) +
|
|
1 * Item::separatorThickness
|
|
+ margins.top() + margins.bottom();
|
|
|
|
QCOMPARE(m->minimumSizeHint().height(), expectedMinHeight);
|
|
|
|
dock1->deleteLater();
|
|
Testing::waitForDeleted(dock1);
|
|
}
|
|
|
|
void TestDocks::tst_dragBySingleTab()
|
|
{
|
|
// Tests dragging via a tab when there's only 1 tab, and we're using Flag_AlwaysShowTabs
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AlwaysShowTabs);
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(400, 400)));
|
|
dock1->show();
|
|
|
|
auto frame1 = dock1->dptr()->frame();
|
|
|
|
QPoint globalPressPos = dragPointForWidget(frame1, 0);
|
|
TabBar *tabBar = frame1->tabWidget()->tabBar();
|
|
QVERIFY(tabBar);
|
|
SetExpectedWarning sew("No window being dragged for"); // because dragging by tab does nothing in this case
|
|
drag(tabBar->asWidget(), globalPressPos, QPoint(0, 0));
|
|
|
|
delete dock1;
|
|
Testing::waitForDeleted(frame1);
|
|
}
|
|
|
|
void TestDocks::tst_dragByTabBar_data()
|
|
{
|
|
QTest::addColumn<bool>("documentMode");
|
|
QTest::addColumn<bool>("tabsAlwaysVisible");
|
|
|
|
QTest::newRow("false-false") << false << false;
|
|
QTest::newRow("true-false") << true << false;
|
|
QTest::newRow("false-true") << false << true;
|
|
QTest::newRow("true-true") << true << true;
|
|
}
|
|
|
|
void TestDocks::tst_dragByTabBar()
|
|
{
|
|
QFETCH(bool, documentMode);
|
|
QFETCH(bool, tabsAlwaysVisible);
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto flags = KDDockWidgets::Config::self().flags() | KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible;
|
|
if (tabsAlwaysVisible)
|
|
flags |= KDDockWidgets::Config::Flag_AlwaysShowTabs;
|
|
|
|
KDDockWidgets::Config::self().setFlags(flags);
|
|
|
|
auto m = createMainWindow();
|
|
|
|
auto dropArea = m->dropArea();
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(400, 400)));
|
|
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(400, 400)));
|
|
auto dock3 = createDockWidget("dock3", new MyWidget2(QSize(400, 400)));
|
|
m->addDockWidgetAsTab(dock1);
|
|
m->resize(osWindowMinWidth(), 200);
|
|
|
|
dock2->addDockWidgetAsTab(dock3);
|
|
#if KDDOCKWIDGETS_QTWIDGETS
|
|
if (documentMode)
|
|
static_cast<QTabWidget *>(
|
|
static_cast<FrameWidget *>(dock2->dptr()->frame())->tabWidget()->asWidget())
|
|
->setDocumentMode(true);
|
|
#else
|
|
Q_UNUSED(documentMode);
|
|
#endif
|
|
auto fw = dock2->floatingWindow();
|
|
fw->move(m->pos() + QPoint(500, 500));
|
|
QVERIFY(fw->isVisible());
|
|
QVERIFY(!fw->titleBar()->isVisible());
|
|
|
|
dragFloatingWindowTo(fw, dropArea, DropIndicatorOverlayInterface::DropLocation_Right);
|
|
}
|
|
|
|
void TestDocks::tst_dock2FloatingWidgetsTabbed()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
if (KDDockWidgets::usesNativeTitleBar())
|
|
return; // Unit-tests can't drag via tab, yet
|
|
|
|
auto dock1 = createDockWidget("doc1", Qt::green);
|
|
auto fw1 = dock1->floatingWindow();
|
|
fw1->setGeometry(QRect(500, 500, 400, 400));
|
|
QVERIFY(dock1);
|
|
QPointer<Frame> frame1 = dock1->dptr()->frame();
|
|
|
|
auto titlebar1 = fw1->titleBar();
|
|
auto dock2 = createDockWidget("doc2", Qt::red);
|
|
|
|
QVERIFY(dock1->isFloating());
|
|
QVERIFY(dock2->isFloating());
|
|
|
|
drag(titlebar1, titlebar1->mapToGlobal(QPoint(5, 5)), dock2->window()->geometry().center(), ButtonAction_Press);
|
|
|
|
// It morphed into a FloatingWindow
|
|
QPointer<Frame> frame2 = dock2->dptr()->frame();
|
|
if (!dock2->floatingWindow()) {
|
|
qWarning() << "dock2->floatingWindow()=" << dock2->floatingWindow();
|
|
QVERIFY(false);
|
|
}
|
|
QVERIFY(frame2);
|
|
QCOMPARE(frame2->dockWidgetCount(), 1);
|
|
|
|
releaseOn(dock2->window()->geometry().center(), titlebar1);
|
|
QCOMPARE(frame2->dockWidgetCount(), 2); // 2.2 Frame has 2 widgets when one is dropped
|
|
|
|
QVERIFY(Testing::waitForDeleted(frame1));
|
|
|
|
// 2.3 Detach tab1 to empty space
|
|
QPoint globalPressPos = dragPointForWidget(frame2.data(), 0);
|
|
TabBar *tabBar = frame2->tabWidget()->tabBar();
|
|
QVERIFY(tabBar);
|
|
drag(tabBar->asWidget(), globalPressPos, frame2->window()->geometry().bottomRight() + QPoint(10, 10));
|
|
|
|
QVERIFY(frame2->dockWidgetCount() == 1);
|
|
QVERIFY(dock1->floatingWindow());
|
|
|
|
// 2.4 Drag the first dock over the second
|
|
frame1 = dock1->dptr()->frame();
|
|
frame2 = dock2->dptr()->frame();
|
|
fw1 = dock1->floatingWindow();
|
|
globalPressPos = fw1->titleBar()->mapToGlobal(QPoint(100,5));
|
|
drag(fw1->titleBar(), globalPressPos, dock2->window()->geometry().center());
|
|
|
|
QCOMPARE(frame2->dockWidgetCount(), 2);
|
|
|
|
// 2.5 Detach and drop to the same place, should tab again
|
|
globalPressPos = dragPointForWidget(frame2.data(), 0);
|
|
tabBar = frame2->tabWidget()->tabBar();
|
|
|
|
drag(tabBar->asWidget(), globalPressPos, dock2->window()->geometry().center());
|
|
QCOMPARE(frame2->dockWidgetCount(), 2);
|
|
|
|
// 2.6 Drag the tabbed group over a 3rd floating window
|
|
auto dock3 = createDockWidget("doc3", Qt::black);
|
|
QTest::qWait(1000); // Test is flaky otherwise
|
|
|
|
auto fw2 = dock2->floatingWindow();
|
|
drag(fw2->titleBar(), frame2->mapToGlobal(QPoint(10, 10)), dock3->window()->geometry().center());
|
|
|
|
QVERIFY(Testing::waitForDeleted(frame1));
|
|
QVERIFY(Testing::waitForDeleted(frame2));
|
|
QVERIFY(dock3->dptr()->frame());
|
|
QCOMPARE(dock3->dptr()->frame()->dockWidgetCount(), 3);
|
|
|
|
auto fw3 = dock3->floatingWindow();
|
|
QVERIFY(fw3);
|
|
QVERIFY(fw3->dropArea()->checkSanity());
|
|
|
|
// 2.7 Drop the window into a MainWindow
|
|
{
|
|
auto m = createMainWindow();
|
|
m->show();
|
|
m->setGeometry(QRect(500, 300, 300, 300));
|
|
QVERIFY(!dock3->isFloating());
|
|
auto fw3 = dock3->floatingWindow();
|
|
drag(fw3->titleBar(), dock3->window()->mapToGlobal(QPoint(10, 10)), m->geometry().center());
|
|
QVERIFY(!dock3->isFloating());
|
|
QVERIFY(dock3->window() == m.get());
|
|
QCOMPARE(dock3->dptr()->frame()->dockWidgetCount(), 3);
|
|
QVERIFY(m->dropArea()->checkSanity());
|
|
|
|
delete dock1;
|
|
delete dock2;
|
|
delete dock3;
|
|
QVERIFY(Testing::waitForDeleted(frame2));
|
|
QVERIFY(Testing::waitForDeleted(fw3));
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_deleteOnClose()
|
|
{
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
// Tests that DockWidget::close() deletes itself if Option_DeleteOnClose is set
|
|
QPointer<DockWidgetBase> dock1 = createDockWidget("1", new MyWidget2(QSize(400, 400)), DockWidgetBase::Option_DeleteOnClose);
|
|
dock1->show();
|
|
dock1->close();
|
|
|
|
QVERIFY(Testing::waitForDeleted(dock1));
|
|
}
|
|
|
|
{
|
|
// Tests that if it's closed via LayoutSaver it's also destroyed when having Option_DeleteOnClose
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
QPointer<DockWidgetBase> dock1 = createDockWidget("1", new MyWidget2(QSize(400, 400)), DockWidgetBase::Option_DeleteOnClose, {}, /*show=*/ false);
|
|
QPointer<DockWidgetBase> dock2 = createDockWidget("2", new MyWidget2(QSize(400, 400)), {}, {}, /*show=*/ false);
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
dock1->show();
|
|
dock2->show();
|
|
QVERIFY(dock1->isVisible());
|
|
QVERIFY(dock2->isVisible());
|
|
|
|
QVERIFY(saver.restoreLayout(saved));
|
|
QVERIFY(!dock1->isVisible());
|
|
QVERIFY(!dock2->isVisible());
|
|
|
|
QVERIFY(Testing::waitForDeleted(dock1));
|
|
QVERIFY(dock2.data());
|
|
delete dock2;
|
|
}
|
|
}
|
|
|
|
void TestDocks::tst_toggleAction()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(400, 400)));
|
|
auto dock2 = createDockWidget("dock2", new MyWidget2(QSize(400, 400)));
|
|
auto dock3 = createDockWidget("dock3", new MyWidget2(QSize(400, 400)));
|
|
|
|
m->addDockWidget(dock1, Location_OnLeft);
|
|
m->addDockWidget(dock2, Location_OnRight);
|
|
m->addDockWidget(dock3, Location_OnRight);
|
|
|
|
auto root = m->multiSplitter()->rootItem();
|
|
QCOMPARE(root->visibleCount_recursive(), 3);
|
|
QVERIFY(dock2->toggleAction()->isChecked());
|
|
QPointer<Frame> frame2 = dock2->dptr()->frame();
|
|
dock2->toggleAction()->toggle();
|
|
QVERIFY(!dock2->toggleAction()->isChecked());
|
|
|
|
QVERIFY(!dock2->isVisible());
|
|
QVERIFY(!dock2->isOpen());
|
|
QVERIFY(Testing::waitForDeleted(frame2));
|
|
|
|
QCOMPARE(root->visibleCount_recursive(), 2);
|
|
}
|
|
|
|
void TestDocks::tst_redocksToPreviousTabIndex()
|
|
{
|
|
// Checks that when reordering tabs with mouse, floating and redocking, they go back to their previous index
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AllowReorderTabs);
|
|
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
|
|
auto dock0 = createDockWidget("dock0", new MyWidget2(QSize(400, 400)));
|
|
auto dock1 = createDockWidget("dock1", new MyWidget2(QSize(400, 400)));
|
|
m->addDockWidget(dock0, Location_OnLeft);
|
|
dock0->addDockWidgetAsTab(dock1);
|
|
|
|
QCOMPARE(dock0->tabIndex(), 0);
|
|
QCOMPARE(dock1->tabIndex(), 1);
|
|
|
|
dock0->setFloating(true);
|
|
QCOMPARE(dock1->tabIndex(), 0);
|
|
|
|
dock0->setFloating(false);
|
|
QCOMPARE(dock0->tabIndex(), 0);
|
|
QCOMPARE(dock1->tabIndex(), 1);
|
|
|
|
Frame *frame = dock0->dptr()->frame();
|
|
auto tb = dock0->dptr()->frame()->tabWidget()->tabBar();
|
|
tb->moveTabTo(0, 1);
|
|
|
|
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
|
QCOMPARE(dock0->tabIndex(), 1);
|
|
QCOMPARE(dock1->tabIndex(), 0);
|
|
|
|
// Also detach via detachTab(), which is what is called when the user detaches with mouse
|
|
frame->detachTab(dock0);
|
|
dock0->setFloating(false);
|
|
|
|
QCOMPARE(dock0->tabIndex(), 1);
|
|
QCOMPARE(dock1->tabIndex(), 0);
|
|
#else
|
|
// An XFAIL so we remember to implement this
|
|
QEXPECT_FAIL("", "TabBar::moveTabTo not implemented for QtQuick yet", Continue);
|
|
QVERIFY(false);
|
|
Q_UNUSED(frame);
|
|
#endif
|
|
}
|
|
|
|
void TestDocks::tst_addMDIDockWidget()
|
|
{
|
|
EnsureTopLevelsDeleted e;
|
|
|
|
// Test that adding a MDI dock widget doesn't produce any warning
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_MDI);
|
|
auto dock0 = createDockWidget("dock0", new MyWidget2(QSize(400, 400)));
|
|
qobject_cast<MDILayoutWidget *>(m->layoutWidget())->addDockWidget(dock0, QPoint(0, 0), {});
|
|
}
|
|
|
|
void TestDocks::tst_redockToMDIRestoresPosition()
|
|
{
|
|
// Tests that setFloating(false) puts the dock widget where it was before floating
|
|
|
|
EnsureTopLevelsDeleted e;
|
|
auto m = createMainWindow(QSize(800, 500), MainWindowOption_MDI);
|
|
auto dock0 = createDockWidget("dock0", new MyWidget2(QSize(400, 400)));
|
|
|
|
auto layoutWidget = qobject_cast<MDILayoutWidget *>(m->layoutWidget());
|
|
const QPoint initialPoint = QPoint(500, 500);
|
|
layoutWidget->addDockWidget(dock0, initialPoint, {});
|
|
|
|
Frame *frame = dock0->DockWidgetBase::d->frame();
|
|
QCOMPARE(frame->QWidgetAdapter::pos(), initialPoint);
|
|
|
|
const QSize initialSize = frame->QWidgetAdapter::size();
|
|
|
|
dock0->setFloating(true);
|
|
dock0->setFloating(false);
|
|
frame = dock0->DockWidgetBase::d->frame();
|
|
QCOMPARE(frame->QWidgetAdapter::pos(), initialPoint);
|
|
|
|
const QPoint anotherPos = QPoint(250, 250);
|
|
dock0->setMDIPosition(anotherPos);
|
|
|
|
dock0->setFloating(true);
|
|
dock0->setFloating(false);
|
|
frame = dock0->DockWidgetBase::d->frame();
|
|
|
|
Item *item = layoutWidget->itemForFrame(frame);
|
|
QCOMPARE(item->pos(), anotherPos);
|
|
QCOMPARE(item->geometry(), frame->QWidgetAdapter::geometry());
|
|
QCOMPARE(frame->QWidgetAdapter::pos(), anotherPos);
|
|
QCOMPARE(frame->QWidgetAdapter::size(), initialSize);
|
|
|
|
const QSize anotherSize = QSize(500, 500);
|
|
dock0->setMDISize(anotherSize);
|
|
QCOMPARE(frame->QWidgetAdapter::size(), anotherSize);
|
|
item = layoutWidget->itemForFrame(frame);
|
|
QCOMPARE(item->geometry(), frame->QWidgetAdapter::geometry());
|
|
|
|
}
|
|
|
|
void TestDocks::tst_restoreWithNativeTitleBar()
|
|
{
|
|
#ifdef Q_OS_WIN // Other OS don't support this
|
|
EnsureTopLevelsDeleted e;
|
|
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_NativeTitleBar);
|
|
|
|
auto dock0 = createDockWidget("dock0", new MyWidget2(QSize(400, 400)));
|
|
dock0->window()->move(100, 100);
|
|
|
|
QVERIFY(!dock0->titleBar()->isVisible());
|
|
QVERIFY(!dock0->floatingWindow()->titleBar()->isVisible());
|
|
QVERIFY(!dock0->d->frame()->titleBar()->isVisible());
|
|
|
|
LayoutSaver saver;
|
|
const QByteArray saved = saver.serializeLayout();
|
|
saver.restoreLayout(saved);
|
|
QVERIFY(!dock0->titleBar()->isVisible());
|
|
QVERIFY(!dock0->floatingWindow()->titleBar()->isVisible());
|
|
QVERIFY(!dock0->d->frame()->titleBar()->isVisible());
|
|
#endif
|
|
}
|
|
|
|
#include "tst_docks.moc"
|