vm conf editor added
This commit is contained in:
parent
c3011cf944
commit
4ec263414d
|
|
@ -0,0 +1,21 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
class VmConfig {
|
||||||
|
final String name;
|
||||||
|
final File file;
|
||||||
|
String rawContent = '';
|
||||||
|
|
||||||
|
VmConfig({required this.name, required String confPath})
|
||||||
|
: file = File(confPath);
|
||||||
|
|
||||||
|
Future<void> load() async {
|
||||||
|
if (file.existsSync()) {
|
||||||
|
rawContent = await file.readAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> save(String updatedContent) async {
|
||||||
|
rawContent = updatedContent;
|
||||||
|
await file.writeAsString(rawContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ import '../globals.dart';
|
||||||
import '../mixins/preferences_mixin.dart';
|
import '../mixins/preferences_mixin.dart';
|
||||||
import '../model/osicons.dart';
|
import '../model/osicons.dart';
|
||||||
import '../model/vminfo.dart';
|
import '../model/vminfo.dart';
|
||||||
|
import '../pages/vm_config_editor.dart';
|
||||||
|
|
||||||
/// VM manager page.
|
/// VM manager page.
|
||||||
/// Displays a list of available VMs, running state and connection info,
|
/// Displays a list of available VMs, running state and connection info,
|
||||||
|
|
@ -28,6 +29,7 @@ class Manager extends StatefulWidget {
|
||||||
|
|
||||||
class _ManagerState extends State<Manager> with PreferencesMixin {
|
class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
List<String> _currentVms = [];
|
List<String> _currentVms = [];
|
||||||
|
Map<String, String> _vmConfFiles = {};
|
||||||
Map<String, VmInfo> _activeVms = {};
|
Map<String, VmInfo> _activeVms = {};
|
||||||
bool _spicy = false;
|
bool _spicy = false;
|
||||||
final List<String> _sshVms = [];
|
final List<String> _sshVms = [];
|
||||||
|
|
@ -146,6 +148,7 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
|
|
||||||
void _getVms(context) async {
|
void _getVms(context) async {
|
||||||
List<String> currentVms = [];
|
List<String> currentVms = [];
|
||||||
|
Map<String, String> vmConfFiles = {};
|
||||||
Map<String, VmInfo> activeVms = {};
|
Map<String, VmInfo> activeVms = {};
|
||||||
|
|
||||||
await for (var entity
|
await for (var entity
|
||||||
|
|
@ -153,6 +156,7 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
if ((entity.path.endsWith('.conf')) && (_isValidConf(entity.path))) {
|
if ((entity.path.endsWith('.conf')) && (_isValidConf(entity.path))) {
|
||||||
String name = path.basenameWithoutExtension(entity.path);
|
String name = path.basenameWithoutExtension(entity.path);
|
||||||
currentVms.add(name);
|
currentVms.add(name);
|
||||||
|
vmConfFiles[name] = entity.path;
|
||||||
File pidFile = File('$name/$name.pid');
|
File pidFile = File('$name/$name.pid');
|
||||||
if (pidFile.existsSync()) {
|
if (pidFile.existsSync()) {
|
||||||
String pid = pidFile.readAsStringSync().trim();
|
String pid = pidFile.readAsStringSync().trim();
|
||||||
|
|
@ -172,6 +176,7 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
currentVms.sort();
|
currentVms.sort();
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentVms = currentVms;
|
_currentVms = currentVms;
|
||||||
|
_vmConfFiles = vmConfFiles;
|
||||||
_activeVms = activeVms;
|
_activeVms = activeVms;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -291,6 +296,21 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.settings, semanticLabel: 'Settings'),
|
||||||
|
tooltip: context.t('Edit configuration'),
|
||||||
|
onPressed: active
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => VmConfigEditorDialog(
|
||||||
|
vmName: currentVm,
|
||||||
|
confPath: _vmConfFiles[currentVm]!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
active ? Icons.play_arrow : Icons.play_arrow_outlined,
|
active ? Icons.play_arrow : Icons.play_arrow_outlined,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import '../model/vm_config.dart';
|
||||||
|
|
||||||
|
class VmConfigEditorDialog extends StatefulWidget {
|
||||||
|
final String vmName;
|
||||||
|
final String confPath;
|
||||||
|
|
||||||
|
const VmConfigEditorDialog({
|
||||||
|
super.key,
|
||||||
|
required this.vmName,
|
||||||
|
required this.confPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<VmConfigEditorDialog> createState() => _VmConfigEditorDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VmConfigEditorDialogState extends State<VmConfigEditorDialog> {
|
||||||
|
late VmConfig config;
|
||||||
|
final TextEditingController _editorController = TextEditingController();
|
||||||
|
bool _loading = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
config = VmConfig(name: widget.vmName, confPath: widget.confPath);
|
||||||
|
config.load().then((_) {
|
||||||
|
_editorController.text = config.rawContent;
|
||||||
|
setState(() => _loading = false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _save() async {
|
||||||
|
await config.save(_editorController.text);
|
||||||
|
if(!mounted) return;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _openDocs() async {
|
||||||
|
const url = 'https://github.com/quickemu-project/quickemu/blob/master/docs/quickemu_conf.5.md';
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('Edit VM: ${widget.vmName}'),
|
||||||
|
content: _loading
|
||||||
|
? const SizedBox(height: 100, child: Center(child: CircularProgressIndicator()))
|
||||||
|
: SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(config.file.path,
|
||||||
|
style: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: TextButton.icon(
|
||||||
|
icon: const Icon(Icons.help_outline),
|
||||||
|
label: const Text("Open config documentation"),
|
||||||
|
onPressed: _openDocs,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Flexible(
|
||||||
|
child: TextField(
|
||||||
|
controller: _editorController,
|
||||||
|
maxLines: null,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
style: const TextStyle(fontFamily: 'monospace'),
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding: EdgeInsets.all(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _save,
|
||||||
|
child: const Text('Save'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue