fix playbackservice

This commit is contained in:
Zhe Fang
2025-07-22 20:36:52 -04:00
parent 757f9f4156
commit ff8c85b2d0
15 changed files with 353 additions and 251 deletions

View File

@@ -302,7 +302,7 @@
<Grid.Resources>
<Style x:Key="SliderThumbStyle" TargetType="Thumb">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="{ThemeResource TextFillColorPrimaryBrush}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
@@ -354,12 +354,12 @@
x:Name="HorizontalTrackRect"
Grid.Row="1"
Grid.ColumnSpan="3"
Height="2"
Fill="{TemplateBinding Background}" />
Height="8"
Fill="Transparent" />
<Rectangle
x:Name="HorizontalDecreaseRect"
Grid.Row="1"
Fill="{TemplateBinding Foreground}" />
Fill="Transparent" />
<TickBar
x:Name="TopTickBar"
Grid.ColumnSpan="3"
@@ -372,7 +372,7 @@
x:Name="HorizontalInlineTickBar"
Grid.Row="1"
Grid.ColumnSpan="3"
Height="2"
Height="8"
Fill="{ThemeResource SliderInlineTickBarFill}"
Visibility="Collapsed" />
<TickBar
@@ -389,8 +389,8 @@
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="1"
Width="2"
Height="2"
Width="8"
Height="8"
AutomationProperties.AccessibilityView="Raw"
DataContext="{TemplateBinding Value}"
FocusVisualMargin="-14,-6,-14,-6"
@@ -452,7 +452,7 @@
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Width="24"
Width="8"
Height="8"
AutomationProperties.AccessibilityView="Raw"
DataContext="{TemplateBinding Value}"

View File

@@ -0,0 +1,43 @@
using ATL;
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public static class CollectionHelper
{
public static ObservableCollection<GroupInfoList> GetGroupedByTitleAsync(this ICollection<Track> tracks)
{
// Grab Contact objects from pre-existing list (list is returned from function GetContactsAsync())
var query = from item in tracks
// Group the items returned from the query, sort and select the ones you want to keep
group item by item.Title.Substring(0, 1).ToUpper() into g
orderby g.Key
// GroupInfoList is a simple custom class that has an IEnumerable type attribute, and
// a key attribute. The IGrouping-typed variable g now holds the Contact objects,
// and these objects will be used to create a new GroupInfoList object.
select new GroupInfoList(g) { Key = g.Key };
return new ObservableCollection<GroupInfoList>(query);
}
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
if (collection == null) throw new ArgumentNullException(nameof(collection));
if (items == null) throw new ArgumentNullException(nameof(items));
foreach (var item in items)
{
collection.Add(item);
}
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
{
public partial class GroupInfoList(IEnumerable<object> items) : List<object>(items)
{
public required object Key { get; set; }
public override string ToString()
{
return "Group " + Key.ToString();
}
}
}

View File

@@ -89,75 +89,79 @@ namespace BetterLyrics.WinUI3.Services
{
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
try
{
if (!provider.IsEnabled)
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
{
continue;
}
string? cachedLyrics;
LyricsFormat lyricsFormat = provider.Provider.GetLyricsFormat();
// Check cache first
if (provider.Provider.IsRemote())
{
cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
if (!string.IsNullOrWhiteSpace(cachedLyrics))
if (!provider.IsEnabled)
{
return (cachedLyrics, provider.Provider);
continue;
}
}
string? searchedLyrics = null;
string? cachedLyrics;
LyricsFormat lyricsFormat = provider.Provider.GetLyricsFormat();
if (provider.Provider.IsLocal())
{
if (provider.Provider == LyricsSearchProvider.LocalMusicFile)
// Check cache first
if (provider.Provider.IsRemote())
{
searchedLyrics = SearchEmbedded(title, artist);
cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
if (!string.IsNullOrWhiteSpace(cachedLyrics))
{
return (cachedLyrics, provider.Provider);
}
}
string? searchedLyrics = null;
if (provider.Provider.IsLocal())
{
if (provider.Provider == LyricsSearchProvider.LocalMusicFile)
{
searchedLyrics = SearchEmbedded(title, artist);
}
else
{
searchedLyrics = await SearchFile(title, artist, lyricsFormat);
}
}
else
{
searchedLyrics = await SearchFile(title, artist, lyricsFormat);
switch (provider.Provider)
{
case LyricsSearchProvider.LrcLib:
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
break;
case LyricsSearchProvider.QQ:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
break;
case LyricsSearchProvider.Kugou:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
break;
case LyricsSearchProvider.Netease:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Netease);
break;
case LyricsSearchProvider.AmllTtmlDb:
searchedLyrics = await SearchAmllTtmlDbAsync(title, artist);
break;
default:
break;
}
}
}
else
{
switch (provider.Provider)
token.ThrowIfCancellationRequested();
if (!string.IsNullOrWhiteSpace(searchedLyrics))
{
case LyricsSearchProvider.LrcLib:
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
break;
case LyricsSearchProvider.QQ:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
break;
case LyricsSearchProvider.Kugou:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
break;
case LyricsSearchProvider.Netease:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Netease);
break;
case LyricsSearchProvider.AmllTtmlDb:
searchedLyrics = await SearchAmllTtmlDbAsync(title, artist);
break;
default:
break;
if (provider.Provider.IsRemote())
{
FileHelper.WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
}
return (searchedLyrics, provider.Provider);
}
}
token.ThrowIfCancellationRequested();
if (!string.IsNullOrWhiteSpace(searchedLyrics))
{
if (provider.Provider.IsRemote())
{
FileHelper.WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
}
return (searchedLyrics, provider.Provider);
}
}
catch (Exception) { }
return (null, null);
}

View File

@@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
using EvtSource;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -22,7 +23,9 @@ using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Media.Control;
using Windows.Storage.Streams;
using Windows.UI.Shell;
using WindowsMediaController;
using static WindowsMediaController.MediaManager;
namespace BetterLyrics.WinUI3.Services
{
@@ -40,6 +43,7 @@ namespace BetterLyrics.WinUI3.Services
private EventSourceReader? _sse = null;
private readonly MediaManager _mediaManager = new();
private MediaManager.MediaSession? _focusedSession = null;
private readonly LatestOnlyTaskRunner _albumArtRefreshRunner = new();
private readonly LatestOnlyTaskRunner _onAnyMediaPropertyChangedRunner = new();
@@ -73,8 +77,6 @@ namespace BetterLyrics.WinUI3.Services
private void InitMediaManager()
{
_mediaManager.Start();
_mediaManager.OnAnySessionOpened += MediaManager_OnAnySessionOpened;
_mediaManager.OnAnySessionClosed += MediaManager_OnAnySessionClosed;
_mediaManager.OnFocusedSessionChanged += MediaManager_OnFocusedSessionChanged;
@@ -82,33 +84,33 @@ namespace BetterLyrics.WinUI3.Services
_mediaManager.OnAnyPlaybackStateChanged += MediaManager_OnAnyPlaybackStateChanged;
_mediaManager.OnAnyTimelinePropertyChanged += MediaManager_OnAnyTimelinePropertyChanged;
MediaManager_OnFocusedSessionChanged(_mediaManager.GetFocusedSession());
_mediaManager.Start();
Task.Run(() =>
{
MediaManager_OnFocusedSessionChanged(null);
});
}
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession mediaSession)
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
{
if (mediaSession == null || !IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId))
if (!_mediaManager.IsStarted) return;
_focusedSession = mediaSession ?? _mediaManager.GetFocusedSession();
if (_focusedSession == null || !IsMediaSourceEnabled(_focusedSession.Id))
{
SendNullMessages();
}
else
{
Task.Run(async () =>
{
try
{
var props = await mediaSession.ControlSession.TryGetMediaPropertiesAsync();
MediaManager_OnAnyMediaPropertyChanged(mediaSession, props);
MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo());
}
catch (Exception) { }
});
SendFocusedMessagesAsync().ConfigureAwait(false);
}
}
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties)
{
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
if (!_mediaManager.IsStarted) return;
if (!IsMediaSourceEnabled(mediaSession.Id) || mediaSession != _focusedSession) return;
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
@@ -118,8 +120,9 @@ namespace BetterLyrics.WinUI3.Services
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
{
if (!_mediaManager.IsStarted) return;
RecordMediaSourceProviderInfo(mediaSession);
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
if (!IsMediaSourceEnabled(mediaSession.Id) || mediaSession != _focusedSession) return;
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
{
@@ -127,19 +130,21 @@ namespace BetterLyrics.WinUI3.Services
_ => false,
};
MediaManager_OnAnyTimelinePropertyChanged(mediaSession, mediaSession.ControlSession.GetTimelineProperties());
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
}
);
});
}
private async void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
{
string id = mediaSession.ControlSession.SourceAppUserModelId;
if (!_mediaManager.IsStarted) return;
string id = mediaSession.Id;
RecordMediaSourceProviderInfo(mediaSession);
if (!IsMediaSourceEnabled(id) || mediaSession != _mediaManager.GetFocusedSession()) return;
if (!IsMediaSourceEnabled(id) || mediaSession != _focusedSession) return;
_cachedSongInfo = new SongInfo
{
@@ -152,7 +157,7 @@ namespace BetterLyrics.WinUI3.Services
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
await _onAnyMediaPropertyChangedRunner.RunAsync(async token =>
_onAnyMediaPropertyChangedRunner.RunAsync(async token =>
{
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
@@ -188,11 +193,15 @@ namespace BetterLyrics.WinUI3.Services
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
});
}
});
MediaManager_OnAnyTimelinePropertyChanged(mediaSession, mediaSession.ControlSession.GetTimelineProperties());
MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo());
}).ConfigureAwait(false);
}
private void MediaManager_OnAnySessionClosed(MediaManager.MediaSession mediaSession)
{
if (!_mediaManager.IsStarted) return;
if (_mediaManager.CurrentMediaSessions.Count == 0)
{
SendNullMessages();
@@ -201,12 +210,16 @@ namespace BetterLyrics.WinUI3.Services
private void MediaManager_OnAnySessionOpened(MediaManager.MediaSession mediaSession)
{
if (!_mediaManager.IsStarted) return;
RecordMediaSourceProviderInfo(mediaSession);
_focusedSession = _mediaManager.GetFocusedSession();
SendFocusedMessagesAsync().ConfigureAwait(false);
}
private void RecordMediaSourceProviderInfo(MediaManager.MediaSession mediaSession)
{
var id = mediaSession?.ControlSession?.SourceAppUserModelId;
if (!_mediaManager.IsStarted) return;
var id = mediaSession?.Id;
if (string.IsNullOrEmpty(id)) return;
var found = _mediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id);
@@ -233,6 +246,14 @@ namespace BetterLyrics.WinUI3.Services
});
}
private async Task SendFocusedMessagesAsync()
{
if (_focusedSession == null) return;
var mediaProps = await _focusedSession.ControlSession.TryGetMediaPropertiesAsync();
MediaManager_OnAnyMediaPropertyChanged(_focusedSession, mediaProps);
}
private async Task UpdateAlbumArtRelated(CancellationToken token)
{
if (_cachedSongInfo == null)
@@ -329,47 +350,27 @@ namespace BetterLyrics.WinUI3.Services
public async Task PlayAsync()
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TryPlayAsync();
}
await _focusedSession?.ControlSession.TryPlayAsync();
}
public async Task PauseAsync()
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TryPauseAsync();
}
await _focusedSession?.ControlSession.TryPauseAsync();
}
public async Task PreviousAsync()
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TrySkipPreviousAsync();
}
await _focusedSession?.ControlSession.TrySkipPreviousAsync();
}
public async Task NextAsync()
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TrySkipNextAsync();
}
await _focusedSession?.ControlSession.TrySkipNextAsync();
}
public async Task ChangePosition(double seconds)
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TryChangePlaybackPositionAsync(TimeSpan.FromSeconds(seconds).Ticks);
}
await _focusedSession?.ControlSession.TryChangePlaybackPositionAsync(TimeSpan.FromSeconds(seconds).Ticks);
}
public void Receive(PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>> message)

View File

@@ -28,7 +28,7 @@ namespace BetterLyrics.WinUI3.Services
{
if (string.IsNullOrWhiteSpace(text))
{
throw new ArgumentException("Text and target language must be provided.");
throw new Exception(text + " is empty or null.");
}
string? originalLangCode = LanguageHelper.DetectLanguageCode(text);
@@ -47,15 +47,7 @@ namespace BetterLyrics.WinUI3.Services
if (string.IsNullOrEmpty(_settingsService.LibreTranslateServer))
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.LyricsWindowNotificationPanel?.Notify(
App.ResourceLoader!.GetString("TranslateServerNotSet"),
Microsoft.UI.Xaml.Controls.InfoBarSeverity.Warning
);
});
throw new InvalidOperationException("LibreTranslate server URL is not configured.");
throw new Exception("LibreTranslate server URL is not set in settings.");
}
var url = $"{_settingsService.LibreTranslateServer}/translate";

View File

@@ -13,6 +13,7 @@ using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using System;
using System.Diagnostics;
using System.Numerics;
using System.Threading.Tasks;

View File

@@ -133,8 +133,7 @@ namespace BetterLyrics.WinUI3.ViewModels
BlackPoint = new Vector2(blackX, blackY),
},
Opacity = opacity,
}, new Vector2(x, y)
);
}, new Vector2(x, y));
}
private void DrawForegroundImgae(ICanvasAnimatedControl control, CanvasDrawingSession ds, CanvasBitmap canvasBitmap, float opacity)

View File

@@ -337,6 +337,5 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
}
}
}

View File

@@ -446,10 +446,12 @@ namespace BetterLyrics.WinUI3.ViewModels
}
else
{
string translated = string.Empty;
try
{
var translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
token.ThrowIfCancellationRequested();
translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
if (translated == string.Empty) return;
if (_showTranslationOnly)
{
_lyricsDataArr[^1] = _lyricsDataArr[0].CreateLyricsDataFrom(translated);
@@ -461,6 +463,7 @@ namespace BetterLyrics.WinUI3.ViewModels
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated);
_langIndex = 0;
}
token.ThrowIfCancellationRequested();
}
catch (Exception) { }
}

View File

@@ -28,16 +28,16 @@ namespace BetterLyrics.WinUI3.ViewModels
private readonly MediaPlayer _mediaPlayer = new();
private readonly MediaTimelineController _timelineController = new();
private readonly SystemMediaTransportControls _smtc;
private List<Track> _tracks = [];
[ObservableProperty]
public partial ObservableCollection<Track> Tracks { get; set; } = [];
public partial ObservableCollection<GroupInfoList> TracksByTitle { get; set; } = [];
[ObservableProperty]
public partial bool IsDataLoading { get; set; } = false;
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService) : base(settingsService)
{
_mediaPlayer.MediaOpened += MediaPlayer_MediaOpened;
_timelineController = _mediaPlayer.TimelineController = new();
_timelineController.PositionChanged += TimelineController_PositionChanged;
_smtc = _mediaPlayer.SystemMediaTransportControls;
@@ -53,11 +53,6 @@ namespace BetterLyrics.WinUI3.ViewModels
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
}
private void MediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
throw new NotImplementedException();
}
private void TimelineController_PositionChanged(MediaTimelineController sender, object args)
{
_smtc.UpdateTimelineProperties(new SystemMediaTransportControlsTimelineProperties()
@@ -96,7 +91,7 @@ namespace BetterLyrics.WinUI3.ViewModels
public void RefreshSongs()
{
IsDataLoading = true;
Tracks.Clear();
_tracks.Clear();
Task.Run(() =>
{
@@ -109,7 +104,7 @@ namespace BetterLyrics.WinUI3.ViewModels
Track track = new(file);
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
Tracks.Add(track);
_tracks.Add(track);
});
}
}
@@ -117,6 +112,7 @@ namespace BetterLyrics.WinUI3.ViewModels
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
TracksByTitle.AddRange(_tracks.GetGroupedByTitleAsync());
IsDataLoading = false;
});
});
@@ -126,7 +122,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
if (index.HasValue)
{
var track = Tracks.ElementAtOrDefault(index.Value);
var track = _tracks.ElementAtOrDefault(index.Value);
if (track != null)
{
_mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(track.Path));

View File

@@ -345,11 +345,11 @@
Margin="0,-32,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Maximum="{x:Bind ViewModel.SongDurationSeconds, Mode=OneWay}"
Maximum="{Binding ElementName=TimelineSlider, Path=Maximum}"
Minimum="0"
Style="{StaticResource TransparentSliderStyle}"
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}"
ValueChanged="TimelineSliderOverlay_ValueChanged" />
Tapped="TimelineSliderOverlay_Tapped"
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}" />
</Grid>
</Grid>

View File

@@ -111,11 +111,6 @@ namespace BetterLyrics.WinUI3.Views
}
}
private async void TimelineSliderOverlay_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
await _playbackService.ChangePosition(e.NewValue);
}
//private void VolumeButton_Click(object sender, RoutedEventArgs e)
//{
// VolumeFlyout.ShowAt(BottomRightCommandStackPanel);
@@ -144,5 +139,10 @@ namespace BetterLyrics.WinUI3.Views
BottomCommandFlyout.ShowAt(BottomCommandFlyoutTrigger);
}
}
private void TimelineSliderOverlay_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
{
_playbackService.ChangePosition(TimelineSliderOverlay.Value);
}
}
}

View File

@@ -3,6 +3,7 @@
x:Class="BetterLyrics.WinUI3.Views.MusicGalleryPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:atl="using:ATL"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
@@ -10,16 +11,32 @@
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:media="using:CommunityToolkit.WinUI.Media"
xmlns:models="using:BetterLyrics.WinUI3.Models"
xmlns:ui="using:CommunityToolkit.WinUI"
Loaded="Page_Loaded"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource
x:Name="TracksByTitleCVS"
IsSourceGrouped="True"
Source="{x:Bind ViewModel.TracksByTitle, Mode=OneWay}" />
</Page.Resources>
<Grid>
<Grid Padding="0,12,0,0">
<Grid Margin="0,12,0,0">
<AutoSuggestBox
x:Name="SongSearchBox"
Margin="36,0"
VerticalAlignment="Top"
PlaceholderText="搜索歌曲"
QueryIcon="Find"
QuerySubmitted="SongSearchBox_QuerySubmitted"
SuggestionChosen="SongSearchBox_SuggestionChosen"
TextChanged="SongSearchBox_TextChanged" />
<controls:Segmented
x:Name="Segmented"
Margin="36,0"
Margin="36,48,36,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
SelectedIndex="0"
@@ -49,121 +66,136 @@
<controls:SegmentedItem Content="艺术家" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEFA9;}" />
</controls:Segmented>
<controls:OpacityMaskView Margin="0,36,0,0" HorizontalContentAlignment="Stretch">
<controls:OpacityMaskView.OpacityMask>
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="Transparent" />
<GradientStop Offset="0.05" Color="White" />
<GradientStop Offset="0.95" Color="White" />
<GradientStop Offset="1" Color="Transparent" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</controls:OpacityMaskView.OpacityMask>
<Grid>
<ScrollViewer>
<controls:SwitchPresenter Padding="36,6" Value="{Binding ElementName=Segmented, Path=Tag, Mode=OneWay}">
<controls:SwitchPresenter.ContentTransitions>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</controls:SwitchPresenter.ContentTransitions>
<controls:SwitchPresenter Margin="0,96,0,0" Value="{Binding ElementName=Segmented, Path=Tag, Mode=OneWay}">
<controls:SwitchPresenter.ContentTransitions>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</controls:SwitchPresenter.ContentTransitions>
<controls:Case Value="Song">
<ListView ItemsSource="{x:Bind ViewModel.Tracks, Mode=OneWay}" SelectionChanged="SongListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Padding="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<!-- 歌曲名 -->
<ColumnDefinition Width="1.5*" />
<!-- 歌手名 -->
<ColumnDefinition Width="1.5*" />
<!-- 专辑名 -->
<ColumnDefinition Width="1*" />
<!-- 年份 -->
<ColumnDefinition Width="1.2*" />
<!-- 流派 -->
<ColumnDefinition Width="1*" />
<!-- 歌曲时长 -->
</Grid.ColumnDefinitions>
<controls:Case Value="Song">
<SemanticZoom>
<SemanticZoom.ZoomedInView>
<GridView
ItemsSource="{x:Bind TracksByTitleCVS.View, Mode=OneWay}"
ScrollViewer.IsHorizontalScrollChainingEnabled="False"
SelectionMode="None">
<GridView.GroupStyle>
<GroupStyle />
</GridView.GroupStyle>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView ItemsSource="{x:Bind TracksByTitleCVS.View.CollectionGroups, Mode=OneWay}" SelectionChanged="SongListView_SelectionChanged">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate x:DataType="models:GroupInfoList">
<Border AutomationProperties.AccessibilityView="Raw">
<TextBlock
AutomationProperties.AccessibilityView="Raw"
Style="{ThemeResource TitleTextBlockStyle}"
Text="{x:Bind Key}" />
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="36,0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="atl:Track">
<Grid Padding="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<!-- 歌曲名 -->
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding Title}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1.5*" />
<!-- 歌手名 -->
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Artist}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1.5*" />
<!-- 专辑名 -->
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Album}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1*" />
<!-- 年份 -->
<TextBlock
Grid.Column="3"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Year}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1.2*" />
<!-- 流派 -->
<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Genre}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1*" />
<!-- 歌曲时长 -->
<TextBlock
Grid.Column="5"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
TextAlignment="Right"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</controls:Case>
</Grid.ColumnDefinitions>
<controls:Case Value="Album">
<ListView />
</controls:Case>
<!-- 歌曲名 -->
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding Title}"
TextWrapping="Wrap" />
<controls:Case Value="Artist">
<ListView />
</controls:Case>
<!-- 歌手名 -->
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Artist}"
TextWrapping="Wrap" />
</controls:SwitchPresenter>
<!-- 专辑名 -->
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Album}"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
</controls:OpacityMaskView>
<!-- 年份 -->
<TextBlock
Grid.Column="3"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Year}"
TextWrapping="Wrap" />
<!-- 流派 -->
<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Genre}"
TextWrapping="Wrap" />
<!-- 歌曲时长 -->
<TextBlock
Grid.Column="5"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
TextAlignment="Right"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</controls:Case>
<controls:Case Value="Album">
<ListView />
</controls:Case>
<controls:Case Value="Artist">
<ListView />
</controls:Case>
</controls:SwitchPresenter>
</Grid>
<Grid Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Visibility="{x:Bind ViewModel.IsDataLoading, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
@@ -184,6 +216,5 @@
</Grid>
<ProgressRing IsActive="{x:Bind ViewModel.IsDataLoading, Mode=OneWay}" />
</Grid>
</Grid>
</Page>

View File

@@ -41,5 +41,20 @@ namespace BetterLyrics.WinUI3.Views
{
ViewModel.RefreshSongs();
}
private void SongSearchBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
}
private void SongSearchBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
{
}
private void SongSearchBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
}
}
}