Files
PilotVault/Fly App/lib/ui/fly/layers/video_layer.dart
T
tajniak81andClaude Opus 5.5 c1770fff73 Give the Fly App GO 4-style camera controls
One settings table in CameraBridge watches every camera value into the
telemetry tick and backs a generic setCameraValue, so the tabbed camera
menu (Exposure / Photo / Video / Other) is built from row specs and the
camera's own capability ranges. Writes show at once and fall back to what
the camera reports. Adds the quick exposure panel (Auto/A/S/M, ISO slider,
shutter and aperture wheels, EV strip, metered EV in M), a new camera
column with record timer and interval/pano progress, a zoom column for
zoom cameras, tap-to-focus / tap-to-meter, and a live histogram.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:57:38 +02:00

127 lines
4.2 KiB
Dart

import 'package:flutter/material.dart';
import '../../../fly/fly_prefs.dart';
import '../../dji_video_view.dart';
import '../../pv_icons.dart';
/// The live feed — or, with nothing connected, a painted stand-in so the HUD
/// can still be laid out and reviewed.
class VideoLayer extends StatelessWidget {
const VideoLayer({super.key, required this.connected});
final bool connected;
@override
Widget build(BuildContext context) =>
connected ? const DjiVideoView() : const CustomPaint(painter: FeedPlaceholderPainter());
}
/// The visual overlays drawn straight on the video: the grid and the centre
/// point, as set in the camera menu (`FlyPrefs`). Never takes touches.
class VisualOverlays extends StatelessWidget {
const VisualOverlays({super.key});
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: Stack(fit: StackFit.expand, children: <Widget>[
ValueListenableBuilder<GridStyle>(
valueListenable: FlyPrefs.grid,
builder: (BuildContext context, GridStyle g, _) =>
g == GridStyle.off ? const SizedBox.shrink() : CustomPaint(painter: GridPainter(diagonals: g == GridStyle.diagonals)),
),
ValueListenableBuilder<bool>(
valueListenable: FlyPrefs.centerPoint,
builder: (BuildContext context, bool on, _) => on
? const Center(child: PVIcon('crosshair', size: 30, stroke: 1.1, color: Color(0xB3FFFFFF)))
: const SizedBox.shrink(),
),
]),
);
}
}
/// Rule-of-thirds grid, optionally with the two diagonals.
class GridPainter extends CustomPainter {
const GridPainter({this.diagonals = false});
final bool diagonals;
@override
void paint(Canvas canvas, Size size) {
final Paint p = Paint()
..color = const Color(0x33FFFFFF)
..strokeWidth = 1;
for (int i = 1; i < 3; i++) {
final double x = size.width * i / 3, y = size.height * i / 3;
canvas.drawLine(Offset(x, 0), Offset(x, size.height), p);
canvas.drawLine(Offset(0, y), Offset(size.width, y), p);
}
if (diagonals) {
canvas.drawLine(Offset.zero, Offset(size.width, size.height), p);
canvas.drawLine(Offset(size.width, 0), Offset(0, size.height), p);
}
}
@override
bool shouldRepaint(covariant GridPainter oldDelegate) => oldDelegate.diagonals != diagonals;
}
/// Painted placeholder feed: graded sky → ground, haze, a perspective grid and
/// a few distant buildings.
class FeedPlaceholderPainter extends CustomPainter {
const FeedPlaceholderPainter();
@override
void paint(Canvas canvas, Size size) {
final double w = size.width, h = size.height;
final double horizon = h * 0.52;
final Rect full = Offset.zero & size;
canvas.drawRect(
full,
Paint()
..shader = const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Color(0xFF2A4E86), Color(0xFF3E6199), Color(0xFF1C2A1E), Color(0xFF0E1710)],
stops: <double>[0.0, 0.51, 0.54, 1.0],
).createShader(full),
);
final Rect hazeRect = Rect.fromLTWH(0, horizon - 30, w, 60);
canvas.drawRect(
hazeRect,
Paint()
..shader = const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Color(0x808FB4D8), Color(0x008FB4D8)],
).createShader(hazeRect),
);
final Paint bld = Paint()..color = const Color(0xE612201A);
void building(double x, double y, double bw, double bh) => canvas.drawRect(Rect.fromLTWH(x * w, horizon + y, bw, bh), bld);
building(0.10, -40, 46, 40);
building(0.17, -52, 30, 52);
building(0.74, -46, 54, 46);
building(0.83, -34, 34, 34);
final Paint grid = Paint()
..color = const Color(0x297FE0B0)
..strokeWidth = 1;
for (int i = 1; i <= 5; i++) {
final double t = i / 5.0;
final double y = horizon + (h - horizon) * t * t;
canvas.drawLine(Offset(0, y), Offset(w, y), grid);
}
final double vx = w / 2;
for (int k = -6; k <= 6; k += 2) {
canvas.drawLine(Offset(vx + k * 10, horizon), Offset(vx + k * (w * 0.16), h), grid);
}
}
@override
bool shouldRepaint(covariant FeedPlaceholderPainter oldDelegate) => false;
}