mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-13 17:30:31 -07:00
feat: Enhance subtitle handling with dynamic menu height adjustment
This commit is contained in:
parent
d60522b021
commit
1fdab92f1f
6 changed files with 47 additions and 20 deletions
|
|
@ -41,6 +41,9 @@ class DesktopControls extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DesktopControlsState extends ConsumerState<DesktopControls> {
|
class _DesktopControlsState extends ConsumerState<DesktopControls> {
|
||||||
|
// Add GlobalKey to measure bottom controls height
|
||||||
|
final GlobalKey _bottomControlsKey = GlobalKey();
|
||||||
|
|
||||||
late RestartableTimer timer = RestartableTimer(
|
late RestartableTimer timer = RestartableTimer(
|
||||||
const Duration(seconds: 5),
|
const Duration(seconds: 5),
|
||||||
() => mounted ? toggleOverlay(value: false) : null,
|
() => mounted ? toggleOverlay(value: false) : null,
|
||||||
|
|
@ -108,11 +111,17 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
|
||||||
timer.reset();
|
timer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method to get actual menu height
|
||||||
|
double? getBottomControlsHeight() {
|
||||||
|
final RenderBox? renderBox = _bottomControlsKey.currentContext?.findRenderObject() as RenderBox?;
|
||||||
|
return renderBox?.size.height;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final mediaSegments = ref.watch(playBackModel.select((value) => value?.mediaSegments));
|
final mediaSegments = ref.watch(playBackModel.select((value) => value?.mediaSegments));
|
||||||
final player = ref.watch(videoPlayerProvider);
|
final player = ref.watch(videoPlayerProvider);
|
||||||
final subtitleWidget = player.subtitleWidget(showOverlay);
|
final subtitleWidget = player.subtitleWidget(showOverlay, menuHeight: getBottomControlsHeight());
|
||||||
return InputHandler(
|
return InputHandler(
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
onKeyEvent: (node, event) => _onKey(event) ? KeyEventResult.handled : KeyEventResult.ignored,
|
onKeyEvent: (node, event) => _onKey(event) ? KeyEventResult.handled : KeyEventResult.ignored,
|
||||||
|
|
@ -293,6 +302,7 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
|
||||||
final mediaPlayback = ref.watch(mediaPlaybackProvider);
|
final mediaPlayback = ref.watch(mediaPlaybackProvider);
|
||||||
final bitRateOptions = ref.watch(playBackModel.select((value) => value?.bitRateOptions));
|
final bitRateOptions = ref.watch(playBackModel.select((value) => value?.bitRateOptions));
|
||||||
return Container(
|
return Container(
|
||||||
|
key: _bottomControlsKey, // Add key to measure height
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.bottomCenter,
|
begin: Alignment.bottomCenter,
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class LibMDK extends BasePlayer {
|
||||||
null;
|
null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget? subtitles(bool showOverlay) => null;
|
Widget? subtitles(bool showOverlay, {double? menuHeight}) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setVolume(double volume) async {}
|
Future<void> setVolume(double volume) async {}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,8 @@ class MediaControlsWrapper extends BaseAudioHandler {
|
||||||
Stream<PlayerState>? get stateStream => _player?.stateStream;
|
Stream<PlayerState>? get stateStream => _player?.stateStream;
|
||||||
PlayerState? get lastState => _player?.lastState;
|
PlayerState? get lastState => _player?.lastState;
|
||||||
|
|
||||||
Widget? subtitleWidget(bool showOverlay) => _player?.subtitles(showOverlay);
|
Widget? subtitleWidget(bool showOverlay, {double? menuHeight}) =>
|
||||||
|
_player?.subtitles(showOverlay, menuHeight: menuHeight);
|
||||||
Widget? videoWidget(Key key, BoxFit fit) => _player?.videoWidget(key, fit);
|
Widget? videoWidget(Key key, BoxFit fit) => _player?.videoWidget(key, fit);
|
||||||
|
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,9 @@ abstract class BasePlayer {
|
||||||
BoxFit fit,
|
BoxFit fit,
|
||||||
);
|
);
|
||||||
Widget? subtitles(
|
Widget? subtitles(
|
||||||
bool showOverlay,
|
bool showOverlay, {
|
||||||
);
|
double? menuHeight,
|
||||||
|
});
|
||||||
Future<void> dispose();
|
Future<void> dispose();
|
||||||
Future<void> open(String url, bool play);
|
Future<void> open(String url, bool play);
|
||||||
Future<void> seek(Duration position);
|
Future<void> seek(Duration position);
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ class LibMDK extends BasePlayer {
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget? subtitles(bool showOverlay) => null;
|
Widget? subtitles(bool showOverlay, {double? menuHeight}) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setVolume(double volume) async => _controller?.setVolume(volume / 100);
|
Future<void> setVolume(double volume) async => _controller?.setVolume(volume / 100);
|
||||||
|
|
|
||||||
|
|
@ -167,12 +167,14 @@ class LibMPV extends BasePlayer {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget? subtitles(
|
Widget? subtitles(
|
||||||
bool showOverlay,
|
bool showOverlay, {
|
||||||
) =>
|
double? menuHeight,
|
||||||
|
}) =>
|
||||||
_controller != null
|
_controller != null
|
||||||
? _VideoSubtitles(
|
? _VideoSubtitles(
|
||||||
controller: _controller!,
|
controller: _controller!,
|
||||||
showOverlay: showOverlay,
|
showOverlay: showOverlay,
|
||||||
|
menuHeight: menuHeight,
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
|
@ -196,10 +198,12 @@ class LibMPV extends BasePlayer {
|
||||||
class _VideoSubtitles extends ConsumerStatefulWidget {
|
class _VideoSubtitles extends ConsumerStatefulWidget {
|
||||||
final VideoController controller;
|
final VideoController controller;
|
||||||
final bool showOverlay;
|
final bool showOverlay;
|
||||||
|
final double? menuHeight;
|
||||||
|
|
||||||
const _VideoSubtitles({
|
const _VideoSubtitles({
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.showOverlay = false,
|
this.showOverlay = false,
|
||||||
|
this.menuHeight,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -207,9 +211,9 @@ class _VideoSubtitles extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VideoSubtitlesState extends ConsumerState<_VideoSubtitles> {
|
class _VideoSubtitlesState extends ConsumerState<_VideoSubtitles> {
|
||||||
// Promote constants to static for better readability and flexibility
|
// Keep fallback constants for when dynamic height isn't available
|
||||||
static const double _menuAreaThreshold = 0.15; // Bottom 15% typically contains controls
|
static const double _fallbackMenuHeightPercentage = 0.15; // 15% fallback
|
||||||
static const double _menuAvoidanceOffset = 0.1; // Move up by 10% when needed
|
static const double _subtitlePadding = 0.005; // 0.5% padding above menu
|
||||||
static const double _maxSubtitleOffset = 0.85; // Max 85% up from bottom
|
static const double _maxSubtitleOffset = 0.85; // Max 85% up from bottom
|
||||||
|
|
||||||
late List<String> subtitle = widget.controller.player.state.subtitle;
|
late List<String> subtitle = widget.controller.player.state.subtitle;
|
||||||
|
|
@ -233,23 +237,34 @@ class _VideoSubtitlesState extends ConsumerState<_VideoSubtitles> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate subtitle offset based on menu visibility
|
/// Calculate subtitle offset using actual menu height when available
|
||||||
double _calculateSubtitleOffset(SubtitleSettingsModel settings) {
|
double _calculateSubtitleOffset(SubtitleSettingsModel settings) {
|
||||||
if (!widget.showOverlay) {
|
if (!widget.showOverlay) {
|
||||||
return settings.verticalOffset;
|
return settings.verticalOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If subtitles are already positioned above the menu area, leave them alone
|
final screenHeight = MediaQuery.of(context).size.height;
|
||||||
if (settings.verticalOffset >= _menuAreaThreshold) {
|
double menuHeightPercentage;
|
||||||
|
|
||||||
|
if (widget.menuHeight != null && screenHeight > 0) {
|
||||||
|
// Convert menu height to percentage (without extra padding here)
|
||||||
|
menuHeightPercentage = widget.menuHeight! / screenHeight;
|
||||||
|
} else {
|
||||||
|
// Fallback to static percentage
|
||||||
|
menuHeightPercentage = _fallbackMenuHeightPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the minimum safe position (menu height + small padding)
|
||||||
|
final minSafeOffset = menuHeightPercentage + _subtitlePadding;
|
||||||
|
|
||||||
|
// If subtitles are already positioned above the safe area, leave them alone
|
||||||
|
if (settings.verticalOffset >= minSafeOffset) {
|
||||||
return settings.verticalOffset;
|
return settings.verticalOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When menu is visible and subtitles are in the menu area,
|
// Instead of replacing user offset, use the minimum safe position
|
||||||
// move them up slightly to avoid overlap
|
// This ensures subtitles are just above the menu, not way up high
|
||||||
final adjustedOffset = settings.verticalOffset + _menuAvoidanceOffset;
|
return math.max(minSafeOffset, math.min(settings.verticalOffset, _maxSubtitleOffset));
|
||||||
|
|
||||||
// Clamp to reasonable bounds (don't go too high or too low)
|
|
||||||
return math.max(0.0, math.min(adjustedOffset, _maxSubtitleOffset));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue