✨ 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:flutter/material.dart';
|
||||||
import 'package:gomix_flutter/connection_selector.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/mixer_state.dart' as mixer;
|
||||||
import 'package:gomix_flutter/mixing_tab.dart';
|
import 'package:gomix_flutter/mixing_tab.dart';
|
||||||
import 'package:gomix_flutter/ports_tab.dart';
|
import 'package:gomix_flutter/ports_tab.dart';
|
||||||
@ -243,7 +244,8 @@ class _GoMixHomeState extends State<GoMixHome> {
|
|||||||
mixerState: mixerState,
|
mixerState: mixerState,
|
||||||
sendAction: sendAction,
|
sendAction: sendAction,
|
||||||
),
|
),
|
||||||
const PortsTab(),
|
PortsTab(
|
||||||
|
mixerState: mixerState, sendAction: sendAction),
|
||||||
const SettingsTab(),
|
const SettingsTab(),
|
||||||
][currentPageIndex];
|
][currentPageIndex];
|
||||||
}),
|
}),
|
||||||
@ -275,6 +277,25 @@ class _GoMixHomeState extends State<GoMixHome> {
|
|||||||
return const SizedBox();
|
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: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 {
|
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
|
@override
|
||||||
State<PortsTab> createState() => _PortsTabState();
|
State<PortsTab> createState() => _PortsTabState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PortsTabState extends State<PortsTab> {
|
class _PortsTabState extends State<PortsTab> {
|
||||||
@override
|
void onEditPort(mixer.Port port) {
|
||||||
Widget build(BuildContext context) {
|
CreatePortDialog().show(context, widget.sendAction, port, false);
|
||||||
return const Padding(
|
}
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Column(
|
void onDeletePort(mixer.Port port) {
|
||||||
children: <Widget>[
|
showDialog<String>(
|
||||||
Card(
|
context: context,
|
||||||
child: ListTile(
|
builder: (BuildContext context) => AlertDialog(
|
||||||
leading: Icon(Icons.mic_none),
|
title: const Text("Delete Port"),
|
||||||
title: Text('Port 1'),
|
content: Text("Are you sure you want to delete ${port.name}?"),
|
||||||
subtitle: Text('...'),
|
actions: <Widget>[
|
||||||
),
|
TextButton(
|
||||||
),
|
onPressed: () => Navigator.pop(context, 'Cancel'),
|
||||||
Card(
|
child: const Text('Cancel'),
|
||||||
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