✨ Implement creating, deleting and editing of ports
This commit is contained in:
103
lib/edit_port_dialog.dart
Normal file
103
lib/edit_port_dialog.dart
Normal file
@ -0,0 +1,103 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gomix_flutter/mixer_state.dart';
|
||||
import 'package:gomix_flutter/select_button.dart';
|
||||
|
||||
const List<DropdownMenuEntry<String>> availableBackends = [
|
||||
DropdownMenuEntry(value: "jack", label: "Jack")
|
||||
];
|
||||
|
||||
const portTypes = ["Input", "Output"];
|
||||
|
||||
class CreatePortDialog {
|
||||
String portType = portTypes[0].toLowerCase();
|
||||
final GlobalKey<FormState> _portDialogForm = GlobalKey<FormState>();
|
||||
|
||||
void show(
|
||||
BuildContext context,
|
||||
final void Function(Map<String, Object> data) sendAction,
|
||||
Port editPortRef,
|
||||
bool isNewPort) {
|
||||
Port editPort = Port.fromJson(editPortRef.toJson());
|
||||
showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
title: isNewPort ? const Text("Create Port") : const Text("Edit Port"),
|
||||
content:
|
||||
StatefulBuilder(builder: (BuildContext ctx, StateSetter setState) {
|
||||
return SizedBox(
|
||||
width: min(MediaQuery.of(context).size.width, 400),
|
||||
child: Form(
|
||||
key: _portDialogForm,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Name", border: OutlineInputBorder()),
|
||||
onSaved: (String? value) {
|
||||
editPort.name = value!;
|
||||
},
|
||||
initialValue: editPort.name,
|
||||
),
|
||||
] +
|
||||
(isNewPort
|
||||
? [
|
||||
const SizedBox(height: 15),
|
||||
SelectButton(
|
||||
options: portTypes,
|
||||
defaultIndex: 0,
|
||||
allowNull: false,
|
||||
onChange: (int selection) {
|
||||
portType = portTypes[selection].toLowerCase();
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
DropdownMenu<String>(
|
||||
initialSelection: availableBackends[0].value,
|
||||
onSelected: (String? value) {
|
||||
editPort.properties.backend = value!;
|
||||
},
|
||||
label: const Text("Backend"),
|
||||
dropdownMenuEntries: availableBackends,
|
||||
),
|
||||
]
|
||||
: [])),
|
||||
),
|
||||
);
|
||||
}),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, 'Cancel'),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_portDialogForm.currentState?.save();
|
||||
if (isNewPort) {
|
||||
sendAction({
|
||||
"method": "createPort",
|
||||
"portData": {
|
||||
"name": editPort.name,
|
||||
"backend": editPort.properties.backend,
|
||||
"channels": 2,
|
||||
"type": portType
|
||||
}
|
||||
});
|
||||
} else {
|
||||
sendAction({
|
||||
"method": "editPort",
|
||||
"UUID": editPort.uuid,
|
||||
"portProperties": {"name": editPort.name}
|
||||
});
|
||||
}
|
||||
Navigator.pop(context, 'Update');
|
||||
},
|
||||
child: isNewPort ? const Text("Create") : const Text("Update"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gomix_flutter/connection_selector.dart';
|
||||
import 'package:gomix_flutter/edit_port_dialog.dart';
|
||||
import 'package:gomix_flutter/mixer_state.dart' as mixer;
|
||||
import 'package:gomix_flutter/mixing_tab.dart';
|
||||
import 'package:gomix_flutter/ports_tab.dart';
|
||||
@ -243,7 +244,8 @@ class _GoMixHomeState extends State<GoMixHome> {
|
||||
mixerState: mixerState,
|
||||
sendAction: sendAction,
|
||||
),
|
||||
const PortsTab(),
|
||||
PortsTab(
|
||||
mixerState: mixerState, sendAction: sendAction),
|
||||
const SettingsTab(),
|
||||
][currentPageIndex];
|
||||
}),
|
||||
@ -275,6 +277,25 @@ class _GoMixHomeState extends State<GoMixHome> {
|
||||
return const SizedBox();
|
||||
}),
|
||||
]),
|
||||
floatingActionButton: currentPageIndex == 1
|
||||
? FloatingActionButton(
|
||||
tooltip: "Create Port",
|
||||
child: const Icon(Icons.add),
|
||||
onPressed: () {
|
||||
mixer.Port newPort = mixer.Port(
|
||||
uuid: "",
|
||||
name: "",
|
||||
properties:
|
||||
mixer.Properties(backend: "jack", channels: 2),
|
||||
state:
|
||||
mixer.State(mute: false, volume: 1, balance: 1),
|
||||
route: []);
|
||||
|
||||
CreatePortDialog()
|
||||
.show(context, sendAction, newPort, true);
|
||||
},
|
||||
)
|
||||
: null,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -1,35 +1,95 @@
|
||||
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 {
|
||||
const PortsTab({super.key});
|
||||
final mixer.MixerState mixerState;
|
||||
final void Function(Map<String, Object> data) sendAction;
|
||||
const PortsTab(
|
||||
{super.key, required this.mixerState, required this.sendAction});
|
||||
|
||||
@override
|
||||
State<PortsTab> createState() => _PortsTabState();
|
||||
}
|
||||
|
||||
class _PortsTabState extends State<PortsTab> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Card(
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.mic_none),
|
||||
title: Text('Port 1'),
|
||||
subtitle: Text('...'),
|
||||
),
|
||||
void onEditPort(mixer.Port port) {
|
||||
CreatePortDialog().show(context, widget.sendAction, port, false);
|
||||
}
|
||||
|
||||
void onDeletePort(mixer.Port port) {
|
||||
showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
title: const Text("Delete Port"),
|
||||
content: Text("Are you sure you want to delete ${port.name}?"),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, 'Cancel'),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
Card(
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.speaker_outlined),
|
||||
title: Text('Port 2'),
|
||||
subtitle: Text('...'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
widget.sendAction({"method": "deletePort", "uuid": port.uuid});
|
||||
Navigator.pop(context, 'Delete');
|
||||
},
|
||||
child: const Text("Delete"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [...widget.mixerState.inputs, ...widget.mixerState.outputs]
|
||||
.indexed
|
||||
.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}'),
|
||||
trailing: PopupMenuButton<MenuAction>(
|
||||
tooltip: "More Options",
|
||||
onSelected: (MenuAction item) {
|
||||
switch (item) {
|
||||
case MenuAction.edit:
|
||||
onEditPort(elem.$2);
|
||||
case MenuAction.delete:
|
||||
onDeletePort(elem.$2);
|
||||
}
|
||||
},
|
||||
itemBuilder: (BuildContext context) =>
|
||||
<PopupMenuEntry<MenuAction>>[
|
||||
const PopupMenuItem<MenuAction>(
|
||||
value: MenuAction.edit,
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.mode_edit),
|
||||
title: Text('Edit'),
|
||||
),
|
||||
),
|
||||
const PopupMenuItem<MenuAction>(
|
||||
value: MenuAction.delete,
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.delete),
|
||||
title: Text('Delete'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
54
lib/select_button.dart
Normal file
54
lib/select_button.dart
Normal file
@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SelectButton extends StatefulWidget {
|
||||
final List<String> options;
|
||||
final int defaultIndex;
|
||||
final bool allowNull;
|
||||
final bool isDisabled;
|
||||
final void Function(int selection) onChange;
|
||||
const SelectButton(
|
||||
{super.key,
|
||||
required this.options,
|
||||
this.defaultIndex = 0,
|
||||
this.allowNull = true,
|
||||
this.isDisabled = false,
|
||||
required this.onChange});
|
||||
|
||||
@override
|
||||
State<SelectButton> createState() => _SelectButtonState();
|
||||
}
|
||||
|
||||
class _SelectButtonState extends State<SelectButton> {
|
||||
int _selection = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selection = widget.defaultIndex;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Wrap(
|
||||
spacing: 5.0,
|
||||
children: List<Widget>.generate(
|
||||
widget.options.length,
|
||||
(int index) {
|
||||
return ChoiceChip(
|
||||
label: Text(widget.options[index]),
|
||||
selected: _selection == index,
|
||||
onSelected: (bool selected) {
|
||||
if (widget.isDisabled) return;
|
||||
if (!selected && !widget.allowNull) return;
|
||||
|
||||
setState(() {
|
||||
_selection = selected ? index : -1;
|
||||
widget.onChange(_selection);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user