vm conf editor added

This commit is contained in:
Deepak Mahat 2025-03-28 16:51:30 +10:30
parent c3011cf944
commit 4ec263414d
3 changed files with 138 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -15,6 +15,7 @@ import '../globals.dart';
import '../mixins/preferences_mixin.dart';
import '../model/osicons.dart';
import '../model/vminfo.dart';
import '../pages/vm_config_editor.dart';
/// VM manager page.
/// 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 {
List<String> _currentVms = [];
Map<String, String> _vmConfFiles = {};
Map<String, VmInfo> _activeVms = {};
bool _spicy = false;
final List<String> _sshVms = [];
@ -146,6 +148,7 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
void _getVms(context) async {
List<String> currentVms = [];
Map<String, String> vmConfFiles = {};
Map<String, VmInfo> activeVms = {};
await for (var entity
@ -153,6 +156,7 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
if ((entity.path.endsWith('.conf')) && (_isValidConf(entity.path))) {
String name = path.basenameWithoutExtension(entity.path);
currentVms.add(name);
vmConfFiles[name] = entity.path;
File pidFile = File('$name/$name.pid');
if (pidFile.existsSync()) {
String pid = pidFile.readAsStringSync().trim();
@ -172,6 +176,7 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
currentVms.sort();
setState(() {
_currentVms = currentVms;
_vmConfFiles = vmConfFiles;
_activeVms = activeVms;
});
}
@ -291,6 +296,21 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
trailing: Row(
mainAxisSize: MainAxisSize.min,
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(
icon: Icon(
active ? Icons.play_arrow : Icons.play_arrow_outlined,

View File

@ -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'),
),
],
);
}
}