Plan P5: a value bubble on the pitch slider while dragging, press-and-drag on the video as a speed joystick (re-sent at 10 Hz, zero on release), and a gimbal settings sheet: mode, 30° extension, yaw follow, recenter / face down / yaw recenter, roll fine-tune, auto calibration and advanced speed/smoothness rows gated by the gimbal's capability ranges. GimbalBridge reports mode, trim, calibration and stop/overload state in the gimbal telemetry group, and getter-only settings in a new gimbalSettings slow group. The reported-plus-pending overlay moves out of CameraState into a SettingsOverlay mixin shared by camera and gimbal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
54 lines
1.8 KiB
Dart
54 lines
1.8 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
|
|
import 'parse.dart';
|
|
|
|
/// Settings as a component reported them, overlaid with the HUD's optimistic
|
|
/// writes (plan P4, reused by the gimbal in P5).
|
|
///
|
|
/// A pending value stands until the component reports that setting changed,
|
|
/// the write fails, or the writer gives up waiting ([dropPending]) — so a
|
|
/// value the component silently refused snaps back.
|
|
mixin SettingsOverlay on ChangeNotifier {
|
|
Map<String, dynamic> _reported = const <String, dynamic>{};
|
|
final Map<String, Object?> _pending = <String, Object?>{};
|
|
|
|
/// What the component reported, overlaid with pending writes.
|
|
Map<String, dynamic> get settings => _pending.isEmpty ? _reported : <String, dynamic>{..._reported, ..._pending};
|
|
|
|
/// Only what the component itself reported.
|
|
Map<String, dynamic> get reportedSettings => _reported;
|
|
|
|
bool isPending(String id) => _pending.containsKey(id);
|
|
|
|
Object? setting(String id) => settings[id];
|
|
|
|
/// Shows [values] ahead of the component's read-back.
|
|
void setPending(Map<String, Object?> values) {
|
|
_pending.addAll(values);
|
|
notifyListeners();
|
|
}
|
|
|
|
/// Rolls [ids] back to what the component last reported.
|
|
void dropPending(Iterable<String> ids) {
|
|
bool changed = false;
|
|
for (final String id in ids) {
|
|
changed = _pending.remove(id) != null || changed;
|
|
}
|
|
if (changed) notifyListeners();
|
|
}
|
|
|
|
/// Takes a new report; a pending write is answered once its setting moves.
|
|
/// Does not notify — the caller does, with its other fields.
|
|
@protected
|
|
void replaceReported(Map<String, dynamic> reported) {
|
|
_pending.removeWhere((String id, _) => !deepEquals(_reported[id], reported[id]));
|
|
_reported = reported;
|
|
}
|
|
|
|
@protected
|
|
void clearSettings() {
|
|
_reported = const <String, dynamic>{};
|
|
_pending.clear();
|
|
}
|
|
}
|