Skip to content

Commit

Permalink
Added a search query option for the addwidget dialog (#81)
Browse files Browse the repository at this point in the history
Wasnt sure how to write the test for this, if you (the reviewer) will be
able to explain me / do it ill be glad

---------

Co-authored-by: Gold87 <[email protected]>
  • Loading branch information
DanPeled and Gold872 authored Aug 18, 2024
1 parent bcb85f7 commit 8710c7f
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 75 deletions.
17 changes: 16 additions & 1 deletion lib/pages/dashboard_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import 'package:elastic_dashboard/services/shuffleboard_nt_listener.dart';
import 'package:elastic_dashboard/services/update_checker.dart';
import 'package:elastic_dashboard/util/tab_data.dart';
import 'package:elastic_dashboard/widgets/custom_appbar.dart';
import 'package:elastic_dashboard/widgets/dialog_widgets/dialog_text_input.dart';
import 'package:elastic_dashboard/widgets/dialog_widgets/dialog_toggle_switch.dart';
import 'package:elastic_dashboard/widgets/dialog_widgets/layout_drag_tile.dart';
import 'package:elastic_dashboard/widgets/draggable_containers/models/widget_container_model.dart';
Expand Down Expand Up @@ -1765,6 +1766,8 @@ class _AddWidgetDialog extends StatefulWidget {

class _AddWidgetDialogState extends State<_AddWidgetDialog> {
bool _hideMetadata = true;
String _searchQuery = '';

@override
Widget build(BuildContext context) {
return Visibility(
Expand Down Expand Up @@ -1802,6 +1805,7 @@ class _AddWidgetDialogState extends State<_AddWidgetDialog> {
NetworkTableTree(
ntConnection: widget.ntConnection,
preferences: widget.preferences,
searchQuery: _searchQuery,
listLayoutBuilder: (
{required title, required children}) {
return widget._grid().createListLayout(
Expand Down Expand Up @@ -1859,7 +1863,18 @@ class _AddWidgetDialogState extends State<_AddWidgetDialog> {
},
);
}),
const Spacer(),
Expanded(
child: SizedBox(
height: 40.0,
child: DialogTextInput(
onSubmit: (value) =>
setState(() => _searchQuery = value),
initialText: _searchQuery,
allowEmptySubmission: true,
label: "Search",
),
),
),
TextButton(
onPressed: () {
widget._onClose?.call();
Expand Down
3 changes: 3 additions & 0 deletions lib/widgets/dialog_widgets/dialog_text_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class DialogTextInput extends StatefulWidget {
final String? label;
final String? initialText;
final bool allowEmptySubmission;
final bool autoFocus;
final bool enabled;

final TextEditingController? textEditingController;
Expand All @@ -20,6 +21,7 @@ class DialogTextInput extends StatefulWidget {
this.enabled = true,
this.formatter,
this.textEditingController,
this.autoFocus = false,
});

@override
Expand Down Expand Up @@ -56,6 +58,7 @@ class _DialogTextInputState extends State<DialogTextInput> {
focused = value;
},
child: TextField(
autofocus: widget.autoFocus,
enabled: widget.enabled,
onSubmitted: (value) {
if (value.isNotEmpty || widget.allowEmptySubmission) {
Expand Down
189 changes: 115 additions & 74 deletions lib/widgets/network_tree/networktables_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class NetworkTableTree extends StatefulWidget {
final Function(Offset globalPosition, WidgetContainerModel widget)?
onDragUpdate;
final Function(WidgetContainerModel widget)? onDragEnd;

final String searchQuery;
final bool hideMetadata;

const NetworkTableTree({
Expand All @@ -37,6 +37,7 @@ class NetworkTableTree extends StatefulWidget {
required this.hideMetadata,
this.onDragUpdate,
this.onDragEnd,
this.searchQuery = "",
});

@override
Expand Down Expand Up @@ -65,22 +66,55 @@ class _NetworkTableTreeState extends State<NetworkTableTree> {
treeController = TreeController<NetworkTableTreeRow>(
roots: root.children,
childrenProvider: (node) {
if (widget.hideMetadata) {
return node.children
.whereNot((element) => element.rowName.startsWith('.'));
List<NetworkTableTreeRow> nodes = node.children;

// Apply the filter to the children
List<NetworkTableTreeRow> filteredChildren = _filterChildren(nodes);

// If there are any filtered children, include the parent node
if (filteredChildren.isNotEmpty || _matchesFilter(node)) {
if (widget.hideMetadata) {
return filteredChildren
.whereNot((element) => element.rowName.startsWith('.'))
.toList();
} else {
return filteredChildren;
}
} else {
return node.children;
return [];
}
},
);

widget.ntConnection.addTopicAnnounceListener(onNewTopicAnnounced = (topic) {
setState(() {
treeController.roots = _filterChildren(root.children);
treeController.rebuild();
});
});
}

List<NetworkTableTreeRow> _filterChildren(
List<NetworkTableTreeRow> children) {
// Apply the filter to each child
return children.where((child) {
if (_matchesFilter(child)) {
return true;
}
// Recursively check if any descendant matches the filter
return _filterChildren(child.children).isNotEmpty;
}).toList();
}

bool _matchesFilter(NetworkTableTreeRow node) {
// Don't filter if there isn't a search
if (widget.searchQuery.isEmpty) {
return true;
}
// Check if the node matches the filter
return node.topic.toLowerCase().contains(widget.searchQuery.toLowerCase());
}

@override
void dispose() {
widget.ntConnection.removeTopicAnnounceListener(onNewTopicAnnounced);
Expand All @@ -90,7 +124,9 @@ class _NetworkTableTreeState extends State<NetworkTableTree> {

@override
void didUpdateWidget(NetworkTableTree oldWidget) {
if (widget.hideMetadata != oldWidget.hideMetadata) {
if (widget.hideMetadata != oldWidget.hideMetadata ||
widget.searchQuery != oldWidget.searchQuery) {
treeController.roots = _filterChildren(root.children);
treeController.rebuild();
}
super.didUpdateWidget(oldWidget);
Expand Down Expand Up @@ -148,6 +184,8 @@ class _NetworkTableTreeState extends State<NetworkTableTree> {

root.sort();

treeController.roots = _filterChildren(root.children);

return TreeView<NetworkTableTreeRow>(
treeController: treeController,
nodeBuilder:
Expand Down Expand Up @@ -198,77 +236,80 @@ class TreeTile extends StatelessWidget {
Widget build(BuildContext context) {
TextStyle trailingStyle =
Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: onTap,
child: GestureDetector(
supportedDevices: PointerDeviceKind.values
.whereNot((element) => element == PointerDeviceKind.trackpad)
.toSet(),
onPanStart: (details) async {
if (draggingWidget != null) {
return;
}

draggingWidget = await entry.node
.toWidgetContainerModel(listLayoutBuilder: listLayoutBuilder);
},
onPanUpdate: (details) {
if (draggingWidget == null) {
return;
}

draggingWidget!.cursorGlobalLocation = details.globalPosition;

Offset position = details.globalPosition -
Offset(
draggingWidget!.displayRect.width,
draggingWidget!.displayRect.height,
) /
2;

onDragUpdate?.call(position, draggingWidget!);
},
onPanEnd: (details) {
if (draggingWidget == null) {
return;
}

onDragEnd?.call(draggingWidget!);

draggingWidget = null;
},
child: Padding(
padding: EdgeInsetsDirectional.only(start: entry.level * 16.0),
child: Column(
children: [
ListTile(
dense: true,
contentPadding: const EdgeInsets.only(right: 20.0),
leading:
(entry.hasChildren || entry.node.containsOnlyMetadata())
? FolderButton(
openedIcon: const Icon(Icons.arrow_drop_down),
closedIcon: const Icon(Icons.arrow_right),
iconSize: 24,
isOpen: entry.hasChildren && entry.isExpanded,
onPressed: entry.hasChildren ? onTap : null,
)
: const SizedBox(width: 8.0),
title: Text(entry.node.rowName),
trailing: (entry.node.ntTopic != null)
? Text(entry.node.ntTopic!.type, style: trailingStyle)
: null,
),
],
// I have absolutely no idea why Material is needed, but otherwise the tiles start bleeding all over the place, it makes zero sense
return Material(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: onTap,
child: GestureDetector(
supportedDevices: PointerDeviceKind.values
.whereNot((element) => element == PointerDeviceKind.trackpad)
.toSet(),
onPanStart: (details) async {
if (draggingWidget != null) {
return;
}

draggingWidget = await entry.node.toWidgetContainerModel(
listLayoutBuilder: listLayoutBuilder);
},
onPanUpdate: (details) {
if (draggingWidget == null) {
return;
}

draggingWidget!.cursorGlobalLocation = details.globalPosition;

Offset position = details.globalPosition -
Offset(
draggingWidget!.displayRect.width,
draggingWidget!.displayRect.height,
) /
2;

onDragUpdate?.call(position, draggingWidget!);
},
onPanEnd: (details) {
if (draggingWidget == null) {
return;
}

onDragEnd?.call(draggingWidget!);

draggingWidget = null;
},
child: Padding(
padding: EdgeInsetsDirectional.only(start: entry.level * 16.0),
child: Column(
children: [
ListTile(
dense: true,
contentPadding: const EdgeInsets.only(right: 20.0),
leading: (entry.hasChildren ||
entry.node.containsOnlyMetadata())
? FolderButton(
openedIcon: const Icon(Icons.arrow_drop_down),
closedIcon: const Icon(Icons.arrow_right),
iconSize: 24,
isOpen: entry.hasChildren && entry.isExpanded,
onPressed: entry.hasChildren ? onTap : null,
)
: const SizedBox(width: 8.0),
title: Text(entry.node.rowName),
trailing: (entry.node.ntTopic != null)
? Text(entry.node.ntTopic!.type, style: trailingStyle)
: null,
),
],
),
),
),
),
),
const Divider(height: 0),
],
const Divider(height: 0),
],
),
);
}
}
Loading

0 comments on commit 8710c7f

Please sign in to comment.