From 8e7631630d07ecca80113e25de92cf33793bfb9a Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Tue, 28 May 2024 06:47:42 -0400 Subject: [PATCH 01/23] marquee effect on song title --- lib/components/PlayerScreen/song_name.dart | 54 +++++++-- lib/components/now_playing_bar.dart | 131 +++++++++++++++------ lib/components/scrolling_text.dart | 128 ++++++++++++++++++++ 3 files changed, 271 insertions(+), 42 deletions(-) create mode 100644 lib/components/scrolling_text.dart diff --git a/lib/components/PlayerScreen/song_name.dart b/lib/components/PlayerScreen/song_name.dart index a81a38cb9..bda23924e 100644 --- a/lib/components/PlayerScreen/song_name.dart +++ b/lib/components/PlayerScreen/song_name.dart @@ -1,4 +1,5 @@ import 'package:audio_service/audio_service.dart'; +import 'package:finamp/components/scrolling_text.dart'; import 'package:finamp/models/jellyfin_models.dart'; import 'package:finamp/screens/artist_screen.dart'; import 'package:finamp/services/finamp_settings_helper.dart'; @@ -130,14 +131,51 @@ class SongNameContent extends StatelessWidget { ), ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), - Text( - mediaItem == null - ? AppLocalizations.of(context)!.noItem - : mediaItem!.title, - style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600), - overflow: TextOverflow.fade, - softWrap: false, - maxLines: 1, + // Text( + // mediaItem == null + // ? AppLocalizations.of(context)!.noItem + // : mediaItem!.title, + // style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600), + // overflow: TextOverflow.fade, + // softWrap: false, + // maxLines: 1, + // ), + LayoutBuilder( + builder: (context, constraints) { + final textPainter = TextPainter( + text: TextSpan( + text: mediaItem == null + ? AppLocalizations.of(context)!.noItem + : mediaItem!.title, + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.w600), + ), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(maxWidth: constraints.maxWidth); + + final isOverflowing = textPainter.didExceedMaxLines; + + return Container( + width: constraints.maxWidth, + child: isOverflowing + ? ScrollingText( + text: mediaItem == null + ? AppLocalizations.of(context)!.noItem + : mediaItem!.title, + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.w600), + ) + : Text( + mediaItem == null + ? AppLocalizations.of(context)!.noItem + : mediaItem!.title, + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.w600), + overflow: TextOverflow.ellipsis, + ), + ); + }, ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), RichText( diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index 41572caa7..e3b58f43e 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:audio_service/audio_service.dart'; import 'package:finamp/color_schemes.g.dart'; import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart'; +import 'package:finamp/components/scrolling_text.dart'; import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/services/current_track_metadata_provider.dart'; import 'package:finamp/services/feedback_helper.dart'; @@ -46,25 +47,20 @@ class NowPlayingBar extends ConsumerWidget { ]); Color getProgressBackgroundColor(BuildContext context) { - return FinampSettingsHelper.finampSettings.showProgressOnNowPlayingBar ? - Color.alphaBlend( - Theme.of(context).brightness == Brightness.dark - ? IconTheme.of(context) - .color! - .withOpacity(0.35) - : IconTheme.of(context) - .color! - .withOpacity(0.5), - Theme.of(context).brightness == - Brightness.dark - ? Colors.black - : Colors.white - ) : - IconTheme.of(context).color!.withOpacity(0.85); + return FinampSettingsHelper.finampSettings.showProgressOnNowPlayingBar + ? Color.alphaBlend( + Theme.of(context).brightness == Brightness.dark + ? IconTheme.of(context).color!.withOpacity(0.35) + : IconTheme.of(context).color!.withOpacity(0.5), + Theme.of(context).brightness == Brightness.dark + ? Colors.black + : Colors.white) + : IconTheme.of(context).color!.withOpacity(0.85); } Widget buildLoadingQueueBar(BuildContext context, Function()? retryCallback) { - final progressBackgroundColor = getProgressBackgroundColor(context).withOpacity(0.5); + final progressBackgroundColor = + getProgressBackgroundColor(context).withOpacity(0.5); return SimpleGestureDetector( onVerticalSwipe: (direction) { @@ -138,7 +134,6 @@ class NowPlayingBar extends ConsumerWidget { Widget buildNowPlayingBar( BuildContext context, FinampQueueItem currentTrack) { - final audioHandler = GetIt.instance(); Duration? playbackPosition; @@ -149,7 +144,7 @@ class NowPlayingBar extends ConsumerWidget { : null; final progressBackgroundColor = getProgressBackgroundColor(context); - + return SafeArea( child: Padding( padding: const EdgeInsets.only(left: 12.0, bottom: 12.0, right: 12.0), @@ -266,7 +261,8 @@ class NowPlayingBar extends ConsumerWidget { Expanded( child: Stack( children: [ - if (FinampSettingsHelper.finampSettings.showProgressOnNowPlayingBar) + if (FinampSettingsHelper.finampSettings + .showProgressOnNowPlayingBar) Positioned( left: 0, top: 0, @@ -279,7 +275,8 @@ class NowPlayingBar extends ConsumerWidget { playbackPosition = snapshot.data; final screenSize = - MediaQuery.of(context).size; + MediaQuery.of(context) + .size; return Container( // rather hacky workaround, using LayoutBuilder would be nice but I couldn't get it to work... width: max( @@ -298,17 +295,20 @@ class NowPlayingBar extends ConsumerWidget { .inMilliseconds)), height: albumImageSize, decoration: ShapeDecoration( - color: IconTheme.of(context) - .color! - .withOpacity(0.75), + color: IconTheme.of( + context) + .color! + .withOpacity(0.75), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topRight: - Radius.circular(12), + Radius.circular( + 12), bottomRight: - Radius.circular(12), + Radius.circular( + 12), ), ), ), @@ -335,15 +335,78 @@ class NowPlayingBar extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - currentTrack.item.title, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: - FontWeight.w500, - overflow: TextOverflow - .ellipsis), + // Text( + // currentTrack.item.title, + // style: const TextStyle( + // color: Colors.white, + // fontSize: 16, + // fontWeight: + // FontWeight.w500, + // overflow: TextOverflow + // .ellipsis), + // ), + LayoutBuilder( + builder: + (context, constraints) { + final textPainter = + TextPainter( + text: TextSpan( + text: snapshot.data! + .mediaItem!.title, + style: + const TextStyle( + fontSize: 16, + fontWeight: + FontWeight.w600, + ), + ), + maxLines: 1, + textDirection: + TextDirection.ltr, + )..layout( + maxWidth: + constraints + .maxWidth); + + final isOverflowing = + textPainter + .didExceedMaxLines; + + return Container( + width: constraints + .maxWidth, + child: isOverflowing + ? ScrollingText( + text: snapshot + .data! + .mediaItem! + .title, + style: + const TextStyle( + fontSize: 16, + fontWeight: + FontWeight + .w600, + ), + ) + : Text( + snapshot + .data! + .mediaItem! + .title, + style: + const TextStyle( + fontSize: 16, + fontWeight: + FontWeight + .w600, + ), + overflow: + TextOverflow + .ellipsis, + ), + ); + }, ), const SizedBox(height: 4), Row( diff --git a/lib/components/scrolling_text.dart b/lib/components/scrolling_text.dart new file mode 100644 index 000000000..ef2030ef2 --- /dev/null +++ b/lib/components/scrolling_text.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; + +class ScrollingText extends StatefulWidget { + final String text; + final TextStyle? style; + final double blankSpace; + final double velocity; + final Duration pauseDuration; + + const ScrollingText({ + Key? key, + required this.text, + this.style, + this.blankSpace = 20.0, + this.velocity = 25.0, + this.pauseDuration = const Duration(seconds: 3), + }) : super(key: key); + + @override + _ScrollingTextState createState() => _ScrollingTextState(); +} + +class _ScrollingTextState extends State + with TickerProviderStateMixin { + late ScrollController _scrollController; + late AnimationController _animationController; + late double _textWidth; + late double _containerWidth; + late double _animationDistance; + final Duration extraScrollDuration = const Duration(milliseconds: 450); + + @override + void initState() { + super.initState(); + _scrollController = ScrollController(); + _animationController = AnimationController(vsync: this); + WidgetsBinding.instance.addPostFrameCallback((_) { + _startScrolling(); + }); + } + + @override + void didUpdateWidget(covariant ScrollingText oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.text != widget.text) { + _resetAndStartScrolling(); + } + } + + @override + void dispose() { + _scrollController.dispose(); + _animationController.dispose(); + super.dispose(); + } + + void _resetAndStartScrolling() { + _animationController.stop(); + _scrollController.jumpTo(0.0); + WidgetsBinding.instance.addPostFrameCallback((_) { + _startScrolling(); + }); + } + + void _startScrolling() { + if (!_scrollController.hasClients) return; + + _scrollController.jumpTo(0.0); + final totalDistance = _animationDistance + + (widget.velocity * extraScrollDuration.inMilliseconds / 1000); + final totalDuration = Duration( + milliseconds: (totalDistance / widget.velocity * 1000).toInt()); + + Future.delayed(widget.pauseDuration, () { + if (!_scrollController.hasClients) return; + _animationController.duration = totalDuration; + _animationController.forward(from: 0.0).then((_) { + if (_scrollController.hasClients) { + _scrollController.jumpTo(0.0); + _startScrolling(); + } + }); + + _animationController.addListener(() { + if (_scrollController.hasClients) { + _scrollController.jumpTo(_animationController.value * totalDistance); + } + }); + }); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final textPainter = TextPainter( + text: TextSpan( + text: widget.text, + style: widget.style, + ), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(maxWidth: double.infinity); + + _textWidth = textPainter.width; + _containerWidth = constraints.maxWidth; + _animationDistance = _textWidth + widget.blankSpace; + + if (_textWidth <= _containerWidth) { + return Text(widget.text, style: widget.style); + } + + return SingleChildScrollView( + controller: _scrollController, + scrollDirection: Axis.horizontal, + physics: const NeverScrollableScrollPhysics(), + child: Row( + children: [ + Text(widget.text, style: widget.style), + SizedBox(width: widget.blankSpace), + Text(widget.text, style: widget.style), + ], + ), + ); + }, + ); + } +} From 27708aaf81f9f51316f7b1a4558d488d6ba2c072 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Tue, 28 May 2024 08:30:58 -0400 Subject: [PATCH 02/23] marquee in song menu for overflow, instead of two lines --- lib/components/PlayerScreen/song_name.dart | 57 +++---------- .../PlayerScreen/song_name_content.dart | 77 +++++++++++------ lib/components/now_playing_bar.dart | 85 +++---------------- lib/services/scrolling_text_helper.dart | 46 ++++++++++ 4 files changed, 123 insertions(+), 142 deletions(-) create mode 100644 lib/services/scrolling_text_helper.dart diff --git a/lib/components/PlayerScreen/song_name.dart b/lib/components/PlayerScreen/song_name.dart index bda23924e..23f9f0137 100644 --- a/lib/components/PlayerScreen/song_name.dart +++ b/lib/components/PlayerScreen/song_name.dart @@ -3,6 +3,7 @@ import 'package:finamp/components/scrolling_text.dart'; import 'package:finamp/models/jellyfin_models.dart'; import 'package:finamp/screens/artist_screen.dart'; import 'package:finamp/services/finamp_settings_helper.dart'; +import 'package:finamp/services/scrolling_text_helper.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -131,51 +132,17 @@ class SongNameContent extends StatelessWidget { ), ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), - // Text( - // mediaItem == null - // ? AppLocalizations.of(context)!.noItem - // : mediaItem!.title, - // style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600), - // overflow: TextOverflow.fade, - // softWrap: false, - // maxLines: 1, - // ), - LayoutBuilder( - builder: (context, constraints) { - final textPainter = TextPainter( - text: TextSpan( - text: mediaItem == null - ? AppLocalizations.of(context)!.noItem - : mediaItem!.title, - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.w600), - ), - maxLines: 1, - textDirection: TextDirection.ltr, - )..layout(maxWidth: constraints.maxWidth); - - final isOverflowing = textPainter.didExceedMaxLines; - - return Container( - width: constraints.maxWidth, - child: isOverflowing - ? ScrollingText( - text: mediaItem == null - ? AppLocalizations.of(context)!.noItem - : mediaItem!.title, - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.w600), - ) - : Text( - mediaItem == null - ? AppLocalizations.of(context)!.noItem - : mediaItem!.title, - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.w600), - overflow: TextOverflow.ellipsis, - ), - ); - }, + ScrollingTextHelper( + text: mediaItem == null + ? AppLocalizations.of(context)!.noItem + : mediaItem!.title, + style: TextStyle( + fontSize: 24, + height: 26 / 20, + fontWeight: Theme.of(context).brightness == Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), RichText( diff --git a/lib/components/PlayerScreen/song_name_content.dart b/lib/components/PlayerScreen/song_name_content.dart index 5b97139e8..c70a4301a 100644 --- a/lib/components/PlayerScreen/song_name_content.dart +++ b/lib/components/PlayerScreen/song_name_content.dart @@ -1,10 +1,10 @@ -import 'package:balanced_text/balanced_text.dart'; +import 'package:finamp/components/scrolling_text.dart'; +import 'package:flutter/material.dart'; import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart'; import 'package:finamp/components/PlayerScreen/player_buttons_more.dart'; import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/models/jellyfin_models.dart' as jellyfin_models; import 'package:finamp/screens/player_screen.dart'; -import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import '../../services/queue_service.dart'; @@ -32,7 +32,6 @@ class SongNameContent extends StatelessWidget { } final currentTrack = snapshot.data!.currentTrack!; - final jellyfin_models.BaseItemDto? songBaseItemDto = currentTrack.baseItem; @@ -49,30 +48,58 @@ class SongNameContent extends StatelessWidget { child: Container( alignment: Alignment.center, constraints: BoxConstraints( - maxHeight: - controller.shouldShow(PlayerHideable.twoLineTitle) - ? 52 - : 24, + maxHeight: 24, maxWidth: 280, ), - child: BalancedText( - currentTrack.item.title, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20, - height: 26 / 20, - fontWeight: - Theme.of(context).brightness == Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - overflow: TextOverflow.visible, - ), - softWrap: true, - overflow: TextOverflow.ellipsis, - maxLines: - controller.shouldShow(PlayerHideable.twoLineTitle) - ? 2 - : 1, + child: LayoutBuilder( + builder: (context, constraints) { + final textPainter = TextPainter( + text: TextSpan( + text: currentTrack.item.title, + style: TextStyle( + fontSize: 20, + height: 26 / 20, + fontWeight: Theme.of(context).brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + ), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(maxWidth: constraints.maxWidth); + + final isOverflowing = textPainter.didExceedMaxLines; + + return Container( + width: constraints.maxWidth, + child: isOverflowing + ? ScrollingText( + text: currentTrack.item.title, + style: TextStyle( + fontSize: 20, + height: 26 / 20, + fontWeight: Theme.of(context).brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + ) + : Text( + currentTrack.item.title, + style: TextStyle( + fontSize: 20, + height: 26 / 20, + fontWeight: Theme.of(context).brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ); + }, ), ), ), diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index e3b58f43e..df864282f 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -8,6 +8,7 @@ import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/services/current_track_metadata_provider.dart'; import 'package:finamp/services/feedback_helper.dart'; import 'package:finamp/services/queue_service.dart'; +import 'package:finamp/services/scrolling_text_helper.dart'; import 'package:finamp/services/theme_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -335,78 +336,18 @@ class NowPlayingBar extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Text( - // currentTrack.item.title, - // style: const TextStyle( - // color: Colors.white, - // fontSize: 16, - // fontWeight: - // FontWeight.w500, - // overflow: TextOverflow - // .ellipsis), - // ), - LayoutBuilder( - builder: - (context, constraints) { - final textPainter = - TextPainter( - text: TextSpan( - text: snapshot.data! - .mediaItem!.title, - style: - const TextStyle( - fontSize: 16, - fontWeight: - FontWeight.w600, - ), - ), - maxLines: 1, - textDirection: - TextDirection.ltr, - )..layout( - maxWidth: - constraints - .maxWidth); - - final isOverflowing = - textPainter - .didExceedMaxLines; - - return Container( - width: constraints - .maxWidth, - child: isOverflowing - ? ScrollingText( - text: snapshot - .data! - .mediaItem! - .title, - style: - const TextStyle( - fontSize: 16, - fontWeight: - FontWeight - .w600, - ), - ) - : Text( - snapshot - .data! - .mediaItem! - .title, - style: - const TextStyle( - fontSize: 16, - fontWeight: - FontWeight - .w600, - ), - overflow: - TextOverflow - .ellipsis, - ), - ); - }, + ScrollingTextHelper( + text: + currentTrack.item.title, + style: TextStyle( + fontSize: 16, + fontWeight: Theme.of( + context) + .brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), ), const SizedBox(height: 4), Row( diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart new file mode 100644 index 000000000..559752472 --- /dev/null +++ b/lib/services/scrolling_text_helper.dart @@ -0,0 +1,46 @@ +import 'package:finamp/components/scrolling_text.dart'; +import 'package:flutter/material.dart'; + +class ScrollingTextHelper extends StatelessWidget { + final String text; + final TextStyle? style; + + const ScrollingTextHelper({ + Key? key, + required this.text, + this.style, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final textPainter = TextPainter( + text: TextSpan( + text: text, + style: style, + ), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(maxWidth: constraints.maxWidth); + + final isOverflowing = textPainter.didExceedMaxLines; + + return Container( + width: constraints.maxWidth, + child: isOverflowing + ? ScrollingText( + text: text, + style: style, + ) + : Text( + text, + style: style, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ); + }, + ); + } +} From da9b587907df0c4f6dd6688293c3a78dd819bb81 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Tue, 28 May 2024 14:36:28 -0400 Subject: [PATCH 03/23] fixed non marquee text alignment issue --- lib/components/PlayerScreen/song_name.dart | 26 ++++---- .../PlayerScreen/song_name_content.dart | 65 ++++--------------- lib/components/now_playing_bar.dart | 1 + lib/services/scrolling_text_helper.dart | 3 + 4 files changed, 31 insertions(+), 64 deletions(-) diff --git a/lib/components/PlayerScreen/song_name.dart b/lib/components/PlayerScreen/song_name.dart index 23f9f0137..55647a4fe 100644 --- a/lib/components/PlayerScreen/song_name.dart +++ b/lib/components/PlayerScreen/song_name.dart @@ -132,18 +132,20 @@ class SongNameContent extends StatelessWidget { ), ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), - ScrollingTextHelper( - text: mediaItem == null - ? AppLocalizations.of(context)!.noItem - : mediaItem!.title, - style: TextStyle( - fontSize: 24, - height: 26 / 20, - fontWeight: Theme.of(context).brightness == Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - ), + // Center( + // child: ScrollingTextHelper( + // text: mediaItem == null + // ? AppLocalizations.of(context)!.noItem + // : mediaItem!.title, + // style: TextStyle( + // fontSize: 24, + // height: 26 / 20, + // fontWeight: Theme.of(context).brightness == Brightness.light + // ? FontWeight.w500 + // : FontWeight.w600, + // ), + // ), + // ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), RichText( text: TextSpan( diff --git a/lib/components/PlayerScreen/song_name_content.dart b/lib/components/PlayerScreen/song_name_content.dart index c70a4301a..ae202eab4 100644 --- a/lib/components/PlayerScreen/song_name_content.dart +++ b/lib/components/PlayerScreen/song_name_content.dart @@ -1,10 +1,11 @@ import 'package:finamp/components/scrolling_text.dart'; +import 'package:finamp/screens/player_screen.dart'; +import 'package:finamp/services/scrolling_text_helper.dart'; import 'package:flutter/material.dart'; import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart'; import 'package:finamp/components/PlayerScreen/player_buttons_more.dart'; import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/models/jellyfin_models.dart' as jellyfin_models; -import 'package:finamp/screens/player_screen.dart'; import 'package:get_it/get_it.dart'; import '../../services/queue_service.dart'; @@ -46,60 +47,20 @@ class SongNameContent extends StatelessWidget { children: [ Center( child: Container( - alignment: Alignment.center, constraints: BoxConstraints( - maxHeight: 24, maxWidth: 280, ), - child: LayoutBuilder( - builder: (context, constraints) { - final textPainter = TextPainter( - text: TextSpan( - text: currentTrack.item.title, - style: TextStyle( - fontSize: 20, - height: 26 / 20, - fontWeight: Theme.of(context).brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - ), - maxLines: 1, - textDirection: TextDirection.ltr, - )..layout(maxWidth: constraints.maxWidth); - - final isOverflowing = textPainter.didExceedMaxLines; - - return Container( - width: constraints.maxWidth, - child: isOverflowing - ? ScrollingText( - text: currentTrack.item.title, - style: TextStyle( - fontSize: 20, - height: 26 / 20, - fontWeight: Theme.of(context).brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - ) - : Text( - currentTrack.item.title, - style: TextStyle( - fontSize: 20, - height: 26 / 20, - fontWeight: Theme.of(context).brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ); - }, + child: ScrollingTextHelper( + alignment: TextAlign.center, + text: currentTrack.item.title, + style: TextStyle( + fontSize: 20, + height: 26 / 20, + fontWeight: + Theme.of(context).brightness == Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), ), ), ), diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index df864282f..d94143c08 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -337,6 +337,7 @@ class NowPlayingBar extends ConsumerWidget { CrossAxisAlignment.start, children: [ ScrollingTextHelper( + alignment: TextAlign.start, text: currentTrack.item.title, style: TextStyle( diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index 559752472..029016cf4 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -4,11 +4,13 @@ import 'package:flutter/material.dart'; class ScrollingTextHelper extends StatelessWidget { final String text; final TextStyle? style; + final TextAlign alignment; const ScrollingTextHelper({ Key? key, required this.text, this.style, + this.alignment = TextAlign.center, }) : super(key: key); @override @@ -38,6 +40,7 @@ class ScrollingTextHelper extends StatelessWidget { style: style, overflow: TextOverflow.ellipsis, maxLines: 1, + textAlign: alignment, ), ); }, From 14bf1b9b811dc11ac1d1d2ac53fcf3dd91690070 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Thu, 30 May 2024 22:19:03 -0400 Subject: [PATCH 04/23] added toggle to switch between 1 line or 2 lines of song title. Defaults to 2 lines --- lib/components/PlayerScreen/song_name.dart | 28 ++--- lib/components/scrolling_text.dart | 134 ++++++++++++++++++++- lib/l10n/app_en.arb | 2 + lib/models/finamp_models.dart | 5 + lib/models/finamp_models.g.dart | 7 +- lib/screens/player_settings_screen.dart | 28 +++++ lib/services/scrolling_text_helper.dart | 112 +++++++++++++---- 7 files changed, 273 insertions(+), 43 deletions(-) diff --git a/lib/components/PlayerScreen/song_name.dart b/lib/components/PlayerScreen/song_name.dart index 55647a4fe..c84c38d10 100644 --- a/lib/components/PlayerScreen/song_name.dart +++ b/lib/components/PlayerScreen/song_name.dart @@ -132,20 +132,20 @@ class SongNameContent extends StatelessWidget { ), ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), - // Center( - // child: ScrollingTextHelper( - // text: mediaItem == null - // ? AppLocalizations.of(context)!.noItem - // : mediaItem!.title, - // style: TextStyle( - // fontSize: 24, - // height: 26 / 20, - // fontWeight: Theme.of(context).brightness == Brightness.light - // ? FontWeight.w500 - // : FontWeight.w600, - // ), - // ), - // ), + Center( + child: ScrollingTextHelper( + text: mediaItem == null + ? AppLocalizations.of(context)!.noItem + : mediaItem!.title, + style: TextStyle( + fontSize: 24, + height: 26 / 20, + fontWeight: Theme.of(context).brightness == Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + ), + ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), RichText( text: TextSpan( diff --git a/lib/components/scrolling_text.dart b/lib/components/scrolling_text.dart index ef2030ef2..59b291c59 100644 --- a/lib/components/scrolling_text.dart +++ b/lib/components/scrolling_text.dart @@ -1,3 +1,131 @@ +// import 'package:flutter/material.dart'; +// +// class ScrollingText extends StatefulWidget { +// final String text; +// final TextStyle? style; +// final double blankSpace; +// final double velocity; +// final Duration pauseDuration; +// +// const ScrollingText({ +// Key? key, +// required this.text, +// this.style, +// this.blankSpace = 20.0, +// this.velocity = 25.0, +// this.pauseDuration = const Duration(seconds: 3), +// }) : super(key: key); +// +// @override +// _ScrollingTextState createState() => _ScrollingTextState(); +// } +// +// class _ScrollingTextState extends State +// with TickerProviderStateMixin { +// late ScrollController _scrollController; +// late AnimationController _animationController; +// late double _textWidth; +// late double _containerWidth; +// late double _animationDistance; +// final Duration extraScrollDuration = const Duration(milliseconds: 450); +// +// @override +// void initState() { +// super.initState(); +// _scrollController = ScrollController(); +// _animationController = AnimationController(vsync: this); +// WidgetsBinding.instance.addPostFrameCallback((_) { +// _startScrolling(); +// }); +// } +// +// @override +// void didUpdateWidget(covariant ScrollingText oldWidget) { +// super.didUpdateWidget(oldWidget); +// if (oldWidget.text != widget.text) { +// _resetAndStartScrolling(); +// } +// } +// +// @override +// void dispose() { +// _scrollController.dispose(); +// _animationController.dispose(); +// super.dispose(); +// } +// +// void _resetAndStartScrolling() { +// _animationController.stop(); +// _scrollController.jumpTo(0.0); +// WidgetsBinding.instance.addPostFrameCallback((_) { +// _startScrolling(); +// }); +// } +// +// void _startScrolling() { +// if (!_scrollController.hasClients) return; +// +// _scrollController.jumpTo(0.0); +// final totalDistance = _animationDistance + +// (widget.velocity * extraScrollDuration.inMilliseconds / 1000); +// final totalDuration = Duration( +// milliseconds: (totalDistance / widget.velocity * 1000).toInt()); +// +// Future.delayed(widget.pauseDuration, () { +// if (!_scrollController.hasClients) return; +// _animationController.duration = totalDuration; +// _animationController.forward(from: 0.0).then((_) { +// if (_scrollController.hasClients) { +// _scrollController.jumpTo(0.0); +// _startScrolling(); +// } +// }); +// +// _animationController.addListener(() { +// if (_scrollController.hasClients) { +// _scrollController.jumpTo(_animationController.value * totalDistance); +// } +// }); +// }); +// } +// +// @override +// Widget build(BuildContext context) { +// return LayoutBuilder( +// builder: (context, constraints) { +// final textPainter = TextPainter( +// text: TextSpan( +// text: widget.text, +// style: widget.style, +// ), +// maxLines: 1, +// textDirection: TextDirection.ltr, +// )..layout(maxWidth: double.infinity); +// +// _textWidth = textPainter.width; +// _containerWidth = constraints.maxWidth; +// _animationDistance = _textWidth + widget.blankSpace; +// +// if (_textWidth <= _containerWidth) { +// return Text(widget.text, style: widget.style); +// } +// +// return SingleChildScrollView( +// controller: _scrollController, +// scrollDirection: Axis.horizontal, +// physics: const NeverScrollableScrollPhysics(), +// child: Row( +// children: [ +// Text(widget.text, style: widget.style), +// SizedBox(width: widget.blankSpace), +// Text(widget.text, style: widget.style), +// ], +// ), +// ); +// }, +// ); +// } +// } import 'package:flutter/material.dart'; class ScrollingText extends StatefulWidget { @@ -6,6 +134,7 @@ class ScrollingText extends StatefulWidget { final double blankSpace; final double velocity; final Duration pauseDuration; + final int maxLines; const ScrollingText({ Key? key, @@ -14,6 +143,7 @@ class ScrollingText extends StatefulWidget { this.blankSpace = 20.0, this.velocity = 25.0, this.pauseDuration = const Duration(seconds: 3), + this.maxLines = 1, }) : super(key: key); @override @@ -98,7 +228,7 @@ class _ScrollingTextState extends State text: widget.text, style: widget.style, ), - maxLines: 1, + maxLines: widget.maxLines, textDirection: TextDirection.ltr, )..layout(maxWidth: double.infinity); @@ -106,7 +236,7 @@ class _ScrollingTextState extends State _containerWidth = constraints.maxWidth; _animationDistance = _textWidth + widget.blankSpace; - if (_textWidth <= _containerWidth) { + if (_textWidth <= _containerWidth && widget.maxLines == 1) { return Text(widget.text, style: widget.style); } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 86bac07de..0dd106148 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1466,6 +1466,8 @@ "@enableVibrationSubtitle": {}, "hideQueueButton": "Hide queue button", "hideQueueButtonSubtitle": "Hide the queue button on the player screen. Swipe up to access the queue.", + "oneLineMarqueeTextButton": "One line track title", + "oneLineMarqueeTextButtonSubtitle": "Sets song title to one line marquee text. Default 2", "prioritizePlayerCover": "Prioritize album cover", "prioritizePlayerCoverSubtitle": "Prioritize showing a larger album cover on player screen. Non-critical controls will be hidden more aggressively at small screen sizes.", "suppressPlayerPadding": "Suppress player controls padding", diff --git a/lib/models/finamp_models.dart b/lib/models/finamp_models.dart index 590284de9..63b5ce9b6 100644 --- a/lib/models/finamp_models.dart +++ b/lib/models/finamp_models.dart @@ -99,6 +99,7 @@ const _enableVibration = true; const _prioritizeCoverFactor = 8.0; const _suppressPlayerPadding = false; const _hideQueueButton = false; +const _oneLineMarqueeTextButton = false; const _reportQueueToServerDefault = false; const _periodicPlaybackSessionUpdateFrequencySecondsDefault = 150; const _showArtistChipImage = true; @@ -171,6 +172,7 @@ class FinampSettings { this.prioritizeCoverFactor = _prioritizeCoverFactor, this.suppressPlayerPadding = _suppressPlayerPadding, this.hideQueueButton = _hideQueueButton, + this.oneLineMarqueeTextButton = _oneLineMarqueeTextButton, this.reportQueueToServer = _reportQueueToServerDefault, this.periodicPlaybackSessionUpdateFrequencySeconds = _periodicPlaybackSessionUpdateFrequencySecondsDefault, @@ -392,6 +394,9 @@ class FinampSettings { @HiveField(65, defaultValue: _startInstantMixForIndividualTracksDefault) bool startInstantMixForIndividualTracks; + @HiveField(66, defaultValue: _oneLineMarqueeTextButton) + bool oneLineMarqueeTextButton; + static Future create() async { final downloadLocation = await DownloadLocation.create( name: "Internal Storage", diff --git a/lib/models/finamp_models.g.dart b/lib/models/finamp_models.g.dart index 116e8c6dc..f5e94c9e7 100644 --- a/lib/models/finamp_models.g.dart +++ b/lib/models/finamp_models.g.dart @@ -158,6 +158,7 @@ class FinampSettingsAdapter extends TypeAdapter { prioritizeCoverFactor: fields[49] == null ? 8.0 : fields[49] as double, suppressPlayerPadding: fields[50] == null ? false : fields[50] as bool, hideQueueButton: fields[51] == null ? false : fields[51] as bool, + oneLineMarqueeTextButton: fields[66] == null ? false : fields[66] as bool, reportQueueToServer: fields[52] == null ? false : fields[52] as bool, periodicPlaybackSessionUpdateFrequencySeconds: fields[53] == null ? 150 : fields[53] as int, @@ -176,7 +177,7 @@ class FinampSettingsAdapter extends TypeAdapter { @override void write(BinaryWriter writer, FinampSettings obj) { writer - ..writeByte(64) + ..writeByte(65) ..writeByte(0) ..write(obj.isOffline) ..writeByte(1) @@ -304,7 +305,9 @@ class FinampSettingsAdapter extends TypeAdapter { ..writeByte(64) ..write(obj.showProgressOnNowPlayingBar) ..writeByte(65) - ..write(obj.startInstantMixForIndividualTracks); + ..write(obj.startInstantMixForIndividualTracks) + ..writeByte(66) + ..write(obj.oneLineMarqueeTextButton); } @override diff --git a/lib/screens/player_settings_screen.dart b/lib/screens/player_settings_screen.dart index fc2c17230..8d82ad1c9 100644 --- a/lib/screens/player_settings_screen.dart +++ b/lib/screens/player_settings_screen.dart @@ -23,6 +23,7 @@ class PlayerSettingsScreen extends StatelessWidget { SuppressPlayerPaddingSwitch(), PrioritizeCoverSwitch(), HideQueueButtonSwitch(), + OneLineMarqueeTextSwitch(), ], ), ); @@ -116,3 +117,30 @@ class PrioritizeCoverSwitch extends StatelessWidget { ); } } + +class OneLineMarqueeTextSwitch extends StatelessWidget { + const OneLineMarqueeTextSwitch({super.key}); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (context, box, child) { + bool? oneLineMarquee = box.get("FinampSettings")?.oneLineMarqueeTextButton; + + return SwitchListTile.adaptive( + title: Text(AppLocalizations.of(context)!.oneLineMarqueeTextButton), + subtitle: Text(AppLocalizations.of(context)!.oneLineMarqueeTextButtonSubtitle), + value: oneLineMarquee ?? false, + onChanged: oneLineMarquee == null + ? null + : (value) { + FinampSettings finampSettingsTemp = box.get("FinampSettings")!; + finampSettingsTemp.oneLineMarqueeTextButton = value; + box.put("FinampSettings", finampSettingsTemp); + }, + ); + }, + ); + } +} \ No newline at end of file diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index 029016cf4..63e9b09ae 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -1,5 +1,58 @@ +// import 'package:finamp/components/scrolling_text.dart'; +// import 'package:flutter/material.dart'; +// +// class ScrollingTextHelper extends StatelessWidget { +// final String text; +// final TextStyle? style; +// final TextAlign alignment; +// +// const ScrollingTextHelper({ +// Key? key, +// required this.text, +// this.style, +// this.alignment = TextAlign.center, +// }) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// return LayoutBuilder( +// builder: (context, constraints) { +// final textPainter = TextPainter( +// text: TextSpan( +// text: text, +// style: style, +// ), +// maxLines: 1, +// textDirection: TextDirection.ltr, +// )..layout(maxWidth: constraints.maxWidth); +// +// final isOverflowing = textPainter.didExceedMaxLines; +// +// return Container( +// width: constraints.maxWidth, +// child: isOverflowing +// ? ScrollingText( +// text: text, +// style: style, +// ) +// : Text( +// text, +// style: style, +// overflow: TextOverflow.ellipsis, +// maxLines: 1, +// textAlign: alignment, +// ), +// ); +// }, +// ); +// } +// } import 'package:finamp/components/scrolling_text.dart'; import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; + +import '../models/finamp_models.dart'; +import 'finamp_settings_helper.dart'; class ScrollingTextHelper extends StatelessWidget { final String text; @@ -15,33 +68,42 @@ class ScrollingTextHelper extends StatelessWidget { @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - final textPainter = TextPainter( - text: TextSpan( - text: text, - style: style, - ), - maxLines: 1, - textDirection: TextDirection.ltr, - )..layout(maxWidth: constraints.maxWidth); + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (context, box, child) { + bool oneLineMarquee = box.get("FinampSettings")?.oneLineMarqueeTextButton ?? false; + int maxLines = oneLineMarquee ? 1 : 2; + + return LayoutBuilder( + builder: (context, constraints) { + final textPainter = TextPainter( + text: TextSpan( + text: text, + style: style, + ), + maxLines: maxLines, + textDirection: TextDirection.ltr, + )..layout(maxWidth: constraints.maxWidth); - final isOverflowing = textPainter.didExceedMaxLines; + final isOverflowing = textPainter.didExceedMaxLines; - return Container( - width: constraints.maxWidth, - child: isOverflowing - ? ScrollingText( - text: text, - style: style, - ) - : Text( - text, - style: style, - overflow: TextOverflow.ellipsis, - maxLines: 1, - textAlign: alignment, - ), + return Container( + width: constraints.maxWidth, + child: isOverflowing + ? ScrollingText( + text: text, + style: style, + maxLines: maxLines, + ) + : Text( + text, + style: style, + overflow: TextOverflow.ellipsis, + maxLines: maxLines, + textAlign: alignment, + ), + ); + }, ); }, ); From 81ca55992bc0c6b3580940b76f893e68a8b600c7 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Thu, 30 May 2024 22:31:42 -0400 Subject: [PATCH 05/23] removed commented code --- lib/components/scrolling_text.dart | 130 +----------------------- lib/services/scrolling_text_helper.dart | 49 --------- 2 files changed, 1 insertion(+), 178 deletions(-) diff --git a/lib/components/scrolling_text.dart b/lib/components/scrolling_text.dart index 59b291c59..08cbf2e00 100644 --- a/lib/components/scrolling_text.dart +++ b/lib/components/scrolling_text.dart @@ -1,131 +1,3 @@ -// import 'package:flutter/material.dart'; -// -// class ScrollingText extends StatefulWidget { -// final String text; -// final TextStyle? style; -// final double blankSpace; -// final double velocity; -// final Duration pauseDuration; -// -// const ScrollingText({ -// Key? key, -// required this.text, -// this.style, -// this.blankSpace = 20.0, -// this.velocity = 25.0, -// this.pauseDuration = const Duration(seconds: 3), -// }) : super(key: key); -// -// @override -// _ScrollingTextState createState() => _ScrollingTextState(); -// } -// -// class _ScrollingTextState extends State -// with TickerProviderStateMixin { -// late ScrollController _scrollController; -// late AnimationController _animationController; -// late double _textWidth; -// late double _containerWidth; -// late double _animationDistance; -// final Duration extraScrollDuration = const Duration(milliseconds: 450); -// -// @override -// void initState() { -// super.initState(); -// _scrollController = ScrollController(); -// _animationController = AnimationController(vsync: this); -// WidgetsBinding.instance.addPostFrameCallback((_) { -// _startScrolling(); -// }); -// } -// -// @override -// void didUpdateWidget(covariant ScrollingText oldWidget) { -// super.didUpdateWidget(oldWidget); -// if (oldWidget.text != widget.text) { -// _resetAndStartScrolling(); -// } -// } -// -// @override -// void dispose() { -// _scrollController.dispose(); -// _animationController.dispose(); -// super.dispose(); -// } -// -// void _resetAndStartScrolling() { -// _animationController.stop(); -// _scrollController.jumpTo(0.0); -// WidgetsBinding.instance.addPostFrameCallback((_) { -// _startScrolling(); -// }); -// } -// -// void _startScrolling() { -// if (!_scrollController.hasClients) return; -// -// _scrollController.jumpTo(0.0); -// final totalDistance = _animationDistance + -// (widget.velocity * extraScrollDuration.inMilliseconds / 1000); -// final totalDuration = Duration( -// milliseconds: (totalDistance / widget.velocity * 1000).toInt()); -// -// Future.delayed(widget.pauseDuration, () { -// if (!_scrollController.hasClients) return; -// _animationController.duration = totalDuration; -// _animationController.forward(from: 0.0).then((_) { -// if (_scrollController.hasClients) { -// _scrollController.jumpTo(0.0); -// _startScrolling(); -// } -// }); -// -// _animationController.addListener(() { -// if (_scrollController.hasClients) { -// _scrollController.jumpTo(_animationController.value * totalDistance); -// } -// }); -// }); -// } -// -// @override -// Widget build(BuildContext context) { -// return LayoutBuilder( -// builder: (context, constraints) { -// final textPainter = TextPainter( -// text: TextSpan( -// text: widget.text, -// style: widget.style, -// ), -// maxLines: 1, -// textDirection: TextDirection.ltr, -// )..layout(maxWidth: double.infinity); -// -// _textWidth = textPainter.width; -// _containerWidth = constraints.maxWidth; -// _animationDistance = _textWidth + widget.blankSpace; -// -// if (_textWidth <= _containerWidth) { -// return Text(widget.text, style: widget.style); -// } -// -// return SingleChildScrollView( -// controller: _scrollController, -// scrollDirection: Axis.horizontal, -// physics: const NeverScrollableScrollPhysics(), -// child: Row( -// children: [ -// Text(widget.text, style: widget.style), -// SizedBox(width: widget.blankSpace), -// Text(widget.text, style: widget.style), -// ], -// ), -// ); -// }, -// ); -// } -// } import 'package:flutter/material.dart'; class ScrollingText extends StatefulWidget { @@ -145,7 +17,7 @@ class ScrollingText extends StatefulWidget { this.pauseDuration = const Duration(seconds: 3), this.maxLines = 1, }) : super(key: key); - +s @override _ScrollingTextState createState() => _ScrollingTextState(); } diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index 63e9b09ae..894ed473d 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -1,52 +1,3 @@ -// import 'package:finamp/components/scrolling_text.dart'; -// import 'package:flutter/material.dart'; -// -// class ScrollingTextHelper extends StatelessWidget { -// final String text; -// final TextStyle? style; -// final TextAlign alignment; -// -// const ScrollingTextHelper({ -// Key? key, -// required this.text, -// this.style, -// this.alignment = TextAlign.center, -// }) : super(key: key); -// -// @override -// Widget build(BuildContext context) { -// return LayoutBuilder( -// builder: (context, constraints) { -// final textPainter = TextPainter( -// text: TextSpan( -// text: text, -// style: style, -// ), -// maxLines: 1, -// textDirection: TextDirection.ltr, -// )..layout(maxWidth: constraints.maxWidth); -// -// final isOverflowing = textPainter.didExceedMaxLines; -// -// return Container( -// width: constraints.maxWidth, -// child: isOverflowing -// ? ScrollingText( -// text: text, -// style: style, -// ) -// : Text( -// text, -// style: style, -// overflow: TextOverflow.ellipsis, -// maxLines: 1, -// textAlign: alignment, -// ), -// ); -// }, -// ); -// } -// } import 'package:finamp/components/scrolling_text.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; From 70753f05bc0d34995b00d6ee091661638ca848dd Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Thu, 30 May 2024 22:48:09 -0400 Subject: [PATCH 06/23] removed a character that was accidentally added --- lib/components/scrolling_text.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/scrolling_text.dart b/lib/components/scrolling_text.dart index 08cbf2e00..2e5d24e8a 100644 --- a/lib/components/scrolling_text.dart +++ b/lib/components/scrolling_text.dart @@ -17,7 +17,7 @@ class ScrollingText extends StatefulWidget { this.pauseDuration = const Duration(seconds: 3), this.maxLines = 1, }) : super(key: key); -s + @override _ScrollingTextState createState() => _ScrollingTextState(); } From d77668ba426c868833c73ec84ac9636d458c7826 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Fri, 31 May 2024 18:36:32 -0400 Subject: [PATCH 07/23] ditched custom scrolling_text for marquee, added dependency override that's a temp fix for marquee after flutter 3.2.2 --- lib/components/PlayerScreen/song_name.dart | 11 +- .../PlayerScreen/song_name_content.dart | 3 +- lib/components/now_playing_bar.dart | 51 +++++-- lib/components/scrolling_text.dart | 130 ------------------ lib/services/scrolling_text_helper.dart | 53 ++++--- pubspec.yaml | 4 + 6 files changed, 84 insertions(+), 168 deletions(-) delete mode 100644 lib/components/scrolling_text.dart diff --git a/lib/components/PlayerScreen/song_name.dart b/lib/components/PlayerScreen/song_name.dart index c84c38d10..1e2639a34 100644 --- a/lib/components/PlayerScreen/song_name.dart +++ b/lib/components/PlayerScreen/song_name.dart @@ -1,5 +1,4 @@ import 'package:audio_service/audio_service.dart'; -import 'package:finamp/components/scrolling_text.dart'; import 'package:finamp/models/jellyfin_models.dart'; import 'package:finamp/screens/artist_screen.dart'; import 'package:finamp/services/finamp_settings_helper.dart'; @@ -133,14 +132,18 @@ class SongNameContent extends StatelessWidget { ), const Padding(padding: EdgeInsets.symmetric(vertical: 2)), Center( - child: ScrollingTextHelper( - text: mediaItem == null + child: + ScrollingTextHelper( + id: ValueKey(mediaItem!.id), + alignment: TextAlign.center, + text: mediaItem == null ? AppLocalizations.of(context)!.noItem : mediaItem!.title, style: TextStyle( fontSize: 24, height: 26 / 20, - fontWeight: Theme.of(context).brightness == Brightness.light + fontWeight: + Theme.of(context).brightness == Brightness.light ? FontWeight.w500 : FontWeight.w600, ), diff --git a/lib/components/PlayerScreen/song_name_content.dart b/lib/components/PlayerScreen/song_name_content.dart index ae202eab4..fe8d3e69b 100644 --- a/lib/components/PlayerScreen/song_name_content.dart +++ b/lib/components/PlayerScreen/song_name_content.dart @@ -1,4 +1,3 @@ -import 'package:finamp/components/scrolling_text.dart'; import 'package:finamp/screens/player_screen.dart'; import 'package:finamp/services/scrolling_text_helper.dart'; import 'package:flutter/material.dart'; @@ -47,10 +46,12 @@ class SongNameContent extends StatelessWidget { children: [ Center( child: Container( + height: 45, constraints: BoxConstraints( maxWidth: 280, ), child: ScrollingTextHelper( + id: ValueKey(currentTrack.item.id), alignment: TextAlign.center, text: currentTrack.item.title, style: TextStyle( diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index d94143c08..fa57710ee 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -3,12 +3,10 @@ import 'dart:math'; import 'package:audio_service/audio_service.dart'; import 'package:finamp/color_schemes.g.dart'; import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart'; -import 'package:finamp/components/scrolling_text.dart'; import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/services/current_track_metadata_provider.dart'; import 'package:finamp/services/feedback_helper.dart'; import 'package:finamp/services/queue_service.dart'; -import 'package:finamp/services/scrolling_text_helper.dart'; import 'package:finamp/services/theme_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -16,6 +14,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:get_it/get_it.dart'; +import 'package:marquee/marquee.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; import '../models/jellyfin_models.dart' as jellyfin_models; @@ -336,18 +335,42 @@ class NowPlayingBar extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ - ScrollingTextHelper( - alignment: TextAlign.start, - text: - currentTrack.item.title, - style: TextStyle( - fontSize: 16, - fontWeight: Theme.of( - context) - .brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, + SizedBox( + height: 20, + child: Marquee( + key: ValueKey( + currentTrack.item.id), + text: currentTrack + .item.title, + style: TextStyle( + fontSize: 16, + fontWeight: Theme.of( + context) + .brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + scrollAxis: + Axis.horizontal, + blankSpace: 20.0, + velocity: 50.0, + pauseAfterRound: + const Duration( + seconds: 3), + accelerationDuration: + const Duration( + seconds: 1), + accelerationCurve: + Curves.linear, + decelerationDuration: + const Duration( + milliseconds: + 500), + decelerationCurve: + Curves.easeOut, + textDirection: + TextDirection.ltr, ), ), const SizedBox(height: 4), diff --git a/lib/components/scrolling_text.dart b/lib/components/scrolling_text.dart deleted file mode 100644 index 2e5d24e8a..000000000 --- a/lib/components/scrolling_text.dart +++ /dev/null @@ -1,130 +0,0 @@ -import 'package:flutter/material.dart'; - -class ScrollingText extends StatefulWidget { - final String text; - final TextStyle? style; - final double blankSpace; - final double velocity; - final Duration pauseDuration; - final int maxLines; - - const ScrollingText({ - Key? key, - required this.text, - this.style, - this.blankSpace = 20.0, - this.velocity = 25.0, - this.pauseDuration = const Duration(seconds: 3), - this.maxLines = 1, - }) : super(key: key); - - @override - _ScrollingTextState createState() => _ScrollingTextState(); -} - -class _ScrollingTextState extends State - with TickerProviderStateMixin { - late ScrollController _scrollController; - late AnimationController _animationController; - late double _textWidth; - late double _containerWidth; - late double _animationDistance; - final Duration extraScrollDuration = const Duration(milliseconds: 450); - - @override - void initState() { - super.initState(); - _scrollController = ScrollController(); - _animationController = AnimationController(vsync: this); - WidgetsBinding.instance.addPostFrameCallback((_) { - _startScrolling(); - }); - } - - @override - void didUpdateWidget(covariant ScrollingText oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.text != widget.text) { - _resetAndStartScrolling(); - } - } - - @override - void dispose() { - _scrollController.dispose(); - _animationController.dispose(); - super.dispose(); - } - - void _resetAndStartScrolling() { - _animationController.stop(); - _scrollController.jumpTo(0.0); - WidgetsBinding.instance.addPostFrameCallback((_) { - _startScrolling(); - }); - } - - void _startScrolling() { - if (!_scrollController.hasClients) return; - - _scrollController.jumpTo(0.0); - final totalDistance = _animationDistance + - (widget.velocity * extraScrollDuration.inMilliseconds / 1000); - final totalDuration = Duration( - milliseconds: (totalDistance / widget.velocity * 1000).toInt()); - - Future.delayed(widget.pauseDuration, () { - if (!_scrollController.hasClients) return; - _animationController.duration = totalDuration; - _animationController.forward(from: 0.0).then((_) { - if (_scrollController.hasClients) { - _scrollController.jumpTo(0.0); - _startScrolling(); - } - }); - - _animationController.addListener(() { - if (_scrollController.hasClients) { - _scrollController.jumpTo(_animationController.value * totalDistance); - } - }); - }); - } - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - final textPainter = TextPainter( - text: TextSpan( - text: widget.text, - style: widget.style, - ), - maxLines: widget.maxLines, - textDirection: TextDirection.ltr, - )..layout(maxWidth: double.infinity); - - _textWidth = textPainter.width; - _containerWidth = constraints.maxWidth; - _animationDistance = _textWidth + widget.blankSpace; - - if (_textWidth <= _containerWidth && widget.maxLines == 1) { - return Text(widget.text, style: widget.style); - } - - return SingleChildScrollView( - controller: _scrollController, - scrollDirection: Axis.horizontal, - physics: const NeverScrollableScrollPhysics(), - child: Row( - children: [ - Text(widget.text, style: widget.style), - SizedBox(width: widget.blankSpace), - Text(widget.text, style: widget.style), - ], - ), - ); - }, - ); - } -} diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index 894ed473d..65d23489a 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -1,17 +1,19 @@ -import 'package:finamp/components/scrolling_text.dart'; import 'package:flutter/material.dart'; +import 'package:marquee/marquee.dart'; import 'package:hive/hive.dart'; import '../models/finamp_models.dart'; import 'finamp_settings_helper.dart'; class ScrollingTextHelper extends StatelessWidget { + final Key id; final String text; final TextStyle? style; final TextAlign alignment; const ScrollingTextHelper({ Key? key, + required this.id, required this.text, this.style, this.alignment = TextAlign.center, @@ -23,7 +25,6 @@ class ScrollingTextHelper extends StatelessWidget { valueListenable: FinampSettingsHelper.finampSettingsListener, builder: (context, box, child) { bool oneLineMarquee = box.get("FinampSettings")?.oneLineMarqueeTextButton ?? false; - int maxLines = oneLineMarquee ? 1 : 2; return LayoutBuilder( builder: (context, constraints) { @@ -32,28 +33,42 @@ class ScrollingTextHelper extends StatelessWidget { text: text, style: style, ), - maxLines: maxLines, + maxLines: 2, textDirection: TextDirection.ltr, )..layout(maxWidth: constraints.maxWidth); final isOverflowing = textPainter.didExceedMaxLines; - return Container( - width: constraints.maxWidth, - child: isOverflowing - ? ScrollingText( - text: text, - style: style, - maxLines: maxLines, - ) - : Text( - text, - style: style, - overflow: TextOverflow.ellipsis, - maxLines: maxLines, - textAlign: alignment, - ), - ); + if (oneLineMarquee || isOverflowing) { + return Container( + height: (style?.fontSize ?? 16.0), + width: constraints.maxWidth, + child: Marquee( + text: text, + style: style, + scrollAxis: Axis.horizontal, + blankSpace: 20.0, + velocity: 50.0, + pauseAfterRound: Duration(seconds: 1), + accelerationDuration: Duration(seconds: 1), + accelerationCurve: Curves.linear, + decelerationDuration: Duration(milliseconds: 500), + decelerationCurve: Curves.easeOut, + textDirection: TextDirection.ltr, + ), + ); + } else { + return Container( + width: constraints.maxWidth, + child: Text( + text, + style: style, + overflow: TextOverflow.ellipsis, + maxLines: 2, + textAlign: alignment, + ), + ); + } }, ); }, diff --git a/pubspec.yaml b/pubspec.yaml index b00f6fc36..bc143594e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -101,6 +101,10 @@ dependencies: window_manager: ^0.3.8 url_launcher: ^6.2.6 +# Fix for marquee dependency after Flutter 3.2.2 upgrade +dependency_overrides: + fading_edge_scrollview: ^4.1.1 + dev_dependencies: flutter_test: sdk: flutter From 8ccb8f78822e6b1015679dbe97c66af125e8d660 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Sat, 1 Jun 2024 03:10:54 -0400 Subject: [PATCH 08/23] changed hive entry to 67, fixed alignment issues in nowplayingbar --- .../PlayerScreen/song_name_content.dart | 2 +- lib/components/now_playing_bar.dart | 27 +++---------------- lib/models/finamp_models.dart | 2 +- lib/services/finamp_settings_helper.dart | 2 +- lib/services/scrolling_text_helper.dart | 10 ++++--- 5 files changed, 14 insertions(+), 29 deletions(-) diff --git a/lib/components/PlayerScreen/song_name_content.dart b/lib/components/PlayerScreen/song_name_content.dart index fe8d3e69b..39c93ce03 100644 --- a/lib/components/PlayerScreen/song_name_content.dart +++ b/lib/components/PlayerScreen/song_name_content.dart @@ -46,7 +46,7 @@ class SongNameContent extends StatelessWidget { children: [ Center( child: Container( - height: 45, + height: MediaQuery.of(context).size.height * 0.06, constraints: BoxConstraints( maxWidth: 280, ), diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index fa57710ee..d7938ed75 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -7,6 +7,7 @@ import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/services/current_track_metadata_provider.dart'; import 'package:finamp/services/feedback_helper.dart'; import 'package:finamp/services/queue_service.dart'; +import 'package:finamp/services/scrolling_text_helper.dart'; import 'package:finamp/services/theme_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -337,8 +338,8 @@ class NowPlayingBar extends ConsumerWidget { children: [ SizedBox( height: 20, - child: Marquee( - key: ValueKey( + child: ScrollingTextHelper( + id: ValueKey( currentTrack.item.id), text: currentTrack .item.title, @@ -350,27 +351,7 @@ class NowPlayingBar extends ConsumerWidget { Brightness.light ? FontWeight.w500 : FontWeight.w600, - ), - scrollAxis: - Axis.horizontal, - blankSpace: 20.0, - velocity: 50.0, - pauseAfterRound: - const Duration( - seconds: 3), - accelerationDuration: - const Duration( - seconds: 1), - accelerationCurve: - Curves.linear, - decelerationDuration: - const Duration( - milliseconds: - 500), - decelerationCurve: - Curves.easeOut, - textDirection: - TextDirection.ltr, + ), alignment: TextAlign.start, ), ), const SizedBox(height: 4), diff --git a/lib/models/finamp_models.dart b/lib/models/finamp_models.dart index 63b5ce9b6..2b34d89be 100644 --- a/lib/models/finamp_models.dart +++ b/lib/models/finamp_models.dart @@ -394,7 +394,7 @@ class FinampSettings { @HiveField(65, defaultValue: _startInstantMixForIndividualTracksDefault) bool startInstantMixForIndividualTracks; - @HiveField(66, defaultValue: _oneLineMarqueeTextButton) + @HiveField(67, defaultValue: _oneLineMarqueeTextButton) bool oneLineMarqueeTextButton; static Future create() async { diff --git a/lib/services/finamp_settings_helper.dart b/lib/services/finamp_settings_helper.dart index a452c2ffc..c2920484b 100644 --- a/lib/services/finamp_settings_helper.dart +++ b/lib/services/finamp_settings_helper.dart @@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:rxdart/rxdart.dart'; - +import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../models/finamp_models.dart'; import '../models/jellyfin_models.dart'; diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index 65d23489a..496b9fe92 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -9,14 +9,16 @@ class ScrollingTextHelper extends StatelessWidget { final Key id; final String text; final TextStyle? style; - final TextAlign alignment; + final TextAlign? alignment; + final bool useMarqueeCondition; const ScrollingTextHelper({ Key? key, required this.id, required this.text, this.style, - this.alignment = TextAlign.center, + required this.alignment, + this.useMarqueeCondition = true, }) : super(key: key); @override @@ -39,11 +41,13 @@ class ScrollingTextHelper extends StatelessWidget { final isOverflowing = textPainter.didExceedMaxLines; - if (oneLineMarquee || isOverflowing) { + if (!useMarqueeCondition || oneLineMarquee || isOverflowing) { return Container( + alignment: Alignment.centerLeft, height: (style?.fontSize ?? 16.0), width: constraints.maxWidth, child: Marquee( + key: id, text: text, style: style, scrollAxis: Axis.horizontal, From 747727c897040e47d51869f37846fc56f15f3714 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Sat, 1 Jun 2024 04:20:35 -0400 Subject: [PATCH 09/23] added checks for properly formatting title in player and nowplayingbar based on toggle switch and title size --- lib/components/PlayerScreen/song_name_content.dart | 1 - lib/components/now_playing_bar.dart | 6 +++++- lib/models/finamp_models.g.dart | 4 ++-- lib/services/scrolling_text_helper.dart | 10 ++++++---- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/components/PlayerScreen/song_name_content.dart b/lib/components/PlayerScreen/song_name_content.dart index 39c93ce03..d4ff336f4 100644 --- a/lib/components/PlayerScreen/song_name_content.dart +++ b/lib/components/PlayerScreen/song_name_content.dart @@ -46,7 +46,6 @@ class SongNameContent extends StatelessWidget { children: [ Center( child: Container( - height: MediaQuery.of(context).size.height * 0.06, constraints: BoxConstraints( maxWidth: 280, ), diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index d7938ed75..fc4393f3b 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -351,7 +351,11 @@ class NowPlayingBar extends ConsumerWidget { Brightness.light ? FontWeight.w500 : FontWeight.w600, - ), alignment: TextAlign.start, + ), + alignment: + TextAlign.start, + useMarqueeCondition: + false, ), ), const SizedBox(height: 4), diff --git a/lib/models/finamp_models.g.dart b/lib/models/finamp_models.g.dart index f5e94c9e7..e2adf2909 100644 --- a/lib/models/finamp_models.g.dart +++ b/lib/models/finamp_models.g.dart @@ -158,7 +158,7 @@ class FinampSettingsAdapter extends TypeAdapter { prioritizeCoverFactor: fields[49] == null ? 8.0 : fields[49] as double, suppressPlayerPadding: fields[50] == null ? false : fields[50] as bool, hideQueueButton: fields[51] == null ? false : fields[51] as bool, - oneLineMarqueeTextButton: fields[66] == null ? false : fields[66] as bool, + oneLineMarqueeTextButton: fields[67] == null ? false : fields[67] as bool, reportQueueToServer: fields[52] == null ? false : fields[52] as bool, periodicPlaybackSessionUpdateFrequencySeconds: fields[53] == null ? 150 : fields[53] as int, @@ -306,7 +306,7 @@ class FinampSettingsAdapter extends TypeAdapter { ..write(obj.showProgressOnNowPlayingBar) ..writeByte(65) ..write(obj.startInstantMixForIndividualTracks) - ..writeByte(66) + ..writeByte(67) ..write(obj.oneLineMarqueeTextButton); } diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index 496b9fe92..a1219d27f 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -26,7 +26,8 @@ class ScrollingTextHelper extends StatelessWidget { return ValueListenableBuilder>( valueListenable: FinampSettingsHelper.finampSettingsListener, builder: (context, box, child) { - bool oneLineMarquee = box.get("FinampSettings")?.oneLineMarqueeTextButton ?? false; + bool oneLineMarquee = + box.get("FinampSettings")?.oneLineMarqueeTextButton ?? false; return LayoutBuilder( builder: (context, constraints) { @@ -35,16 +36,17 @@ class ScrollingTextHelper extends StatelessWidget { text: text, style: style, ), - maxLines: 2, + maxLines: useMarqueeCondition ? 2 : 1, textDirection: TextDirection.ltr, )..layout(maxWidth: constraints.maxWidth); final isOverflowing = textPainter.didExceedMaxLines; - if (!useMarqueeCondition || oneLineMarquee || isOverflowing) { + if (oneLineMarquee || isOverflowing) { return Container( alignment: Alignment.centerLeft, - height: (style?.fontSize ?? 16.0), + height: + (style?.fontSize ?? 16.0) * (useMarqueeCondition ? 2 : 1), width: constraints.maxWidth, child: Marquee( key: id, From 36771123d8788beb6c22ae598036316102ad3298 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Sat, 1 Jun 2024 04:32:56 -0400 Subject: [PATCH 10/23] increased pause duration from 1 to 3 seconds. still need to fix one line marquee duplicates for short titles --- lib/services/scrolling_text_helper.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index a1219d27f..bf74e7636 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -55,10 +55,10 @@ class ScrollingTextHelper extends StatelessWidget { scrollAxis: Axis.horizontal, blankSpace: 20.0, velocity: 50.0, - pauseAfterRound: Duration(seconds: 1), - accelerationDuration: Duration(seconds: 1), + pauseAfterRound: const Duration(seconds: 3), + accelerationDuration: const Duration(seconds: 1), accelerationCurve: Curves.linear, - decelerationDuration: Duration(milliseconds: 500), + decelerationDuration: const Duration(milliseconds: 500), decelerationCurve: Curves.easeOut, textDirection: TextDirection.ltr, ), From 69514524d2f2d7cb82acb72762978dc1438112e2 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Sat, 1 Jun 2024 18:42:57 -0400 Subject: [PATCH 11/23] fixed duplicates in nowplaying bar --- .../PlayerScreen/song_name_content.dart | 1 + lib/components/now_playing_bar.dart | 99 +++++++++++++++---- 2 files changed, 83 insertions(+), 17 deletions(-) diff --git a/lib/components/PlayerScreen/song_name_content.dart b/lib/components/PlayerScreen/song_name_content.dart index d4ff336f4..aa5e47f3e 100644 --- a/lib/components/PlayerScreen/song_name_content.dart +++ b/lib/components/PlayerScreen/song_name_content.dart @@ -61,6 +61,7 @@ class SongNameContent extends StatelessWidget { ? FontWeight.w500 : FontWeight.w600, ), + useMarqueeCondition: true, ), ), ), diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index fc4393f3b..29ee6cf88 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -338,24 +338,89 @@ class NowPlayingBar extends ConsumerWidget { children: [ SizedBox( height: 20, - child: ScrollingTextHelper( - id: ValueKey( - currentTrack.item.id), - text: currentTrack - .item.title, - style: TextStyle( - fontSize: 16, - fontWeight: Theme.of( - context) + child: LayoutBuilder( + builder: (context, constraints) { + final textPainter = TextPainter( + text: TextSpan( + text: currentTrack + .item.title, + style: TextStyle( + fontSize: 16, + fontWeight: Theme.of( + context) + .brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + ), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(maxWidth: constraints.maxWidth); + + final isOverflowing = textPainter.didExceedMaxLines; + + if (isOverflowing) { + return Container( + alignment: Alignment.centerLeft, + height: + (TextStyle( + fontSize: 16, + fontWeight: Theme.of( + context) + .brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ).fontSize ?? 16.0), + width: constraints.maxWidth, + child: Marquee( + key: ValueKey( + currentTrack.item.id), + text: currentTrack + .item.title, + style: TextStyle( + fontSize: 16, + fontWeight: Theme.of( + context) .brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - alignment: - TextAlign.start, - useMarqueeCondition: - false, + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + scrollAxis: Axis.horizontal, + blankSpace: 20.0, + velocity: 50.0, + pauseAfterRound: const Duration(seconds: 3), + accelerationDuration: const Duration(seconds: 1), + accelerationCurve: Curves.linear, + decelerationDuration: const Duration(milliseconds: 500), + decelerationCurve: Curves.easeOut, + textDirection: TextDirection.ltr, + ), + ); + } else { + return Container( + width: constraints.maxWidth, + child: Text( + currentTrack + .item.title, + style: TextStyle( + fontSize: 16, + fontWeight: Theme.of( + context) + .brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + overflow: TextOverflow.ellipsis, + maxLines: 2, + textAlign: TextAlign.start, + ), + ); + } + }, ), ), const SizedBox(height: 4), From 3f111d2551a7c32bf4b01a935364b64a6b6b529a Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Sat, 1 Jun 2024 18:54:36 -0400 Subject: [PATCH 12/23] changed subtitle description --- lib/l10n/app_en.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0dd106148..1ffe9428b 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1467,7 +1467,7 @@ "hideQueueButton": "Hide queue button", "hideQueueButtonSubtitle": "Hide the queue button on the player screen. Swipe up to access the queue.", "oneLineMarqueeTextButton": "One line track title", - "oneLineMarqueeTextButtonSubtitle": "Sets song title to one line marquee text. Default 2", + "oneLineMarqueeTextButtonSubtitle": "Sets song title to one line marquee text in player screen. Default 2", "prioritizePlayerCover": "Prioritize album cover", "prioritizePlayerCoverSubtitle": "Prioritize showing a larger album cover on player screen. Non-critical controls will be hidden more aggressively at small screen sizes.", "suppressPlayerPadding": "Suppress player controls padding", From e5772046450541ddc8613c80c4c4bdeac45ba050 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Sun, 2 Jun 2024 17:43:20 -0400 Subject: [PATCH 13/23] added BalancedText fallback if marquee isn't needed. Marquee text color on the now playing bar adapts to the theme. Fixed bug where with the one line marquee enabled, even non-overflowing text uses a marquee on the player screen. --- .../PlayerScreen/song_name_content.dart | 2 +- lib/components/now_playing_bar.dart | 135 ++++++++++++------ lib/services/scrolling_text_helper.dart | 10 +- 3 files changed, 95 insertions(+), 52 deletions(-) diff --git a/lib/components/PlayerScreen/song_name_content.dart b/lib/components/PlayerScreen/song_name_content.dart index aa5e47f3e..426af45fd 100644 --- a/lib/components/PlayerScreen/song_name_content.dart +++ b/lib/components/PlayerScreen/song_name_content.dart @@ -61,7 +61,7 @@ class SongNameContent extends StatelessWidget { ? FontWeight.w500 : FontWeight.w600, ), - useMarqueeCondition: true, + useMarqueeCondition: false, ), ), ), diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index 29ee6cf88..0f4d53226 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:audio_service/audio_service.dart'; +import 'package:balanced_text/balanced_text.dart'; import 'package:finamp/color_schemes.g.dart'; import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart'; import 'package:finamp/models/finamp_models.dart'; @@ -339,84 +340,126 @@ class NowPlayingBar extends ConsumerWidget { SizedBox( height: 20, child: LayoutBuilder( - builder: (context, constraints) { - final textPainter = TextPainter( + builder: (context, + constraints) { + final textPainter = + TextPainter( text: TextSpan( text: currentTrack .item.title, style: TextStyle( fontSize: 16, fontWeight: Theme.of( - context) - .brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, + context) + .brightness == + Brightness + .light + ? FontWeight + .w500 + : FontWeight + .w600, ), ), maxLines: 1, - textDirection: TextDirection.ltr, - )..layout(maxWidth: constraints.maxWidth); + textDirection: + TextDirection.ltr, + )..layout( + maxWidth: + constraints + .maxWidth); - final isOverflowing = textPainter.didExceedMaxLines; + final isOverflowing = + textPainter + .didExceedMaxLines; if (isOverflowing) { return Container( - alignment: Alignment.centerLeft, - height: - (TextStyle( - fontSize: 16, - fontWeight: Theme.of( - context) - .brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ).fontSize ?? 16.0), - width: constraints.maxWidth, + alignment: Alignment + .centerLeft, + height: (TextStyle( + fontSize: 16, + fontWeight: Theme.of(context) + .brightness == + Brightness + .light + ? FontWeight + .w500 + : FontWeight + .w600, + ).fontSize ?? + 16.0), + width: constraints + .maxWidth, child: Marquee( key: ValueKey( - currentTrack.item.id), + currentTrack + .item.id), text: currentTrack .item.title, style: TextStyle( fontSize: 16, - fontWeight: Theme.of( - context) - .brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, + color: Colors.white, + fontWeight: Theme.of(context) + .brightness == + Brightness + .light + ? FontWeight + .w500 + : FontWeight + .w600, ), - scrollAxis: Axis.horizontal, + scrollAxis: Axis + .horizontal, blankSpace: 20.0, velocity: 50.0, - pauseAfterRound: const Duration(seconds: 3), - accelerationDuration: const Duration(seconds: 1), - accelerationCurve: Curves.linear, - decelerationDuration: const Duration(milliseconds: 500), - decelerationCurve: Curves.easeOut, - textDirection: TextDirection.ltr, + pauseAfterRound: + const Duration( + seconds: + 3), + accelerationDuration: + const Duration( + seconds: + 1), + accelerationCurve: + Curves.linear, + decelerationDuration: + const Duration( + milliseconds: + 500), + decelerationCurve: + Curves + .easeOut, + textDirection: + TextDirection + .ltr, ), ); } else { return Container( - width: constraints.maxWidth, - child: Text( + width: constraints + .maxWidth, + child: BalancedText( currentTrack .item.title, style: TextStyle( fontSize: 16, - fontWeight: Theme.of( - context) - .brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, + color: Colors.white, + fontWeight: Theme.of(context) + .brightness == + Brightness + .light + ? FontWeight + .w500 + : FontWeight + .w600, ), - overflow: TextOverflow.ellipsis, + overflow: + TextOverflow + .ellipsis, maxLines: 2, - textAlign: TextAlign.start, + textAlign: + TextAlign + .start, ), ); } diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index bf74e7636..0720e010d 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -1,3 +1,4 @@ +import 'package:balanced_text/balanced_text.dart'; import 'package:flutter/material.dart'; import 'package:marquee/marquee.dart'; import 'package:hive/hive.dart'; @@ -42,11 +43,10 @@ class ScrollingTextHelper extends StatelessWidget { final isOverflowing = textPainter.didExceedMaxLines; - if (oneLineMarquee || isOverflowing) { + if (oneLineMarquee && isOverflowing) { return Container( alignment: Alignment.centerLeft, - height: - (style?.fontSize ?? 16.0) * (useMarqueeCondition ? 2 : 1), + height: (style?.fontSize ?? 16.0) + 10, width: constraints.maxWidth, child: Marquee( key: id, @@ -66,11 +66,11 @@ class ScrollingTextHelper extends StatelessWidget { } else { return Container( width: constraints.maxWidth, - child: Text( + child: BalancedText( text, style: style, overflow: TextOverflow.ellipsis, - maxLines: 2, + maxLines: oneLineMarquee ? 1 : 2, textAlign: alignment, ), ); From 38ce9ec200526ec8b46f7ac8bdb1462af1679934 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Sun, 2 Jun 2024 18:07:16 -0400 Subject: [PATCH 14/23] marquee added in queue list --- lib/components/PlayerScreen/queue_list.dart | 118 ++++++++++++++-- .../PlayerScreen/queue_list_item.dart | 129 ++++++++++++++++-- 2 files changed, 227 insertions(+), 20 deletions(-) diff --git a/lib/components/PlayerScreen/queue_list.dart b/lib/components/PlayerScreen/queue_list.dart index 50560c0aa..0752fe371 100644 --- a/lib/components/PlayerScreen/queue_list.dart +++ b/lib/components/PlayerScreen/queue_list.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:audio_service/audio_service.dart'; +import 'package:balanced_text/balanced_text.dart'; import 'package:finamp/components/AlbumScreen/song_menu.dart'; import 'package:finamp/components/Buttons/simple_button.dart'; import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart'; @@ -17,6 +18,7 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:get_it/get_it.dart'; +import 'package:marquee/marquee.dart'; import 'package:rxdart/rxdart.dart'; import '../../models/jellyfin_models.dart' as jellyfin_models; @@ -842,15 +844,113 @@ class _CurrentTrackState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - currentTrack?.item.title ?? - AppLocalizations.of(context)! - .unknownName, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - overflow: TextOverflow.ellipsis), + SizedBox( + height: 20, + child: LayoutBuilder( + builder: (context, + constraints) { + final textPainter = + TextPainter( + text: TextSpan( + text: currentTrack?.item.title ?? + AppLocalizations.of(context)! + .unknownName, + style: const TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + overflow: TextOverflow.ellipsis + ), + ), + maxLines: 1, + textDirection: + TextDirection.ltr, + )..layout( + maxWidth: + constraints + .maxWidth); + + final isOverflowing = + textPainter + .didExceedMaxLines; + + if (isOverflowing) { + return Container( + alignment: Alignment + .centerLeft, + height: (const TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + overflow: TextOverflow.ellipsis + ).fontSize ?? + 16.0), + width: constraints + .maxWidth, + child: Marquee( + key: ValueKey( + currentTrack?.item.id), + text: currentTrack?.item.title ?? + AppLocalizations.of(context)! + .unknownName, + style: const TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + overflow: TextOverflow.ellipsis + ), + scrollAxis: Axis + .horizontal, + blankSpace: 20.0, + velocity: 50.0, + pauseAfterRound: + const Duration( + seconds: + 3), + accelerationDuration: + const Duration( + seconds: + 1), + accelerationCurve: + Curves.linear, + decelerationDuration: + const Duration( + milliseconds: + 500), + decelerationCurve: + Curves + .easeOut, + textDirection: + TextDirection + .ltr, + ), + ); + } else { + return Container( + width: constraints + .maxWidth, + child: BalancedText( + currentTrack?.item.title ?? + AppLocalizations.of(context)! + .unknownName, + style: const TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + overflow: TextOverflow.ellipsis + ), + overflow: + TextOverflow + .ellipsis, + maxLines: 2, + textAlign: + TextAlign + .start, + ), + ); + } + }, + ), ), const SizedBox(height: 4), Row( diff --git a/lib/components/PlayerScreen/queue_list_item.dart b/lib/components/PlayerScreen/queue_list_item.dart index 8140c065a..89244a3ed 100644 --- a/lib/components/PlayerScreen/queue_list_item.dart +++ b/lib/components/PlayerScreen/queue_list_item.dart @@ -1,3 +1,4 @@ +import 'package:balanced_text/balanced_text.dart'; import 'package:finamp/components/AlbumScreen/song_menu.dart'; import 'package:finamp/components/PlayerScreen/queue_source_helper.dart'; import 'package:finamp/components/album_image.dart'; @@ -11,6 +12,7 @@ import 'package:flutter/material.dart' hide ReorderableList; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:get_it/get_it.dart'; +import 'package:marquee/marquee.dart'; import '../../services/theme_provider.dart'; @@ -131,17 +133,122 @@ class _QueueListItemState extends State children: [ Padding( padding: const EdgeInsets.all(0.0), - child: Text( - widget.item.item.title, - style: widget.isCurrentTrack - ? TextStyle( - color: - Theme.of(context).colorScheme.secondary, - fontSize: 16, - fontWeight: FontWeight.w400, - overflow: TextOverflow.ellipsis) - : null, - overflow: TextOverflow.ellipsis, + child: SizedBox( + height: 20, + child: LayoutBuilder( + builder: (context, + constraints) { + final textPainter = + TextPainter( + text: TextSpan( + text: widget.item.item.title, + style: TextStyle( + fontSize: 16, + fontWeight: Theme.of( + context) + .brightness == + Brightness + .light + ? FontWeight + .w500 + : FontWeight + .w600, + ), + ), + maxLines: 1, + textDirection: + TextDirection.ltr, + )..layout( + maxWidth: + constraints + .maxWidth); + + final isOverflowing = + textPainter + .didExceedMaxLines; + + if (isOverflowing) { + return Container( + alignment: Alignment + .centerLeft, + height: (TextStyle( + fontSize: 16, + fontWeight: Theme.of(context) + .brightness == + Brightness + .light + ? FontWeight + .w500 + : FontWeight + .w600, + ).fontSize ?? + 16.0), + width: constraints + .maxWidth, + child: Marquee( + key: ValueKey( + widget.item.item.id), + text: widget.item.item.title, + style: widget.isCurrentTrack + ? TextStyle( + color: + Theme.of(context).colorScheme.secondary, + fontSize: 16, + fontWeight: FontWeight.w400, + overflow: TextOverflow.ellipsis) + : null, + scrollAxis: Axis + .horizontal, + blankSpace: 20.0, + velocity: 50.0, + pauseAfterRound: + const Duration( + seconds: + 3), + accelerationDuration: + const Duration( + seconds: + 1), + accelerationCurve: + Curves.linear, + decelerationDuration: + const Duration( + milliseconds: + 500), + decelerationCurve: + Curves + .easeOut, + textDirection: + TextDirection + .ltr, + ), + ); + } else { + return Container( + width: constraints + .maxWidth, + child: BalancedText( + widget.item.item.title, + style: widget.isCurrentTrack + ? TextStyle( + color: + Theme.of(context).colorScheme.secondary, + fontSize: 16, + fontWeight: FontWeight.w400, + overflow: TextOverflow.ellipsis) + : null, + overflow: + TextOverflow + .ellipsis, + maxLines: 2, + textAlign: + TextAlign + .start, + ), + ); + } + }, + ), ), ), Padding( From 79ddda61a0f5906d3610f2d70c14220acb1ef47e Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Mon, 3 Jun 2024 07:05:08 -0400 Subject: [PATCH 15/23] added checks to properly handle >2 lines song titles to change to marquee --- lib/services/scrolling_text_helper.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/services/scrolling_text_helper.dart b/lib/services/scrolling_text_helper.dart index 0720e010d..49e963e22 100644 --- a/lib/services/scrolling_text_helper.dart +++ b/lib/services/scrolling_text_helper.dart @@ -37,16 +37,17 @@ class ScrollingTextHelper extends StatelessWidget { text: text, style: style, ), - maxLines: useMarqueeCondition ? 2 : 1, textDirection: TextDirection.ltr, )..layout(maxWidth: constraints.maxWidth); - final isOverflowing = textPainter.didExceedMaxLines; + final lineHeight = textPainter.preferredLineHeight; + final textHeight = textPainter.size.height; + final lineCount = (textHeight / lineHeight).ceil(); - if (oneLineMarquee && isOverflowing) { + if (oneLineMarquee && lineCount > 1 || lineCount > 2) { return Container( alignment: Alignment.centerLeft, - height: (style?.fontSize ?? 16.0) + 10, + height: lineHeight, width: constraints.maxWidth, child: Marquee( key: id, From 6e57dfde966ade81fc1418d24252f0286a89f7b5 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Mon, 3 Jun 2024 07:21:25 -0400 Subject: [PATCH 16/23] implemented marquee in track/album menu --- .../AlbumScreen/song_list_tile.dart | 107 ++++++++++++++---- 1 file changed, 86 insertions(+), 21 deletions(-) diff --git a/lib/components/AlbumScreen/song_list_tile.dart b/lib/components/AlbumScreen/song_list_tile.dart index 0e42ff31f..22d770d15 100644 --- a/lib/components/AlbumScreen/song_list_tile.dart +++ b/lib/components/AlbumScreen/song_list_tile.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:audio_service/audio_service.dart'; +import 'package:balanced_text/balanced_text.dart'; import 'package:collection/collection.dart'; import 'package:finamp/components/AlbumScreen/song_menu.dart'; import 'package:finamp/components/MusicScreen/music_screen_tab_view.dart'; @@ -14,6 +15,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:get_it/get_it.dart'; +import 'package:marquee/marquee.dart'; import 'package:mini_music_visualizer/mini_music_visualizer.dart'; import '../../services/audio_service_helper.dart'; @@ -131,32 +133,95 @@ class _SongListTileState extends ConsumerState ), title: Opacity( opacity: playable ? 1.0 : 0.5, - child: RichText( - text: TextSpan( - children: [ - // third condition checks if the item is viewed from its album (instead of e.g. a playlist) - // same horrible check as in canGoToAlbum in GestureDetector below - if (widget.item.indexNumber != null && - !widget.isSong && - widget.item.albumId == widget.parentItem?.id) - TextSpan( - text: "${widget.item.indexNumber}. ", - style: TextStyle( - color: Theme.of(context).disabledColor)), - TextSpan( - text: widget.item.name ?? - AppLocalizations.of(context)!.unknownName, + child: Row( + children: [ + if (widget.item.indexNumber != null && + !widget.isSong && + widget.item.albumId == widget.parentItem?.id) + Text( + "${widget.item.indexNumber}. ", style: TextStyle( - color: isCurrentlyPlaying - ? Theme.of(context).colorScheme.secondary - : null, + color: Theme.of(context).disabledColor, ), ), - ], - style: Theme.of(context).textTheme.titleMedium, - ), + Expanded( + child: SizedBox( + height: 20, + child: LayoutBuilder( + builder: (context, constraints) { + final textPainter = TextPainter( + text: TextSpan( + text: widget.item.name ?? AppLocalizations.of(context)!.unknownName, + style: TextStyle( + fontSize: 16, + fontWeight: Theme.of(context).brightness == Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + ), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(maxWidth: constraints.maxWidth); + + final isOverflowing = textPainter.didExceedMaxLines; + + if (isOverflowing) { + return Container( + alignment: Alignment.centerLeft, + height: (TextStyle( + fontSize: 16, + fontWeight: Theme.of(context).brightness == Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ).fontSize ?? 16.0), + width: constraints.maxWidth, + child: Marquee( + key: ValueKey(widget.item.id), + text: widget.item.name ?? AppLocalizations.of(context)!.unknownName, + style: TextStyle( + fontSize: 16, + color: isCurrentlyPlaying ? Theme.of(context).colorScheme.secondary : null, + fontWeight: Theme.of(context).brightness == Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + scrollAxis: Axis.horizontal, + blankSpace: 20.0, + velocity: 50.0, + pauseAfterRound: const Duration(seconds: 3), + accelerationDuration: const Duration(seconds: 1), + accelerationCurve: Curves.linear, + decelerationDuration: const Duration(milliseconds: 500), + decelerationCurve: Curves.easeOut, + textDirection: TextDirection.ltr, + ), + ); + } else { + return Container( + width: constraints.maxWidth, + child: BalancedText( + widget.item.name ?? AppLocalizations.of(context)!.unknownName, + style: TextStyle( + fontSize: 16, + color: isCurrentlyPlaying ? Theme.of(context).colorScheme.secondary : null, + fontWeight: Theme.of(context).brightness == Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), + overflow: TextOverflow.ellipsis, + maxLines: 2, + textAlign: TextAlign.start, + ), + ); + } + }, + ), + ), + ), + ], ), ), + subtitle: Opacity( opacity: playable ? 1.0 : 0.5, child: Text.rich( From 214e1e92264e2290f1bdab58456b7791dfe7f282 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Mon, 3 Jun 2024 07:53:07 -0400 Subject: [PATCH 17/23] refactored code --- .../AlbumScreen/song_list_tile.dart | 95 +++--------- lib/components/PlayerScreen/queue_list.dart | 121 ++------------- .../PlayerScreen/queue_list_item.dart | 127 ++------------- lib/components/now_playing_bar.dart | 144 ++---------------- lib/services/one_line_marquee_helper.dart | 89 +++++++++++ 5 files changed, 151 insertions(+), 425 deletions(-) create mode 100644 lib/services/one_line_marquee_helper.dart diff --git a/lib/components/AlbumScreen/song_list_tile.dart b/lib/components/AlbumScreen/song_list_tile.dart index 22d770d15..35e8a483d 100644 --- a/lib/components/AlbumScreen/song_list_tile.dart +++ b/lib/components/AlbumScreen/song_list_tile.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:audio_service/audio_service.dart'; -import 'package:balanced_text/balanced_text.dart'; import 'package:collection/collection.dart'; import 'package:finamp/components/AlbumScreen/song_menu.dart'; import 'package:finamp/components/MusicScreen/music_screen_tab_view.dart'; @@ -9,13 +8,13 @@ import 'package:finamp/components/global_snackbar.dart'; import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/models/jellyfin_models.dart' as jellyfin_models; import 'package:finamp/services/finamp_user_helper.dart'; +import 'package:finamp/services/one_line_marquee_helper.dart'; import 'package:finamp/services/queue_service.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:get_it/get_it.dart'; -import 'package:marquee/marquee.dart'; import 'package:mini_music_visualizer/mini_music_visualizer.dart'; import '../../services/audio_service_helper.dart'; @@ -147,74 +146,18 @@ class _SongListTileState extends ConsumerState Expanded( child: SizedBox( height: 20, - child: LayoutBuilder( - builder: (context, constraints) { - final textPainter = TextPainter( - text: TextSpan( - text: widget.item.name ?? AppLocalizations.of(context)!.unknownName, - style: TextStyle( - fontSize: 16, - fontWeight: Theme.of(context).brightness == Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - ), - maxLines: 1, - textDirection: TextDirection.ltr, - )..layout(maxWidth: constraints.maxWidth); - - final isOverflowing = textPainter.didExceedMaxLines; - - if (isOverflowing) { - return Container( - alignment: Alignment.centerLeft, - height: (TextStyle( - fontSize: 16, - fontWeight: Theme.of(context).brightness == Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ).fontSize ?? 16.0), - width: constraints.maxWidth, - child: Marquee( - key: ValueKey(widget.item.id), - text: widget.item.name ?? AppLocalizations.of(context)!.unknownName, - style: TextStyle( - fontSize: 16, - color: isCurrentlyPlaying ? Theme.of(context).colorScheme.secondary : null, - fontWeight: Theme.of(context).brightness == Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - scrollAxis: Axis.horizontal, - blankSpace: 20.0, - velocity: 50.0, - pauseAfterRound: const Duration(seconds: 3), - accelerationDuration: const Duration(seconds: 1), - accelerationCurve: Curves.linear, - decelerationDuration: const Duration(milliseconds: 500), - decelerationCurve: Curves.easeOut, - textDirection: TextDirection.ltr, - ), - ); - } else { - return Container( - width: constraints.maxWidth, - child: BalancedText( - widget.item.name ?? AppLocalizations.of(context)!.unknownName, - style: TextStyle( - fontSize: 16, - color: isCurrentlyPlaying ? Theme.of(context).colorScheme.secondary : null, - fontWeight: Theme.of(context).brightness == Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - overflow: TextOverflow.ellipsis, - maxLines: 2, - textAlign: TextAlign.start, - ), - ); - } - }, + child: OneLineMarqueeHelper( + key: ValueKey(widget.item.id), + text: widget.item.name ?? + AppLocalizations.of(context)!.unknownName, + style: TextStyle( + fontSize: 16, + height: 26 / 20, + fontWeight: + Theme.of(context).brightness == Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), ), ), ), @@ -346,12 +289,12 @@ class _SongListTileState extends ConsumerState List offlineItems; // If we're on the songs tab, just get all of the downloaded items offlineItems = await downloadService.getAllSongs( - // nameFilter: widget.searchTerm, - viewFilter: finampUserHelper.currentUser?.currentView?.id, - nullableViewFilters: - settings.showDownloadsWithUnknownLibrary, - onlyFavorites: - settings.onlyShowFavourite && settings.trackOfflineFavorites, + // nameFilter: widget.searchTerm, + viewFilter: finampUserHelper.currentUser?.currentView?.id, + nullableViewFilters: + settings.showDownloadsWithUnknownLibrary, + onlyFavorites: settings.onlyShowFavourite && + settings.trackOfflineFavorites, ); var items = offlineItems diff --git a/lib/components/PlayerScreen/queue_list.dart b/lib/components/PlayerScreen/queue_list.dart index 0752fe371..505d6fe8f 100644 --- a/lib/components/PlayerScreen/queue_list.dart +++ b/lib/components/PlayerScreen/queue_list.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:audio_service/audio_service.dart'; -import 'package:balanced_text/balanced_text.dart'; import 'package:finamp/components/AlbumScreen/song_menu.dart'; import 'package:finamp/components/Buttons/simple_button.dart'; import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart'; @@ -10,6 +9,7 @@ import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/screens/blurred_player_screen_background.dart'; import 'package:finamp/services/feedback_helper.dart'; import 'package:finamp/services/finamp_settings_helper.dart'; +import 'package:finamp/services/one_line_marquee_helper.dart'; import 'package:finamp/services/theme_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -18,7 +18,6 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:get_it/get_it.dart'; -import 'package:marquee/marquee.dart'; import 'package:rxdart/rxdart.dart'; import '../../models/jellyfin_models.dart' as jellyfin_models; @@ -846,110 +845,20 @@ class _CurrentTrackState extends State { children: [ SizedBox( height: 20, - child: LayoutBuilder( - builder: (context, - constraints) { - final textPainter = - TextPainter( - text: TextSpan( - text: currentTrack?.item.title ?? - AppLocalizations.of(context)! - .unknownName, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - overflow: TextOverflow.ellipsis - ), - ), - maxLines: 1, - textDirection: - TextDirection.ltr, - )..layout( - maxWidth: - constraints - .maxWidth); - - final isOverflowing = - textPainter - .didExceedMaxLines; - - if (isOverflowing) { - return Container( - alignment: Alignment - .centerLeft, - height: (const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - overflow: TextOverflow.ellipsis - ).fontSize ?? - 16.0), - width: constraints - .maxWidth, - child: Marquee( - key: ValueKey( - currentTrack?.item.id), - text: currentTrack?.item.title ?? - AppLocalizations.of(context)! - .unknownName, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - overflow: TextOverflow.ellipsis - ), - scrollAxis: Axis - .horizontal, - blankSpace: 20.0, - velocity: 50.0, - pauseAfterRound: - const Duration( - seconds: - 3), - accelerationDuration: - const Duration( - seconds: - 1), - accelerationCurve: - Curves.linear, - decelerationDuration: - const Duration( - milliseconds: - 500), - decelerationCurve: - Curves - .easeOut, - textDirection: - TextDirection - .ltr, - ), - ); - } else { - return Container( - width: constraints - .maxWidth, - child: BalancedText( - currentTrack?.item.title ?? - AppLocalizations.of(context)! - .unknownName, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - overflow: TextOverflow.ellipsis - ), - overflow: - TextOverflow - .ellipsis, - maxLines: 2, - textAlign: - TextAlign - .start, - ), - ); - } - }, + child: OneLineMarqueeHelper( + key: ValueKey(currentTrack?.item.id), + text: currentTrack?.item.title ?? + AppLocalizations.of(context)! + .unknownName, + style: TextStyle( + fontSize: 16, + height: 26 / 20, + fontWeight: + Theme.of(context).brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), ), ), const SizedBox(height: 4), diff --git a/lib/components/PlayerScreen/queue_list_item.dart b/lib/components/PlayerScreen/queue_list_item.dart index 89244a3ed..641c8b76b 100644 --- a/lib/components/PlayerScreen/queue_list_item.dart +++ b/lib/components/PlayerScreen/queue_list_item.dart @@ -1,4 +1,3 @@ -import 'package:balanced_text/balanced_text.dart'; import 'package:finamp/components/AlbumScreen/song_menu.dart'; import 'package:finamp/components/PlayerScreen/queue_source_helper.dart'; import 'package:finamp/components/album_image.dart'; @@ -6,13 +5,13 @@ import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/models/jellyfin_models.dart' as jellyfin_models; import 'package:finamp/services/feedback_helper.dart'; import 'package:finamp/services/finamp_settings_helper.dart'; +import 'package:finamp/services/one_line_marquee_helper.dart'; import 'package:finamp/services/process_artist.dart'; import 'package:finamp/services/queue_service.dart'; import 'package:flutter/material.dart' hide ReorderableList; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:get_it/get_it.dart'; -import 'package:marquee/marquee.dart'; import '../../services/theme_provider.dart'; @@ -135,119 +134,17 @@ class _QueueListItemState extends State padding: const EdgeInsets.all(0.0), child: SizedBox( height: 20, - child: LayoutBuilder( - builder: (context, - constraints) { - final textPainter = - TextPainter( - text: TextSpan( - text: widget.item.item.title, - style: TextStyle( - fontSize: 16, - fontWeight: Theme.of( - context) - .brightness == - Brightness - .light - ? FontWeight - .w500 - : FontWeight - .w600, - ), - ), - maxLines: 1, - textDirection: - TextDirection.ltr, - )..layout( - maxWidth: - constraints - .maxWidth); - - final isOverflowing = - textPainter - .didExceedMaxLines; - - if (isOverflowing) { - return Container( - alignment: Alignment - .centerLeft, - height: (TextStyle( - fontSize: 16, - fontWeight: Theme.of(context) - .brightness == - Brightness - .light - ? FontWeight - .w500 - : FontWeight - .w600, - ).fontSize ?? - 16.0), - width: constraints - .maxWidth, - child: Marquee( - key: ValueKey( - widget.item.item.id), - text: widget.item.item.title, - style: widget.isCurrentTrack - ? TextStyle( - color: - Theme.of(context).colorScheme.secondary, - fontSize: 16, - fontWeight: FontWeight.w400, - overflow: TextOverflow.ellipsis) - : null, - scrollAxis: Axis - .horizontal, - blankSpace: 20.0, - velocity: 50.0, - pauseAfterRound: - const Duration( - seconds: - 3), - accelerationDuration: - const Duration( - seconds: - 1), - accelerationCurve: - Curves.linear, - decelerationDuration: - const Duration( - milliseconds: - 500), - decelerationCurve: - Curves - .easeOut, - textDirection: - TextDirection - .ltr, - ), - ); - } else { - return Container( - width: constraints - .maxWidth, - child: BalancedText( - widget.item.item.title, - style: widget.isCurrentTrack - ? TextStyle( - color: - Theme.of(context).colorScheme.secondary, - fontSize: 16, - fontWeight: FontWeight.w400, - overflow: TextOverflow.ellipsis) - : null, - overflow: - TextOverflow - .ellipsis, - maxLines: 2, - textAlign: - TextAlign - .start, - ), - ); - } - }, + child: OneLineMarqueeHelper( + key: ValueKey(widget.item.item.id), + text: widget.item.item.title, + style: TextStyle( + fontSize: 16, + height: 26 / 20, + fontWeight: Theme.of(context).brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), ), ), ), diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index 0f4d53226..c245b5bc7 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -1,14 +1,13 @@ import 'dart:math'; import 'package:audio_service/audio_service.dart'; -import 'package:balanced_text/balanced_text.dart'; import 'package:finamp/color_schemes.g.dart'; import 'package:finamp/components/AddToPlaylistScreen/add_to_playlist_button.dart'; import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/services/current_track_metadata_provider.dart'; import 'package:finamp/services/feedback_helper.dart'; +import 'package:finamp/services/one_line_marquee_helper.dart'; import 'package:finamp/services/queue_service.dart'; -import 'package:finamp/services/scrolling_text_helper.dart'; import 'package:finamp/services/theme_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -16,7 +15,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:get_it/get_it.dart'; -import 'package:marquee/marquee.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; import '../models/jellyfin_models.dart' as jellyfin_models; @@ -339,131 +337,21 @@ class NowPlayingBar extends ConsumerWidget { children: [ SizedBox( height: 20, - child: LayoutBuilder( - builder: (context, - constraints) { - final textPainter = - TextPainter( - text: TextSpan( - text: currentTrack - .item.title, - style: TextStyle( - fontSize: 16, - fontWeight: Theme.of( - context) - .brightness == - Brightness - .light - ? FontWeight - .w500 - : FontWeight - .w600, - ), - ), - maxLines: 1, - textDirection: - TextDirection.ltr, - )..layout( - maxWidth: - constraints - .maxWidth); - - final isOverflowing = - textPainter - .didExceedMaxLines; - - if (isOverflowing) { - return Container( - alignment: Alignment - .centerLeft, - height: (TextStyle( - fontSize: 16, - fontWeight: Theme.of(context) - .brightness == - Brightness - .light - ? FontWeight - .w500 - : FontWeight - .w600, - ).fontSize ?? - 16.0), - width: constraints - .maxWidth, - child: Marquee( - key: ValueKey( - currentTrack - .item.id), - text: currentTrack - .item.title, - style: TextStyle( - fontSize: 16, - color: Colors.white, - fontWeight: Theme.of(context) - .brightness == - Brightness - .light - ? FontWeight - .w500 - : FontWeight - .w600, - ), - scrollAxis: Axis - .horizontal, - blankSpace: 20.0, - velocity: 50.0, - pauseAfterRound: - const Duration( - seconds: - 3), - accelerationDuration: - const Duration( - seconds: - 1), - accelerationCurve: - Curves.linear, - decelerationDuration: - const Duration( - milliseconds: - 500), - decelerationCurve: - Curves - .easeOut, - textDirection: - TextDirection - .ltr, - ), - ); - } else { - return Container( - width: constraints - .maxWidth, - child: BalancedText( - currentTrack - .item.title, - style: TextStyle( - fontSize: 16, - color: Colors.white, - fontWeight: Theme.of(context) - .brightness == - Brightness - .light - ? FontWeight - .w500 - : FontWeight - .w600, - ), - overflow: - TextOverflow - .ellipsis, - maxLines: 2, - textAlign: - TextAlign - .start, - ), - ); - } - }, + child: OneLineMarqueeHelper( + key: ValueKey( + currentTrack.item.id), + text: currentTrack + .item.title, + style: TextStyle( + fontSize: 16, + height: 26 / 20, + fontWeight: Theme.of( + context) + .brightness == + Brightness.light + ? FontWeight.w500 + : FontWeight.w600, + ), ), ), const SizedBox(height: 4), diff --git a/lib/services/one_line_marquee_helper.dart b/lib/services/one_line_marquee_helper.dart new file mode 100644 index 000000000..1eb22e566 --- /dev/null +++ b/lib/services/one_line_marquee_helper.dart @@ -0,0 +1,89 @@ +// Use this helper in places that don't require +// two lines marquee + +import 'package:flutter/material.dart'; +import 'package:marquee/marquee.dart'; +import 'package:balanced_text/balanced_text.dart'; + +class OneLineMarqueeHelper extends StatelessWidget { + final String text; + final TextStyle style; + final Key key; + + const OneLineMarqueeHelper({ + required this.text, + required this.style, + required this.key, + }); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final textPainter = TextPainter( + text: TextSpan( + text: text, + style: style, + ), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(maxWidth: constraints.maxWidth); + + final isOverflowing = textPainter.didExceedMaxLines; + + if (isOverflowing) { + return Container( + alignment: Alignment.centerLeft, + height: style.fontSize ?? 16.0, + width: constraints.maxWidth, + child: Stack( + children: [ + Positioned.fill( + child: Marquee( + key: key, + text: text, + style: style, + scrollAxis: Axis.horizontal, + blankSpace: 20.0, + velocity: 50.0, + pauseAfterRound: const Duration(seconds: 3), + accelerationDuration: const Duration(seconds: 1), + accelerationCurve: Curves.linear, + decelerationDuration: const Duration(milliseconds: 500), + decelerationCurve: Curves.easeOut, + textDirection: TextDirection.ltr, + ), + ), + Positioned( + left: 0, + child: Container( + width: 20, + color: Theme.of(context).scaffoldBackgroundColor, + ), + ), + Positioned( + right: 0, + child: Container( + width: 20, + color: Theme.of(context).scaffoldBackgroundColor, + ), + ), + ], + ), + ); + } else { + return Container( + width: constraints.maxWidth, + child: BalancedText( + text, + style: style, + overflow: TextOverflow.ellipsis, + maxLines: 2, + textAlign: TextAlign.start, + ), + ); + } + }, + ); + } +} From 3bb3cb9c336361b75f84d6536b6edea173691d7f Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Tue, 4 Jun 2024 17:36:36 -0400 Subject: [PATCH 18/23] removed marquee from track/album screen --- .../AlbumScreen/song_list_tile.dart | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/lib/components/AlbumScreen/song_list_tile.dart b/lib/components/AlbumScreen/song_list_tile.dart index 35e8a483d..5fcf2f2e7 100644 --- a/lib/components/AlbumScreen/song_list_tile.dart +++ b/lib/components/AlbumScreen/song_list_tile.dart @@ -132,36 +132,30 @@ class _SongListTileState extends ConsumerState ), title: Opacity( opacity: playable ? 1.0 : 0.5, - child: Row( - children: [ - if (widget.item.indexNumber != null && - !widget.isSong && - widget.item.albumId == widget.parentItem?.id) - Text( - "${widget.item.indexNumber}. ", + child: RichText( + text: TextSpan( + children: [ + // third condition checks if the item is viewed from its album (instead of e.g. a playlist) + // same horrible check as in canGoToAlbum in GestureDetector below + if (widget.item.indexNumber != null && + !widget.isSong && + widget.item.albumId == widget.parentItem?.id) + TextSpan( + text: "${widget.item.indexNumber}. ", + style: TextStyle( + color: Theme.of(context).disabledColor)), + TextSpan( + text: widget.item.name ?? + AppLocalizations.of(context)!.unknownName, style: TextStyle( - color: Theme.of(context).disabledColor, + color: isCurrentlyPlaying + ? Theme.of(context).colorScheme.secondary + : null, ), ), - Expanded( - child: SizedBox( - height: 20, - child: OneLineMarqueeHelper( - key: ValueKey(widget.item.id), - text: widget.item.name ?? - AppLocalizations.of(context)!.unknownName, - style: TextStyle( - fontSize: 16, - height: 26 / 20, - fontWeight: - Theme.of(context).brightness == Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - ), - ), - ), - ], + ], + style: Theme.of(context).textTheme.titleMedium, + ), ), ), From c09cd94c26ed53670592df5cc9bbafdfe1b2f690 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Tue, 4 Jun 2024 17:42:34 -0400 Subject: [PATCH 19/23] nowplayingbar font color doesn't adapt to system color --- lib/components/now_playing_bar.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index c245b5bc7..ab171afd3 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -345,6 +345,7 @@ class NowPlayingBar extends ConsumerWidget { style: TextStyle( fontSize: 16, height: 26 / 20, + color: Colors.white, fontWeight: Theme.of( context) .brightness == From 5d358334d8f2f6d4ebbad44f660ffcb51d82ec60 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Tue, 4 Jun 2024 17:53:50 -0400 Subject: [PATCH 20/23] kept marquee only for current playing track in queue list --- lib/components/PlayerScreen/queue_list.dart | 1 + .../PlayerScreen/queue_list_item.dart | 23 ++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/components/PlayerScreen/queue_list.dart b/lib/components/PlayerScreen/queue_list.dart index 505d6fe8f..121139025 100644 --- a/lib/components/PlayerScreen/queue_list.dart +++ b/lib/components/PlayerScreen/queue_list.dart @@ -853,6 +853,7 @@ class _CurrentTrackState extends State { style: TextStyle( fontSize: 16, height: 26 / 20, + color: Colors.white, fontWeight: Theme.of(context).brightness == Brightness.light diff --git a/lib/components/PlayerScreen/queue_list_item.dart b/lib/components/PlayerScreen/queue_list_item.dart index 641c8b76b..280076557 100644 --- a/lib/components/PlayerScreen/queue_list_item.dart +++ b/lib/components/PlayerScreen/queue_list_item.dart @@ -132,20 +132,17 @@ class _QueueListItemState extends State children: [ Padding( padding: const EdgeInsets.all(0.0), - child: SizedBox( - height: 20, - child: OneLineMarqueeHelper( - key: ValueKey(widget.item.item.id), - text: widget.item.item.title, - style: TextStyle( + child: Text( + widget.item.item.title, + style: widget.isCurrentTrack + ? TextStyle( + color: + Theme.of(context).colorScheme.secondary, fontSize: 16, - height: 26 / 20, - fontWeight: Theme.of(context).brightness == - Brightness.light - ? FontWeight.w500 - : FontWeight.w600, - ), - ), + fontWeight: FontWeight.w400, + overflow: TextOverflow.ellipsis) + : null, + overflow: TextOverflow.ellipsis, ), ), Padding( From a6436eb7363baa53cd9815232680654182eb1ed4 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Tue, 4 Jun 2024 18:10:20 -0400 Subject: [PATCH 21/23] added marquee in hamburger menu song title --- lib/components/AlbumScreen/song_menu.dart | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/components/AlbumScreen/song_menu.dart b/lib/components/AlbumScreen/song_menu.dart index faa2c43fe..de65da660 100644 --- a/lib/components/AlbumScreen/song_menu.dart +++ b/lib/components/AlbumScreen/song_menu.dart @@ -10,6 +10,7 @@ import 'package:finamp/screens/artist_screen.dart'; import 'package:finamp/services/current_track_metadata_provider.dart'; import 'package:finamp/services/feedback_helper.dart'; import 'package:finamp/services/metadata_provider.dart'; +import 'package:finamp/services/one_line_marquee_helper.dart'; import 'package:finamp/services/music_player_background_task.dart'; import 'package:finamp/services/queue_service.dart'; import 'package:finamp/services/theme_provider.dart'; @@ -836,20 +837,18 @@ class _SongInfoState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Text( - widget.item.name ?? + OneLineMarqueeHelper( + key: ValueKey( + widget.item.id), + text: widget.item.name ?? AppLocalizations.of(context)!.unknownName, - textAlign: TextAlign.start, style: TextStyle( fontSize: widget.condensed ? 16 : 18, height: 1.2, color: - Theme.of(context).textTheme.bodyMedium?.color ?? - Colors.white, + Theme.of(context).textTheme.bodyMedium?.color ?? + Colors.white, ), - overflow: TextOverflow.ellipsis, - softWrap: true, - maxLines: 2, ), Padding( padding: widget.condensed From afb8def4688d8853ee6f9a82dddb22d72fa6c753 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Wed, 10 Jul 2024 05:44:47 -0400 Subject: [PATCH 22/23] Moved the existing "one line marquee" setting to the customization settings and renamed it "Allow one line break before using scrolling text" --- lib/l10n/app_en.arb | 2 +- lib/screens/player_settings_screen.dart | 28 ------------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1ffe9428b..9514ad85f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1466,7 +1466,7 @@ "@enableVibrationSubtitle": {}, "hideQueueButton": "Hide queue button", "hideQueueButtonSubtitle": "Hide the queue button on the player screen. Swipe up to access the queue.", - "oneLineMarqueeTextButton": "One line track title", + "oneLineMarqueeTextButton": "Allow one line break before using scrolling text", "oneLineMarqueeTextButtonSubtitle": "Sets song title to one line marquee text in player screen. Default 2", "prioritizePlayerCover": "Prioritize album cover", "prioritizePlayerCoverSubtitle": "Prioritize showing a larger album cover on player screen. Non-critical controls will be hidden more aggressively at small screen sizes.", diff --git a/lib/screens/player_settings_screen.dart b/lib/screens/player_settings_screen.dart index 8d82ad1c9..fc2c17230 100644 --- a/lib/screens/player_settings_screen.dart +++ b/lib/screens/player_settings_screen.dart @@ -23,7 +23,6 @@ class PlayerSettingsScreen extends StatelessWidget { SuppressPlayerPaddingSwitch(), PrioritizeCoverSwitch(), HideQueueButtonSwitch(), - OneLineMarqueeTextSwitch(), ], ), ); @@ -117,30 +116,3 @@ class PrioritizeCoverSwitch extends StatelessWidget { ); } } - -class OneLineMarqueeTextSwitch extends StatelessWidget { - const OneLineMarqueeTextSwitch({super.key}); - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder>( - valueListenable: FinampSettingsHelper.finampSettingsListener, - builder: (context, box, child) { - bool? oneLineMarquee = box.get("FinampSettings")?.oneLineMarqueeTextButton; - - return SwitchListTile.adaptive( - title: Text(AppLocalizations.of(context)!.oneLineMarqueeTextButton), - subtitle: Text(AppLocalizations.of(context)!.oneLineMarqueeTextButtonSubtitle), - value: oneLineMarquee ?? false, - onChanged: oneLineMarquee == null - ? null - : (value) { - FinampSettings finampSettingsTemp = box.get("FinampSettings")!; - finampSettingsTemp.oneLineMarqueeTextButton = value; - box.put("FinampSettings", finampSettingsTemp); - }, - ); - }, - ); - } -} \ No newline at end of file From 81cc7fccabbd7d48c2aefb68f03ce7c5e65134d0 Mon Sep 17 00:00:00 2001 From: Anirudh Kurma Date: Wed, 10 Jul 2024 06:02:03 -0400 Subject: [PATCH 23/23] Added new customization option for truncating track title with ellipses instead of using marquee --- lib/l10n/app_en.arb | 2 + lib/models/finamp_models.dart | 5 ++ .../customization_settings_screen.dart | 64 +++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9514ad85f..699e508e9 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1468,6 +1468,8 @@ "hideQueueButtonSubtitle": "Hide the queue button on the player screen. Swipe up to access the queue.", "oneLineMarqueeTextButton": "Allow one line break before using scrolling text", "oneLineMarqueeTextButtonSubtitle": "Sets song title to one line marquee text in player screen. Default 2", + "marqueeOrTruncateButton": "Use marquee effect or truncate by ellipses", + "marqueeOrTruncateButtonSubtitle": "If enabled, long track title overflow will be handled by ellipses (...)", "prioritizePlayerCover": "Prioritize album cover", "prioritizePlayerCoverSubtitle": "Prioritize showing a larger album cover on player screen. Non-critical controls will be hidden more aggressively at small screen sizes.", "suppressPlayerPadding": "Suppress player controls padding", diff --git a/lib/models/finamp_models.dart b/lib/models/finamp_models.dart index 2b34d89be..758e71e0a 100644 --- a/lib/models/finamp_models.dart +++ b/lib/models/finamp_models.dart @@ -100,6 +100,7 @@ const _prioritizeCoverFactor = 8.0; const _suppressPlayerPadding = false; const _hideQueueButton = false; const _oneLineMarqueeTextButton = false; +const _marqueeOrTruncateButton = false; const _reportQueueToServerDefault = false; const _periodicPlaybackSessionUpdateFrequencySecondsDefault = 150; const _showArtistChipImage = true; @@ -173,6 +174,7 @@ class FinampSettings { this.suppressPlayerPadding = _suppressPlayerPadding, this.hideQueueButton = _hideQueueButton, this.oneLineMarqueeTextButton = _oneLineMarqueeTextButton, + this.marqueeOrTruncateButton = _marqueeOrTruncateButton, this.reportQueueToServer = _reportQueueToServerDefault, this.periodicPlaybackSessionUpdateFrequencySeconds = _periodicPlaybackSessionUpdateFrequencySecondsDefault, @@ -397,6 +399,9 @@ class FinampSettings { @HiveField(67, defaultValue: _oneLineMarqueeTextButton) bool oneLineMarqueeTextButton; + @HiveField(68, defaultValue: _oneLineMarqueeTextButton) + bool marqueeOrTruncateButton; + static Future create() async { final downloadLocation = await DownloadLocation.create( name: "Internal Storage", diff --git a/lib/screens/customization_settings_screen.dart b/lib/screens/customization_settings_screen.dart index 2e344203b..d02b240e1 100644 --- a/lib/screens/customization_settings_screen.dart +++ b/lib/screens/customization_settings_screen.dart @@ -1,7 +1,9 @@ import 'package:finamp/components/LayoutSettingsScreen/CustomizationSettingsScreen/playback_speed_control_visibility_dropdown_list_tile.dart'; +import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/services/finamp_settings_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:hive_flutter/hive_flutter.dart'; class CustomizationSettingsScreen extends StatefulWidget { const CustomizationSettingsScreen({Key? key}) : super(key: key); @@ -35,8 +37,70 @@ class _CustomizationSettingsScreenState body: ListView( children: const [ PlaybackSpeedControlVisibilityDropdownListTile(), + OneLineMarqueeTextSwitch(), + MarqueeOrTruncate(), ], ), ); } } + +class OneLineMarqueeTextSwitch extends StatelessWidget { + const OneLineMarqueeTextSwitch({super.key}); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (context, box, child) { + bool? oneLineMarquee = + box.get("FinampSettings")?.oneLineMarqueeTextButton; + + return SwitchListTile.adaptive( + title: Text(AppLocalizations.of(context)!.oneLineMarqueeTextButton), + subtitle: Text( + AppLocalizations.of(context)!.oneLineMarqueeTextButtonSubtitle), + value: oneLineMarquee ?? false, + onChanged: oneLineMarquee == null + ? null + : (value) { + FinampSettings finampSettingsTemp = + box.get("FinampSettings")!; + finampSettingsTemp.oneLineMarqueeTextButton = value; + box.put("FinampSettings", finampSettingsTemp); + }, + ); + }, + ); + } +} + +class MarqueeOrTruncate extends StatelessWidget { + const MarqueeOrTruncate({super.key}); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (context, box, child) { + bool? oneLineMarquee = + box.get("FinampSettings")?.marqueeOrTruncateButton; + + return SwitchListTile.adaptive( + title: Text(AppLocalizations.of(context)!.marqueeOrTruncateButton), + subtitle: Text( + AppLocalizations.of(context)!.marqueeOrTruncateButtonSubtitle), + value: oneLineMarquee ?? false, + onChanged: oneLineMarquee == null + ? null + : (value) { + FinampSettings finampSettingsTemp = + box.get("FinampSettings")!; + finampSettingsTemp.marqueeOrTruncateButton = value; + box.put("FinampSettings", finampSettingsTemp); + }, + ); + }, + ); + } +}