import 'package:flutter/material.dart'; import 'package:gomix_flutter/edit_port_dialog.dart'; import 'package:gomix_flutter/mixer_state.dart' as mixer; enum MenuAction { edit, delete } class PortsTab extends StatefulWidget { final mixer.MixerState mixerState; final void Function(Map data) sendAction; final bool Function(mixer.Port port, bool isOutput)? filter; final void Function(mixer.Port port)? selectionCallback; const PortsTab( {super.key, required this.mixerState, required this.sendAction, this.filter, this.selectionCallback}); @override State createState() => _PortsTabState(); } class _PortsTabState extends State { final searchController = TextEditingController(); String searchText = ""; void onEditPort(mixer.Port port) { CreatePortDialog().show(context, widget.sendAction, port, false); } void onDeletePort(mixer.Port port) { showDialog( context: context, builder: (BuildContext context) => AlertDialog( title: const Text("Delete Port"), content: Text("Are you sure you want to delete ${port.name}?"), actions: [ TextButton( onPressed: () => Navigator.pop(context, 'Cancel'), child: const Text('Cancel'), ), TextButton( onPressed: () { widget.sendAction({"method": "deletePort", "uuid": port.uuid}); Navigator.pop(context, 'Delete'); }, child: const Text("Delete"), ), ], ), ); } @override Widget build(BuildContext context) { return SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ TextField( decoration: InputDecoration( prefixIcon: const Icon(Icons.search), suffixIcon: IconButton( icon: const Icon(Icons.clear), onPressed: () { setState(() { searchController.text = ""; searchText = ""; }); }, ), labelText: 'Search', border: const OutlineInputBorder(), ), onChanged: (val) { setState(() { searchText = val; }); }, controller: searchController), const SizedBox(height: 10), ...[...widget.mixerState.inputs, ...widget.mixerState.outputs] .indexed .where((elem) => widget.filter != null ? widget.filter!( elem.$2, (elem.$1 >= widget.mixerState.inputs.length)) : true) .where((elem) => elem.$2.name .toLowerCase() .contains(searchText.toLowerCase())) .map( (elem) => Card( child: ListTile( leading: (elem.$1 >= widget.mixerState.inputs.length) ? const Icon(Icons.speaker_outlined) : const Icon(Icons.mic_none_outlined), title: Text(elem.$2.name), subtitle: Text('Backend: ${elem.$2.properties.backend}'), onTap: widget.selectionCallback != null ? () => widget.selectionCallback!(elem.$2) : null, trailing: PopupMenuButton( tooltip: "More Options", onSelected: (MenuAction item) { switch (item) { case MenuAction.edit: onEditPort(elem.$2); case MenuAction.delete: onDeletePort(elem.$2); } }, itemBuilder: (BuildContext context) => >[ const PopupMenuItem( value: MenuAction.edit, child: ListTile( leading: Icon(Icons.mode_edit), title: Text('Edit'), ), ), const PopupMenuItem( value: MenuAction.delete, child: ListTile( leading: Icon(Icons.delete), title: Text('Delete'), ), ), ], ), ), ), ) ], ), ), ); } }