/// UI-04: hosts every visible HUD telemetry widget over a given area, and owns /// entering/exiting edit mode -- the chrome (resize handles, the "Done" affordance) /// only exists while editing, per the ticket's own reasoning against permanent-on- /// screen edit chrome during a live ride. /// /// Deliberately has no idea what a metric's live *value* is -- that's `RecordUiState` /// (or equivalent), owned by whichever screen actually assembles a real HUD (UI-05). /// [metricBuilder] is how that content gets in without this widget depending on it. library; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../app/providers.dart'; import '../../hud/hud_metric.dart'; import 'draggable_resizable_hud_widget.dart'; class HudEditOverlay extends ConsumerStatefulWidget { const HudEditOverlay({super.key, required this.metricBuilder}); final Widget Function(BuildContext context, HudMetric metric) metricBuilder; @override ConsumerState createState() => _HudEditOverlayState(); } class _HudEditOverlayState extends ConsumerState { bool _editing = false; void _exitAndPersist() { setState(() => _editing = false); ref.read(hudLayoutControllerProvider.notifier).persist(); } @override Widget build(BuildContext context) { final layout = ref.watch(hudLayoutControllerProvider); final notifier = ref.read(hudLayoutControllerProvider.notifier); return LayoutBuilder( builder: (context, constraints) { final areaSize = Size(constraints.maxWidth, constraints.maxHeight); return GestureDetector( key: const Key('hud-edit-background'), behavior: HitTestBehavior.translucent, // Long-press empty HUD space to enter edit mode; while already editing, a // plain tap on empty space exits it (and persists) -- both per the ticket's // Design section. onLongPress: _editing ? null : () { HapticFeedback.selectionClick(); setState(() => _editing = true); }, onTap: _editing ? _exitAndPersist : null, child: Stack( clipBehavior: Clip.none, children: [ for (final entry in layout.entries) if (entry.value.visible) DraggableResizableHudWidget( key: ValueKey(entry.key), layout: entry.value, areaSize: areaSize, editing: _editing, onMoved: (col, row) => notifier.updatePosition(entry.key, col, row), onResized: (colSpan, rowSpan) => notifier.updateSize(entry.key, colSpan, rowSpan), child: widget.metricBuilder(context, entry.key), ), if (_editing) Positioned( top: 8, right: 8, child: FilledButton( key: const Key('hud-edit-done'), onPressed: _exitAndPersist, child: const Text('Done'), ), ), ], ), ); }, ); } }