166 lines
5.1 KiB
Dart
166 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../session/status_snapshot.dart';
|
|
import '../session/view_state.dart';
|
|
import '../transport/connection_state.dart';
|
|
import 'app_scope.dart';
|
|
|
|
class StatusBar extends StatelessWidget {
|
|
const StatusBar({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final scope = AppScope.of(context);
|
|
return Material(
|
|
color: Theme.of(context).colorScheme.surface,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
child: ValueListenableBuilder<StatusSnapshot>(
|
|
valueListenable: scope.session.statusSnapshot,
|
|
builder: (_, snap, __) {
|
|
return Wrap(
|
|
spacing: 14,
|
|
runSpacing: 6,
|
|
crossAxisAlignment: WrapCrossAlignment.center,
|
|
children: [
|
|
_ConnectionPill(snap.connection, scope.settings.wsUrl),
|
|
_Sep(),
|
|
_PpsPill(snap.pps),
|
|
_Sep(),
|
|
_PausePill(scope.session.pauseSource),
|
|
_Sep(),
|
|
const Text('Status:',
|
|
style: TextStyle(fontSize: 11, color: Colors.grey)),
|
|
...List.generate(8, (i) {
|
|
return _StatusPill(
|
|
name: scope.layout.statusNames[i],
|
|
value: snap.statusValues[i],
|
|
);
|
|
}),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _Sep extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) =>
|
|
Container(width: 1, height: 14, color: Colors.grey.shade400);
|
|
}
|
|
|
|
class _ConnectionPill extends StatelessWidget {
|
|
const _ConnectionPill(this.state, this.url);
|
|
final WsConnectionState state;
|
|
final String url;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final (color, label) = switch (state) {
|
|
WsConnectionState.connected => (Colors.green, 'WS connected'),
|
|
WsConnectionState.connecting => (Colors.orange, 'Connecting'),
|
|
WsConnectionState.reconnecting => (Colors.orange, 'Reconnecting'),
|
|
WsConnectionState.disconnected => (Colors.grey, 'Disconnected'),
|
|
};
|
|
return Row(mainAxisSize: MainAxisSize.min, children: [
|
|
_Dot(color),
|
|
const SizedBox(width: 6),
|
|
Text(label, style: const TextStyle(fontSize: 11)),
|
|
const SizedBox(width: 6),
|
|
Text(url,
|
|
style: const TextStyle(
|
|
fontFamily: 'monospace', fontSize: 11, color: Colors.grey)),
|
|
]);
|
|
}
|
|
}
|
|
|
|
class _PpsPill extends StatelessWidget {
|
|
const _PpsPill(this.pps);
|
|
final double pps;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Row(mainAxisSize: MainAxisSize.min, children: [
|
|
const Text('PPS ',
|
|
style: TextStyle(fontSize: 11, color: Colors.grey)),
|
|
Text(pps.round().toString(),
|
|
style: const TextStyle(
|
|
fontFamily: 'monospace',
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w500)),
|
|
]);
|
|
}
|
|
}
|
|
|
|
class _PausePill extends StatelessWidget {
|
|
const _PausePill(this.source);
|
|
final PauseSource source;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (source == PauseSource.none) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
final label = switch (source) {
|
|
PauseSource.user => 'Paused (user)',
|
|
PauseSource.proto => 'Paused (proto)',
|
|
PauseSource.both => 'Paused (user + proto)',
|
|
PauseSource.none => '',
|
|
};
|
|
return Row(mainAxisSize: MainAxisSize.min, children: [
|
|
_Dot(Colors.orange),
|
|
const SizedBox(width: 6),
|
|
Text(label, style: const TextStyle(fontSize: 11)),
|
|
]);
|
|
}
|
|
}
|
|
|
|
class _StatusPill extends StatelessWidget {
|
|
const _StatusPill({required this.name, required this.value});
|
|
final String name;
|
|
final int? value;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final (bg, fg, dot, text) = switch (value) {
|
|
null => (Colors.grey.shade200, Colors.grey.shade700, Colors.grey,
|
|
'$name · ?'),
|
|
0 => (Colors.grey.shade200, Colors.grey.shade700, Colors.grey,
|
|
'$name · 0'),
|
|
1 => (Colors.red.shade100, Colors.red.shade900, Colors.red,
|
|
'$name · 1'),
|
|
2 => (Colors.amber.shade100, Colors.amber.shade900, Colors.amber,
|
|
'$name · 2'),
|
|
3 => (Colors.green.shade100, Colors.green.shade900, Colors.green,
|
|
'$name · 3'),
|
|
_ => (Colors.grey.shade200, Colors.grey.shade700, Colors.grey,
|
|
'$name · $value'),
|
|
};
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color: bg,
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
|
_Dot(dot, size: 7),
|
|
const SizedBox(width: 5),
|
|
Text(text, style: TextStyle(fontSize: 10, color: fg)),
|
|
]),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _Dot extends StatelessWidget {
|
|
const _Dot(this.color, {this.size = 8});
|
|
final Color color;
|
|
final double size;
|
|
@override
|
|
Widget build(BuildContext context) => Container(
|
|
width: size,
|
|
height: size,
|
|
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
|
);
|
|
} |