diff --git a/Changelog b/Changelog index 48fde829..e6bd4092 100644 --- a/Changelog +++ b/Changelog @@ -6,6 +6,7 @@ as TitleBarWidget already reimplements sizeHint. If you're inheriting directly from TitleBar then make sure to provide a sizeHint. - Added MainWindow::closeDockWidgets() + - Fixed crash in MainWindow::layoutEqually() * v1.3.1 (unreleased) - Improve restoring layout when RestoreOption_RelativeToMainWindow is used (#171) diff --git a/src/private/multisplitter/Item.cpp b/src/private/multisplitter/Item.cpp index 72408ac6..fc009a14 100644 --- a/src/private/multisplitter/Item.cpp +++ b/src/private/multisplitter/Item.cpp @@ -2384,8 +2384,9 @@ void ItemBoxContainer::layoutEqually(SizingInfo::List &sizes) auto lengthToGive = length() - (d->m_separators.size() * Item::separatorThickness); // clear the sizes before we start distributing - for (SizingInfo &size : sizes) - size.setLength(0, d->m_orientation); + for (SizingInfo &size : sizes) { + size.setLength(0, d->m_orientation); + } while (satisfiedIndexes.count() < sizes.count()) { const auto remainingItems = sizes.count() - satisfiedIndexes.count(); @@ -2403,9 +2404,25 @@ void ItemBoxContainer::layoutEqually(SizingInfo::List &sizes) continue; } - const auto newItemLenght = qBound(size.minLength(d->m_orientation), - size.length(d->m_orientation) + suggestedToGive, - size.maxLengthHint(d->m_orientation)); + // Bound the max length. Our max can't be bigger than the remaining space. + // The layout's min length minus our own min length is the amount of space that we + // need to guarantee. We can't go larger and overwrite that + + const auto othersMissing = // The size that the others are missing to satisfy their + // minimum length + std::accumulate(sizes.constBegin(), sizes.constEnd(), 0, + [this](size_t sum, const SizingInfo &sz) { + return sum + sz.missingLength(d->m_orientation); + }) + - size.missingLength(d->m_orientation); + + const auto maxLength = + qMin(size.length(d->m_orientation) + lengthToGive - othersMissing, + size.maxLengthHint(d->m_orientation)); + + const auto newItemLenght = + qBound(size.minLength(d->m_orientation), + size.length(d->m_orientation) + suggestedToGive, maxLength); const auto toGive = newItemLenght - size.length(d->m_orientation); if (toGive == 0) { diff --git a/tests/layouts/layoutEquallyCrash.json b/tests/layouts/layoutEquallyCrash.json new file mode 100644 index 00000000..1824b029 --- /dev/null +++ b/tests/layouts/layoutEquallyCrash.json @@ -0,0 +1,279 @@ +{ + "allDockWidgets": [ + { + "affinities": [ + "{7829427d-88e3-402e-9120-50c628dfd0bc}" + ], + "lastPosition": { + "lastFloatingGeometry": { + "height": 0, + "width": 0, + "x": 0, + "y": 0 + }, + "placeholders": [ + { + "isFloatingWindow": false, + "itemIndex": 2, + "mainWindowUniqueName": "{7829427d-88e3-402e-9120-50c628dfd0bc}" + } + ], + "tabIndex": 0, + "wasFloating": false + }, + "uniqueName": "Favorite-481" + }, + { + "affinities": [ + "{7829427d-88e3-402e-9120-50c628dfd0bc}" + ], + "lastPosition": { + "lastFloatingGeometry": { + "height": 0, + "width": 0, + "x": 0, + "y": 0 + }, + "placeholders": [ + { + "isFloatingWindow": false, + "itemIndex": 0, + "mainWindowUniqueName": "{7829427d-88e3-402e-9120-50c628dfd0bc}" + } + ], + "tabIndex": 0, + "wasFloating": false + }, + "uniqueName": "Favorite-482" + }, + { + "affinities": [ + "{7829427d-88e3-402e-9120-50c628dfd0bc}" + ], + "lastPosition": { + "lastFloatingGeometry": { + "height": 0, + "width": 0, + "x": 0, + "y": 0 + }, + "placeholders": [ + { + "isFloatingWindow": false, + "itemIndex": 1, + "mainWindowUniqueName": "{7829427d-88e3-402e-9120-50c628dfd0bc}" + } + ], + "tabIndex": 0, + "wasFloating": false + }, + "uniqueName": "Favorite-483" + } + ], + "closedDockWidgets": [ + ], + "floatingWindows": [ + ], + "mainWindows": [ + { + "affinities": [ + "{7829427d-88e3-402e-9120-50c628dfd0bc}" + ], + "geometry": { + "height": 1228, + "width": 1644, + "x": 501, + "y": 110 + }, + "isVisible": true, + "multiSplitterLayout": { + "frames": { + "10": { + "currentTabIndex": 0, + "dockWidgets": [ + "Favorite-483" + ], + "geometry": { + "height": 1206, + "width": 536, + "x": 541, + "y": 0 + }, + "id": "10", + "isNull": false, + "objectName": "Favorite-483", + "options": 0 + }, + "12": { + "currentTabIndex": 0, + "dockWidgets": [ + "Favorite-481" + ], + "geometry": { + "height": 1206, + "width": 540, + "x": 1082, + "y": 0 + }, + "id": "12", + "isNull": false, + "objectName": "Favorite-481", + "options": 0 + }, + "9": { + "currentTabIndex": 0, + "dockWidgets": [ + "Favorite-482" + ], + "geometry": { + "height": 1206, + "width": 536, + "x": 0, + "y": 0 + }, + "id": "9", + "isNull": false, + "objectName": "Favorite-482", + "options": 0 + } + }, + "layout": { + "children": [ + { + "children": [ + { + "guestId": "9", + "isContainer": false, + "isVisible": true, + "objectName": "Favorite-482", + "sizingInfo": { + "geometry": { + "height": 1206, + "width": 536, + "x": 0, + "y": 0 + }, + "maxSize": { + "height": 524315, + "width": 524432 + }, + "minSize": { + "height": 118, + "width": 233 + } + } + }, + { + "guestId": "10", + "isContainer": false, + "isVisible": true, + "objectName": "Favorite-483", + "sizingInfo": { + "geometry": { + "height": 1206, + "width": 536, + "x": 541, + "y": 0 + }, + "maxSize": { + "height": 524287, + "width": 524291 + }, + "minSize": { + "height": 90, + "width": 360 + } + } + }, + { + "guestId": "12", + "isContainer": false, + "isVisible": true, + "objectName": "Favorite-481", + "sizingInfo": { + "geometry": { + "height": 1206, + "width": 540, + "x": 1082, + "y": 0 + }, + "maxSize": { + "height": 524317, + "width": 524291 + }, + "minSize": { + "height": 438, + "width": 540 + } + } + } + ], + "isContainer": true, + "isVisible": false, + "objectName": "", + "orientation": 1, + "sizingInfo": { + "geometry": { + "height": 1206, + "width": 1622, + "x": 0, + "y": 0 + }, + "maxSize": { + "height": 524287, + "width": 1048728 + }, + "minSize": { + "height": 118, + "width": 598 + } + } + } + ], + "isContainer": true, + "isVisible": false, + "objectName": "", + "orientation": 2, + "sizingInfo": { + "geometry": { + "height": 1206, + "width": 1622, + "x": 0, + "y": 0 + }, + "maxSize": { + "height": 16777215, + "width": 16777215 + }, + "minSize": { + "height": 90, + "width": 80 + } + } + } + }, + "options": 0, + "screenIndex": 0, + "screenSize": { + "height": 1440, + "width": 2560 + }, + "uniqueName": "{7829427d-88e3-402e-9120-50c628dfd0bc}", + "windowState": 0 + } + ], + "screenInfo": [ + { + "devicePixelRatio": 1.5, + "geometry": { + "height": 1440, + "width": 2560, + "x": 0, + "y": 0 + }, + "index": 0, + "name": "DP-2" + } + ], + "serializationVersion": 3 +} diff --git a/tests/test_resources.qrc b/tests/test_resources.qrc index fd8b3525..40578558 100644 --- a/tests/test_resources.qrc +++ b/tests/test_resources.qrc @@ -4,6 +4,7 @@ layouts/overlapping-item.json layouts/unsupported-serialization-version.json layouts/stuck-separator.json + layouts/layoutEquallyCrash.json main.qml diff --git a/tests/tst_docks.cpp b/tests/tst_docks.cpp index 2045efaf..114be322 100644 --- a/tests/tst_docks.cpp +++ b/tests/tst_docks.cpp @@ -118,6 +118,7 @@ private Q_SLOTS: void tst_restoreMaximizedState(); void tst_shutdown(); void tst_closeDockWidgets(); + void tst_layoutEqually(); void tst_doubleClose(); void tst_dockInternal(); void tst_maximizeAndRestore(); @@ -936,6 +937,30 @@ void TestDocks::tst_closeDockWidgets() 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))); + + m->setContentsMargins(10, 0, 10, 0); + + dock1->setAffinities({ mainWindowId }); + dock2->setAffinities({ mainWindowId }); + dock3->setAffinities({ mainWindowId }); + + LayoutSaver restorer; + restorer.restoreFromFile(":/layouts/layoutEquallyCrash.json"); + + m->layoutEqually(); +} + void TestDocks::tst_doubleClose() { EnsureTopLevelsDeleted e;