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

Reply via email to