diff --git a/.github/workflows/flutter-dev.yml b/.github/workflows/flutter-dev.yml index 36381f8c0..40aeeb997 100644 --- a/.github/workflows/flutter-dev.yml +++ b/.github/workflows/flutter-dev.yml @@ -38,6 +38,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + with: + submodules: true - name: Install dependencies on Ubuntu run: | diff --git a/examples/flutter/lib/main.dart b/examples/flutter/lib/main.dart index 0cb7e3ff6..77cff643c 100644 --- a/examples/flutter/lib/main.dart +++ b/examples/flutter/lib/main.dart @@ -10,7 +10,6 @@ */ import 'dart:developer'; -import 'package:KDDockWidgets/models/DockItem.dart'; import 'package:KDDockWidgets/KDDockWidgets.dart'; import 'package:KDDockWidgets/widgets/DockWidget.dart'; diff --git a/src/flutter/dart/lib/KDDockWidgets.dart b/src/flutter/dart/lib/KDDockWidgets.dart index 041710869..eb9de05e5 100644 --- a/src/flutter/dart/lib/KDDockWidgets.dart +++ b/src/flutter/dart/lib/KDDockWidgets.dart @@ -11,7 +11,6 @@ library kddockwidgets; -import 'package:KDDockWidgets/models/DockItem.dart'; import 'package:KDDockWidgets/models/GeometryItem.dart'; import 'package:KDDockWidgets/models/Separator.dart'; import 'package:KDDockWidgets/private/Bindings.dart'; @@ -24,6 +23,9 @@ import 'package:signals_slots/signals_slots.dart'; part 'models/Group.dart'; part 'models/DropArea.dart'; part 'models/TitleBar.dart'; +part 'models/FloatingItem.dart'; +part 'models/DockRegistry.dart'; +part 'models/DockItem.dart'; class DropLocation { static const int DropLocation_None = 0; diff --git a/src/flutter/dart/lib/models/DockItem.dart b/src/flutter/dart/lib/models/DockItem.dart index 4a8939423..3954e3927 100644 --- a/src/flutter/dart/lib/models/DockItem.dart +++ b/src/flutter/dart/lib/models/DockItem.dart @@ -9,8 +9,7 @@ Contact KDAB at for commercial licensing options. */ -import 'package:flutter/widgets.dart'; -import 'package:signals_slots/signals_slots.dart'; +part of kddockwidgets; /// represents the state of a dock widget @@ -24,7 +23,9 @@ class DockItem { DockItem({ required this.uniqueName, this.guestWidget, - }); + }) { + DockRegistry.instance.addDockItem(this); + } String get title { if (_title.isNotEmpty) return _title; diff --git a/src/flutter/dart/lib/models/DockRegistry.dart b/src/flutter/dart/lib/models/DockRegistry.dart new file mode 100644 index 000000000..26c769b2d --- /dev/null +++ b/src/flutter/dart/lib/models/DockRegistry.dart @@ -0,0 +1,76 @@ +/* + This file is part of KDDockWidgets. + + SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company + Author: Sérgio Martins + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only + + Contact KDAB at for commercial licensing options. +*/ + +part of kddockwidgets; + +class DockRegistry { + static final DockRegistry _instance = DockRegistry._internal(); + + final floatingItems = []; + final dockItems = []; + + void addFloatingItem(FloatingItem floatingItem) { + if (floatingItems.contains(floatingItem)) { + throw Exception("FloatingItem already exists in registry"); + } + + if (floatingItemForDropArea(floatingItem.dropArea) != null) { + throw Exception("Already have a floating item for this drop area"); + } + + floatingItems.add(floatingItem); + } + + void removeFloatingItem(FloatingItem floatingItem) { + if (!floatingItems.contains(floatingItem)) { + throw Exception("FloatingItem doesn't exists in registry"); + } + + floatingItems.remove(floatingItem); + } + + void addDockItem(DockItem dockItem) { + if (dockItems.contains(dockItem)) { + throw Exception("DockItem already exists in registry"); + } + + dockItems.add(dockItem); + } + + void removeDockItem(DockItem dockItem) { + if (!dockItems.contains(dockItem)) { + throw Exception("DockItem doesn't exists in registry"); + } + + dockItems.remove(dockItem); + } + + FloatingItem? floatingItemForDropArea(DropArea da) { + for (FloatingItem floatingItem in floatingItems) { + if (floatingItem.dropArea == da) { + return floatingItem; + } + } + + return null; + } + + bool containsFloatingItem(FloatingItem floatingItem) { + return floatingItems.contains(floatingItem); + } + + factory DockRegistry() { + return _instance; + } + DockRegistry._internal(); + + static DockRegistry get instance => _instance; +} diff --git a/src/flutter/dart/lib/models/DropArea.dart b/src/flutter/dart/lib/models/DropArea.dart index 11b26759e..0fc8f84a8 100644 --- a/src/flutter/dart/lib/models/DropArea.dart +++ b/src/flutter/dart/lib/models/DropArea.dart @@ -98,6 +98,10 @@ class DropArea implements ffi.Finalizable { return _groups.any((group) => group.containsDockItem(name)); } + bool hasGroups() { + return _groups.isNotEmpty; + } + void setLayoutSize(int width, int height) { Bindings.instance.nativeLibrary .on_flutter_droparea_widget_resized(_hostCpp.cast(), width, height); diff --git a/src/flutter/dart/lib/models/FloatingItem.dart b/src/flutter/dart/lib/models/FloatingItem.dart new file mode 100644 index 000000000..45a306dce --- /dev/null +++ b/src/flutter/dart/lib/models/FloatingItem.dart @@ -0,0 +1,43 @@ +/* + This file is part of KDDockWidgets. + + SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company + Author: Sérgio Martins + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only + + Contact KDAB at for commercial licensing options. +*/ + +part of kddockwidgets; + +class FloatingItem implements ItemWithTitleBar { + final dropArea = DropArea(); + late final TitleBar titleBar; + late final Connection _groupCountChangedConnection; + + FloatingItem() { + DockRegistry.instance.addFloatingItem(this); + titleBar = TitleBar(this); + + _groupCountChangedConnection = + dropArea.layoutChanged.connect(onGroupCountChanged); + } + + void onGroupCountChanged() { + if (!dropArea.hasGroups()) { + close(); + } + } + + @override + void close() { + if (!DockRegistry.instance.containsFloatingItem(this)) { + // we're closed already, can be removed once we can _groupCountChangedConnection.disconnect() + return; + } + + // _groupCountChangedConnection.disconnect(); signals_slot bug + DockRegistry.instance.removeFloatingItem(this); + } +} diff --git a/src/flutter/dart/lib/models/Group.dart b/src/flutter/dart/lib/models/Group.dart index 952fe25b6..04e4ab5df 100644 --- a/src/flutter/dart/lib/models/Group.dart +++ b/src/flutter/dart/lib/models/Group.dart @@ -35,7 +35,7 @@ void _geometryChangedCallback(ffi.Pointer guest, int x, int y, } } -class Group extends GeometryItem implements ffi.Finalizable { +class Group extends GeometryItem implements ffi.Finalizable, ItemWithTitleBar { static final _finalizer = ffi.NativeFinalizer(finalizerFunc("delete_guest").cast()); @@ -160,6 +160,7 @@ class Group extends GeometryItem implements ffi.Finalizable { } } + @override void close() { dropArea._removeGroup(this); } diff --git a/src/flutter/dart/lib/models/TitleBar.dart b/src/flutter/dart/lib/models/TitleBar.dart index 3470f0dcb..44645396a 100644 --- a/src/flutter/dart/lib/models/TitleBar.dart +++ b/src/flutter/dart/lib/models/TitleBar.dart @@ -11,17 +11,22 @@ part of kddockwidgets; +// Floating windows and tab groups have titlebars +abstract class ItemWithTitleBar { + // called when close button on the titlebar is clicked + void close(); +} + class TitleBar { String title = ""; bool isExplicitlyHidden = false; - final Group? _group; + final ItemWithTitleBar _itemWithTitleBar; - TitleBar(Group? group, {this.title = ""}) : _group = group {} + TitleBar(ItemWithTitleBar itemWithTitleBar, {this.title = ""}) + : _itemWithTitleBar = itemWithTitleBar {} void onCloseClicked() { - if (_group != null) { - _group.close(); - } + _itemWithTitleBar.close(); } void onFloatClicked() {} diff --git a/src/flutter/dart/lib/widgets/DockWidget.dart b/src/flutter/dart/lib/widgets/DockWidget.dart index c2b4fdf1f..6c3ad5d4d 100644 --- a/src/flutter/dart/lib/widgets/DockWidget.dart +++ b/src/flutter/dart/lib/widgets/DockWidget.dart @@ -9,8 +9,8 @@ Contact KDAB at for commercial licensing options. */ -import 'package:KDDockWidgets/models/DockItem.dart'; import 'package:flutter/widgets.dart'; +import 'package:KDDockWidgets/KDDockWidgets.dart'; class DockWidget extends StatefulWidget { final DockItem dockItem; diff --git a/src/flutter/dart/lib/widgets/GroupWidget.dart b/src/flutter/dart/lib/widgets/GroupWidget.dart index fcd210433..299c2b13e 100644 --- a/src/flutter/dart/lib/widgets/GroupWidget.dart +++ b/src/flutter/dart/lib/widgets/GroupWidget.dart @@ -10,7 +10,6 @@ */ import 'package:KDDockWidgets/KDDockWidgets.dart'; -import 'package:KDDockWidgets/models/DockItem.dart'; import 'package:KDDockWidgets/widgets/PositionedWidget.dart'; import 'package:KDDockWidgets/widgets/TabBarWidget.dart'; import 'package:KDDockWidgets/widgets/TitleBarWidget.dart'; diff --git a/src/flutter/dart/test/models/droparea_test.dart b/src/flutter/dart/test/models/droparea_test.dart index e66b7621e..c64dc0f27 100644 --- a/src/flutter/dart/test/models/droparea_test.dart +++ b/src/flutter/dart/test/models/droparea_test.dart @@ -9,7 +9,6 @@ Contact KDAB at for commercial licensing options. */ -import 'package:KDDockWidgets/models/DockItem.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:KDDockWidgets/KDDockWidgets.dart'; import 'package:KDDockWidgets/private/Bindings.dart'; diff --git a/tests/flutter/integration_test/ui_test.dart b/tests/flutter/integration_test/ui_test.dart index 316b56748..5d3fdc0d0 100644 --- a/tests/flutter/integration_test/ui_test.dart +++ b/tests/flutter/integration_test/ui_test.dart @@ -12,7 +12,6 @@ import 'dart:io'; import 'dart:ui'; -import 'package:KDDockWidgets/models/DockItem.dart'; import 'package:KDDockWidgets/KDDockWidgets.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; diff --git a/tests/reference-images b/tests/reference-images index 8fd0add29..944470420 160000 --- a/tests/reference-images +++ b/tests/reference-images @@ -1 +1 @@ -Subproject commit 8fd0add29de4d0671f1b05f1b1f354f807d8b99d +Subproject commit 944470420ccc054771f3d7b127ad5358e170c0f0