8d95514897
From smart-serow: the DIN 1451 face (Alte DIN 1451 Mittelschrift, via gps-speedo) and GpsCompass. A cosmetic font is opt-in: widgets name hudFont in their own TextStyle, the rest keeps the basic font (CJK). Boxes and telltales shrink long labels instead of wrapping. Telltale and Reading get their own files; lib/README.md catalogues folders and widgets like smart-serow's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
95 lines
2.4 KiB
Dart
95 lines
2.4 KiB
Dart
// Ported from smart-serow (pi/ui/lib/widgets/gps_compass.dart), itself from android-stuff/gps-speedo:
|
|
// a heading arrow and its bearing. Changes: the palette import and lint fixes.
|
|
|
|
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../theme/palette.dart';
|
|
|
|
class GpsCompass extends StatelessWidget {
|
|
final double? heading;
|
|
final String? gpsState; // "acquiring", "fix", "lost"
|
|
|
|
const GpsCompass({super.key, this.heading, this.gpsState});
|
|
|
|
bool get _hasSignal => heading != null;
|
|
bool get _isAcquiring => gpsState == 'acquiring';
|
|
|
|
String get _displayHeading {
|
|
if (!_hasSignal) return 'N/A';
|
|
return '${(heading! % 360).round()}';
|
|
}
|
|
|
|
String get _compassDirection {
|
|
if (!_hasSignal) return '';
|
|
final directions = [
|
|
'N',
|
|
'NNE',
|
|
'NE',
|
|
'ENE',
|
|
'E',
|
|
'ESE',
|
|
'SE',
|
|
'SSE',
|
|
'S',
|
|
'SSW',
|
|
'SW',
|
|
'WSW',
|
|
'W',
|
|
'WNW',
|
|
'NW',
|
|
'NNW',
|
|
];
|
|
final index = ((heading! % 360) / 22.5).round() % 16;
|
|
return directions[index];
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = AppTheme.of(context);
|
|
|
|
// No signal = subdued color, valid = foreground
|
|
final iconColour = _hasSignal ? theme.foreground : theme.highlight;
|
|
|
|
// Convert to radians, 0 = no rotation when no signal
|
|
final angle = _hasSignal ? (heading! * math.pi / 180.0) : 0.0;
|
|
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Flexible(
|
|
flex: 3,
|
|
child: Transform.rotate(
|
|
angle: _hasSignal ? angle : 0,
|
|
child: FittedBox(
|
|
fit: BoxFit.contain,
|
|
child: Icon(
|
|
_hasSignal ? Icons.navigation : Icons.navigation_outlined,
|
|
size: 120,
|
|
color: iconColour,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Flexible(
|
|
flex: 1,
|
|
child: FittedBox(
|
|
fit: BoxFit.contain,
|
|
child: Text(
|
|
_hasSignal
|
|
? "$_displayHeading $_compassDirection"
|
|
: (_isAcquiring ? "ACQ" : "N/A"),
|
|
style: TextStyle(
|
|
fontSize: 80,
|
|
color: theme.subdued, // less emphasis on text, let the icon have semantic colour
|
|
fontFamily: hudFont,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|