#380 subtitle vertical position option

This commit is contained in:
Thibault Deckers 2022-11-23 11:25:10 +01:00
parent f3bee6ec7e
commit cbfbc436ed
10 changed files with 163 additions and 28 deletions

View file

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Viewer: Info page editing actions available as quick actions - Viewer: Info page editing actions available as quick actions
- Video: subtitle vertical position option
- Info: export metadata to text file - Info: export metadata to text file
- Accessibility: apply bold font system setting - Accessibility: apply bold font system setting
- `libre` app flavor (no mobile service maps, no Crashlytics) - `libre` app flavor (no mobile service maps, no Crashlytics)

View file

@ -198,6 +198,9 @@
"displayRefreshRatePreferHighest": "Highest rate", "displayRefreshRatePreferHighest": "Highest rate",
"displayRefreshRatePreferLowest": "Lowest rate", "displayRefreshRatePreferLowest": "Lowest rate",
"subtitlePositionTop": "Top",
"subtitlePositionBottom": "Bottom",
"videoPlaybackSkip": "Skip", "videoPlaybackSkip": "Skip",
"videoPlaybackMuted": "Play muted", "videoPlaybackMuted": "Play muted",
"videoPlaybackWithSound": "Play with sound", "videoPlaybackWithSound": "Play with sound",
@ -738,6 +741,8 @@
"settingsSubtitleThemeSample": "This is a sample.", "settingsSubtitleThemeSample": "This is a sample.",
"settingsSubtitleThemeTextAlignmentTile": "Text alignment", "settingsSubtitleThemeTextAlignmentTile": "Text alignment",
"settingsSubtitleThemeTextAlignmentDialogTitle": "Text Alignment", "settingsSubtitleThemeTextAlignmentDialogTitle": "Text Alignment",
"settingsSubtitleThemeTextPositionTile": "Text position",
"settingsSubtitleThemeTextPositionDialogTitle": "Text Position",
"settingsSubtitleThemeTextSize": "Text size", "settingsSubtitleThemeTextSize": "Text size",
"settingsSubtitleThemeShowOutline": "Show outline and shadow", "settingsSubtitleThemeShowOutline": "Show outline and shadow",
"settingsSubtitleThemeTextColor": "Text color", "settingsSubtitleThemeTextColor": "Text color",

View file

@ -98,6 +98,7 @@ class SettingsDefaults {
// subtitles // subtitles
static const subtitleFontSize = 20.0; static const subtitleFontSize = 20.0;
static const subtitleTextAlignment = TextAlign.center; static const subtitleTextAlignment = TextAlign.center;
static const subtitleTextPosition = SubtitlePosition.bottom;
static const subtitleShowOutline = true; static const subtitleShowOutline = true;
static const subtitleTextColor = Colors.white; static const subtitleTextColor = Colors.white;
static const subtitleBackgroundColor = Colors.transparent; static const subtitleBackgroundColor = Colors.transparent;

View file

@ -20,6 +20,8 @@ enum KeepScreenOn { never, viewerOnly, always }
enum SlideshowVideoPlayback { skip, playMuted, playWithSound } enum SlideshowVideoPlayback { skip, playMuted, playWithSound }
enum SubtitlePosition { top, bottom }
enum UnitSystem { metric, imperial } enum UnitSystem { metric, imperial }
enum VideoControls { play, playSeek, playOutside, none } enum VideoControls { play, playSeek, playOutside, none }

View file

@ -0,0 +1,24 @@
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/widgets.dart';
import 'enums.dart';
extension ExtraSubtitlePosition on SubtitlePosition {
String getName(BuildContext context) {
switch (this) {
case SubtitlePosition.top:
return context.l10n.subtitlePositionTop;
case SubtitlePosition.bottom:
return context.l10n.subtitlePositionBottom;
}
}
TextAlignVertical toTextAlignVertical() {
switch (this) {
case SubtitlePosition.top:
return TextAlignVertical.top;
case SubtitlePosition.bottom:
return TextAlignVertical.bottom;
}
}
}

View file

@ -123,6 +123,7 @@ class Settings extends ChangeNotifier {
// subtitles // subtitles
static const subtitleFontSizeKey = 'subtitle_font_size'; static const subtitleFontSizeKey = 'subtitle_font_size';
static const subtitleTextAlignmentKey = 'subtitle_text_alignment'; static const subtitleTextAlignmentKey = 'subtitle_text_alignment';
static const subtitleTextPositionKey = 'subtitle_text_position';
static const subtitleShowOutlineKey = 'subtitle_show_outline'; static const subtitleShowOutlineKey = 'subtitle_show_outline';
static const subtitleTextColorKey = 'subtitle_text_color'; static const subtitleTextColorKey = 'subtitle_text_color';
static const subtitleBackgroundColorKey = 'subtitle_background_color'; static const subtitleBackgroundColorKey = 'subtitle_background_color';
@ -573,6 +574,10 @@ class Settings extends ChangeNotifier {
set subtitleTextAlignment(TextAlign newValue) => setAndNotify(subtitleTextAlignmentKey, newValue.toString()); set subtitleTextAlignment(TextAlign newValue) => setAndNotify(subtitleTextAlignmentKey, newValue.toString());
SubtitlePosition get subtitleTextPosition => getEnumOrDefault(subtitleTextPositionKey, SettingsDefaults.subtitleTextPosition, SubtitlePosition.values);
set subtitleTextPosition(SubtitlePosition newValue) => setAndNotify(subtitleTextPositionKey, newValue.toString());
bool get subtitleShowOutline => getBool(subtitleShowOutlineKey) ?? SettingsDefaults.subtitleShowOutline; bool get subtitleShowOutline => getBool(subtitleShowOutlineKey) ?? SettingsDefaults.subtitleShowOutline;
set subtitleShowOutline(bool newValue) => setAndNotify(subtitleShowOutlineKey, newValue); set subtitleShowOutline(bool newValue) => setAndNotify(subtitleShowOutlineKey, newValue);
@ -963,6 +968,7 @@ class Settings extends ChangeNotifier {
case videoLoopModeKey: case videoLoopModeKey:
case videoControlsKey: case videoControlsKey:
case subtitleTextAlignmentKey: case subtitleTextAlignmentKey:
case subtitleTextPositionKey:
case mapStyleKey: case mapStyleKey:
case mapDefaultCenterKey: case mapDefaultCenterKey:
case coordinateFormatKey: case coordinateFormatKey:

View file

@ -1,3 +1,4 @@
import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/common/basic/outlined_text.dart'; import 'package:aves/widgets/common/basic/outlined_text.dart';
@ -20,6 +21,7 @@ class SubtitleSample extends StatelessWidget {
return Consumer<Settings>( return Consumer<Settings>(
builder: (context, settings, child) { builder: (context, settings, child) {
final textAlign = settings.subtitleTextAlignment; final textAlign = settings.subtitleTextAlignment;
final textPosition = settings.subtitleTextPosition;
final outlineColor = Colors.black.withOpacity(settings.subtitleTextColor.opacity); final outlineColor = Colors.black.withOpacity(settings.subtitleTextColor.opacity);
final shadows = [ final shadows = [
Shadow( Shadow(
@ -40,7 +42,7 @@ class SubtitleSample extends StatelessWidget {
), ),
height: 128, height: 128,
child: AnimatedAlign( child: AnimatedAlign(
alignment: _getAlignment(textAlign), alignment: _getAlignment(textAlign, textPosition),
curve: Curves.easeInOutCubic, curve: Curves.easeInOutCubic,
duration: const Duration(milliseconds: 400), duration: const Duration(milliseconds: 400),
child: Padding( child: Padding(
@ -75,7 +77,19 @@ class SubtitleSample extends StatelessWidget {
); );
} }
Alignment _getAlignment(TextAlign textAlign) { Alignment _getAlignment(TextAlign textAlign, SubtitlePosition textPosition) {
switch (textPosition) {
case SubtitlePosition.top:
switch (textAlign) {
case TextAlign.left:
return Alignment.topLeft;
case TextAlign.right:
return Alignment.topRight;
case TextAlign.center:
default:
return Alignment.topCenter;
}
case SubtitlePosition.bottom:
switch (textAlign) { switch (textAlign) {
case TextAlign.left: case TextAlign.left:
return Alignment.bottomLeft; return Alignment.bottomLeft;
@ -87,3 +101,4 @@ class SubtitleSample extends StatelessWidget {
} }
} }
} }
}

View file

@ -1,3 +1,5 @@
import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/settings/enums/subtitle_position.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/basic/color_list_tile.dart'; import 'package:aves/widgets/common/basic/color_list_tile.dart';
import 'package:aves/widgets/common/basic/slider_list_tile.dart'; import 'package:aves/widgets/common/basic/slider_list_tile.dart';
@ -40,6 +42,14 @@ class SubtitleThemePage extends StatelessWidget {
tileTitle: context.l10n.settingsSubtitleThemeTextAlignmentTile, tileTitle: context.l10n.settingsSubtitleThemeTextAlignmentTile,
dialogTitle: context.l10n.settingsSubtitleThemeTextAlignmentDialogTitle, dialogTitle: context.l10n.settingsSubtitleThemeTextAlignmentDialogTitle,
), ),
SettingsSelectionListTile<SubtitlePosition>(
values: const [SubtitlePosition.top, SubtitlePosition.bottom],
getName: (context, v) => v.getName(context),
selector: (context, s) => s.subtitleTextPosition,
onSelection: (v) => settings.subtitleTextPosition = v,
tileTitle: context.l10n.settingsSubtitleThemeTextPositionTile,
dialogTitle: context.l10n.settingsSubtitleThemeTextPositionDialogTitle,
),
SliderListTile( SliderListTile(
title: context.l10n.settingsSubtitleThemeTextSize, title: context.l10n.settingsSubtitleThemeTextSize,
value: settings.subtitleFontSize, value: settings.subtitleFontSize,

View file

@ -1,3 +1,4 @@
import 'package:aves/model/settings/enums/subtitle_position.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/basic/outlined_text.dart'; import 'package:aves/widgets/common/basic/outlined_text.dart';
import 'package:aves/widgets/common/basic/text_background_painter.dart'; import 'package:aves/widgets/common/basic/text_background_painter.dart';
@ -33,6 +34,7 @@ class VideoSubtitles extends StatelessWidget {
child: Consumer<Settings>( child: Consumer<Settings>(
builder: (context, settings, child) { builder: (context, settings, child) {
final baseTextAlign = settings.subtitleTextAlignment; final baseTextAlign = settings.subtitleTextAlignment;
final baseTextAlignY = settings.subtitleTextPosition.toTextAlignVertical();
final baseOutlineWidth = settings.subtitleShowOutline ? 1 : 0; final baseOutlineWidth = settings.subtitleShowOutline ? 1 : 0;
final baseOutlineColor = Colors.black.withOpacity(settings.subtitleTextColor.opacity); final baseOutlineColor = Colors.black.withOpacity(settings.subtitleTextColor.opacity);
final baseShadows = [ final baseShadows = [
@ -119,7 +121,8 @@ class VideoSubtitles extends StatelessWidget {
); );
}).toList(); }).toList();
final drawingPaths = extraStyle.drawingPaths; final drawingPaths = extraStyle.drawingPaths;
final textAlign = extraStyle.hAlign ?? (position != null ? TextAlign.center : baseTextAlign); final textHAlign = extraStyle.hAlign ?? (position != null ? TextAlign.center : baseTextAlign);
final textVAlign = extraStyle.vAlign ?? (position != null ? TextAlignVertical.bottom : baseTextAlignY);
Widget child; Widget child;
if (drawingPaths != null) { if (drawingPaths != null) {
@ -138,7 +141,7 @@ class VideoSubtitles extends StatelessWidget {
outlineWidth: outlineWidth * (position != null ? viewScale : baseOutlineWidth), outlineWidth: outlineWidth * (position != null ? viewScale : baseOutlineWidth),
outlineColor: extraStyle.borderColor ?? baseOutlineColor, outlineColor: extraStyle.borderColor ?? baseOutlineColor,
outlineBlurSigma: extraStyle.edgeBlur ?? 0, outlineBlurSigma: extraStyle.edgeBlur ?? 0,
textAlign: textAlign, textAlign: textHAlign,
); );
} }
@ -154,7 +157,7 @@ class VideoSubtitles extends StatelessWidget {
final textHeight = para.getMaxIntrinsicHeight(double.infinity); final textHeight = para.getMaxIntrinsicHeight(double.infinity);
late double anchorOffsetX, anchorOffsetY; late double anchorOffsetX, anchorOffsetY;
switch (textAlign) { switch (textHAlign) {
case TextAlign.left: case TextAlign.left:
anchorOffsetX = 0; anchorOffsetX = 0;
break; break;
@ -166,7 +169,7 @@ class VideoSubtitles extends StatelessWidget {
anchorOffsetX = -textWidth / 2; anchorOffsetX = -textWidth / 2;
break; break;
} }
switch (extraStyle.vAlign ?? TextAlignVertical.bottom) { switch (textVAlign) {
case TextAlignVertical.top: case TextAlignVertical.top:
anchorOffsetY = 0; anchorOffsetY = 0;
break; break;
@ -214,7 +217,7 @@ class VideoSubtitles extends StatelessWidget {
if (position == null) { if (position == null) {
late double alignX; late double alignX;
switch (textAlign) { switch (textHAlign) {
case TextAlign.left: case TextAlign.left:
alignX = -1; alignX = -1;
break; break;
@ -227,7 +230,7 @@ class VideoSubtitles extends StatelessWidget {
break; break;
} }
late double alignY; late double alignY;
switch (extraStyle.vAlign) { switch (textVAlign) {
case TextAlignVertical.top: case TextAlignVertical.top:
alignY = -bottom; alignY = -bottom;
break; break;
@ -248,7 +251,7 @@ class VideoSubtitles extends StatelessWidget {
style: DefaultTextStyle.of(context).style.merge(spans.first.style!.copyWith( style: DefaultTextStyle.of(context).style.merge(spans.first.style!.copyWith(
backgroundColor: settings.subtitleBackgroundColor, backgroundColor: settings.subtitleBackgroundColor,
)), )),
textAlign: textAlign, textAlign: textHAlign,
child: child, child: child,
), ),
), ),

View file

@ -1,15 +1,27 @@
{ {
"de": [ "de": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"el": [ "el": [
"entryInfoActionExportMetadata", "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle",
"tagEditorSectionPlaceholders" "tagEditorSectionPlaceholders"
], ],
"es": [ "es": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"fa": [ "fa": [
@ -151,6 +163,8 @@
"accessibilityAnimationsKeep", "accessibilityAnimationsKeep",
"displayRefreshRatePreferHighest", "displayRefreshRatePreferHighest",
"displayRefreshRatePreferLowest", "displayRefreshRatePreferLowest",
"subtitlePositionTop",
"subtitlePositionBottom",
"videoPlaybackSkip", "videoPlaybackSkip",
"videoPlaybackMuted", "videoPlaybackMuted",
"videoPlaybackWithSound", "videoPlaybackWithSound",
@ -476,6 +490,8 @@
"settingsSubtitleThemeSample", "settingsSubtitleThemeSample",
"settingsSubtitleThemeTextAlignmentTile", "settingsSubtitleThemeTextAlignmentTile",
"settingsSubtitleThemeTextAlignmentDialogTitle", "settingsSubtitleThemeTextAlignmentDialogTitle",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle",
"settingsSubtitleThemeTextSize", "settingsSubtitleThemeTextSize",
"settingsSubtitleThemeShowOutline", "settingsSubtitleThemeShowOutline",
"settingsSubtitleThemeTextColor", "settingsSubtitleThemeTextColor",
@ -596,7 +612,11 @@
], ],
"fr": [ "fr": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"gl": [ "gl": [
@ -605,6 +625,8 @@
"accessibilityAnimationsKeep", "accessibilityAnimationsKeep",
"displayRefreshRatePreferHighest", "displayRefreshRatePreferHighest",
"displayRefreshRatePreferLowest", "displayRefreshRatePreferLowest",
"subtitlePositionTop",
"subtitlePositionBottom",
"videoPlaybackSkip", "videoPlaybackSkip",
"videoPlaybackMuted", "videoPlaybackMuted",
"videoPlaybackWithSound", "videoPlaybackWithSound",
@ -930,6 +952,8 @@
"settingsSubtitleThemeSample", "settingsSubtitleThemeSample",
"settingsSubtitleThemeTextAlignmentTile", "settingsSubtitleThemeTextAlignmentTile",
"settingsSubtitleThemeTextAlignmentDialogTitle", "settingsSubtitleThemeTextAlignmentDialogTitle",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle",
"settingsSubtitleThemeTextSize", "settingsSubtitleThemeTextSize",
"settingsSubtitleThemeShowOutline", "settingsSubtitleThemeShowOutline",
"settingsSubtitleThemeTextColor", "settingsSubtitleThemeTextColor",
@ -1050,20 +1074,36 @@
], ],
"id": [ "id": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"it": [ "it": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"ja": [ "ja": [
"chipActionFilterIn", "chipActionFilterIn",
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"ko": [ "ko": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"nb": [ "nb": [
@ -1077,6 +1117,8 @@
"mapStyleStamenToner", "mapStyleStamenToner",
"mapStyleStamenWatercolor", "mapStyleStamenWatercolor",
"keepScreenOnViewerOnly", "keepScreenOnViewerOnly",
"subtitlePositionTop",
"subtitlePositionBottom",
"viewerTransitionFade", "viewerTransitionFade",
"albumTierSpecial", "albumTierSpecial",
"storageAccessDialogMessage", "storageAccessDialogMessage",
@ -1154,6 +1196,8 @@
"settingsViewerEnableOverlayBlurEffect", "settingsViewerEnableOverlayBlurEffect",
"settingsSlideshowShuffle", "settingsSlideshowShuffle",
"settingsSubtitleThemeSample", "settingsSubtitleThemeSample",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle",
"settingsAllowInstalledAppAccess", "settingsAllowInstalledAppAccess",
"settingsAllowMediaManagement", "settingsAllowMediaManagement",
"settingsHiddenFiltersBanner", "settingsHiddenFiltersBanner",
@ -1181,7 +1225,11 @@
], ],
"nl": [ "nl": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"pl": [ "pl": [
@ -1230,6 +1278,8 @@
"accessibilityAnimationsKeep", "accessibilityAnimationsKeep",
"displayRefreshRatePreferHighest", "displayRefreshRatePreferHighest",
"displayRefreshRatePreferLowest", "displayRefreshRatePreferLowest",
"subtitlePositionTop",
"subtitlePositionBottom",
"videoPlaybackSkip", "videoPlaybackSkip",
"videoPlaybackMuted", "videoPlaybackMuted",
"videoPlaybackWithSound", "videoPlaybackWithSound",
@ -1555,6 +1605,8 @@
"settingsSubtitleThemeSample", "settingsSubtitleThemeSample",
"settingsSubtitleThemeTextAlignmentTile", "settingsSubtitleThemeTextAlignmentTile",
"settingsSubtitleThemeTextAlignmentDialogTitle", "settingsSubtitleThemeTextAlignmentDialogTitle",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle",
"settingsSubtitleThemeTextSize", "settingsSubtitleThemeTextSize",
"settingsSubtitleThemeShowOutline", "settingsSubtitleThemeShowOutline",
"settingsSubtitleThemeTextColor", "settingsSubtitleThemeTextColor",
@ -1675,20 +1727,36 @@
], ],
"pt": [ "pt": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"ru": [ "ru": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"tr": [ "tr": [
"entryInfoActionExportMetadata" "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle"
], ],
"zh": [ "zh": [
"entryInfoActionExportMetadata", "entryInfoActionExportMetadata",
"subtitlePositionTop",
"subtitlePositionBottom",
"editEntryLocationDialogSetCustom", "editEntryLocationDialogSetCustom",
"settingsSubtitleThemeTextPositionTile",
"settingsSubtitleThemeTextPositionDialogTitle",
"settingsAllowMediaManagement", "settingsAllowMediaManagement",
"tagEditorSectionPlaceholders", "tagEditorSectionPlaceholders",
"tagPlaceholderCountry", "tagPlaceholderCountry",