diff --git a/Screenbox.Core/Services/ISettingsService.cs b/Screenbox.Core/Services/ISettingsService.cs index cf456da4e..beccce782 100644 --- a/Screenbox.Core/Services/ISettingsService.cs +++ b/Screenbox.Core/Services/ISettingsService.cs @@ -12,6 +12,7 @@ public interface ISettingsService bool PlayerTapGesture { get; set; } bool PlayerShowControls { get; set; } bool PlayerShowChapters { get; set; } + int PlayerControlsHideDelay { get; set; } int PersistentVolume { get; set; } string PersistentSubtitleLanguage { get; set; } bool ShowRecent { get; set; } diff --git a/Screenbox.Core/Services/SettingsService.cs b/Screenbox.Core/Services/SettingsService.cs index 7aed0928b..946eb5b0e 100644 --- a/Screenbox.Core/Services/SettingsService.cs +++ b/Screenbox.Core/Services/SettingsService.cs @@ -20,6 +20,7 @@ public sealed class SettingsService : ISettingsService private const string PlayerSeekGestureKey = "Player/Gesture/Seek"; private const string PlayerTapGestureKey = "Player/Gesture/Tap"; private const string PlayerShowControlsKey = "Player/ShowControls"; + private const string PlayerControlsHideDelayKey = "Player/ControlsHideDelay"; private const string PlayerLivelyPathKey = "Player/Lively/Path"; private const string LibrariesUseIndexerKey = "Libraries/UseIndexer"; private const string LibrariesSearchRemovableStorageKey = "Libraries/SearchRemovableStorage"; @@ -114,6 +115,12 @@ public bool PlayerShowControls set => SetValue(PlayerShowControlsKey, value); } + public int PlayerControlsHideDelay + { + get => GetValue(PlayerControlsHideDelayKey); + set => SetValue(PlayerControlsHideDelayKey, value); + } + public bool SearchRemovableStorage { get => GetValue(LibrariesSearchRemovableStorageKey); @@ -169,6 +176,7 @@ public SettingsService() SetDefault(PlayerSeekGestureKey, true); SetDefault(PlayerTapGestureKey, true); SetDefault(PlayerShowControlsKey, true); + SetDefault(PlayerControlsHideDelayKey, 3); SetDefault(PersistentVolumeKey, 100); SetDefault(MaxVolumeKey, 100); SetDefault(LibrariesUseIndexerKey, true); diff --git a/Screenbox.Core/ViewModels/PlayerPageViewModel.cs b/Screenbox.Core/ViewModels/PlayerPageViewModel.cs index 6cb387ea2..e68345f1f 100644 --- a/Screenbox.Core/ViewModels/PlayerPageViewModel.cs +++ b/Screenbox.Core/ViewModels/PlayerPageViewModel.cs @@ -576,9 +576,11 @@ public bool TryHideControls(bool skipFocusCheck = false) return true; } - private void DelayHideControls(int delayInSeconds = 3) + private void DelayHideControls() { if (PlayerVisibility != PlayerVisibilityState.Visible || AudioOnly) return; + + int delayInSeconds = _settingsService.PlayerControlsHideDelay; _controlsVisibilityTimer.Debounce(() => TryHideControls(), TimeSpan.FromSeconds(delayInSeconds)); } @@ -592,7 +594,7 @@ private void FocusManagerOnFocusChanged(object sender, FocusManagerGotFocusEvent { if (_visibilityOverride) return; ControlsHidden = false; - DelayHideControls(4); + DelayHideControls(); } private async void ProcessOpeningMedia(MediaViewModel? current) diff --git a/Screenbox.Core/ViewModels/SettingsPageViewModel.cs b/Screenbox.Core/ViewModels/SettingsPageViewModel.cs index 839bda72f..77b8e0e8c 100644 --- a/Screenbox.Core/ViewModels/SettingsPageViewModel.cs +++ b/Screenbox.Core/ViewModels/SettingsPageViewModel.cs @@ -29,6 +29,7 @@ public sealed partial class SettingsPageViewModel : ObservableRecipient [ObservableProperty] private bool _playerTapGesture; [ObservableProperty] private bool _playerShowControls; [ObservableProperty] private bool _playerShowChapters; + [ObservableProperty] private int _playerControlsHideDelay; [ObservableProperty] private int _volumeBoost; [ObservableProperty] private bool _useIndexer; [ObservableProperty] private bool _showRecent; @@ -51,6 +52,8 @@ public sealed partial class SettingsPageViewModel : ObservableRecipient public List AvailableLanguages { get; } + public int[] PlayerControlsHideDelayOptions { get; } = { 1, 2, 3, 4, 5 }; + private readonly ISettingsService _settingsService; private readonly ILibraryService _libraryService; private readonly DispatcherQueue _dispatcherQueue; @@ -99,6 +102,7 @@ public SettingsPageViewModel(ISettingsService settingsService, ILibraryService l _playerTapGesture = _settingsService.PlayerTapGesture; _playerShowControls = _settingsService.PlayerShowControls; _playerShowChapters = _settingsService.PlayerShowChapters; + _playerControlsHideDelay = _settingsService.PlayerControlsHideDelay; _useIndexer = _settingsService.UseIndexer; _showRecent = _settingsService.ShowRecent; _theme = ((int)_settingsService.Theme + 2) % 3; @@ -187,6 +191,12 @@ partial void OnPlayerShowChaptersChanged(bool value) Messenger.Send(new SettingsChangedMessage(nameof(PlayerShowChapters), typeof(SettingsPageViewModel))); } + partial void OnPlayerControlsHideDelayChanged(int value) + { + _settingsService.PlayerControlsHideDelay = value; + Messenger.Send(new SettingsChangedMessage(nameof(PlayerControlsHideDelay), typeof(SettingsPageViewModel))); + } + partial void OnUseIndexerChanged(bool value) { _settingsService.UseIndexer = value; diff --git a/Screenbox/Converters/IntToUnitStringConverter.cs b/Screenbox/Converters/IntToUnitStringConverter.cs new file mode 100644 index 000000000..fcb7fca59 --- /dev/null +++ b/Screenbox/Converters/IntToUnitStringConverter.cs @@ -0,0 +1,76 @@ +#nullable enable + +using System; +using Windows.UI.Xaml.Data; + +namespace Screenbox.Converters; + +/// +/// Defines the unit types for formatting numeric values with localized and pluralized resource strings. +/// +/// This enumeration is used by the property. +public enum UnitKind +{ + None = 0, + Albums = 1, + Songs = 2, + Seconds = 6, + Items = 13, +} + +/// +/// Converts an integer representing a quantity into a localized string representation for a specified unit, +/// automatically adjusting for singular or plural forms based on the value. +/// +public sealed class IntToUnitStringConverter : IValueConverter +{ + /// + /// Gets or sets the unit type used to represent the count. + /// + public UnitKind Unit { get; set; } = UnitKind.None; + + /// + /// Converts an value to a localized , + /// using the appropriate singular or plural resource for the specified unit type. + /// + /// The being passed to the target. + /// The type of the target property. Not used. + /// An optional parameter to be used in the converter logic. Not used. + /// The language of the conversion. Not used. + /// The representing the amount and its unit; otherwise, the value as a . + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is int quantity && Unit != UnitKind.None) + { + return ToUnitString(quantity, Unit); + } + + return value?.ToString() ?? string.Empty; + } + + /// + /// Not implemented. + /// + /// The target data being passed to the source. + /// The type of the target property. + /// An optional parameter to be used in the converter logic. + /// The language of the conversion. + /// The value to be passed to the source object. + /// + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + + private string ToUnitString(int value, UnitKind unit) + { + return unit switch + { + UnitKind.Albums => Strings.Resources.AlbumsCount(value), + UnitKind.Songs => Strings.Resources.SongsCount(value), + UnitKind.Seconds => Strings.Resources.SecondsCount(value), + UnitKind.Items => Strings.Resources.ItemsCount(value), + _ => $"{value} {unit.ToString().ToLowerInvariant()}" + }; + } +} diff --git a/Screenbox/Pages/SettingsPage.xaml b/Screenbox/Pages/SettingsPage.xaml index 7a037a5e5..45f3ebc1f 100644 --- a/Screenbox/Pages/SettingsPage.xaml +++ b/Screenbox/Pages/SettingsPage.xaml @@ -30,6 +30,7 @@ 0,0,0,4 +