Skip to content

Commit

Permalink
implemented lyrics alignment options
Browse files Browse the repository at this point in the history
- slightly modifies how lyrics are wrapped, but the new behavior, while not as pretty, uses the available space more efficiently
  • Loading branch information
Chaphasilor committed Jun 3, 2024
1 parent bae5d40 commit 83d532d
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 38 deletions.
2 changes: 1 addition & 1 deletion lib/components/LayoutSettingsScreen/theme_selector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class ThemeSelector extends StatelessWidget {
valueListenable: ThemeModeHelper.themeModeListener,
builder: (_, box, __) {
return ListTile(
title: const Text("Theme"),
title: Text(AppLocalizations.of(context)!.theme),
trailing: DropdownButton<ThemeMode>(
value: box.get("ThemeMode"),
items: ThemeMode.values
Expand Down
20 changes: 20 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1585,5 +1585,25 @@
"showSeekControlsOnMediaNotificationSubtitle": "Controls if the media notification has a seekable progress bar. This lets you change the playback position without opening the app.",
"@showSeekControlsOnMediaNotificationSubtitle": {
"description": "Subtitle for the setting that controls if the media notification has a seekable progress bar."
},
"alignmentOptionStart": "Start",
"@alignmentOptionStart": {
"description": "Alignment option for things like the lyrics view. Start means left-aligned in LTR languages and right-aligned in RTL languages."
},
"alignmentOptionCenter": "Center",
"@alignmentOptionCenter": {
"description": "Alignment option for things like the lyrics view. Center means centered."
},
"alignmentOptionEnd": "End",
"@alignmentOptionEnd": {
"description": "Alignment option for things like the lyrics view. End means right-aligned in LTR languages and left-aligned in RTL languages."
},
"lyricsAlignmentTitle": "Lyrics alignment",
"@lyricsAlignmentTitle": {
"description": "Title for the setting that controls the alignment of lyrics in the lyrics view"
},
"lyricsAlignmentSubtitle": "Controls the alignment of lyrics in the lyrics view.",
"@lyricsAlignmentSubtitle": {
"description": "Subtitle for the setting that controls the alignment of lyrics in the lyrics view"
}
}
1 change: 1 addition & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ Future<void> setupHive() async {
Hive.registerAdapter(LyricMetadataAdapter());
Hive.registerAdapter(LyricLineAdapter());
Hive.registerAdapter(LyricDtoAdapter());
Hive.registerAdapter(LyricsAlignmentAdapter());

final dir = (Platform.isAndroid || Platform.isIOS)
? await getApplicationDocumentsDirectory()
Expand Down
53 changes: 50 additions & 3 deletions lib/models/finamp_models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ const _showArtistChipImage = true;
const _trackOfflineFavoritesDefault = true;
const _showProgressOnNowPlayingBarDefault = true;
const _startInstantMixForIndividualTracksDefault = true;
const _showLyricsTimestampsDefault = true;
const _lyricsAlignmentDefault = LyricsAlignment.start;
const _showStopButtonOnMediaNotificationDefault = false;
const _showSeekControlsOnMediaNotificationDefault = true;
const _showLyricsTimestampsDefault = true;

@HiveType(typeId: 28)
class FinampSettings {
Expand Down Expand Up @@ -182,6 +183,7 @@ class FinampSettings {
this.showProgressOnNowPlayingBar = _showProgressOnNowPlayingBarDefault,
this.startInstantMixForIndividualTracks = _startInstantMixForIndividualTracksDefault,
this.showLyricsTimestamps = _showLyricsTimestampsDefault,
this.lyricsAlignment = _lyricsAlignmentDefault,
this.showStopButtonOnMediaNotification = _showStopButtonOnMediaNotificationDefault,
this.showSeekControlsOnMediaNotification = _showSeekControlsOnMediaNotificationDefault,
});
Expand Down Expand Up @@ -400,10 +402,13 @@ class FinampSettings {
@HiveField(66, defaultValue: _showLyricsTimestampsDefault)
bool showLyricsTimestamps;

@HiveField(67, defaultValue: _showStopButtonOnMediaNotificationDefault)
@HiveField(67, defaultValue: _lyricsAlignmentDefault)
LyricsAlignment lyricsAlignment;

@HiveField(68, defaultValue: _showStopButtonOnMediaNotificationDefault)
bool showStopButtonOnMediaNotification;

@HiveField(68, defaultValue: _showSeekControlsOnMediaNotificationDefault)
@HiveField(69, defaultValue: _showSeekControlsOnMediaNotificationDefault)
bool showSeekControlsOnMediaNotification;

static Future<FinampSettings> create() async {
Expand Down Expand Up @@ -454,6 +459,7 @@ class FinampSettings {
SortOrder getSortOrder(TabContentType tabType) {
return tabSortOrder[tabType] ?? SortOrder.ascending;
}

}

enum CustomPlaybackActions {
Expand Down Expand Up @@ -2079,3 +2085,44 @@ class MediaItemId {
}

}

@HiveType(typeId: 70)
enum LyricsAlignment {
@HiveField(0)
start,
@HiveField(1)
center,
@HiveField(2)
end;

/// Human-readable version of the [LyricsAlignment]
@override
@Deprecated("Use toLocalisedString when possible")
String toString() => _humanReadableName(this);

String toLocalisedString(BuildContext context) =>
_humanReadableLocalisedName(this, context);

String _humanReadableName(LyricsAlignment tabContentType) {
switch (tabContentType) {
case LyricsAlignment.start:
return "Start";
case LyricsAlignment.center:
return "Center";
case LyricsAlignment.end:
return "End";
}
}

String _humanReadableLocalisedName(
LyricsAlignment tabContentType, BuildContext context) {
switch (tabContentType) {
case LyricsAlignment.start:
return AppLocalizations.of(context)!.alignmentOptionStart;
case LyricsAlignment.center:
return AppLocalizations.of(context)!.alignmentOptionCenter;
case LyricsAlignment.end:
return AppLocalizations.of(context)!.alignmentOptionEnd;
}
}
}
57 changes: 53 additions & 4 deletions lib/models/finamp_models.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 46 additions & 30 deletions lib/screens/lyrics_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:io';

import 'package:finamp/color_schemes.g.dart';
import 'package:finamp/components/PlayerScreen/player_screen_appbar_title.dart';
import 'package:finamp/models/finamp_models.dart';
import 'package:finamp/models/jellyfin_models.dart';
import 'package:finamp/services/current_track_metadata_provider.dart';
import 'package:finamp/services/feedback_helper.dart';
Expand Down Expand Up @@ -367,6 +368,7 @@ class _LyricsViewState extends ConsumerState<LyricsView>
(nextLine.start ?? 0) ~/ 10);

return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (index == 0)
AutoScrollTag(
Expand Down Expand Up @@ -461,47 +463,50 @@ class _LyricLine extends ConsumerWidget {
onTap: isSynchronized ? onTap : null,
child: Padding(
padding: EdgeInsets.symmetric(vertical: isSynchronized ? 10.0 : 6.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
child:
Text.rich(
textAlign: lyricsAlignmentToTextAlign(finampSettings?.lyricsAlignment ?? LyricsAlignment.start),
softWrap: true,
TextSpan(
children: [
if (
line.start != null
&& (line.text?.trim().isNotEmpty ?? false)
&& (finampSettings?.showLyricsTimestamps ?? true)
)
Text(
"${Duration(microseconds: (line.start ?? 0) ~/ 10).inMinutes}:${(Duration(microseconds: (line.start ?? 0) ~/ 10).inSeconds % 60).toString().padLeft(2, '0')}",
WidgetSpan(
alignment: PlaceholderAlignment.bottom,
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(
"${Duration(microseconds: (line.start ?? 0) ~/ 10).inMinutes}:${(Duration(microseconds: (line.start ?? 0) ~/ 10).inSeconds % 60).toString().padLeft(2, '0')}",
style: TextStyle(
color: lowlightLine
? Colors.grey
: Theme.of(context).textTheme.bodyLarge!.color,
fontSize: 16,
height: 1.75,
),
),
),
),
TextSpan(
text: line.text ?? "<missing lyric line>",
style: TextStyle(
color: lowlightLine
? Colors.grey
: Theme.of(context).textTheme.bodyLarge!.color,
fontSize: 16,
height: 1.75,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 12.0),
child: Text(
line.text ?? "<missing lyric line>",
textAlign: TextAlign.start,
style: TextStyle(
color: lowlightLine
? Colors.grey
: Theme.of(context).textTheme.bodyLarge!.color,
fontWeight: lowlightLine || !isSynchronized
? FontWeight.normal
: FontWeight.w500,
letterSpacing: lowlightLine || !isSynchronized
? 0.05
: -0.045, // keep text width consistent across the different weights
fontSize: isSynchronized ? 26 : 20,
height: 1.25,
),
fontWeight: lowlightLine || !isSynchronized
? FontWeight.normal
: FontWeight.w500,
letterSpacing: lowlightLine || !isSynchronized
? 0.05
: -0.045, // keep text width consistent across the different weights
fontSize: isSynchronized ? 26 : 20,
height: 1.25,
),
),
),
],
]),
),
),
);
Expand Down Expand Up @@ -581,3 +586,14 @@ class EnableAutoScrollButton extends StatelessWidget {
: const SizedBox.shrink();
}
}

TextAlign lyricsAlignmentToTextAlign(LyricsAlignment alignment) {
switch (alignment) {
case LyricsAlignment.start:
return TextAlign.start;
case LyricsAlignment.center:
return TextAlign.center;
case LyricsAlignment.end:
return TextAlign.end;
}
}
37 changes: 37 additions & 0 deletions lib/screens/lyrics_settings_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class LyricsSettingsScreen extends StatelessWidget {
body: ListView(
children: const [
ShowLyricsTimestampsToggle(),
LyricsAlignmentSelector(),
],
),
);
Expand Down Expand Up @@ -55,3 +56,39 @@ class ShowLyricsTimestampsToggle extends StatelessWidget {
);
}
}

class LyricsAlignmentSelector extends StatelessWidget {
const LyricsAlignmentSelector({super.key});

@override
Widget build(BuildContext context) {
return ValueListenableBuilder<Box<FinampSettings>>(
valueListenable: FinampSettingsHelper.finampSettingsListener,
builder: (_, box, __) {
final finampSettings = box.get("FinampSettings")!;
return ListTile(
title: Text(AppLocalizations.of(context)!.lyricsAlignmentTitle),
subtitle: Text(AppLocalizations.of(context)!.lyricsAlignmentSubtitle),
trailing: DropdownButton<LyricsAlignment>(
value: finampSettings.lyricsAlignment,
items: LyricsAlignment.values
.map((e) => DropdownMenuItem<LyricsAlignment>(
value: e,
child: Text(e.toLocalisedString(context)),
))
.toList(),
onChanged: (value) {
if (value != null) {
FinampSettings finampSettingsTemp = finampSettings;
finampSettingsTemp.lyricsAlignment = value;
Hive.box<FinampSettings>("FinampSettings")
.put("FinampSettings", finampSettingsTemp);
}
},
),
);
},
);
}
}

0 comments on commit 83d532d

Please sign in to comment.