diff --git a/lib/pages/dashboard_page.dart b/lib/pages/dashboard_page.dart index d4bbc3d2..b2d7c7cc 100644 --- a/lib/pages/dashboard_page.dart +++ b/lib/pages/dashboard_page.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:elastic_dashboard/widgets/draggable_containers/models/widget_container_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; @@ -30,7 +31,6 @@ import 'package:elastic_dashboard/widgets/network_tree/networktables_tree.dart'; import 'package:elastic_dashboard/widgets/settings_dialog.dart'; import 'package:elastic_dashboard/widgets/tab_grid.dart'; import '../widgets/draggable_containers/models/layout_container_model.dart'; -import '../widgets/draggable_containers/models/nt_widget_container_model.dart'; class DashboardPage extends StatefulWidget { final SharedPreferences preferences; @@ -1276,17 +1276,17 @@ class _DashboardPageState extends State with WindowListener { visible: addWidgetDialogVisible, onNTDragUpdate: (globalPosition, widget) { grids[currentTabIndex] - .addNTDragInWidget(widget, globalPosition); + .addDragInWidget(widget, globalPosition); }, onNTDragEnd: (widget) { - grids[currentTabIndex].placeNTDragInWidget(widget); + grids[currentTabIndex].placeDragInWidget(widget); }, onLayoutDragUpdate: (globalPosition, widget) { grids[currentTabIndex] - .addLayoutDragInWidget(widget, globalPosition); + .addDragInWidget(widget, globalPosition); }, onLayoutDragEnd: (widget) { - grids[currentTabIndex].placeLayoutDragInWidget(widget); + grids[currentTabIndex].placeDragInWidget(widget); }, onClose: () { setState(() => addWidgetDialogVisible = false); @@ -1355,9 +1355,9 @@ class AddWidgetDialog extends StatelessWidget { final TabGrid Function() grid; final bool visible; - final Function(Offset globalPosition, NTWidgetContainerModel widget)? + final Function(Offset globalPosition, WidgetContainerModel widget)? onNTDragUpdate; - final Function(NTWidgetContainerModel widget)? onNTDragEnd; + final Function(WidgetContainerModel widget)? onNTDragEnd; final Function(Offset globalPosition, LayoutContainerModel widget)? onLayoutDragUpdate; @@ -1411,10 +1411,15 @@ class AddWidgetDialog extends StatelessWidget { child: TabBarView( children: [ NetworkTableTree( + listLayoutBuilder: ( + {required title, required children}) { + return grid().createListLayout( + title: title, + children: children, + ); + }, onDragUpdate: onNTDragUpdate, onDragEnd: onNTDragEnd, - widgetContainerBuilder: (widgetContainer) => - grid().createNTWidgetContainer(widgetContainer), ), ListView( children: [ diff --git a/lib/widgets/draggable_containers/models/list_layout_model.dart b/lib/widgets/draggable_containers/models/list_layout_model.dart index 16d535ed..840bfaea 100644 --- a/lib/widgets/draggable_containers/models/list_layout_model.dart +++ b/lib/widgets/draggable_containers/models/list_layout_model.dart @@ -39,6 +39,7 @@ class ListLayoutModel extends LayoutContainerModel { required super.title, required this.tabGrid, required this.onDragCancel, + this.children = const [], super.minWidth, super.minHeight, this.labelPosition = 'TOP', diff --git a/lib/widgets/network_tree/networktables_tree.dart b/lib/widgets/network_tree/networktables_tree.dart index 6acbcd99..7f19891a 100644 --- a/lib/widgets/network_tree/networktables_tree.dart +++ b/lib/widgets/network_tree/networktables_tree.dart @@ -1,5 +1,8 @@ import 'dart:ui'; +import 'package:elastic_dashboard/widgets/draggable_containers/models/list_layout_model.dart'; +import 'package:elastic_dashboard/widgets/draggable_containers/models/nt_widget_container_model.dart'; +import 'package:elastic_dashboard/widgets/draggable_containers/models/widget_container_model.dart'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; @@ -7,22 +10,26 @@ import 'package:flutter_fancy_tree_view/flutter_fancy_tree_view.dart'; import 'package:elastic_dashboard/services/nt4_client.dart'; import 'package:elastic_dashboard/services/nt_connection.dart'; -import 'package:elastic_dashboard/widgets/draggable_containers/draggable_widget_container.dart'; import 'package:elastic_dashboard/widgets/network_tree/networktables_tree_row.dart'; -import '../draggable_containers/models/nt_widget_container_model.dart'; + +typedef ListLayoutBuilder = ListLayoutModel Function({ + required String title, + required List children, +}); class NetworkTableTree extends StatefulWidget { - final Function(Offset globalPosition, NTWidgetContainerModel widget)? + final ListLayoutBuilder listLayoutBuilder; + + final Function(Offset globalPosition, WidgetContainerModel widget)? onDragUpdate; - final Function(NTWidgetContainerModel widget)? onDragEnd; - final NTWidgetContainerModel? Function(WidgetContainer? widget)? - widgetContainerBuilder; + final Function(WidgetContainerModel widget)? onDragEnd; - const NetworkTableTree( - {super.key, - this.onDragUpdate, - this.onDragEnd, - this.widgetContainerBuilder}); + const NetworkTableTree({ + super.key, + required this.listLayoutBuilder, + this.onDragUpdate, + this.onDragEnd, + }); @override State createState() => _NetworkTableTreeState(); @@ -32,12 +39,10 @@ class _NetworkTableTreeState extends State { final NetworkTableTreeRow root = NetworkTableTreeRow(topic: '/', rowName: ''); late final TreeController treeController; - late final Function(Offset globalPosition, NTWidgetContainerModel widget)? + late final Function(Offset globalPosition, WidgetContainerModel widget)? onDragUpdate = widget.onDragUpdate; - late final Function(NTWidgetContainerModel widget)? onDragEnd = + late final Function(WidgetContainerModel widget)? onDragEnd = widget.onDragEnd; - late final NTWidgetContainerModel? Function(WidgetContainer? widget)? - widgetContainerBuilder = widget.widgetContainerBuilder; late final Function(NT4Topic topic) onNewTopicAnnounced; @@ -122,9 +127,9 @@ class _NetworkTableTreeState extends State { return TreeTile( key: UniqueKey(), entry: entry, + listLayoutBuilder: widget.listLayoutBuilder, onDragUpdate: onDragUpdate, onDragEnd: onDragEnd, - widgetContainerBuilder: widgetContainerBuilder, onTap: () { setState(() => treeController.toggleExpansion(entry.node)); }, @@ -139,20 +144,21 @@ class TreeTile extends StatelessWidget { super.key, required this.entry, required this.onTap, + required this.listLayoutBuilder, this.onDragUpdate, this.onDragEnd, - this.widgetContainerBuilder, }); final TreeEntry entry; final VoidCallback onTap; - final Function(Offset globalPosition, NTWidgetContainerModel widget)? + + final ListLayoutBuilder listLayoutBuilder; + + final Function(Offset globalPosition, WidgetContainerModel widget)? onDragUpdate; - final Function(NTWidgetContainerModel widget)? onDragEnd; - final NTWidgetContainerModel? Function(WidgetContainer? widget)? - widgetContainerBuilder; + final Function(WidgetContainerModel widget)? onDragEnd; - NTWidgetContainerModel? draggingWidget; + WidgetContainerModel? draggingWidget; @override Widget build(BuildContext context) { @@ -172,8 +178,8 @@ class TreeTile extends StatelessWidget { return; } - draggingWidget = widgetContainerBuilder - ?.call(await entry.node.toWidgetContainer()); + draggingWidget = await entry.node + .toWidgetContainerModel(listLayoutBuilder: listLayoutBuilder); }, onPanUpdate: (details) { if (draggingWidget == null) { diff --git a/lib/widgets/network_tree/networktables_tree_row.dart b/lib/widgets/network_tree/networktables_tree_row.dart index 67a036fd..a2e6a6ae 100644 --- a/lib/widgets/network_tree/networktables_tree_row.dart +++ b/lib/widgets/network_tree/networktables_tree_row.dart @@ -1,3 +1,6 @@ +import 'package:elastic_dashboard/widgets/draggable_containers/models/nt_widget_container_model.dart'; +import 'package:elastic_dashboard/widgets/draggable_containers/models/widget_container_model.dart'; +import 'package:elastic_dashboard/widgets/network_tree/networktables_tree.dart'; import 'package:flutter/material.dart'; import 'package:elastic_dashboard/services/nt4_client.dart'; @@ -154,6 +157,58 @@ class NetworkTableTreeRow { return NTWidgetBuilder.buildNTWidgetFromType(type, topic); } + Future?> getListLayoutChildren() async { + List listChildren = []; + for (NetworkTableTreeRow child in children) { + if (child.rowName.startsWith('.')) { + continue; + } + WidgetContainerModel? childModel = + await child.toWidgetContainerModel(resortToListLayout: false); + + if (childModel is NTWidgetContainerModel) { + listChildren.add(childModel); + } + } + + if (listChildren.isEmpty) { + return null; + } + + return listChildren; + } + + Future toWidgetContainerModel({ + bool resortToListLayout = true, + ListLayoutBuilder? listLayoutBuilder, + }) async { + NTWidget? primary = await getPrimaryWidget(); + + if (primary == null) { + if (resortToListLayout) { + List? listLayoutChildren = + await getListLayoutChildren(); + + if (listLayoutChildren != null) { + return listLayoutBuilder?.call( + title: rowName, + children: listLayoutChildren, + ); + } + } + return null; + } + + double width = NTWidgetBuilder.getDefaultWidth(primary); + double height = NTWidgetBuilder.getDefaultHeight(primary); + + return NTWidgetContainerModel( + initialPosition: Rect.fromLTWH(0.0, 0.0, width, height), + title: rowName, + child: primary, + ); + } + Future toWidgetContainer() async { NTWidget? primary = await getPrimaryWidget(); diff --git a/lib/widgets/tab_grid.dart b/lib/widgets/tab_grid.dart index 76e6fadf..fa5c1320 100644 --- a/lib/widgets/tab_grid.dart +++ b/lib/widgets/tab_grid.dart @@ -398,7 +398,7 @@ class TabGrid extends StatelessWidget { void layoutDragOutEnd(WidgetContainerModel widget) { if (widget is NTWidgetContainerModel) { - placeNTDragInWidget(widget, true); + placeDragInWidget(widget, true); } } @@ -432,58 +432,7 @@ class TabGrid extends StatelessWidget { refresh(); } - void addLayoutDragInWidget( - LayoutContainerModel layout, Offset globalPosition) { - Offset localPosition = getLocalPosition(globalPosition); - layout.setDraggingRect( - Rect.fromLTWH( - localPosition.dx, - localPosition.dy, - layout.draggingRect.width, - layout.draggingRect.height, - ), - ); - _containerDraggingIn = MapEntry(layout, globalPosition); - refresh(); - } - - void placeLayoutDragInWidget(LayoutContainerModel layout) { - if (_containerDraggingIn == null) { - return; - } - - Offset globalPosition = _containerDraggingIn!.value; - - Offset localPosition = getLocalPosition(globalPosition); - - double previewX = DraggableWidgetContainer.snapToGrid(localPosition.dx); - double previewY = DraggableWidgetContainer.snapToGrid(localPosition.dy); - - Rect previewLocation = Rect.fromLTWH(previewX, previewY, - layout.displayRect.width, layout.displayRect.height); - - if (!isValidLocation(previewLocation)) { - _containerDraggingIn = null; - - refresh(); - return; - } - - double width = layout.displayRect.width; - double height = layout.displayRect.height; - - layout.setDisplayRect(Rect.fromLTWH(previewX, previewY, width, height)); - layout.setDraggingRect(Rect.fromLTWH(previewX, previewY, width, height)); - layout - .setDragStartLocation(Rect.fromLTWH(previewX, previewY, width, height)); - - addWidget(layout); - _containerDraggingIn = null; - - refresh(); - } - - void addNTDragInWidget(NTWidgetContainerModel widget, Offset globalPosition) { + void addDragInWidget(WidgetContainerModel widget, Offset globalPosition) { Offset localPosition = getLocalPosition(globalPosition); widget.setDraggingRect( Rect.fromLTWH( @@ -497,7 +446,7 @@ class TabGrid extends StatelessWidget { refresh(); } - void placeNTDragInWidget(NTWidgetContainerModel widget, + void placeDragInWidget(WidgetContainerModel widget, [bool fromLayout = false]) { if (_containerDraggingIn == null) { return; @@ -516,11 +465,12 @@ class TabGrid extends StatelessWidget { Rect previewLocation = Rect.fromLTWH(previewX, previewY, width, height); widget.setPreviewRect(previewLocation); - widget.updateMinimumSize(); + widget.tryCast()?.updateMinimumSize(); widget.setEnabled(ntConnection.isNT4Connected); // If dragging into layout - if (isValidLayoutLocation(widget.cursorGlobalLocation)) { + if (widget is NTWidgetContainerModel && + isValidLayoutLocation(widget.cursorGlobalLocation)) { LayoutContainerModel layoutContainer = getLayoutAtLocation(widget.cursorGlobalLocation)!; @@ -530,10 +480,12 @@ class TabGrid extends StatelessWidget { } else if (!isValidLocation(previewLocation)) { _containerDraggingIn = null; - widget.child.dispose(deleting: !fromLayout); - if (!fromLayout) { - widget.child.unSubscribe(); - widget.forceDispose(); + if (widget is NTWidgetContainerModel) { + widget.child.dispose(deleting: !fromLayout); + if (!fromLayout) { + widget.child.unSubscribe(); + widget.forceDispose(); + } } refresh(); @@ -547,7 +499,7 @@ class TabGrid extends StatelessWidget { _containerDraggingIn = null; - widget.child.dispose(); + widget.tryCast()?.child.dispose(); refresh(); } @@ -574,15 +526,18 @@ class TabGrid extends StatelessWidget { ); } - ListLayoutModel createListLayout() { + ListLayoutModel createListLayout( + {String title = 'List Layout', + List children = const []}) { return ListLayoutModel( - title: 'List Layout', + title: title, initialPosition: Rect.fromLTWH( 0.0, 0.0, Settings.gridSize.toDouble() * 2, Settings.gridSize.toDouble() * 2, ), + children: children, minWidth: 128.0 * 2, minHeight: 128.0 * 2, tabGrid: this,