Files
plezy/lib/widgets/video_controls/helpers/track_selection_helper.dart
T

155 lines
4.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:plezy/widgets/app_icon.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../../../i18n/strings.g.dart';
import '../../../mpv/mpv.dart';
import '../../../theme/mono_tokens.dart';
import '../../../utils/track_label_builder.dart';
import '../../../widgets/focusable_list_tile.dart';
class TrackSelectionHelper {
/// Get the appropriate empty message based on track type
static String getEmptyMessage<T>() {
if (T == SubtitleTrack) {
return t.videoControls.noSubtitlesAvailable;
} else if (T == AudioTrack) {
return t.videoControls.noAudioTracksAvailable;
}
return t.videoControls.noTracksAvailable;
}
static Widget buildEmptyState<T>() {
return Center(child: Text(getEmptyMessage<T>()));
}
/// Check if "Off" is selected for a track
static bool isOffSelected<T>(T? selectedTrack, bool Function(T track)? isOffTrack) {
return selectedTrack == null || (isOffTrack?.call(selectedTrack) ?? false);
}
static String getTrackId<T>(T track) {
if (track is AudioTrack) {
return track.id;
} else if (track is SubtitleTrack) {
return track.id;
}
return '';
}
static Widget buildOffTile<T>({
required BuildContext context,
required bool isSelected,
required VoidCallback onTap,
Key? key,
FocusNode? focusNode,
VoidCallback? onLongPress,
VoidCallback? onSecondaryTap,
Widget? badge,
}) {
return _buildSelectableTile(
context: context,
key: key,
label: t.common.off,
isSelected: isSelected,
onTap: onTap,
focusNode: focusNode,
onLongPress: onLongPress,
onSecondaryTap: onSecondaryTap,
badge: badge,
);
}
static Widget buildTrackTile<T>({
required BuildContext context,
required TrackLabel label,
required bool isSelected,
required VoidCallback onTap,
Key? key,
FocusNode? focusNode,
VoidCallback? onLongPress,
VoidCallback? onSecondaryTap,
Widget? badge,
}) {
return _buildSelectableTile(
context: context,
key: key,
label: label.primary,
secondaryLabel: label.secondary,
isSelected: isSelected,
onTap: onTap,
focusNode: focusNode,
onLongPress: onLongPress,
onSecondaryTap: onSecondaryTap,
badge: badge,
);
}
/// Build a numbered badge for primary/secondary subtitle indicators.
static Widget buildTrackBadge(BuildContext context, int number) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
width: 18,
height: 18,
decoration: BoxDecoration(color: colorScheme.primary, borderRadius: BorderRadius.circular(4)),
alignment: .center,
child: Text(
number.toString(),
style: TextStyle(color: colorScheme.onPrimary, fontSize: 11, fontWeight: .bold),
),
);
}
static Widget _buildSelectableTile({
required BuildContext context,
required String label,
required bool isSelected,
required VoidCallback onTap,
String? secondaryLabel,
Key? key,
FocusNode? focusNode,
VoidCallback? onLongPress,
VoidCallback? onSecondaryTap,
Widget? badge,
}) {
final primaryColor = Theme.of(context).colorScheme.primary;
Widget? trailing;
if (badge != null) {
trailing = badge;
} else if (isSelected) {
trailing = AppIcon(Symbols.check_rounded, fill: 1, color: primaryColor);
}
Widget tile = FocusableListTile(
key: key,
focusNode: focusNode,
selected: isSelected,
title: Text(
label,
style: TextStyle(color: isSelected ? primaryColor : null),
maxLines: 1,
overflow: .ellipsis,
),
subtitle: secondaryLabel == null
? null
: Text(
secondaryLabel,
style: TextStyle(
color: isSelected ? primaryColor.withValues(alpha: 0.7) : tokens(context).textMuted,
fontSize: 12,
),
maxLines: 1,
overflow: .ellipsis,
),
trailing: trailing,
onTap: onTap,
onLongPress: onLongPress,
);
if (onSecondaryTap != null) {
tile = GestureDetector(onSecondaryTap: onSecondaryTap, child: tile);
}
return tile;
}
}