From a6cb88bb08ba2916450478ec92bf5429ad041e19 Mon Sep 17 00:00:00 2001 From: AlexanderDotH Date: Tue, 6 Jun 2023 17:09:20 +0200 Subject: [PATCH] feat(seek): Added the ability to seek. --- .../Handler/Services/ServiceHandler.cs | 34 +++- .../Handler/Services/Services/IService.cs | 14 +- .../Services/Spotify/SpotifyService.cs | 70 ++++--- .../Song/SongProvider/SongProviderChooser.cs | 60 ++---- .../Spotify/SpotifySongProvider.cs | 8 +- .../Extensions/ColorBrushExtension.cs | 22 +++ .../Models/Custom/CustomScrollViewer.cs | 4 +- .../View/Custom/NewLyricsScroller.axaml | 3 +- .../View/Custom/NewLyricsScroller.axaml.cs | 10 +- .../View/Custom/Tile/LyricsTile.axaml.cs | 22 ++- .../Custom/Tile/Overlays/NoteOverlay.axaml | 125 ++----------- .../Custom/Tile/Overlays/NoteOverlay.axaml.cs | 97 +++++++++- .../Custom/Tile/Overlays/TextOverlay.axaml | 16 +- .../Custom/Tile/Overlays/TextOverlay.axaml.cs | 171 +++++++++++++----- 14 files changed, 382 insertions(+), 274 deletions(-) create mode 100644 OpenLyricsClient/Frontend/Extensions/ColorBrushExtension.cs diff --git a/OpenLyricsClient/Backend/Handler/Services/ServiceHandler.cs b/OpenLyricsClient/Backend/Handler/Services/ServiceHandler.cs index 309143c..d0b0620 100644 --- a/OpenLyricsClient/Backend/Handler/Services/ServiceHandler.cs +++ b/OpenLyricsClient/Backend/Handler/Services/ServiceHandler.cs @@ -25,7 +25,7 @@ public ServiceHandler() public bool IsConnected(string serviceName) { - return GetServiceByName(serviceName).IsConnected(); + return GetServiceByName(serviceName).Connected; } public string GetAccessToken(IService service) @@ -35,9 +35,35 @@ public string GetAccessToken(IService service) if (!DataValidator.ValidateData(s)) return null; - return s.GetAccessToken(); + return s.AccessToken; } + public void MarkServiceAsActive(IService service) + { + for (int i = 0; i < this._services.Length; i++) + { + IService s = this._services.Get(i); + + s.Active = false; + + if (s.Equals(service)) + s.Active = true; + } + } + + public IService GetActiveService() + { + for (int i = 0; i < this._services.Length; i++) + { + IService s = this._services.Get(i); + + if (s.Active) + return s; + } + + return null; + } + public string GetAccessToken(string serviceName) { return GetAccessToken(GetServiceByName(serviceName)); @@ -53,7 +79,7 @@ public IService GetServiceByName(string serviceName) for (int i = 0; i < this._services.Length; i++) { IService s = this._services.Get(i); - if (s.ServiceName().Equals(serviceName)) + if (s.Name.Equals(serviceName)) { return s; } @@ -66,7 +92,7 @@ public bool IsAnyConnected() { for (int i = 0; i < this._services.Length; i++) { - if (this._services.Get(i).IsConnected()) + if (this._services.Get(i).Connected) return true; } diff --git a/OpenLyricsClient/Backend/Handler/Services/Services/IService.cs b/OpenLyricsClient/Backend/Handler/Services/Services/IService.cs index 4dc8cce..8c82eb0 100644 --- a/OpenLyricsClient/Backend/Handler/Services/Services/IService.cs +++ b/OpenLyricsClient/Backend/Handler/Services/Services/IService.cs @@ -4,15 +4,15 @@ namespace OpenLyricsClient.Backend.Handler.Services.Services { interface IService { - string ServiceName(); Task StartAuthorization(); - - string GetAccessToken(); - bool IsConnected(); Task TestConnection(); - + bool CanSeek(); + Task Seek(long position); void Dispose(); - - string ProcessName(); + string Name { get; } + string ProcessName { get; } + string AccessToken { get; } + bool Connected { get; } + bool Active { get; set; } } } diff --git a/OpenLyricsClient/Backend/Handler/Services/Services/Spotify/SpotifyService.cs b/OpenLyricsClient/Backend/Handler/Services/Services/Spotify/SpotifyService.cs index dda86b9..9f02226 100644 --- a/OpenLyricsClient/Backend/Handler/Services/Services/Spotify/SpotifyService.cs +++ b/OpenLyricsClient/Backend/Handler/Services/Services/Spotify/SpotifyService.cs @@ -49,7 +49,7 @@ private async Task RefreshToken() await this._refreshTokenSuspensionToken.WaitForRelease(); await Task.Delay(1000); - if (!IsConnected()) + if (!Connected) continue; long now = DateTimeOffset.Now.ToUnixTimeMilliseconds(); @@ -64,22 +64,19 @@ private async Task RefreshToken() } } - public bool IsConnected() - { - return Core.INSTANCE.SettingsHandler.Settings().GetValue("IsSpotifyConnected") == true; - } + public bool Connected => Core.INSTANCE.SettingsHandler.Settings().GetValue("IsSpotifyConnected"); public async Task TestConnection() { - if (!IsConnected()) + if (!Connected) return false; - if (!DataValidator.ValidateData(GetAccessToken())) + if (!DataValidator.ValidateData(this.AccessToken)) return false; try { - CurrentlyPlayingContext currentlyPlayingContext = await new SpotifyClient(GetAccessToken()).Player.GetCurrentPlayback(); + CurrentlyPlayingContext currentlyPlayingContext = await new SpotifyClient(this.AccessToken).Player.GetCurrentPlayback(); return DataValidator.ValidateData(currentlyPlayingContext); } catch (Exception e) @@ -88,9 +85,35 @@ public async Task TestConnection() } } + public bool CanSeek() + { + return true; + } + + public async Task Seek(long position) + { + if (!CanSeek()) + return false; + + if (!Connected) + return false; + + if (!DataValidator.ValidateData(this.AccessToken)) + return false; + + try + { + return await new SpotifyClient(this.AccessToken).Player.SeekTo(new PlayerSeekToRequest(position)); + } + catch (Exception e) + { + return false; + } + } + public async Task GetStatistics(string accessToken = "") { - SpotifyClient client = new SpotifyClient(accessToken.Equals(String.Empty) ? GetAccessToken() : accessToken); + SpotifyClient client = new SpotifyClient(accessToken.Equals(String.Empty) ? this.AccessToken : accessToken); PersonalizationTopRequest topRequest = new PersonalizationTopRequest { @@ -114,9 +137,8 @@ public async Task GetStatistics(string accessToken = "") public async Task UpdatePlayback(EnumPlayback playback) { - SpotifyClient spotifyClient = new SpotifyClient(GetAccessToken()); - Shared.Structure.Song.Song song = Core.INSTANCE.SongHandler?.CurrentSong!; - + SpotifyClient spotifyClient = new SpotifyClient(this.AccessToken); + switch (playback) { case EnumPlayback.PREVOUS_TRACK: @@ -141,16 +163,8 @@ public async Task UpdatePlayback(EnumPlayback playback) } } } - - public string GetAccessToken() - { - // Task.Factory.StartNew(async() => - // { - // await RefreshTokenRequest(); - // - // }).Wait(Core.INSTANCE.CancellationTokenSource.Token); - return Core.INSTANCE.SettingsHandler.Settings().GetValue("AccessToken"); - } + + public string AccessToken => Core.INSTANCE.SettingsHandler.Settings().GetValue("AccessToken"); private async Task RefreshTokenRequest() { @@ -247,16 +261,12 @@ public async Task StartAuthorization() await Core.INSTANCE.SettingsHandler.TriggerEvent(typeof(SpotifySection), "SpotifyExpireTime"); await Core.INSTANCE.SettingsHandler.TriggerEvent(typeof(SpotifySection), "IsSpotifyConnected"); } + + public bool Active { get; set; } - string IService.ServiceName() - { - return "Spotify"; - } + public string Name => "Spotify"; - public string ProcessName() - { - return "Spotify"; - } + string IService.ProcessName => "Spotify"; public void Dispose() { diff --git a/OpenLyricsClient/Backend/Handler/Song/SongProvider/SongProviderChooser.cs b/OpenLyricsClient/Backend/Handler/Song/SongProvider/SongProviderChooser.cs index 8aef63a..54f836e 100644 --- a/OpenLyricsClient/Backend/Handler/Song/SongProvider/SongProviderChooser.cs +++ b/OpenLyricsClient/Backend/Handler/Song/SongProvider/SongProviderChooser.cs @@ -55,10 +55,16 @@ private void OnTickHandler(object sender) IService tidalService = Core.INSTANCE.ServiceHandler.GetServiceByName("Tidal"); if (IsInUse(tidalService)) + { songProvider = EnumSongProvider.TIDAL; + Core.INSTANCE.ServiceHandler.MarkServiceAsActive(tidalService); + } if (IsInUse(spotifyService)) + { songProvider = EnumSongProvider.SPOTIFY; + Core.INSTANCE.ServiceHandler.MarkServiceAsActive(spotifyService); + } if (songProvider == EnumSongProvider.NONE) return; @@ -81,67 +87,23 @@ private void OnTickHandler(object sender) } } - // private async Task SongProviderChooserTask() - // { - // while (!this._disposed) - // { - // await this._taskSuspensionToken.WaitForRelease(); - // - // if (!DataValidator.ValidateData(Core.INSTANCE.WindowLogger)) - // continue; - // - // EnumSongProvider songProvider = EnumSongProvider.NONE; - // - // IService spotifyService = Core.INSTANCE.ServiceHandler.GetServiceByName("Spotify"); - // IService tidalService = Core.INSTANCE.ServiceHandler.GetServiceByName("Tidal"); - // - // if (await IsInUse(tidalService)) - // songProvider = EnumSongProvider.TIDAL; - // - // if (await IsInUse(spotifyService)) - // songProvider = EnumSongProvider.SPOTIFY; - // - // if (songProvider == EnumSongProvider.NONE) - // continue; - // - // if (!songProvider.Equals(this._currentSongProvider)) - // { - // this._debugger.Write("SongProvider has been changed to: " + new AString(songProvider.ToString()).CapitalizeFirst(), DebugType.INFO); - // this._currentSongProvider = songProvider; - // - // if (songProvider == EnumSongProvider.SPOTIFY) - // { - // ResumeProvider(EnumSongProvider.SPOTIFY); - // SuspendProvider(EnumSongProvider.TIDAL); - // } - // else if (songProvider == EnumSongProvider.TIDAL) - // { - // ResumeProvider(EnumSongProvider.TIDAL); - // SuspendProvider(EnumSongProvider.SPOTIFY); - // } - // } - // - // await Task.Delay(3000); - // } - // } - private bool IsInUse(IService service) { if (!DataValidator.ValidateData(service)) return false; - if (!service.IsConnected()) + if (!service.Connected) return false; - AList lastWindows = Core.INSTANCE.WindowLogger.LastWindows(service.ProcessName()); - AList foundProcesses = ProcessUtils.GetRunningProcesses(service.ProcessName()); + AList lastWindows = Core.INSTANCE.WindowLogger.LastWindows(service.ProcessName); + AList foundProcesses = ProcessUtils.GetRunningProcesses(service.ProcessName); if (!lastWindows.IsEmpty()) - if (lastWindows.Get(0).Equals(service.ProcessName())) + if (lastWindows.Get(0).Equals(service.ProcessName)) return true; if (!foundProcesses.IsEmpty()) - if (foundProcesses.Get(0).Equals(service.ProcessName())) + if (foundProcesses.Get(0).Equals(service.ProcessName)) return true; if (service.TestConnection().GetAwaiter().GetResult()) diff --git a/OpenLyricsClient/Backend/Handler/Song/SongProvider/Spotify/SpotifySongProvider.cs b/OpenLyricsClient/Backend/Handler/Song/SongProvider/Spotify/SpotifySongProvider.cs index dd5b812..9ada28c 100644 --- a/OpenLyricsClient/Backend/Handler/Song/SongProvider/Spotify/SpotifySongProvider.cs +++ b/OpenLyricsClient/Backend/Handler/Song/SongProvider/Spotify/SpotifySongProvider.cs @@ -53,7 +53,7 @@ private async Task TimeSyncTask() await Task.Delay(20); await this._timeSyncSuspensionToken.WaitForRelease(); - if (!this._service.IsConnected()) + if (!this._service.Connected) continue; if (DataValidator.ValidateData(this._currentSong) && @@ -109,7 +109,7 @@ private async Task UpdateSongDataTask() await Task.Delay(750); await this._updateSongDataSuspensionToken.WaitForRelease(); - if (!this._service.IsConnected()) + if (!this._service.Connected) continue; if (!DataValidator.ValidateData(this._currentSong)) @@ -145,7 +145,7 @@ private async Task UpdateSongDataTask() //Song changed -> get new song public async Task UpdateCurrentPlaybackTrack() { - if (!this._service.IsConnected()) + if (!this._service.Connected) return null; if (DataValidator.ValidateData(this._spotifyClient)) @@ -174,7 +174,7 @@ private async Task UpdateSongDataTask() private SpotifyClient GetPlayerApi() { - return new SpotifyClient(this._service.GetAccessToken()); + return new SpotifyClient(this._service.AccessToken); } public void Dispose() diff --git a/OpenLyricsClient/Frontend/Extensions/ColorBrushExtension.cs b/OpenLyricsClient/Frontend/Extensions/ColorBrushExtension.cs new file mode 100644 index 0000000..f691d9f --- /dev/null +++ b/OpenLyricsClient/Frontend/Extensions/ColorBrushExtension.cs @@ -0,0 +1,22 @@ +using System; +using Avalonia.Media; + +namespace OpenLyricsClient.Frontend.Extensions; + +public static class ColorBrushExtension +{ + public static SolidColorBrush AdjustBrightness(this SolidColorBrush solidColorBrush, double percentage) + { + Color color = solidColorBrush.Color; + + byte r = (byte)((color.R * 0.01) * percentage); + byte g = (byte)((color.G * 0.01) * percentage); + byte b = (byte)((color.B * 0.01) * percentage); + + r = Math.Clamp(r, (byte)0, (byte)255); + g = Math.Clamp(g, (byte)0, (byte)255); + b = Math.Clamp(b, (byte)0, (byte)255); + + return new SolidColorBrush(new Color(color.A, r, g, b)); + } +} \ No newline at end of file diff --git a/OpenLyricsClient/Frontend/Models/Custom/CustomScrollViewer.cs b/OpenLyricsClient/Frontend/Models/Custom/CustomScrollViewer.cs index b83a445..fff03df 100644 --- a/OpenLyricsClient/Frontend/Models/Custom/CustomScrollViewer.cs +++ b/OpenLyricsClient/Frontend/Models/Custom/CustomScrollViewer.cs @@ -281,7 +281,7 @@ public Vector Offset { Vector diff = vector - this._oldOffsetValue; - if (diff.Y < 0 && ScrollDirection == ScrollDirection.DOWN) + if (diff.Y < 0 && ScrollDirection == ScrollDirection.DOWN && !this.BypassScrollDirectionCheck) { this.Offset = this._oldOffsetValue; } @@ -306,6 +306,8 @@ public ScrollDirection ScrollDirection } } + public bool BypassScrollDirectionCheck { get; set; } = false; + /// /// Gets the size of the viewport on the scrollable content. /// diff --git a/OpenLyricsClient/Frontend/View/Custom/NewLyricsScroller.axaml b/OpenLyricsClient/Frontend/View/Custom/NewLyricsScroller.axaml index 4402e6f..655012e 100644 --- a/OpenLyricsClient/Frontend/View/Custom/NewLyricsScroller.axaml +++ b/OpenLyricsClient/Frontend/View/Custom/NewLyricsScroller.axaml @@ -23,7 +23,8 @@ IsVisible="False"> - + diff --git a/OpenLyricsClient/Frontend/View/Custom/NewLyricsScroller.axaml.cs b/OpenLyricsClient/Frontend/View/Custom/NewLyricsScroller.axaml.cs index 245f28b..979cb73 100644 --- a/OpenLyricsClient/Frontend/View/Custom/NewLyricsScroller.axaml.cs +++ b/OpenLyricsClient/Frontend/View/Custom/NewLyricsScroller.axaml.cs @@ -102,14 +102,14 @@ private void UiThreadRenderTimerOnTick(TimeSpan obj) double y = this._customScrollViewer.Offset.Y; - if (this.IsSynced && !this._isSyncing) + if (this.IsSynced && !this._isSyncing && this._nextScrollOffset > y) { y = SmoothAnimator.Lerp( this._currentScrollOffset, this._nextScrollOffset, (int)obj.Milliseconds, this.Speed, EnumAnimationStyle.CIRCULAREASEOUT); } - else if (!this.IsSynced && this._isSyncing) + else if (!this.IsSynced && this._isSyncing || this.IsSynced && !this._isSyncing && this._nextScrollOffset < y) { y = CalcResyncStep(this._currentScrollOffset, this._nextScrollOffset, this.Speed); } @@ -124,7 +124,7 @@ private void UiThreadRenderTimerOnTick(TimeSpan obj) private double CalcResyncStep(double currentOffset, double nextOffset, double speed) { - double step = Math.Abs(nextOffset - currentOffset) / (speed); + double step = Math.Abs(nextOffset - currentOffset) / (speed * 0.4); currentOffset += (currentOffset < nextOffset) ? step : -step; @@ -134,6 +134,7 @@ private double CalcResyncStep(double currentOffset, double nextOffset, double sp { this.IsSynced = true; this._isSyncing = false; + this._customScrollViewer.BypassScrollDirectionCheck = false; } return currentOffset; @@ -149,8 +150,6 @@ private void SongHandlerOnSongChanged(object sender, SongChangedEventArgs songch private void LyricHandlerOnLyricsFound(object sender, LyricsFoundEventArgs args) { - //Reset(); - Dispatcher.UIThread.InvokeAsync(async () => { this._customScrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; @@ -260,6 +259,7 @@ protected override void OnPointerWheelChanged(PointerWheelEventArgs e) if (e.Delta.Y != 0) { this.IsSynced = false; + this._customScrollViewer.BypassScrollDirectionCheck = true; } if (e.Delta.Y > 0) diff --git a/OpenLyricsClient/Frontend/View/Custom/Tile/LyricsTile.axaml.cs b/OpenLyricsClient/Frontend/View/Custom/Tile/LyricsTile.axaml.cs index 22f852d..3daa4c6 100644 --- a/OpenLyricsClient/Frontend/View/Custom/Tile/LyricsTile.axaml.cs +++ b/OpenLyricsClient/Frontend/View/Custom/Tile/LyricsTile.axaml.cs @@ -26,6 +26,9 @@ public partial class LyricsTile : UserControl, INotifyPropertyChanged public static readonly DirectProperty LyricPartProperty = AvaloniaProperty.RegisterDirect(nameof(LyricPart), o => o.LyricPart, (o, v) => o.LyricPart = v); + public static readonly DirectProperty HeadlessProperty = + AvaloniaProperty.RegisterDirect(nameof(Headless), o => o.Headless, (o, v) => o.Headless = v); + public event PropertyChangedEventHandler? PropertyChanged; private LyricPart _lyricPart; @@ -36,6 +39,8 @@ public partial class LyricsTile : UserControl, INotifyPropertyChanged private UserControl _overlay; private double _speed; + + private bool _headless; public LyricsTile() { @@ -122,7 +127,22 @@ public LyricPart LyricPart SetAndRaise(LyricPartProperty, ref _lyricPart, value); } } - + + public bool Headless + { + get { return this._headless; } + set + { + SetAndRaise(HeadlessProperty, ref _headless, value); + + if (this._overlay is NoteOverlay overlay) + overlay.Headless = value; + + if (this._overlay is TextOverlay text) + text.Headless = value; + } + } + private void ApplyDataToOverlay(LyricPart lyricPart) { if (lyricPart.Part.Contains("♪")) diff --git a/OpenLyricsClient/Frontend/View/Custom/Tile/Overlays/NoteOverlay.axaml b/OpenLyricsClient/Frontend/View/Custom/Tile/Overlays/NoteOverlay.axaml index f0d4f17..5f25be4 100644 --- a/OpenLyricsClient/Frontend/View/Custom/Tile/Overlays/NoteOverlay.axaml +++ b/OpenLyricsClient/Frontend/View/Custom/Tile/Overlays/NoteOverlay.axaml @@ -11,122 +11,21 @@ + + + + + + - - - - + (); + this.Headless = false; + this._isPointerOver = false; + + this._animatale = new AList<(string, Avalonia.Animation.Animation)>(); this._idleTimeSpan = TimeSpan.FromSeconds(2); this._noteTimeSpan = TimeSpan.FromSeconds(3); @@ -79,13 +90,24 @@ public NoteOverlay() ApplyAnimationToClasses(this._idleTimeSpan, this._noteTimeSpan); } + #region Events + private void LyricHandlerOnLyricsFound(object sender, LyricsFoundEventArgs lyricsfoundeventargs) { - Speed = lyricsfoundeventargs.LyricData.LyricSpeed; + if (this._headlessMode) + return; + + Dispatcher.UIThread.InvokeAsync(() => + { + Speed = lyricsfoundeventargs.LyricData.LyricSpeed; + }); } private void SettingsHandlerOnSettingsChanged(object sender, SettingsChangedEventArgs args) { + if (this._headlessMode) + return; + if (!args.Section.Equals(typeof(SettingsLyricsViewModel))) return; @@ -95,6 +117,9 @@ private void SettingsHandlerOnSettingsChanged(object sender, SettingsChangedEven private void LyricHandlerOnLyricsPercentageUpdated(object sender, LyricsPercentageUpdatedEventArgs args) { + if (this._headlessMode) + return; + if (this._lyricPart.Equals(args.LyricPart)) { this.Percentage = CalculateWidthPercentage(args.Percentage); @@ -106,11 +131,55 @@ private void LyricHandlerOnLyricsPercentageUpdated(object sender, LyricsPercenta Animate = false; } } + + private void InputElement_OnPointerPressed(object? sender, PointerPressedEventArgs e) + { + IService service = Core.INSTANCE.ServiceHandler.GetActiveService(); + + if (!DataValidator.ValidateData(service)) + return; + + if (service.CanSeek()) + Task.Factory.StartNew(async () => await service.Seek(this._lyricPart.Time)); + } + + private void InputElement_OnPointerEnter(object? sender, PointerEventArgs e) + { + IService service = Core.INSTANCE.ServiceHandler.GetActiveService(); + + if (!DataValidator.ValidateData(service)) + return; + + if (!service.CanSeek()) + return; + + this._isPointerOver = true; + OnPropertyChanged("UnSelectedLineBrush"); + } + + private void InputElement_OnPointerLeave(object? sender, PointerEventArgs e) + { + IService service = Core.INSTANCE.ServiceHandler.GetActiveService(); + + if (!DataValidator.ValidateData(service)) + return; + + if (!service.CanSeek()) + return; + + this._isPointerOver = false; + OnPropertyChanged("UnSelectedLineBrush"); + } + + #endregion #region Animations private void ApplyAnimationToClasses(TimeSpan idleTimeSpan, TimeSpan noteTimeSpan) { + if (this._headlessMode) + return; + Styles styles = new Styles(); double modifier = 0.8d; @@ -207,16 +276,16 @@ private Style ActiveAnimationStyle(string className, TimeSpan duration, TimeSpan private void ApplyDelay(string classes, TimeSpan span) { double h = span.TotalMilliseconds / 3; - double factor = (h / (3 * 4)); + double factor = (h / (3 * 4)) * 0.01d; int position = 0; for (int i = 0; i < this._animatale.Length; i++) { (string, Avalonia.Animation.Animation) element = this._animatale.Get(i); - if (element.Item1.SequenceEqual($"{classes}{position}")) + if (element.Item1.SequenceEqual($"{classes}{position + 1}")) { - element.Item2.Delay = TimeSpan.FromMilliseconds(position * 0.8d); + element.Item2.Delay = TimeSpan.FromMilliseconds(position * factor); Debug.WriteLine($"{element.Item2.Delay} : {factor} : {i}"); position++; } @@ -340,7 +409,17 @@ public SolidColorBrush UnSelectedLineBrush get { if (Core.INSTANCE.SettingsHandler.Settings()!.GetValue("Artwork Background")) - return App.Current.FindResource("UnSelectedLineFontColorBrush") as SolidColorBrush; + { + SolidColorBrush colorBrush = App.Current.FindResource("UnSelectedLineFontColorBrush") as SolidColorBrush; + + if (this._isPointerOver) + return colorBrush.AdjustBrightness(120); + + return colorBrush; + } + + if (this._isPointerOver) + return SelectedLineBrush.AdjustBrightness(90); return SolidColorBrush.Parse("#646464"); } @@ -412,6 +491,12 @@ public bool Animate get => this._animate; set => this.SetField(ref this._animate, value); } + + public bool Headless + { + get => this._headlessMode; + set => this.SetField(ref this._headlessMode, value); + } #endregion } \ No newline at end of file diff --git a/OpenLyricsClient/Frontend/View/Custom/Tile/Overlays/TextOverlay.axaml b/OpenLyricsClient/Frontend/View/Custom/Tile/Overlays/TextOverlay.axaml index ab725bb..04b405e 100644 --- a/OpenLyricsClient/Frontend/View/Custom/Tile/Overlays/TextOverlay.axaml +++ b/OpenLyricsClient/Frontend/View/Custom/Tile/Overlays/TextOverlay.axaml @@ -10,14 +10,16 @@ - - + + TextAlignment="{Binding LyricsAlignment, RelativeSource={RelativeSource AncestorType=overlays:TextOverlay}}"> + + + + + + LyricPartProperty = AvaloniaProperty.RegisterDirect(nameof(LyricPart), o => o.LyricPart, (o, v) => o.LyricPart = v); @@ -41,6 +48,8 @@ public partial class TextOverlay : UserControl o => o.LyricLines, (o, v) => o.LyricLines = v); + public event PropertyChangedEventHandler? PropertyChanged; + private Backend.Romanization.Romanization _romanization; private LyricPart _lyricPart; @@ -54,11 +63,18 @@ public partial class TextOverlay : UserControl private bool _initialized; + private bool _isPointerOver; + + private bool _headlessMode; + public TextOverlay() { - InitializeComponent(); + AvaloniaXamlLoader.Load(this); this._initialized = false; + this.Headless = false; + + this._isPointerOver = false; this._lines = new ObservableCollection(); @@ -71,47 +87,12 @@ public TextOverlay() this._romanization = new Backend.Romanization.Romanization(); NewLyricsScroller.Instance.EffectiveViewportChanged += InstanceOnEffectiveViewportChanged; - Core.INSTANCE.LyricHandler.LyricsFound += LyricHandlerOnLyricsFound; Core.INSTANCE.LyricHandler.LyricsPercentageUpdated += LyricHandlerOnLyricsPercentageUpdated; - Core.INSTANCE.SettingsHandler.SettingsChanged += SettingsHandlerOnSettingsChanged; - this._lyricPart = new LyricPart(-9999, "Hello there ;)"); } - private void SettingsHandlerOnSettingsChanged(object sender, SettingsChangedEventArgs settingschangedeventargs) - { - - } - - private void LyricHandlerOnLyricsPercentageUpdated(object sender, LyricsPercentageUpdatedEventArgs args) - { - if (args.LyricPart.Equals(this._lyricPart)) - { - CalculatePercentage(args.Percentage); - } - else - { - ResetWidths(); - } - } - - private void LyricHandlerOnLyricsFound(object sender, LyricsFoundEventArgs args) - { - } - - - private void InstanceOnEffectiveViewportChanged(object? sender, EffectiveViewportChangedEventArgs e) - { - UpdateView(e.EffectiveViewport.Width, e.EffectiveViewport.Height); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - - public void UpdateView(double width, double height) + private void UpdateView(double width, double height) { UpdateTextWrappingLines(this._lyricPart.Part, width, height); } @@ -176,19 +157,91 @@ private void ResetWidths() } } - private Rect MeasureSingleString(string line, TextWrapping wrapping = TextWrapping.NoWrap) + #region Events + + private void LyricHandlerOnLyricsPercentageUpdated(object sender, LyricsPercentageUpdatedEventArgs args) { - FormattedText formattedCandidateLine = new FormattedText( - line, - this._typeface, - this.LyricsSize, - this.LyricsAlignment, - wrapping, - new Size(double.PositiveInfinity, double.PositiveInfinity)); - - return formattedCandidateLine.Bounds; + if (this._headlessMode) + return; + + if (args.LyricPart.Equals(this._lyricPart)) + { + CalculatePercentage(args.Percentage); + } + else + { + ResetWidths(); + } + } + + private void InstanceOnEffectiveViewportChanged(object? sender, EffectiveViewportChangedEventArgs e) + { + if (this._headlessMode) + return; + + UpdateView(e.EffectiveViewport.Width, e.EffectiveViewport.Height); } + private void InputElement_OnPointerPressed(object? sender, PointerPressedEventArgs e) + { + IService service = Core.INSTANCE.ServiceHandler.GetActiveService(); + + if (!DataValidator.ValidateData(service)) + return; + + if (service.CanSeek()) + Task.Factory.StartNew(async () => await service.Seek(this._lyricPart.Time)); + } + + private void InputElement_OnPointerEnter(object? sender, PointerEventArgs e) + { + IService service = Core.INSTANCE.ServiceHandler.GetActiveService(); + + if (!DataValidator.ValidateData(service)) + return; + + if (!service.CanSeek()) + return; + + this._isPointerOver = true; + OnPropertyChanged("UnSelectedLineBrush"); + } + + private void InputElement_OnPointerLeave(object? sender, PointerEventArgs e) + { + IService service = Core.INSTANCE.ServiceHandler.GetActiveService(); + + if (!DataValidator.ValidateData(service)) + return; + + if (!service.CanSeek()) + return; + + this._isPointerOver = false; + OnPropertyChanged("UnSelectedLineBrush"); + } + + #endregion + + #region MVVM Stuff + + protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + + #endregion + + #region Getter and Setter + public LyricPart LyricPart { get { return this._lyricPart; } @@ -243,7 +296,17 @@ public SolidColorBrush UnSelectedLineBrush get { if (Core.INSTANCE.SettingsHandler.Settings()!.GetValue("Artwork Background")) - return App.Current.FindResource("UnSelectedLineFontColorBrush") as SolidColorBrush; + { + SolidColorBrush colorBrush = App.Current.FindResource("UnSelectedLineFontColorBrush") as SolidColorBrush; + + if (this._isPointerOver) + return colorBrush.AdjustBrightness(120); + + return colorBrush; + } + + if (this._isPointerOver) + return SelectedLineBrush.AdjustBrightness(90); return SolidColorBrush.Parse("#646464"); } @@ -264,6 +327,12 @@ public TextAlignment LyricsAlignment get => Core.INSTANCE.SettingsHandler.Settings().GetValue("Lyrics Alignment"); } + public bool Headless + { + get => this._headlessMode; + set => this._headlessMode = value; + } + public Size Size { get @@ -279,5 +348,7 @@ public Size Size return new Size(width, height); } - } + } + + #endregion } \ No newline at end of file