It seems this patch does not apply anymore due to f263217. On Wed Jul 30, 2025 at 2:37 PM CEST, Shan Shaji wrote: > On the options page for VMs and CTs it was easy to change the > configs by mistake. To avoid that, added an edit button on the top > of the screen. The toggle buttons will only be enabled if the edit > button is clicked. > > Suggested-by: Thomas Lamprecht <t.lampre...@proxmox.com> > Signed-off-by: Shan Shaji <s.sh...@proxmox.com> > --- > lib/bloc/pve_lxc_overview_bloc.dart | 11 +++++ > lib/bloc/pve_qemu_overview_bloc.dart | 11 +++++ > lib/states/pve_lxc_overview_state.dart | 21 ++++++---- > lib/states/pve_qemu_overview_state.dart | 21 ++++++---- > lib/widgets/pve_config_switch_list_tile.dart | 8 +++- > lib/widgets/pve_icon_button_widget.dart | 43 ++++++++++++++++++++ > lib/widgets/pve_lxc_options_widget.dart | 16 +++++++- > lib/widgets/pve_lxc_overview.dart | 22 ++++++---- > lib/widgets/pve_qemu_options_widget.dart | 17 ++++++++ > lib/widgets/pve_qemu_overview.dart | 2 +- > 10 files changed, 143 insertions(+), 29 deletions(-) > create mode 100644 lib/widgets/pve_icon_button_widget.dart > > diff --git a/lib/bloc/pve_lxc_overview_bloc.dart > b/lib/bloc/pve_lxc_overview_bloc.dart > index e287f97..b856006 100644 > --- a/lib/bloc/pve_lxc_overview_bloc.dart > +++ b/lib/bloc/pve_lxc_overview_bloc.dart > @@ -89,6 +89,11 @@ class PveLxcOverviewBloc > yield latestState.rebuild((b) => b..errorMessage = ''); > } > } > + > + if (event is LockLxcOptions) { > + yield latestState > + .rebuild((b) => b..isOptionsLocked = event.isLockOptions); > + } > } > > Future<List<PveGuestRRDdataModel>> _preProcessRRDdata() async { > @@ -131,3 +136,9 @@ class RevertPendingLxcConfig extends PveLxcOverviewEvent { > > RevertPendingLxcConfig(this.cField); > } > + > +class LockLxcOptions extends PveLxcOverviewEvent { > + final bool isLockOptions; > + > + LockLxcOptions(this.isLockOptions); > +} > diff --git a/lib/bloc/pve_qemu_overview_bloc.dart > b/lib/bloc/pve_qemu_overview_bloc.dart > index 3d0fd0e..98b1261 100644 > --- a/lib/bloc/pve_qemu_overview_bloc.dart > +++ b/lib/bloc/pve_qemu_overview_bloc.dart > @@ -94,6 +94,11 @@ class PveQemuOverviewBloc > yield latestState.rebuild((b) => b..errorMessage = ''); > } > } > + > + if (event is LockQemuOptions) { > + yield latestState > + .rebuild((b) => b..isOptionsLocked = event.isLockOptions); > + } > } > > Future<List<PveGuestRRDdataModel>> _preProcessRRDdata() async { > @@ -136,3 +141,9 @@ class RevertPendingQemuConfig extends > PveQemuOverviewEvent { > > RevertPendingQemuConfig(this.cField); > } > + > +class LockQemuOptions extends PveQemuOverviewEvent { > + final bool isLockOptions; > + > + LockQemuOptions(this.isLockOptions); > +} > \ No newline at end of file > diff --git a/lib/states/pve_lxc_overview_state.dart > b/lib/states/pve_lxc_overview_state.dart > index c10c2e7..a162121 100644 > --- a/lib/states/pve_lxc_overview_state.dart > +++ b/lib/states/pve_lxc_overview_state.dart > @@ -10,6 +10,7 @@ abstract class PveLxcOverviewState > implements Built<PveLxcOverviewState, PveLxcOverviewStateBuilder> { > // Fields > String get nodeID; > + bool get isOptionsLocked; > PveNodesLxcStatusModel? get currentStatus; > BuiltList<PveGuestRRDdataModel>? get rrdData; > PveNodesLxcConfigModel? get config; > @@ -20,13 +21,15 @@ abstract class PveLxcOverviewState > [void Function(PveLxcOverviewStateBuilder) updates]) = > _$PveLxcOverviewState; > > - factory PveLxcOverviewState.init(String nodeID) => > - PveLxcOverviewState((b) => b > - //base > - ..errorMessage = '' > - ..isBlank = true > - ..isLoading = false > - ..isSuccess = false > - //class > - ..nodeID = nodeID); > + factory PveLxcOverviewState.init(String nodeID) => PveLxcOverviewState( > + (b) => b > + //base > + ..errorMessage = '' > + ..isBlank = true > + ..isLoading = false > + ..isSuccess = false > + //class > + ..nodeID = nodeID > + ..isOptionsLocked = true, > + ); > } > diff --git a/lib/states/pve_qemu_overview_state.dart > b/lib/states/pve_qemu_overview_state.dart > index 43201bc..8d8dd96 100644 > --- a/lib/states/pve_qemu_overview_state.dart > +++ b/lib/states/pve_qemu_overview_state.dart > @@ -8,6 +8,7 @@ abstract class PveQemuOverviewState > with PveBaseState > implements Built<PveQemuOverviewState, PveQemuOverviewStateBuilder> { > String get nodeID; > + bool get isOptionsLocked; > PveQemuStatusModel? get currentStatus; > BuiltList<PveGuestRRDdataModel>? get rrdData; > PveNodesQemuConfigModel? get config; > @@ -18,13 +19,15 @@ abstract class PveQemuOverviewState > [void Function(PveQemuOverviewStateBuilder) updates]) = > _$PveQemuOverviewState; > > - factory PveQemuOverviewState.init(String nodeID) => > - PveQemuOverviewState((b) => b > - //base > - ..errorMessage = '' > - ..isBlank = true > - ..isLoading = false > - ..isSuccess = false > - //class > - ..nodeID = nodeID); > + factory PveQemuOverviewState.init(String nodeID) => PveQemuOverviewState( > + (b) => b > + //base > + ..errorMessage = '' > + ..isBlank = true > + ..isLoading = false > + ..isSuccess = false > + //class > + ..nodeID = nodeID > + ..isOptionsLocked = true, > + ); > } > diff --git a/lib/widgets/pve_config_switch_list_tile.dart > b/lib/widgets/pve_config_switch_list_tile.dart > index c209fbe..1f2e6c0 100644 > --- a/lib/widgets/pve_config_switch_list_tile.dart > +++ b/lib/widgets/pve_config_switch_list_tile.dart > @@ -7,6 +7,7 @@ class PveConfigSwitchListTile extends StatelessWidget { > final Widget? title; > final ValueChanged<bool>? onChanged; > final VoidCallback? onDeleted; > + final bool disable; > > const PveConfigSwitchListTile({ > super.key, > @@ -16,6 +17,7 @@ class PveConfigSwitchListTile extends StatelessWidget { > this.title, > this.onChanged, > this.onDeleted, > + this.disable = false, > }); > @override > Widget build(BuildContext context) { > @@ -26,7 +28,11 @@ class PveConfigSwitchListTile extends StatelessWidget { > return SwitchListTile( > title: _getTitle(), > value: pBool ?? value ?? defaultValue!, > - onChanged: pending != null ? null : onChanged, > + onChanged: disable > + ? null > + : pending != null > + ? null > + : onChanged, > ); > } > > diff --git a/lib/widgets/pve_icon_button_widget.dart > b/lib/widgets/pve_icon_button_widget.dart > new file mode 100644 > index 0000000..0d30ce8 > --- /dev/null > +++ b/lib/widgets/pve_icon_button_widget.dart > @@ -0,0 +1,43 @@ > +import 'package:flutter/material.dart'; > + > +class PveIconButton extends StatelessWidget { > + final IconData icon; > + final String label; > + final Color? color; > + final VoidCallback? onPressed; > + > + const PveIconButton({ > + super.key, > + required this.icon, > + required this.label, > + this.color, > + this.onPressed, > + }); > + > + const PveIconButton.edit({ > + super.key, > + this.color, > + this.onPressed, > + }) : icon = Icons.edit, > + label = 'Edit'; > + > + @override > + Widget build(BuildContext context) { > + return TextButton( > + onPressed: onPressed, > + child: Row( > + spacing: 2, > + children: [ > + Icon( > + icon, > + color: color, > + ), > + Text( > + label, > + style: TextStyle(color: color), > + ), > + ], > + ), > + ); > + } > +} > diff --git a/lib/widgets/pve_lxc_options_widget.dart > b/lib/widgets/pve_lxc_options_widget.dart > index 913f4b1..9165020 100644 > --- a/lib/widgets/pve_lxc_options_widget.dart > +++ b/lib/widgets/pve_lxc_options_widget.dart > @@ -4,6 +4,7 @@ import > 'package:pve_flutter_frontend/states/pve_lxc_overview_state.dart'; > import 'package:pve_flutter_frontend/widgets/colored_safe_area.dart'; > import > 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart'; > import > 'package:pve_flutter_frontend/widgets/pve_config_switch_list_tile.dart'; > +import 'package:pve_flutter_frontend/widgets/pve_icon_button_widget.dart'; > > class PveLxcOptions extends StatelessWidget { > final PveLxcOverviewBloc? lxcBloc; > @@ -18,7 +19,17 @@ class PveLxcOptions extends StatelessWidget { > if (config != null) { > return ColoredSafeArea( > child: Scaffold( > - appBar: AppBar(), > + appBar: AppBar( > + actions: [ > + if (state.isOptionsLocked) > + PveIconButton.edit( > + color: Theme.of(context).colorScheme.onPrimary, > + onPressed: () => lxcBloc!.events.add( > + LockLxcOptions(false), > + ), > + ) > + ], > + ), > body: SingleChildScrollView( > child: Column( > children: <Widget>[ > @@ -27,6 +38,7 @@ class PveLxcOptions extends StatelessWidget { > subtitle: Text(config.hostname ?? 'undefined'), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("Start on boot"), > value: config.onboot, > defaultValue: false, > @@ -49,6 +61,7 @@ class PveLxcOptions extends StatelessWidget { > subtitle: Text("${config.arch}"), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("/dev/console"), > value: config.console, > defaultValue: true, > @@ -67,6 +80,7 @@ class PveLxcOptions extends StatelessWidget { > subtitle: Text(config.cmode?.name ?? 'tty'), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("Protection"), > value: config.protection, > defaultValue: false, > diff --git a/lib/widgets/pve_lxc_overview.dart > b/lib/widgets/pve_lxc_overview.dart > index 684dfb6..b49c6ea 100644 > --- a/lib/widgets/pve_lxc_overview.dart > +++ b/lib/widgets/pve_lxc_overview.dart > @@ -153,14 +153,20 @@ class PveLxcOverview extends StatelessWidget { > state.nodeID, > 'lxc')), > createActionCard( > - 'Options', > - Icons.settings, > - () => Navigator.of(context) > - .push(MaterialPageRoute( > - builder: (context) => > PveLxcOptions( > - lxcBloc: lxcBloc, > - ), > - fullscreenDialog: true))), > + 'Options', > + Icons.settings, > + () { > + return Navigator.of(context).push( > + MaterialPageRoute( > + builder: (context) => PveLxcOptions( > + lxcBloc: lxcBloc > + ..events.add(LockLxcOptions(true)), > + ), > + fullscreenDialog: true, > + ), > + ); > + }, > + ), > if (!resourceBloc.latestState.isStandalone) > createActionCard( > 'Migrate', > diff --git a/lib/widgets/pve_qemu_options_widget.dart > b/lib/widgets/pve_qemu_options_widget.dart > index bb1e11a..60b60b3 100644 > --- a/lib/widgets/pve_qemu_options_widget.dart > +++ b/lib/widgets/pve_qemu_options_widget.dart > @@ -5,6 +5,7 @@ import > 'package:pve_flutter_frontend/states/pve_qemu_overview_state.dart'; > import 'package:pve_flutter_frontend/widgets/colored_safe_area.dart'; > import > 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart'; > import > 'package:pve_flutter_frontend/widgets/pve_config_switch_list_tile.dart'; > +import 'package:pve_flutter_frontend/widgets/pve_icon_button_widget.dart'; > > class PveQemuOptions extends StatelessWidget { > final String guestID; > @@ -26,6 +27,15 @@ class PveQemuOptions extends StatelessWidget { > icon: const Icon(Icons.close), > onPressed: () => Navigator.of(context).pop(), > ), > + actions: [ > + if (state.isOptionsLocked) > + PveIconButton.edit( > + onPressed: () => bloc.events.add( > + LockQemuOptions(false), > + ), > + color: Theme.of(context).colorScheme.onPrimary, > + ) > + ], > ), > body: SingleChildScrollView( > child: Form( > @@ -38,6 +48,7 @@ class PveQemuOptions extends StatelessWidget { > subtitle: Text(config.name ?? 'VM$guestID'), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("Start on boot"), > value: config.onboot, > defaultValue: false, > @@ -63,6 +74,7 @@ class PveQemuOptions extends StatelessWidget { > subtitle: Text(config.boot ?? 'Disk, Network, > USB'), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("Use tablet for pointer"), > value: config.tablet, > defaultValue: true, > @@ -77,6 +89,7 @@ class PveQemuOptions extends StatelessWidget { > subtitle: Text(config.hotplug ?? > 'disk,network,usb'), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("ACPI support"), > value: config.acpi, > defaultValue: true, > @@ -87,6 +100,7 @@ class PveQemuOptions extends StatelessWidget { > > bloc.events.add(RevertPendingQemuConfig('acpi')), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("KVM hardware virtualization"), > value: config.kvm, > defaultValue: true, > @@ -97,6 +111,7 @@ class PveQemuOptions extends StatelessWidget { > > bloc.events.add(RevertPendingQemuConfig('kvm')), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("Freeze CPU on startup"), > value: config.freeze, > defaultValue: false, > @@ -107,6 +122,7 @@ class PveQemuOptions extends StatelessWidget { > .add(RevertPendingQemuConfig('freeze')), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("Use local time for RTC"), > value: config.localtime, > defaultValue: false, > @@ -130,6 +146,7 @@ class PveQemuOptions extends StatelessWidget { > subtitle: Text(config.agent ?? 'Default > (disabled)'), > ), > PveConfigSwitchListTile( > + disable: state.isOptionsLocked, > title: const Text("Protection"), > value: config.protection, > defaultValue: false, > diff --git a/lib/widgets/pve_qemu_overview.dart > b/lib/widgets/pve_qemu_overview.dart > index aa91bcc..d347722 100644 > --- a/lib/widgets/pve_qemu_overview.dart > +++ b/lib/widgets/pve_qemu_overview.dart > @@ -259,7 +259,7 @@ class PveQemuOverview extends StatelessWidget { > Route _createOptionsRoute(PveQemuOverviewBloc bloc) { > return PageRouteBuilder( > pageBuilder: (context, animation, secondaryAnimation) => > Provider.value( > - value: bloc, > + value: bloc..events.add(LockQemuOptions(true)), > child: PveQemuOptions( > guestID: guestID, > ),
_______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel