change: remove system tray, combine desktop and in-app lyrics mode into one single window

This commit is contained in:
Zhe Fang
2025-06-17 14:01:52 -04:00
parent aa1f9f071f
commit 66d8705066
59 changed files with 740 additions and 1665 deletions

View File

@@ -76,16 +76,11 @@ namespace BetterLyrics.WinUI3
.AddSingleton<IDatabaseService, DatabaseService>()
.AddSingleton<IPlaybackService, PlaybackService>()
// ViewModels (Singleton)
.AddSingleton<SystemTrayPageViewModel>()
.AddSingleton<HostWindowViewModel>()
.AddSingleton<OverlayWindowViewModel>()
.AddSingleton<SettingsViewModel>()
.AddSingleton<InAppLyricsPageViewModel>()
.AddSingleton<InAppLyricsRendererViewModel>()
.AddSingleton<InAppLyricsSettingsControlViewModel>()
.AddSingleton<DesktopLyricsPageViewModel>()
.AddSingleton<DesktopLyricsRendererViewModel>()
.AddSingleton<DesktopLyricsSettingsControlViewModel>()
.AddSingleton<LyricsPageViewModel>()
.AddSingleton<LyricsRendererViewModel>()
.AddSingleton<LyricsSettingsControlViewModel>()
.BuildServiceProvider()
);
}
@@ -105,7 +100,7 @@ namespace BetterLyrics.WinUI3
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
WindowHelper.OpenSystemTrayWindow();
WindowHelper.OpenLyricsWindow();
}
}
}

View File

@@ -12,11 +12,6 @@
</PropertyGroup>
<ItemGroup>
<None Remove="Controls\LyricsSettingsControl.xaml" />
<None Remove="Renderer\DesktopLyricsRenderer.xaml" />
<None Remove="Renderer\InAppLyricsRenderer.xaml" />
<None Remove="Views\DesktopLyricsPage.xaml" />
<None Remove="Views\OverlayWindow.xaml" />
<None Remove="Views\SystemTrayPage.xaml" />
</ItemGroup>
<ItemGroup>
<Content Include="Logo.ico" />
@@ -74,24 +69,9 @@
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Views\DesktopLyricsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Views\OverlayWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Folder Include="ViewModels\Lyrics\" />
</ItemGroup>
<ItemGroup>
<Page Update="Views\SystemTrayPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<!-- Publish Properties -->
<PropertyGroup>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
@@ -115,7 +95,7 @@
<InheritWinAppVersionFrom>AssemblyVersion</InheritWinAppVersionFrom>
<PackageVersionSettings>AssemblyVersion.None.None</PackageVersionSettings>
<Version>2025.6.0</Version>
<AssemblyVersion>2025.6.17.0154</AssemblyVersion>
<FileVersion>2025.6.17.0154</FileVersion>
<AssemblyVersion>2025.6.17.1756</AssemblyVersion>
<FileVersion>2025.6.17.1756</FileVersion>
</PropertyGroup>
</Project>

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="BetterLyrics.WinUI3.HostWindowViewModel" Collapsed="true">
<Position X="10" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>ABCAAAgAABAAAAAAABAABAAAggAAQAAQAIAAAAAAAAg=</HashCode>
<FileName>ViewModels\HostWindowViewModel.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="BetterLyrics.WinUI3.ViewModels.SettingsViewModel" Collapsed="true">
<Position X="7.5" Y="5.25" Width="1.5" />
<TypeIdentifier>
<HashCode>IAQABAAACZABAgIQEAAABAAAAFCBAAACIAAAYIAAAiA=</HashCode>
<FileName>ViewModels\SettingsViewModel.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="BetterLyrics.WinUI3.Views.DesktopLyricsPage" Collapsed="true">
<Position X="2.75" Y="3.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAQAAAAACAAA=</HashCode>
<FileName>Views\DesktopLyricsPage.xaml.cs</FileName>
</TypeIdentifier>
</Class>
<Font Name="Microsoft YaHei UI" Size="9" />
</ClassDiagram>

View File

@@ -40,15 +40,15 @@ namespace BetterLyrics.WinUI3.Controls
// new PropertyMetadata(null)
//);
public BaseLyricsSettingsControlViewModel ViewModel
public LyricsSettingsControlViewModel ViewModel
{
get => (BaseLyricsSettingsControlViewModel)GetValue(ViewModelProperty);
get => (LyricsSettingsControlViewModel)GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
nameof(ViewModel),
typeof(BaseLyricsSettingsControlViewModel),
typeof(LyricsSettingsControlViewModel),
typeof(LyricsSettingsControl),
new PropertyMetadata(null)
);

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum AutoStartWindowType
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum BackdropType
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum Language
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum LyricsAlignmentType
{

View File

@@ -1,6 +1,6 @@
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum InAppLyricsDisplayType
public enum LyricsDisplayType
{
AlbumArtOnly,
LyricsOnly,

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum LyricsFontColorType
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum LyricsPlayingState
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum LyricsType
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum LyricsWindowType
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Enums
{
public enum TitleBarType
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Events
{
public class IsPlayingChangedEventArgs(bool isPlaying) : EventArgs
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Events
{
public class PositionChangedEventArgs(TimeSpan position) : EventArgs()
{

View File

@@ -3,8 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Models;
namespace BetterLyrics.WinUI3.Models
namespace BetterLyrics.WinUI3.Events
{
public class SongInfoChangedEventArgs(SongInfo? songInfo) : EventArgs
{

View File

@@ -11,51 +11,85 @@ using WinUIEx;
namespace BetterLyrics.WinUI3.Helper
{
public static class AppBarHelper
public static class DockHelper
{
// 记录哪些 HWND 已经注册 AppBar
private static readonly HashSet<IntPtr> _registered = [];
/// <summary>
/// Enable AppBar function
/// </summary>
/// <param name="window">Target window</param>
/// <param name="appBarHeight">App bar height</param>
/// <param name="clickThrough">Is click-through enabled</param>
private static readonly Dictionary<IntPtr, RECT> _originalPositions = [];
private static readonly Dictionary<IntPtr, WindowStyle> _originalWindowStyle = [];
public static void Disable(Window window)
{
window.SetIsShownInSwitchers(true);
window.ExtendsContentIntoTitleBar = true;
IntPtr hwnd = WindowNative.GetWindowHandle(window);
window.SetWindowStyle(_originalWindowStyle[hwnd]);
_originalWindowStyle.Remove(hwnd);
if (_originalPositions.TryGetValue(hwnd, out var rect))
{
SetWindowPos(
hwnd,
IntPtr.Zero,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_SHOWWINDOW
);
_originalPositions.Remove(hwnd);
}
window.SetIsAlwaysOnTop(false);
UnregisterAppBar(hwnd);
}
public static void Enable(Window window, int appBarHeight)
{
window.SetIsShownInSwitchers(false);
window.ExtendsContentIntoTitleBar = false;
IntPtr hwnd = WindowNative.GetWindowHandle(window);
if (!_originalWindowStyle.ContainsKey(hwnd))
{
_originalWindowStyle[hwnd] = window.GetWindowStyle();
}
window.SetWindowStyle(WindowStyle.Popup | WindowStyle.Visible);
if (!_originalPositions.ContainsKey(hwnd))
{
if (GetWindowRect(hwnd, out var rect))
{
_originalPositions[hwnd] = rect;
}
}
RegisterAppBar(hwnd, appBarHeight);
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
SetWindowPos(
hwnd,
HWND_TOPMOST,
IntPtr.Zero,
0,
0,
screenWidth,
appBarHeight,
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_SHOWWINDOW
);
window.SetIsAlwaysOnTop(true);
}
/// <summary>
/// 关闭并注销 AppBar占位恢复。
/// </summary>
public static void Disable(Window window)
{
IntPtr hwnd = WindowNative.GetWindowHandle(window);
UnregisterAppBar(hwnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
// 移除 WS_EX_TRANSPARENT可根据需求恢复其他样式
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
exStyle &= ~WS_EX_TRANSPARENT;
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle);
}
#region AppBar
#region AppBar registration
private const uint ABM_NEW = 0x00000000;
private const uint ABM_REMOVE = 0x00000001;
private const uint ABM_SETPOS = 0x00000003;
@@ -121,15 +155,11 @@ namespace BetterLyrics.WinUI3.Helper
}
#endregion
#region Win32 Helper &
private const int GWL_EXSTYLE = -20;
private const int WS_EX_TRANSPARENT = 0x20;
#region Win32 Helper and Constants
private const int SWP_NOACTIVATE = 0x0010;
private const int SWP_NOOWNERZORDER = 0x0200;
private const int SWP_SHOWWINDOW = 0x0040;
private static readonly IntPtr HWND_TOPMOST = new(-1);
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 0;
@@ -137,12 +167,6 @@ namespace BetterLyrics.WinUI3.Helper
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(int nIndex);
[DllImport("user32.dll", SetLastError = true)]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(
IntPtr hWnd,
@@ -153,14 +177,6 @@ namespace BetterLyrics.WinUI3.Helper
int cy,
uint uFlags
);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetLayeredWindowAttributes(
IntPtr hwnd,
uint crKey,
byte bAlpha,
uint dwFlags
);
#endregion
}
}

View File

@@ -1,4 +1,4 @@
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Enums;
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Xaml.Media;

View File

@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using BetterLyrics.WinUI3.Views;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using WinUIEx;
@@ -7,46 +9,61 @@ namespace BetterLyrics.WinUI3.Helper
{
public static class WindowHelper
{
public static void OpenSystemTrayWindow()
private static readonly Dictionary<Type, Window> _windowCache = new();
public static void HideSystemTitleBar(this Window window)
{
var window = new OverlayWindow(clickThrough: true);
TrackWindow(window);
window.Navigate(typeof(SystemTrayPage));
window.Activate();
window.ExtendsContentIntoTitleBar = true;
window.AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
}
public static void OpenDesktopLyricsWindow()
public static void HideSystemTitleBarAndSetCustomTitleBar(
this Window window,
UIElement titleBar
)
{
var window = new OverlayWindow(listenOnActivatedWindowChange: true);
TrackWindow(window);
window.Navigate(typeof(DesktopLyricsPage));
AppBarHelper.Enable(window, 48);
window.Activate();
window.HideSystemTitleBar();
window.SetTitleBar(titleBar);
}
public static void OpenSettingsWindow()
{
var window = new HostWindow();
TrackWindow(window);
window.Navigate(typeof(SettingsPage));
window.Activate();
OpenOrShowWindow(typeof(SettingsPage));
}
public static void OpenInAppLyricsWindow()
public static void OpenLyricsWindow()
{
var window = new HostWindow();
TrackWindow(window);
window.Navigate(typeof(InAppLyricsPage));
window.Activate();
OpenOrShowWindow(typeof(LyricsPage));
}
public static void TrackWindow(Window window)
private static void OpenOrShowWindow(Type pageType)
{
window.Closed += (sender, args) =>
if (_windowCache.TryGetValue(pageType, out var window))
{
_activeWindows.Remove(window);
};
_activeWindows.Add(window);
if (window is HostWindow hostWindow)
{
hostWindow.Navigate(pageType);
}
window.TryShow();
}
else
{
var newWindow = new HostWindow();
TrackWindow(newWindow, pageType);
newWindow.Navigate(pageType);
newWindow.Activate();
}
}
public static void TrackWindow(Window window, Type pageType = null)
{
if (pageType != null)
{
_windowCache[pageType] = window;
}
if (!_activeWindows.Contains(window))
_activeWindows.Add(window);
}
public static Window GetWindowForElement(UIElement element)
@@ -86,5 +103,21 @@ namespace BetterLyrics.WinUI3.Helper
}
private static List<Window> _activeWindows = new List<Window>();
public static void TryShow(this Window window)
{
if (window is not null)
{
window.Activate();
}
}
public static void TryHide(this Window window)
{
if (window is not null)
{
window.Hide();
}
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Numerics;
using BetterLyrics.WinUI3.Enums;
namespace BetterLyrics.WinUI3.Models
{

View File

@@ -32,8 +32,7 @@ namespace BetterLyrics.WinUI3.Models
public byte[]? AlbumArt { get; set; } = null;
[ObservableProperty]
public partial ObservableCollection<Color> CoverImageDominantColors { get; set; } =
[.. Enumerable.Repeat(Colors.Transparent, ImageHelper.AccentColorCount)];
public partial List<Color>? CoverImageDominantColors { get; set; } = null;
public SongInfo() { }

View File

@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Renderer.DesktopLyricsRenderer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Renderer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<canvas:CanvasAnimatedControl
x:Name="LyricsCanvas"
Draw="LyricsCanvas_Draw"
Loaded="LyricsCanvas_Loaded"
Paused="{x:Bind ViewModel.IsPlaying, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
Update="LyricsCanvas_Update" />
<Grid x:Name="LyricsNotFound">
<TextBlock
x:Uid="DesktopLyricsRendererPageLyricsNotFound"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LyricsExistenceStates">
<VisualState x:Name="Existed">
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ViewModel.SongInfo.IsLyricsExisted, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="LyricsNotFound.Opacity" Value="0" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="NotExisted">
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ViewModel.SongInfo.IsLyricsExisted, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="LyricsNotFound.Opacity" Value=".5" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>

View File

@@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace BetterLyrics.WinUI3.Renderer
{
public sealed partial class DesktopLyricsRenderer : UserControl
{
public DesktopLyricsRendererViewModel ViewModel =
Ioc.Default.GetRequiredService<DesktopLyricsRendererViewModel>();
public DesktopLyricsRenderer()
{
InitializeComponent();
}
private void LyricsCanvas_Draw(
Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedDrawEventArgs args
)
{
ViewModel.Draw(sender, args.DrawingSession);
}
private void LyricsCanvas_Update(
Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedUpdateEventArgs args
)
{
ViewModel.Calculate(sender, args);
}
private void LyricsCanvas_Loaded(object sender, RoutedEventArgs e)
{
ViewModel.RequestRelayout();
}
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Renderer.InAppLyricsRenderer"
x:Class="BetterLyrics.WinUI3.Renderer.LyricsRenderer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"

View File

@@ -9,14 +9,14 @@ using Microsoft.UI.Xaml.Controls;
namespace BetterLyrics.WinUI3.Renderer
{
public sealed partial class InAppLyricsRenderer : UserControl
public sealed partial class LyricsRenderer : UserControl
{
public InAppLyricsRendererViewModel ViewModel { get; set; }
public LyricsRendererViewModel ViewModel { get; set; }
public InAppLyricsRenderer()
public LyricsRenderer()
{
InitializeComponent();
ViewModel = Ioc.Default.GetRequiredService<InAppLyricsRendererViewModel>();
ViewModel = Ioc.Default.GetRequiredService<LyricsRendererViewModel>();
}
private void LyricsCanvas_Draw(

View File

@@ -161,17 +161,13 @@ namespace BetterLyrics.WinUI3.Services.Database
if (initSongInfo.AlbumArt == null)
{
initSongInfo.CoverImageDominantColors =
[
.. Enumerable.Repeat(Colors.Transparent, ImageHelper.AccentColorCount),
];
initSongInfo.CoverImageDominantColors = null;
}
else
{
initSongInfo.CoverImageDominantColors =
[
.. await ImageHelper.GetAccentColorsFromByte(initSongInfo.AlbumArt),
];
initSongInfo.CoverImageDominantColors = await ImageHelper.GetAccentColorsFromByte(
initSongInfo.AlbumArt
);
}
if (initSongInfo.LyricsLines == null) { }

View File

@@ -1,4 +1,5 @@
using System;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Models;
namespace BetterLyrics.WinUI3.Services.Playback

View File

@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.Database;

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Enums;
using Microsoft.UI.Xaml;
namespace BetterLyrics.WinUI3.Services.Settings

View File

@@ -2,7 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Enums;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Newtonsoft.Json;
@@ -243,7 +243,7 @@ namespace BetterLyrics.WinUI3.Services.Settings
SetDefault(ThemeTypeKey, (int)ElementTheme.Default);
SetDefault(LanguageKey, (int)Language.FollowSystem);
SetDefault(MusicLibrariesKey, "[]");
SetDefault(BackdropTypeKey, (int)Models.BackdropType.DesktopAcrylic);
SetDefault(BackdropTypeKey, (int)BackdropType.DesktopAcrylic);
// App behavior
SetDefault(AutoStartWindowTypeKey, (int)AutoStartWindowType.InAppLyrics);
// Album art

View File

@@ -223,9 +223,9 @@
<value>Album art background opacity</value>
</data>
<data name="SettingsPageTitle" xml:space="preserve">
<value>Settings</value>
<value>Settings - BetterLyrics</value>
</data>
<data name="InAppLyricsPageTitle" xml:space="preserve">
<data name="LyricsPageTitle" xml:space="preserve">
<value>BetterLyrics</value>
</data>
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
@@ -382,7 +382,7 @@
<value>Switch to desktop lyrics mode</value>
</data>
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
<value>Enter picture-in-picture mode</value>
<value>Picture-in-picture mode</value>
</data>
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
<value>Exit picture-in-picture mode</value>
@@ -390,8 +390,8 @@
<data name="MainPageLyricsNotFound.Text" xml:space="preserve">
<value>Lyrics not found</value>
</data>
<data name="SettingsPageInAppLyrics.Content" xml:space="preserve">
<value>In-app lyrics style &amp; effect</value>
<data name="SettingsPageLyrics.Content" xml:space="preserve">
<value>Lyrics style &amp; effect</value>
</data>
<data name="SettingsPageDesktopLyrics.Content" xml:space="preserve">
<value>Desktop lyrics style &amp; effect</value>
@@ -420,4 +420,10 @@
<data name="DesktopLyricsRendererPageLyricsNotFound.Text" xml:space="preserve">
<value>Lyrics not found</value>
</data>
<data name="SystemTrayPageTitle" xml:space="preserve">
<value>System tray - BetterLyrics</value>
</data>
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
<value>Dock mode</value>
</data>
</root>

View File

@@ -223,9 +223,9 @@
<value>专辑图片背景不透明度</value>
</data>
<data name="SettingsPageTitle" xml:space="preserve">
<value>设置</value>
<value>设置 - BetterLyrics</value>
</data>
<data name="InAppLyricsPageTitle" xml:space="preserve">
<data name="LyricsPageTitle" xml:space="preserve">
<value>BetterLyrics</value>
</data>
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
@@ -382,7 +382,7 @@
<value>切换到桌面歌词模式</value>
</data>
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
<value>进入画中画模式</value>
<value>画中画模式</value>
</data>
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
<value>退出画中画模式</value>
@@ -390,8 +390,8 @@
<data name="MainPageLyricsNotFound.Text" xml:space="preserve">
<value>未找到歌词</value>
</data>
<data name="SettingsPageInAppLyrics.Content" xml:space="preserve">
<value>应用内歌词样式与动效</value>
<data name="SettingsPageLyrics.Content" xml:space="preserve">
<value>歌词样式与动效</value>
</data>
<data name="SettingsPageDesktopLyrics.Content" xml:space="preserve">
<value>桌面歌词样式与动效</value>
@@ -420,4 +420,10 @@
<data name="DesktopLyricsRendererPageLyricsNotFound.Text" xml:space="preserve">
<value>未找到歌词</value>
</data>
<data name="SystemTrayPageTitle" xml:space="preserve">
<value>系统托盘 - BetterLyrics</value>
</data>
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
<value>停靠模式</value>
</data>
</root>

View File

@@ -223,9 +223,9 @@
<value>專輯圖片背景不透明度</value>
</data>
<data name="SettingsPageTitle" xml:space="preserve">
<value>設定</value>
<value>設定 - BetterLyrics</value>
</data>
<data name="InAppLyricsPageTitle" xml:space="preserve">
<data name="LyricsPageTitle" xml:space="preserve">
<value>BetterLyrics</value>
</data>
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
@@ -382,7 +382,7 @@
<value>切換到桌面歌詞模式</value>
</data>
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
<value>進入畫中畫模式</value>
<value>畫中畫模式</value>
</data>
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
<value>退出畫中畫模式</value>
@@ -390,8 +390,8 @@
<data name="MainPageLyricsNotFound.Text" xml:space="preserve">
<value>找不到歌詞</value>
</data>
<data name="SettingsPageInAppLyrics.Content" xml:space="preserve">
<value>應用程式內歌詞樣式與動效</value>
<data name="SettingsPageLyrics.Content" xml:space="preserve">
<value>歌詞樣式與動效</value>
</data>
<data name="SettingsPageDesktopLyrics.Content" xml:space="preserve">
<value>桌面歌詞樣式與動效</value>
@@ -420,4 +420,10 @@
<data name="DesktopLyricsRendererPageLyricsNotFound.Text" xml:space="preserve">
<value>找不到歌詞</value>
</data>
<data name="SystemTrayPageTitle" xml:space="preserve">
<value>系統托盤 - BetterLyrics</value>
</data>
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
<value>停靠模式</value>
</data>
</root>

View File

@@ -1,76 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.Playback;
using BetterLyrics.WinUI3.Services.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI;
using Windows.UI;
namespace BetterLyrics.WinUI3.ViewModels
{
public abstract partial class BaseLyricsSettingsControlViewModel : BaseViewModel
{
[ObservableProperty]
public partial ObservableCollection<Color> CoverImageDominantColors { get; set; }
public abstract LyricsAlignmentType LyricsAlignmentType { get; set; }
public abstract int LyricsBlurAmount { get; set; }
public abstract int LyricsVerticalEdgeOpacity { get; set; }
public abstract float LyricsLineSpacingFactor { get; set; }
public abstract int LyricsFontSize { get; set; }
public abstract bool IsLyricsGlowEffectEnabled { get; set; }
public abstract bool IsLyricsDynamicGlowEffectEnabled { get; set; }
public abstract LyricsFontColorType LyricsFontColorType { get; set; }
private readonly IPlaybackService _playbackService;
public BaseLyricsSettingsControlViewModel(
ISettingsService settingsService,
IPlaybackService playbackService
)
: base(settingsService)
{
_playbackService = playbackService;
_playbackService.SongInfoChanged += (s, e) =>
{
UpdateCoverImageDominantColors(e.SongInfo?.CoverImageDominantColors);
};
UpdateCoverImageDominantColors(_playbackService.SongInfo?.CoverImageDominantColors);
}
private void UpdateCoverImageDominantColors(ObservableCollection<Color>? value)
{
CoverImageDominantColors ??=
[
.. Enumerable.Repeat(Colors.Transparent, ImageHelper.AccentColorCount),
];
for (int i = 0; i < CoverImageDominantColors.Count; i++)
{
CoverImageDominantColors[i] = Colors.Transparent;
}
if (value != null)
{
for (int i = 0; i < Math.Min(value.Count, CoverImageDominantColors.Count); i++)
{
CoverImageDominantColors[i] = value[i];
}
}
}
}
}

View File

@@ -1,62 +0,0 @@
using System;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.Settings;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
namespace BetterLyrics.WinUI3.ViewModels
{
public abstract partial class BaseWindowViewModel
: BaseViewModel,
IRecipient<PropertyChangedMessage<Models.TitleBarType>>,
IRecipient<PropertyChangedMessage<ElementTheme>>,
IRecipient<PropertyChangedMessage<Models.BackdropType>>
{
public abstract Models.BackdropType BackdropType { get; set; }
public abstract Models.TitleBarType TitleBarType { get; set; }
[ObservableProperty]
public partial ElementTheme ThemeType { get; set; }
public BaseWindowViewModel(ISettingsService settingsService)
: base(settingsService)
{
BackdropType = _settingsService.BackdropType;
TitleBarType = _settingsService.TitleBarType;
ThemeType = _settingsService.ThemeType;
}
public void Receive(PropertyChangedMessage<Models.TitleBarType> message)
{
if (message.Sender is SettingsViewModel)
{
if (message.PropertyName == nameof(SettingsViewModel.TitleBarType))
{
TitleBarType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<Models.BackdropType> message)
{
if (message.Sender is SettingsViewModel)
{
if (message.PropertyName == nameof(SettingsViewModel.BackdropType))
{
BackdropType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<ElementTheme> message)
{
ThemeType = message.NewValue;
}
}
}

View File

@@ -1,21 +0,0 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class DesktopLyricsPageViewModel(ISettingsService settingsService)
: BaseViewModel(settingsService)
{
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial double LimitedLineWidth { get; set; } = 0.0;
[RelayCommand]
private void OpenSettingsWindow()
{
WindowHelper.OpenSettingsWindow();
}
}
}

View File

@@ -1,220 +0,0 @@
using System.Numerics;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.Playback;
using BetterLyrics.WinUI3.Services.Settings;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.WinUI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Windows.UI;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class DesktopLyricsRendererViewModel
: BaseLyricsRendererViewModel,
IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<float>>,
IRecipient<PropertyChangedMessage<double>>,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<Color>>,
IRecipient<PropertyChangedMessage<LyricsFontColorType>>,
IRecipient<PropertyChangedMessage<LyricsAlignmentType>>
{
private Color _startColor;
private Color _targetColor;
private float _progress; // From 0 to 1
private const float TransitionSeconds = 0.3f;
private bool _isTransitioning;
public Color ActivatedWindowAccentColor { get; set; }
public DesktopLyricsRendererViewModel(
ISettingsService settingsService,
IPlaybackService playbackService
)
: base(settingsService, playbackService)
{
LyricsFontColorType = _settingsService.DesktopLyricsFontColorType;
LyricsAlignmentType = _settingsService.DesktopLyricsAlignmentType;
LyricsVerticalEdgeOpacity = _settingsService.DesktopLyricsVerticalEdgeOpacity;
LyricsLineSpacingFactor = _settingsService.DesktopLyricsLineSpacingFactor;
LyricsFontSize = _settingsService.DesktopLyricsFontSize;
LyricsBlurAmount = _settingsService.DesktopLyricsBlurAmount;
IsLyricsGlowEffectEnabled = _settingsService.IsDesktopLyricsGlowEffectEnabled;
IsLyricsDynamicGlowEffectEnabled =
_settingsService.IsDesktopLyricsDynamicGlowEffectEnabled;
_startColor = _targetColor = ActivatedWindowAccentColor;
_progress = 0f;
UpdateFontColor();
}
public override void Calculate(
ICanvasAnimatedControl control,
CanvasAnimatedUpdateEventArgs args
)
{
base.Calculate(control, args);
// Detect if the accent color has changed
if (ActivatedWindowAccentColor != _targetColor)
{
_startColor = _isTransitioning
? ColorHelper.GetInterpolatedColor(_progress, _startColor, _targetColor)
: _targetColor;
_targetColor = ActivatedWindowAccentColor;
_progress = 0f;
_isTransitioning = true;
}
// Update the transition progress
if (_isTransitioning)
{
_progress += (float)(ElapsedTime.TotalSeconds / TransitionSeconds);
if (_progress >= 1f)
{
_progress = 1f;
_isTransitioning = false;
}
}
}
public override void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)
{
var color = _isTransitioning
? ColorHelper.GetInterpolatedColor(_progress, _startColor, _targetColor)
: _targetColor;
float padding = 0f;
var rect = control.Size.ToRect();
rect.X += padding;
rect.Y += padding;
rect.Width -= 2 * padding;
rect.Height -= 2 * padding;
ds.FillRectangle(rect, color);
base.Draw(control, ds);
}
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is DesktopLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(DesktopLyricsSettingsControlViewModel.IsLyricsGlowEffectEnabled)
)
{
IsLyricsGlowEffectEnabled = message.NewValue;
}
else if (
message.PropertyName
== nameof(
DesktopLyricsSettingsControlViewModel.IsLyricsDynamicGlowEffectEnabled
)
)
{
IsLyricsDynamicGlowEffectEnabled = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.Sender is DesktopLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(DesktopLyricsSettingsControlViewModel.LyricsVerticalEdgeOpacity)
)
{
LyricsVerticalEdgeOpacity = message.NewValue;
}
else if (
message.PropertyName
== nameof(DesktopLyricsSettingsControlViewModel.LyricsBlurAmount)
)
{
LyricsBlurAmount = message.NewValue;
}
else if (
message.PropertyName
== nameof(DesktopLyricsSettingsControlViewModel.LyricsFontSize)
)
{
LyricsFontSize = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<Color> message)
{
if (message.Sender is OverlayWindowViewModel)
{
if (
message.PropertyName
== nameof(OverlayWindowViewModel.ActivatedWindowAccentColor)
)
ActivatedWindowAccentColor = message.NewValue;
}
}
public void Receive(PropertyChangedMessage<LyricsFontColorType> message)
{
if (message.Sender is DesktopLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(DesktopLyricsSettingsControlViewModel.LyricsFontColorType)
)
{
LyricsFontColorType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<LyricsAlignmentType> message)
{
if (message.Sender is DesktopLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(DesktopLyricsSettingsControlViewModel.LyricsAlignmentType)
)
{
LyricsAlignmentType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<float> message)
{
if (message.Sender is DesktopLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(DesktopLyricsSettingsControlViewModel.LyricsLineSpacingFactor)
)
{
LyricsLineSpacingFactor = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<double> message)
{
if (message.Sender is DesktopLyricsPageViewModel)
{
if (message.PropertyName == nameof(DesktopLyricsPageViewModel.LimitedLineWidth))
{
LimitedLineWidth = message.NewValue;
}
}
}
}
}

View File

@@ -1,99 +0,0 @@
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.Playback;
using BetterLyrics.WinUI3.Services.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class DesktopLyricsSettingsControlViewModel : BaseLyricsSettingsControlViewModel
{
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial LyricsAlignmentType LyricsAlignmentType { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial int LyricsBlurAmount { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial int LyricsVerticalEdgeOpacity { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial float LyricsLineSpacingFactor { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial int LyricsFontSize { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial bool IsLyricsGlowEffectEnabled { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial bool IsLyricsDynamicGlowEffectEnabled { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial LyricsFontColorType LyricsFontColorType { get; set; }
public DesktopLyricsSettingsControlViewModel(
ISettingsService settingsService,
IPlaybackService playbackService
)
: base(settingsService, playbackService)
{
LyricsAlignmentType = _settingsService.DesktopLyricsAlignmentType;
LyricsBlurAmount = _settingsService.DesktopLyricsBlurAmount;
LyricsVerticalEdgeOpacity = _settingsService.DesktopLyricsVerticalEdgeOpacity;
LyricsLineSpacingFactor = _settingsService.DesktopLyricsLineSpacingFactor;
LyricsFontSize = _settingsService.DesktopLyricsFontSize;
IsLyricsGlowEffectEnabled = _settingsService.IsDesktopLyricsGlowEffectEnabled;
IsLyricsDynamicGlowEffectEnabled =
_settingsService.IsDesktopLyricsDynamicGlowEffectEnabled;
LyricsFontColorType = _settingsService.DesktopLyricsFontColorType;
}
partial void OnLyricsAlignmentTypeChanged(LyricsAlignmentType value)
{
_settingsService.DesktopLyricsAlignmentType = value;
}
partial void OnLyricsBlurAmountChanged(int value)
{
_settingsService.DesktopLyricsBlurAmount = value;
}
partial void OnLyricsVerticalEdgeOpacityChanged(int value)
{
_settingsService.DesktopLyricsVerticalEdgeOpacity = value;
}
partial void OnLyricsLineSpacingFactorChanged(float value)
{
_settingsService.DesktopLyricsLineSpacingFactor = value;
}
partial void OnLyricsFontSizeChanged(int value)
{
_settingsService.DesktopLyricsFontSize = value;
}
partial void OnIsLyricsGlowEffectEnabledChanged(bool value)
{
_settingsService.IsDesktopLyricsGlowEffectEnabled = value;
}
partial void OnIsLyricsDynamicGlowEffectEnabledChanged(bool value)
{
_settingsService.IsDesktopLyricsDynamicGlowEffectEnabled = value;
}
partial void OnLyricsFontColorTypeChanged(LyricsFontColorType value)
{
_settingsService.DesktopLyricsFontColorType = value;
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Messages;
using BetterLyrics.WinUI3.Models;
@@ -9,13 +10,18 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Xaml;
using Windows.UI;
namespace BetterLyrics.WinUI3
{
public partial class HostWindowViewModel
: BaseWindowViewModel,
IRecipient<PropertyChangedMessage<bool>>
: BaseViewModel,
IRecipient<PropertyChangedMessage<TitleBarType>>,
IRecipient<PropertyChangedMessage<ElementTheme>>
{
[ObservableProperty]
public partial ElementTheme ThemeType { get; set; }
[ObservableProperty]
public partial double AppLogoImageIconHeight { get; set; }
@@ -32,23 +38,22 @@ namespace BetterLyrics.WinUI3
public partial bool ShowInfoBar { get; set; } = false;
[ObservableProperty]
public override partial BackdropType BackdropType { get; set; }
public partial TitleBarType TitleBarType { get; set; }
[ObservableProperty]
public override partial TitleBarType TitleBarType { get; set; }
[NotifyPropertyChangedRecipients]
public partial bool IsDockMode { get; set; } = false;
[ObservableProperty]
public partial bool IsImmersiveMode { get; set; }
[ObservableProperty]
public partial int TopCommandGridOpacity { get; set; } = 1;
[NotifyPropertyChangedRecipients]
public partial Color ActivatedWindowAccentColor { get; set; }
public HostWindowViewModel(ISettingsService settingsService)
: base(settingsService)
{
TitleBarType = _settingsService.TitleBarType;
ThemeType = _settingsService.ThemeType;
OnTitleBarTypeChanged(TitleBarType);
BackdropType = _settingsService.BackdropType;
WeakReferenceMessenger.Default.Register<ShowNotificatonMessage>(
this,
@@ -71,20 +76,13 @@ namespace BetterLyrics.WinUI3
);
}
partial void OnIsImmersiveModeChanged(bool value)
public void UpdateAccentColor(nint hwnd)
{
if (value)
{
TopCommandGridOpacity = 0;
}
else
{
TopCommandGridOpacity = 1;
}
ActivatedWindowAccentColor = WindowColorHelper
.GetDominantColorBelow(hwnd)
.ToWindowsUIColor();
}
partial void OnBackdropTypeChanged(BackdropType value) { }
partial void OnTitleBarTypeChanged(TitleBarType value)
{
switch (value)
@@ -118,15 +116,20 @@ namespace BetterLyrics.WinUI3
return null;
}
public void Receive(PropertyChangedMessage<bool> message)
public void Receive(PropertyChangedMessage<TitleBarType> message)
{
if (message.Sender is InAppLyricsPageViewModel)
if (message.Sender is SettingsViewModel)
{
if (message.PropertyName == nameof(InAppLyricsPageViewModel.IsImmersiveMode))
if (message.PropertyName == nameof(SettingsViewModel.TitleBarType))
{
IsImmersiveMode = message.NewValue;
TitleBarType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<ElementTheme> message)
{
ThemeType = message.NewValue;
}
}
}

View File

@@ -1,350 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using BetterInAppLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Messages;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Rendering;
using BetterLyrics.WinUI3.Services.Playback;
using BetterLyrics.WinUI3.Services.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI.Dispatching;
using Windows.Graphics.Imaging;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class InAppLyricsRendererViewModel
: BaseLyricsRendererViewModel,
IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<float>>,
IRecipient<PropertyChangedMessage<double>>,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<InAppLyricsDisplayType>>,
IRecipient<PropertyChangedMessage<LyricsFontColorType>>,
IRecipient<PropertyChangedMessage<LyricsAlignmentType>>
{
public InAppLyricsDisplayType DisplayType { get; set; }
private float _rotateAngle = 0f;
[ObservableProperty]
public override partial SongInfo? SongInfo { get; set; }
private SoftwareBitmap? _lastSoftwareBitmap = null;
private SoftwareBitmap? _softwareBitmap = null;
private SoftwareBitmap? SoftwareBitmap
{
get => _softwareBitmap;
set
{
if (_softwareBitmap != null)
{
_lastSoftwareBitmap = _softwareBitmap;
_transitionStartTime = DateTimeOffset.Now;
_isTransitioning = true;
_transitionAlpha = 0f;
}
_softwareBitmap = value;
}
}
public int CoverImageRadius { get; set; }
public bool IsCoverOverlayEnabled { get; set; }
public bool IsDynamicCoverOverlayEnabled { get; set; }
public int CoverOverlayOpacity { get; set; }
public int CoverOverlayBlurAmount { get; set; }
private float _transitionAlpha = 1f;
private TimeSpan _transitionDuration = TimeSpan.FromMilliseconds(1000);
private DateTimeOffset _transitionStartTime;
private bool _isTransitioning = false;
private readonly float _coverRotateSpeed = 0.003f;
public InAppLyricsRendererViewModel(
ISettingsService settingsService,
IPlaybackService playbackService
)
: base(settingsService, playbackService)
{
CoverImageRadius = _settingsService.CoverImageRadius;
IsCoverOverlayEnabled = _settingsService.IsCoverOverlayEnabled;
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
LyricsFontColorType = _settingsService.InAppLyricsFontColorType;
LyricsAlignmentType = _settingsService.InAppLyricsAlignmentType;
LyricsVerticalEdgeOpacity = _settingsService.InAppLyricsVerticalEdgeOpacity;
LyricsLineSpacingFactor = _settingsService.InAppLyricsLineSpacingFactor;
LyricsFontSize = _settingsService.InAppLyricsFontSize;
LyricsBlurAmount = _settingsService.InAppLyricsBlurAmount;
IsLyricsGlowEffectEnabled = _settingsService.IsInAppLyricsGlowEffectEnabled;
IsLyricsDynamicGlowEffectEnabled =
_settingsService.IsInAppLyricsDynamicGlowEffectEnabled;
UpdateFontColor();
}
async partial void OnSongInfoChanged(SongInfo? value)
{
if (value?.AlbumArt is byte[] bytes)
SoftwareBitmap = await (
await ImageHelper.GetDecoderFromByte(bytes)
).GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}
public override void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)
{
if (!IsCoverOverlayEnabled || SoftwareBitmap == null)
return;
ds.Transform = Matrix3x2.CreateRotation(_rotateAngle, control.Size.ToVector2() * 0.5f);
var overlappedCovers = new CanvasCommandList(control.Device);
using var overlappedCoversDs = overlappedCovers.CreateDrawingSession();
if (_isTransitioning && _lastSoftwareBitmap != null)
{
DrawImgae(control, overlappedCoversDs, _lastSoftwareBitmap, 1 - _transitionAlpha);
DrawImgae(control, overlappedCoversDs, SoftwareBitmap, _transitionAlpha);
}
else
{
DrawImgae(control, overlappedCoversDs, SoftwareBitmap, 1);
}
using var coverOverlayEffect = new OpacityEffect
{
Opacity = CoverOverlayOpacity / 100f,
Source = new GaussianBlurEffect
{
BlurAmount = CoverOverlayBlurAmount,
Source = overlappedCovers,
},
};
ds.DrawImage(coverOverlayEffect);
ds.Transform = Matrix3x2.Identity;
switch (DisplayType)
{
case InAppLyricsDisplayType.AlbumArtOnly:
case InAppLyricsDisplayType.PlaceholderOnly:
break;
case InAppLyricsDisplayType.LyricsOnly:
case InAppLyricsDisplayType.SplitView:
base.Draw(control, ds);
break;
default:
break;
}
}
private static void DrawImgae(
ICanvasAnimatedControl control,
CanvasDrawingSession ds,
SoftwareBitmap softwareBitmap,
float opacity
)
{
using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, softwareBitmap);
float imageWidth = (float)canvasBitmap.Size.Width;
float imageHeight = (float)canvasBitmap.Size.Height;
var scaleFactor =
(float)Math.Sqrt(Math.Pow(control.Size.Width, 2) + Math.Pow(control.Size.Height, 2))
/ Math.Min(imageWidth, imageHeight);
ds.DrawImage(
new OpacityEffect
{
Source = new ScaleEffect
{
InterpolationMode = CanvasImageInterpolation.HighQualityCubic,
BorderMode = EffectBorderMode.Hard,
Scale = new Vector2(scaleFactor),
Source = canvasBitmap,
},
Opacity = opacity,
},
(float)control.Size.Width / 2 - imageWidth * scaleFactor / 2,
(float)control.Size.Height / 2 - imageHeight * scaleFactor / 2
);
}
public override void Calculate(
ICanvasAnimatedControl control,
CanvasAnimatedUpdateEventArgs args
)
{
if (_isTransitioning)
{
var elapsed = DateTimeOffset.Now - _transitionStartTime;
float progress = (float)(
elapsed.TotalMilliseconds / _transitionDuration.TotalMilliseconds
);
_transitionAlpha = Math.Clamp(progress, 0f, 1f);
if (_transitionAlpha >= 1f)
{
_isTransitioning = false;
_lastSoftwareBitmap?.Dispose();
_lastSoftwareBitmap = null;
}
}
if (IsDynamicCoverOverlayEnabled)
{
_rotateAngle += _coverRotateSpeed;
_rotateAngle %= MathF.PI * 2;
}
base.Calculate(control, args);
}
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is SettingsViewModel)
{
if (message.PropertyName == nameof(SettingsViewModel.IsDynamicCoverOverlayEnabled))
{
IsDynamicCoverOverlayEnabled = message.NewValue;
}
else if (message.PropertyName == nameof(SettingsViewModel.IsCoverOverlayEnabled))
{
IsCoverOverlayEnabled = message.NewValue;
}
}
else if (message.Sender is InAppLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(InAppLyricsSettingsControlViewModel.IsLyricsGlowEffectEnabled)
)
{
IsLyricsGlowEffectEnabled = message.NewValue;
}
else if (
message.PropertyName
== nameof(InAppLyricsSettingsControlViewModel.IsLyricsDynamicGlowEffectEnabled)
)
{
IsLyricsDynamicGlowEffectEnabled = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.Sender is SettingsViewModel)
{
if (message.PropertyName == nameof(SettingsViewModel.CoverImageRadius))
{
CoverImageRadius = message.NewValue;
}
else if (message.PropertyName == nameof(SettingsViewModel.CoverOverlayOpacity))
{
CoverOverlayOpacity = message.NewValue;
}
else if (message.PropertyName == nameof(SettingsViewModel.CoverOverlayBlurAmount))
{
CoverOverlayBlurAmount = message.NewValue;
}
}
else if (message.Sender is InAppLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(InAppLyricsSettingsControlViewModel.LyricsVerticalEdgeOpacity)
)
{
LyricsVerticalEdgeOpacity = message.NewValue;
}
else if (
message.PropertyName
== nameof(InAppLyricsSettingsControlViewModel.LyricsBlurAmount)
)
{
LyricsBlurAmount = message.NewValue;
}
else if (
message.PropertyName
== nameof(InAppLyricsSettingsControlViewModel.LyricsFontSize)
)
{
LyricsFontSize = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<LyricsFontColorType> message)
{
if (message.Sender is InAppLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(InAppLyricsSettingsControlViewModel.LyricsFontColorType)
)
{
LyricsFontColorType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<LyricsAlignmentType> message)
{
if (message.Sender is InAppLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(InAppLyricsSettingsControlViewModel.LyricsAlignmentType)
)
{
LyricsAlignmentType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<float> message)
{
if (message.Sender is InAppLyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(InAppLyricsSettingsControlViewModel.LyricsLineSpacingFactor)
)
{
LyricsLineSpacingFactor = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<double> message)
{
if (message.Sender is InAppLyricsPageViewModel)
{
if (message.PropertyName == nameof(InAppLyricsPageViewModel.LimitedLineWidth))
{
LimitedLineWidth = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<InAppLyricsDisplayType> message)
{
DisplayType = message.NewValue;
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Threading.Tasks;
using BetterInAppLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Messages;
using BetterLyrics.WinUI3.Models;
@@ -17,9 +18,10 @@ using WinUIEx.Messaging;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class InAppLyricsPageViewModel
public partial class LyricsPageViewModel
: BaseViewModel,
IRecipient<PropertyChangedMessage<int>>
IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<bool>>
{
[ObservableProperty]
public partial bool IsDesktopLyricsOpened { get; set; } = false;
@@ -30,8 +32,8 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial InAppLyricsDisplayType DisplayType { get; set; } =
InAppLyricsDisplayType.PlaceholderOnly;
public partial LyricsDisplayType DisplayType { get; set; } =
LyricsDisplayType.PlaceholderOnly;
[ObservableProperty]
public partial BitmapImage? CoverImage { get; set; }
@@ -40,16 +42,12 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial SongInfo? SongInfo { get; set; } = null;
[ObservableProperty]
public partial InAppLyricsDisplayType? PreferredDisplayType { get; set; } =
InAppLyricsDisplayType.SplitView;
public partial LyricsDisplayType? PreferredDisplayType { get; set; } =
LyricsDisplayType.SplitView;
[ObservableProperty]
public partial bool AboutToUpdateUI { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsImmersiveMode { get; set; }
[ObservableProperty]
public partial double CoverImageGridActualHeight { get; set; }
@@ -65,9 +63,12 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial bool IsFirstRun { get; set; }
[ObservableProperty]
public partial bool IsNotMockMode { get; set; } = true;
private readonly IPlaybackService _playbackService;
public InAppLyricsPageViewModel(
public LyricsPageViewModel(
ISettingsService settingsService,
IPlaybackService playbackService
)
@@ -112,8 +113,8 @@ namespace BetterLyrics.WinUI3.ViewModels
private void OnDisplayTypeChanged(object value)
{
int index = Convert.ToInt32(value);
PreferredDisplayType = (InAppLyricsDisplayType)index;
DisplayType = (InAppLyricsDisplayType)index;
PreferredDisplayType = (LyricsDisplayType)index;
DisplayType = (LyricsDisplayType)index;
}
[RelayCommand]
@@ -134,19 +135,19 @@ namespace BetterLyrics.WinUI3.ViewModels
? null
: await ImageHelper.GetBitmapImageFromBytesAsync(songInfo.AlbumArt);
InAppLyricsDisplayType displayType;
LyricsDisplayType displayType;
if (songInfo == null)
{
displayType = InAppLyricsDisplayType.PlaceholderOnly;
displayType = LyricsDisplayType.PlaceholderOnly;
}
else if (PreferredDisplayType is InAppLyricsDisplayType preferredDisplayType)
else if (PreferredDisplayType is LyricsDisplayType preferredDisplayType)
{
displayType = preferredDisplayType;
}
else
{
displayType = InAppLyricsDisplayType.SplitView;
displayType = LyricsDisplayType.SplitView;
}
DisplayType = displayType;
@@ -176,5 +177,16 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
}
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is HostWindowViewModel)
{
if (message.PropertyName == nameof(HostWindowViewModel.IsDockMode))
{
IsNotMockMode = !message.NewValue;
}
}
}
}
}

View File

@@ -3,6 +3,11 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using BetterInAppLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Messages;
using BetterLyrics.WinUI3.Models;
@@ -21,15 +26,21 @@ using Microsoft.UI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.UI;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class BaseLyricsRendererViewModel
public partial class LyricsRendererViewModel
: BaseRendererViewModel,
IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<float>>,
IRecipient<PropertyChangedMessage<double>>,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<LyricsDisplayType>>,
IRecipient<PropertyChangedMessage<LyricsFontColorType>>,
IRecipient<PropertyChangedMessage<LyricsAlignmentType>>,
IRecipient<PropertyChangedMessage<ElementTheme>>
{
private protected CanvasTextFormat _textFormat = new()
@@ -40,8 +51,37 @@ namespace BetterLyrics.WinUI3.ViewModels
//FontFamily = "Segoe UI Mono",
};
public LyricsDisplayType DisplayType { get; set; }
private float _rotateAngle = 0f;
[ObservableProperty]
public virtual partial SongInfo? SongInfo { get; set; }
public partial SongInfo? SongInfo { get; set; }
private SoftwareBitmap? _lastSoftwareBitmap = null;
private SoftwareBitmap? _softwareBitmap = null;
private SoftwareBitmap? SoftwareBitmap
{
get => _softwareBitmap;
set
{
if (_softwareBitmap != null)
{
_lastSoftwareBitmap = _softwareBitmap;
_transitionStartTime = DateTimeOffset.Now;
_isTransitioning = true;
_transitionAlpha = 0f;
}
_softwareBitmap = value;
}
}
public int CoverImageRadius { get; set; }
public bool IsCoverOverlayEnabled { get; set; }
public bool IsDynamicCoverOverlayEnabled { get; set; }
public int CoverOverlayOpacity { get; set; }
public int CoverOverlayBlurAmount { get; set; }
[ObservableProperty]
public partial bool IsPlaying { get; set; }
@@ -85,7 +125,6 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial ElementTheme Theme { get; set; }
public List<Color> CoverImageDominantColors { get; set; }
[ObservableProperty]
public partial LyricsFontColorType LyricsFontColorType { get; set; }
@@ -104,22 +143,43 @@ namespace BetterLyrics.WinUI3.ViewModels
private protected readonly IPlaybackService _playbackService;
public BaseLyricsRendererViewModel(
private float _transitionAlpha = 1f;
private TimeSpan _transitionDuration = TimeSpan.FromMilliseconds(1000);
private DateTimeOffset _transitionStartTime;
private bool _isTransitioning = false;
private readonly float _coverRotateSpeed = 0.003f;
public LyricsRendererViewModel(
ISettingsService settingsService,
IPlaybackService playbackService
)
: base(settingsService)
{
CoverImageDominantColors =
[
.. Enumerable.Repeat(Colors.Transparent, ImageHelper.AccentColorCount),
];
CoverImageRadius = _settingsService.CoverImageRadius;
IsCoverOverlayEnabled = _settingsService.IsCoverOverlayEnabled;
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
LyricsFontColorType = _settingsService.InAppLyricsFontColorType;
LyricsAlignmentType = _settingsService.InAppLyricsAlignmentType;
LyricsVerticalEdgeOpacity = _settingsService.InAppLyricsVerticalEdgeOpacity;
LyricsLineSpacingFactor = _settingsService.InAppLyricsLineSpacingFactor;
LyricsFontSize = _settingsService.InAppLyricsFontSize;
LyricsBlurAmount = _settingsService.InAppLyricsBlurAmount;
IsLyricsGlowEffectEnabled = _settingsService.IsInAppLyricsGlowEffectEnabled;
IsLyricsDynamicGlowEffectEnabled =
_settingsService.IsInAppLyricsDynamicGlowEffectEnabled;
_playbackService = playbackService;
_playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
_playbackService.PositionChanged += PlaybackService_PositionChanged;
RefreshPlaybackInfo();
UpdateFontColor();
}
public void RequestRelayout()
@@ -154,12 +214,13 @@ namespace BetterLyrics.WinUI3.ViewModels
_isRelayoutNeeded = true;
}
partial void OnSongInfoChanged(SongInfo? value)
async partial void OnSongInfoChanged(SongInfo? value)
{
if (value != null)
{
CoverImageDominantColors = [.. value.CoverImageDominantColors];
}
if (value?.AlbumArt is byte[] bytes)
SoftwareBitmap = await (
await ImageHelper.GetDecoderFromByte(bytes)
).GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
UpdateFontColor();
_isRelayoutNeeded = true;
}
@@ -214,7 +275,7 @@ namespace BetterLyrics.WinUI3.ViewModels
}
break;
case LyricsFontColorType.Dominant:
_fontColor = CoverImageDominantColors[0];
_fontColor = SongInfo?.CoverImageDominantColors?[0] ?? _lightFontColor;
break;
default:
break;
@@ -375,13 +436,55 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
public virtual void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)
public void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)
{
if (!IsCoverOverlayEnabled || SoftwareBitmap == null)
return;
ds.Transform = Matrix3x2.CreateRotation(_rotateAngle, control.Size.ToVector2() * 0.5f);
var overlappedCovers = new CanvasCommandList(control.Device);
using var overlappedCoversDs = overlappedCovers.CreateDrawingSession();
if (_isTransitioning && _lastSoftwareBitmap != null)
{
DrawImgae(control, overlappedCoversDs, _lastSoftwareBitmap, 1 - _transitionAlpha);
DrawImgae(control, overlappedCoversDs, SoftwareBitmap, _transitionAlpha);
}
else
{
DrawImgae(control, overlappedCoversDs, SoftwareBitmap, 1);
}
using var coverOverlayEffect = new OpacityEffect
{
Opacity = CoverOverlayOpacity / 100f,
Source = new GaussianBlurEffect
{
BlurAmount = CoverOverlayBlurAmount,
Source = overlappedCovers,
},
};
ds.DrawImage(coverOverlayEffect);
ds.Transform = Matrix3x2.Identity;
// Lyrics only layer
using var lyrics = new CanvasCommandList(control);
using (var lyricsDs = lyrics.CreateDrawingSession())
{
DrawLyrics(control, lyricsDs);
switch (DisplayType)
{
case LyricsDisplayType.AlbumArtOnly:
case LyricsDisplayType.PlaceholderOnly:
break;
case LyricsDisplayType.LyricsOnly:
case LyricsDisplayType.SplitView:
DrawLyrics(control, lyricsDs);
break;
default:
break;
}
}
using var glowedLyrics = new CanvasCommandList(control);
@@ -542,6 +645,28 @@ namespace BetterLyrics.WinUI3.ViewModels
{
base.Calculate(control, args);
if (_isTransitioning)
{
var elapsed = DateTimeOffset.Now - _transitionStartTime;
float progress = (float)(
elapsed.TotalMilliseconds / _transitionDuration.TotalMilliseconds
);
_transitionAlpha = Math.Clamp(progress, 0f, 1f);
if (_transitionAlpha >= 1f)
{
_isTransitioning = false;
_lastSoftwareBitmap?.Dispose();
_lastSoftwareBitmap = null;
}
}
if (IsDynamicCoverOverlayEnabled)
{
_rotateAngle += _coverRotateSpeed;
_rotateAngle %= MathF.PI * 2;
}
if (_isRelayoutNeeded)
{
ReLayout(control);
@@ -779,5 +904,168 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
}
private static void DrawImgae(
ICanvasAnimatedControl control,
CanvasDrawingSession ds,
SoftwareBitmap softwareBitmap,
float opacity
)
{
using var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, softwareBitmap);
float imageWidth = (float)canvasBitmap.Size.Width;
float imageHeight = (float)canvasBitmap.Size.Height;
var scaleFactor =
(float)Math.Sqrt(Math.Pow(control.Size.Width, 2) + Math.Pow(control.Size.Height, 2))
/ Math.Min(imageWidth, imageHeight);
ds.DrawImage(
new OpacityEffect
{
Source = new ScaleEffect
{
InterpolationMode = CanvasImageInterpolation.HighQualityCubic,
BorderMode = EffectBorderMode.Hard,
Scale = new Vector2(scaleFactor),
Source = canvasBitmap,
},
Opacity = opacity,
},
(float)control.Size.Width / 2 - imageWidth * scaleFactor / 2,
(float)control.Size.Height / 2 - imageHeight * scaleFactor / 2
);
}
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is SettingsViewModel)
{
if (message.PropertyName == nameof(SettingsViewModel.IsDynamicCoverOverlayEnabled))
{
IsDynamicCoverOverlayEnabled = message.NewValue;
}
else if (message.PropertyName == nameof(SettingsViewModel.IsCoverOverlayEnabled))
{
IsCoverOverlayEnabled = message.NewValue;
}
}
else if (message.Sender is LyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(LyricsSettingsControlViewModel.IsLyricsGlowEffectEnabled)
)
{
IsLyricsGlowEffectEnabled = message.NewValue;
}
else if (
message.PropertyName
== nameof(LyricsSettingsControlViewModel.IsLyricsDynamicGlowEffectEnabled)
)
{
IsLyricsDynamicGlowEffectEnabled = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.Sender is SettingsViewModel)
{
if (message.PropertyName == nameof(SettingsViewModel.CoverImageRadius))
{
CoverImageRadius = message.NewValue;
}
else if (message.PropertyName == nameof(SettingsViewModel.CoverOverlayOpacity))
{
CoverOverlayOpacity = message.NewValue;
}
else if (message.PropertyName == nameof(SettingsViewModel.CoverOverlayBlurAmount))
{
CoverOverlayBlurAmount = message.NewValue;
}
}
else if (message.Sender is LyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(LyricsSettingsControlViewModel.LyricsVerticalEdgeOpacity)
)
{
LyricsVerticalEdgeOpacity = message.NewValue;
}
else if (
message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsBlurAmount)
)
{
LyricsBlurAmount = message.NewValue;
}
else if (
message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize)
)
{
LyricsFontSize = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<LyricsFontColorType> message)
{
if (message.Sender is LyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(LyricsSettingsControlViewModel.LyricsFontColorType)
)
{
LyricsFontColorType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<LyricsAlignmentType> message)
{
if (message.Sender is LyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(LyricsSettingsControlViewModel.LyricsAlignmentType)
)
{
LyricsAlignmentType = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<float> message)
{
if (message.Sender is LyricsSettingsControlViewModel)
{
if (
message.PropertyName
== nameof(LyricsSettingsControlViewModel.LyricsLineSpacingFactor)
)
{
LyricsLineSpacingFactor = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<double> message)
{
if (message.Sender is LyricsPageViewModel)
{
if (message.PropertyName == nameof(LyricsPageViewModel.LimitedLineWidth))
{
LimitedLineWidth = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<LyricsDisplayType> message)
{
DisplayType = message.NewValue;
}
}
}

View File

@@ -1,50 +1,53 @@
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.Playback;
using BetterLyrics.WinUI3.Services.Settings;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI;
using Windows.UI;
namespace BetterInAppLyrics.WinUI3.ViewModels
{
public partial class InAppLyricsSettingsControlViewModel : BaseLyricsSettingsControlViewModel
public partial class LyricsSettingsControlViewModel : BaseViewModel
{
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial LyricsAlignmentType LyricsAlignmentType { get; set; }
public partial LyricsAlignmentType LyricsAlignmentType { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial int LyricsBlurAmount { get; set; }
public partial int LyricsBlurAmount { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial int LyricsVerticalEdgeOpacity { get; set; }
public partial int LyricsVerticalEdgeOpacity { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial float LyricsLineSpacingFactor { get; set; }
public partial float LyricsLineSpacingFactor { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial int LyricsFontSize { get; set; }
public partial int LyricsFontSize { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial bool IsLyricsGlowEffectEnabled { get; set; }
public partial bool IsLyricsGlowEffectEnabled { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial bool IsLyricsDynamicGlowEffectEnabled { get; set; }
public partial bool IsLyricsDynamicGlowEffectEnabled { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public override partial LyricsFontColorType LyricsFontColorType { get; set; }
public partial LyricsFontColorType LyricsFontColorType { get; set; }
public InAppLyricsSettingsControlViewModel(
ISettingsService settingsService,
IPlaybackService playbackService
)
: base(settingsService, playbackService)
public LyricsSettingsControlViewModel(ISettingsService settingsService)
: base(settingsService)
{
IsActive = true;

View File

@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using Windows.UI;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class OverlayWindowViewModel(ISettingsService settingsService)
: BaseViewModel(settingsService)
{
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial Color ActivatedWindowAccentColor { get; set; }
public void UpdateAccentColor(nint hwnd)
{
ActivatedWindowAccentColor = WindowColorHelper
.GetDominantColorBelow(hwnd)
.ToWindowsUIColor();
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Messages;
using BetterLyrics.WinUI3.Models;
@@ -89,22 +90,22 @@ namespace BetterLyrics.WinUI3.ViewModels
}
[ObservableProperty]
public partial Models.Language Language { get; set; }
public partial Enums.Language Language { get; set; }
partial void OnLanguageChanged(Models.Language value)
partial void OnLanguageChanged(Enums.Language value)
{
switch (value)
{
case Models.Language.FollowSystem:
case Enums.Language.FollowSystem:
ApplicationLanguages.PrimaryLanguageOverride = "";
break;
case Models.Language.English:
case Enums.Language.English:
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
break;
case Models.Language.SimplifiedChinese:
case Enums.Language.SimplifiedChinese:
ApplicationLanguages.PrimaryLanguageOverride = "zh-CN";
break;
case Models.Language.TraditionalChinese:
case Enums.Language.TraditionalChinese:
ApplicationLanguages.PrimaryLanguageOverride = "zh-TW";
break;
default:

View File

@@ -1,42 +0,0 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class SystemTrayPageViewModel : BaseViewModel
{
public SystemTrayPageViewModel(ISettingsService settingsService)
: base(settingsService)
{
switch (_settingsService.AutoStartWindowType)
{
case AutoStartWindowType.None:
break;
case AutoStartWindowType.InAppLyrics:
WindowHelper.OpenInAppLyricsWindow();
break;
case AutoStartWindowType.DesktopLyrics:
WindowHelper.OpenDesktopLyricsWindow();
break;
default:
break;
}
}
[RelayCommand]
private void OpenSettingsWindow()
{
WindowHelper.OpenSettingsWindow();
}
[RelayCommand]
private void ExitApp()
{
Application.Current.Exit();
}
}
}

View File

@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<Page
x:Class="BetterLyrics.WinUI3.Views.DesktopLyricsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:renderer="using:BetterLyrics.WinUI3.Renderer"
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
mc:Ignorable="d">
<Grid>
<interactivity:Interaction.Behaviors>
<interactivity:EventTriggerBehavior EventName="PointerEntered">
<interactivity:ChangePropertyAction
PropertyName="Opacity"
TargetObject="{Binding ElementName=RightCommandArea}"
Value=".5" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerExited">
<interactivity:ChangePropertyAction
PropertyName="Opacity"
TargetObject="{Binding ElementName=RightCommandArea}"
Value="0" />
</interactivity:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<Grid
x:Name="LyricsPlaceholderGrid"
Margin="36,0"
SizeChanged="LyricsPlaceholderGrid_SizeChanged" />
<renderer:DesktopLyricsRenderer />
<StackPanel
x:Name="RightCommandArea"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Loaded="RightCommandArea_Loaded"
Orientation="Horizontal">
<StackPanel.OpacityTransition>
<ScalarTransition />
</StackPanel.OpacityTransition>
<Button
x:Name="InAppLyricsSwitchButton"
Click="InAppLyricsSwitchButton_Click"
Style="{StaticResource GhostButtonStyle}">
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xED1E;" />
</Button>
<Button
x:Name="SettingsButton"
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
Style="{StaticResource GhostButtonStyle}">
<FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE713;" />
</Button>
</StackPanel>
</Grid>
</Page>

View File

@@ -1,51 +0,0 @@
using System;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Messages;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace BetterLyrics.WinUI3.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class DesktopLyricsPage : Page
{
public DesktopLyricsPageViewModel ViewModel => (DesktopLyricsPageViewModel)DataContext;
public DesktopLyricsSettingsControlViewModel DesktopLyricsSettingsControlViewModel =>
Ioc.Default.GetService<DesktopLyricsSettingsControlViewModel>()!;
public DesktopLyricsPage()
{
this.InitializeComponent();
DataContext = Ioc.Default.GetService<DesktopLyricsPageViewModel>();
}
private void LyricsPlaceholderGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
ViewModel.LimitedLineWidth = e.NewSize.Width;
}
private void InAppLyricsSwitchButton_Click(object sender, RoutedEventArgs e)
{
var window = WindowHelper.GetWindowForElement(this);
AppBarHelper.Disable(window);
window.Close();
WindowHelper.OpenInAppLyricsWindow();
}
private void RightCommandArea_Loaded(object sender, RoutedEventArgs e)
{
RightCommandArea.Opacity = 0;
}
}
}

View File

@@ -14,7 +14,7 @@
<Grid
x:Name="RootGrid"
KeyDown="RootGrid_KeyDown"
Loaded="RootGrid_Loaded"
RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
<Frame
@@ -27,7 +27,9 @@
Height="{x:Bind ViewModel.TitleBarHeight, Mode=OneWay}"
VerticalAlignment="Top"
Background="Transparent"
Opacity="{x:Bind ViewModel.TopCommandGridOpacity, Mode=OneWay}">
Opacity="0"
PointerEntered="TopCommandGrid_PointerEntered"
PointerExited="TopCommandGrid_PointerExited">
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
@@ -44,15 +46,11 @@
x:Name="AppTitleTextBlock"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
FontWeight="SemiBold"
Opacity=".5"
Text="{x:Bind Title, Mode=OneWay}" />
</StackPanel>
<StackPanel
HorizontalAlignment="Right"
Opacity=".5"
Orientation="Horizontal">
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button x:Name="MoreButton" Style="{StaticResource TitleBarButtonStyle}">
<Grid>
@@ -84,14 +82,14 @@
x:Name="FullScreenFlyoutItem"
x:Uid="BaseWindowFullScreenFlyoutItem"
Click="FullScreenFlyoutItem_Click" />
<MenuFlyoutItem
<ToggleMenuFlyoutItem
x:Name="DockFlyoutItem"
x:Uid="HostWindowDockFlyoutItem"
Click="DockFlyoutItem_Click" />
<ToggleMenuFlyoutItem
x:Name="MiniFlyoutItem"
x:Uid="BaseWindowMiniFlyoutItem"
Click="MiniFlyoutItem_Click" />
<MenuFlyoutItem
x:Name="UnMiniFlyoutItem"
x:Uid="BaseWindowUnMiniFlyoutItem"
Click="UnMiniFlyoutItem_Click" />
</MenuFlyout>
</Button.Flyout>
</Button>

View File

@@ -1,7 +1,7 @@
using System;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Messages;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.Settings;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
@@ -14,6 +14,8 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using WinRT.Interop;
using WinUIEx;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -27,25 +29,53 @@ namespace BetterLyrics.WinUI3.Views
: Window,
IRecipient<PropertyChangedMessage<BackdropType>>
{
public HostWindowViewModel ViewModel { get; set; } =
Ioc.Default.GetService<HostWindowViewModel>()!;
public HostWindowViewModel ViewModel { get; set; }
private readonly ILogger<HostWindow> _logger = Ioc.Default.GetService<
ILogger<HostWindow>
>()!;
private readonly ISettingsService _settingsService =
Ioc.Default.GetRequiredService<ISettingsService>();
public HostWindow()
private readonly bool _listenOnActivatedWindowChange;
public HostWindow(
bool alwaysOnTop = false,
bool clickThrough = false,
bool listenOnActivatedWindowChange = false
)
{
this.InitializeComponent();
ViewModel = Ioc.Default.GetRequiredService<HostWindowViewModel>();
_listenOnActivatedWindowChange = listenOnActivatedWindowChange;
AppWindow.Changed += AppWindow_Changed;
ExtendsContentIntoTitleBar = true;
SetTitleBar(TopCommandGrid);
this.HideSystemTitleBarAndSetCustomTitleBar(TopCommandGrid);
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
if (clickThrough)
this.SetExtendedWindowStyle(
ExtendedWindowStyle.Transparent | ExtendedWindowStyle.Layered
);
SystemBackdrop = new MicaBackdrop();
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(
_settingsService.BackdropType
);
if (alwaysOnTop)
((OverlappedPresenter)AppWindow.Presenter).IsAlwaysOnTop = true;
if (listenOnActivatedWindowChange)
{
var hwnd = WindowNative.GetWindowHandle(this);
var windowWatcher = new ForegroundWindowWatcherHelper(
hwnd,
onWindowChanged =>
{
ViewModel.UpdateAccentColor(hwnd);
}
);
windowWatcher.Start();
}
}
private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
@@ -66,7 +96,14 @@ namespace BetterLyrics.WinUI3.Views
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
Close();
if (RootFrame.SourcePageType == typeof(LyricsPage))
{
Application.Current.Exit();
}
else
{
AppWindow.Hide();
}
}
private void MaximiseButton_Click(object sender, RoutedEventArgs e)
@@ -105,9 +142,8 @@ namespace BetterLyrics.WinUI3.Views
RestoreButton.Visibility =
AOTFlyoutItem.Visibility =
FullScreenFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
DockFlyoutItem.Visibility =
Visibility.Collapsed;
UnMiniFlyoutItem.Visibility = Visibility.Visible;
break;
case AppWindowPresenterKind.FullScreen:
MinimiseButton.Visibility =
@@ -115,30 +151,43 @@ namespace BetterLyrics.WinUI3.Views
RestoreButton.Visibility =
AOTFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
UnMiniFlyoutItem.Visibility =
DockFlyoutItem.Visibility =
Visibility.Collapsed;
FullScreenFlyoutItem.IsChecked = true;
break;
case AppWindowPresenterKind.Overlapped:
DockFlyoutItem.Visibility = Visibility.Visible;
var overlappedPresenter = (OverlappedPresenter)AppWindow.Presenter;
MinimiseButton.Visibility =
AOTFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
FullScreenFlyoutItem.Visibility =
Visibility.Visible;
FullScreenFlyoutItem.IsChecked = false;
AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop;
UnMiniFlyoutItem.Visibility = Visibility.Collapsed;
if (overlappedPresenter.State == OverlappedPresenterState.Maximized)
if (DockFlyoutItem.IsChecked)
{
MaximiseButton.Visibility = Visibility.Collapsed;
RestoreButton.Visibility = Visibility.Visible;
MinimiseButton.Visibility =
MaximiseButton.Visibility =
RestoreButton.Visibility =
AOTFlyoutItem.Visibility =
FullScreenFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
Visibility.Collapsed;
}
else if (overlappedPresenter.State == OverlappedPresenterState.Restored)
else
{
MaximiseButton.Visibility = Visibility.Visible;
RestoreButton.Visibility = Visibility.Collapsed;
MinimiseButton.Visibility =
AOTFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
FullScreenFlyoutItem.Visibility =
Visibility.Visible;
FullScreenFlyoutItem.IsChecked = false;
AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop;
if (overlappedPresenter.State == OverlappedPresenterState.Maximized)
{
MaximiseButton.Visibility = Visibility.Collapsed;
RestoreButton.Visibility = Visibility.Visible;
}
else if (overlappedPresenter.State == OverlappedPresenterState.Restored)
{
MaximiseButton.Visibility = Visibility.Visible;
RestoreButton.Visibility = Visibility.Collapsed;
}
}
break;
default:
@@ -178,28 +227,56 @@ namespace BetterLyrics.WinUI3.Views
}
}
private void RootGrid_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (
AppWindow.Presenter.Kind == AppWindowPresenterKind.FullScreen
&& e.Key == Windows.System.VirtualKey.Escape
)
AppWindow.SetPresenter(AppWindowPresenterKind.Overlapped);
}
private void MiniFlyoutItem_Click(object sender, RoutedEventArgs e)
{
AppWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
}
private void UnMiniFlyoutItem_Click(object sender, RoutedEventArgs e)
{
AppWindow.SetPresenter(AppWindowPresenterKind.Overlapped);
if (MiniFlyoutItem.IsChecked)
{
AppWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
}
else
{
AppWindow.SetPresenter(AppWindowPresenterKind.Overlapped);
}
}
public void Receive(PropertyChangedMessage<BackdropType> message)
{
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(message.NewValue);
}
private void RootGrid_Loaded(object sender, RoutedEventArgs e)
{
if (_listenOnActivatedWindowChange)
{
var hwnd = WindowNative.GetWindowHandle(this);
ViewModel.UpdateAccentColor(hwnd);
}
}
private void DockFlyoutItem_Click(object sender, RoutedEventArgs e)
{
if (DockFlyoutItem.IsChecked)
{
DockHelper.Enable(this, 48);
}
else
{
DockHelper.Disable(this);
}
ViewModel.IsDockMode = DockFlyoutItem.IsChecked;
UpdateTitleBarWindowButtonsVisibility();
}
private void TopCommandGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
{
if (TopCommandGrid.Opacity == 0)
TopCommandGrid.Opacity = .5;
}
private void TopCommandGrid_PointerExited(object sender, PointerRoutedEventArgs e)
{
if (TopCommandGrid.Opacity == .5)
TopCommandGrid.Opacity = 0;
}
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<Page
x:Class="BetterLyrics.WinUI3.Views.InAppLyricsPage"
x:Class="BetterLyrics.WinUI3.Views.LyricsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
@@ -22,7 +22,7 @@
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<renderer:InAppLyricsRenderer />
<renderer:LyricsRenderer />
</Grid>
<Grid Margin="36">
@@ -63,13 +63,13 @@
<!-- Song info area -->
<Grid x:Name="SongInfoInnerGrid">
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<!-- Cover area -->
<RowDefinition Height="20*" />
<!-- Spacer -->
<RowDefinition Height="2*" />
<!-- Cover area -->
<RowDefinition Height="9*" />
<!-- Spacer -->
<RowDefinition Height="*" />
<!-- Title and artist area -->
<RowDefinition Height="5*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Grid.OpacityTransition>
@@ -124,7 +124,10 @@
</Grid>
<!-- Title and artist -->
<StackPanel Grid.Row="3" Orientation="Vertical">
<StackPanel
Grid.Row="3"
VerticalAlignment="Top"
Orientation="Vertical">
<!-- Song title -->
<controls:OpacityMaskView x:Name="TitleOpacityMaskView" HorizontalAlignment="Center">
<controls:OpacityMaskView.OpacityMask>
@@ -241,43 +244,16 @@
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Background="Transparent"
Opacity=".5"
Opacity="0"
PointerEntered="BottomCommandGrid_PointerEntered"
PointerExited="BottomCommandGrid_PointerExited">
PointerExited="BottomCommandGrid_PointerExited"
Visibility="{x:Bind ViewModel.IsNotMockMode, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.IsImmersiveMode, Mode=OneWay}"
ComparisonCondition="Equal"
Value="False">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0.5" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.IsImmersiveMode, Mode=OneWay}"
ComparisonCondition="Equal"
Value="True">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<StackPanel HorizontalAlignment="Right" Spacing="4">
<Button
x:Name="DesktopLyricsToggleButton"
Click="DesktopLyricsToggleButton_Click"
x:Uid="MainPageDesktopLyricsToggler"
Content="{ui:FontIcon Glyph=&#xEB41;}"
Style="{StaticResource GhostButtonStyle}">
<Button.OpacityTransition>
<ScalarTransition />
</Button.OpacityTransition>
</Button>
<Button
x:Name="DisplayTypeSwitchButton"
x:Uid="MainPageDisplayTypeSwitcher"
@@ -315,13 +291,6 @@
</Button.Flyout>
</Button>
<ToggleButton
x:Name="ImmersiveModeButton"
x:Uid="MainWindowImmersiveMode"
Content="{ui:FontIcon Glyph=&#xF131;}"
IsChecked="{x:Bind ViewModel.IsImmersiveMode, Mode=TwoWay}"
Style="{StaticResource GhostToggleButtonStyle}" />
<Button
x:Name="MusicInfoButton"
Content="{ui:FontIcon Glyph=&#xF167;}"
@@ -518,8 +487,6 @@
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="DisplayTypeSwitchButton.Visibility" Value="Visible" />
<Setter Target="DesktopLyricsToggleButton.Visibility" Value="Visible" />
<Setter Target="ImmersiveModeButton.Visibility" Value="Visible" />
<Setter Target="MusicInfoButton.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
@@ -532,8 +499,6 @@
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="DisplayTypeSwitchButton.Visibility" Value="Collapsed" />
<Setter Target="DesktopLyricsToggleButton.Visibility" Value="Collapsed" />
<Setter Target="ImmersiveModeButton.Visibility" Value="Collapsed" />
<Setter Target="MusicInfoButton.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>

View File

@@ -2,8 +2,12 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using WinUIEx;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -13,15 +17,15 @@ namespace BetterLyrics.WinUI3.Views
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class InAppLyricsPage : Page
public sealed partial class LyricsPage : Page
{
public InAppLyricsPageViewModel ViewModel => (InAppLyricsPageViewModel)DataContext;
public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext;
public InAppLyricsPage()
public LyricsPage()
{
this.InitializeComponent();
DataContext = Ioc.Default.GetService<InAppLyricsPageViewModel>();
DataContext = Ioc.Default.GetService<LyricsPageViewModel>();
}
private void WelcomeTeachingTip_Closed(TeachingTip sender, TeachingTipClosedEventArgs args)
@@ -42,7 +46,7 @@ namespace BetterLyrics.WinUI3.Views
Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e
)
{
if (ViewModel.IsImmersiveMode && BottomCommandGrid.Opacity == 0)
if (BottomCommandGrid.Opacity == 0)
BottomCommandGrid.Opacity = .5;
}
@@ -51,7 +55,7 @@ namespace BetterLyrics.WinUI3.Views
Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e
)
{
if (ViewModel.IsImmersiveMode && BottomCommandGrid.Opacity == .5)
if (BottomCommandGrid.Opacity == .5)
BottomCommandGrid.Opacity = 0;
}
@@ -69,11 +73,5 @@ namespace BetterLyrics.WinUI3.Views
{
ViewModel.CoverImageGridActualHeight = e.NewSize.Height;
}
private void DesktopLyricsToggleButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.GetWindowForElement(this).Close();
WindowHelper.OpenDesktopLyricsWindow();
}
}
}

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="BetterLyrics.WinUI3.Views.OverlayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="OverlayWindow"
mc:Ignorable="d">
<Grid x:Name="RootGrid" Loaded="RootGrid_Loaded">
<Frame x:Name="RootFrame" />
</Grid>
</Window>

View File

@@ -1,88 +0,0 @@
using System;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using WinRT.Interop;
using WinUIEx;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace BetterLyrics.WinUI3.Views
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class OverlayWindow : Window
{
public OverlayWindowViewModel ViewModel =>
Ioc.Default.GetRequiredService<OverlayWindowViewModel>();
private readonly bool _listenOnActivatedWindowChange;
public OverlayWindow(
bool alwaysOnTop = true,
bool clickThrough = false,
bool listenOnActivatedWindowChange = false
)
{
this.InitializeComponent();
_listenOnActivatedWindowChange = listenOnActivatedWindowChange;
// Hide titlebar
ExtendsContentIntoTitleBar = true;
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
// Hide border
this.SetWindowStyle(WindowStyle.Popup);
if (clickThrough)
this.SetExtendedWindowStyle(
ExtendedWindowStyle.Transparent | ExtendedWindowStyle.Layered
);
// Hide from taskbar and alt-tab
this.SetIsShownInSwitchers(false);
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
if (alwaysOnTop)
((OverlappedPresenter)AppWindow.Presenter).IsAlwaysOnTop = true;
if (listenOnActivatedWindowChange)
{
var hwnd = WindowNative.GetWindowHandle(this);
var windowWatcher = new ForegroundWindowWatcherHelper(
hwnd,
onWindowChanged =>
{
ViewModel.UpdateAccentColor(hwnd);
}
);
Closed += (_, _) =>
{
windowWatcher.Stop();
};
windowWatcher.Start();
}
}
public void Navigate(Type type)
{
RootFrame.Navigate(type);
}
private void RootGrid_Loaded(object sender, RoutedEventArgs e)
{
if (_listenOnActivatedWindowChange)
{
var hwnd = WindowNative.GetWindowHandle(this);
ViewModel.UpdateAccentColor(hwnd);
}
}
}
}

View File

@@ -45,13 +45,9 @@
Icon="{ui:FontIcon Glyph=&#xE80A;}"
Tag="AlbumArtStyle" />
<NavigationViewItem
x:Uid="SettingsPageInAppLyrics"
x:Uid="SettingsPageLyrics"
Icon="{ui:FontIcon Glyph=&#xED1E;}"
Tag="InAppLyrics" />
<NavigationViewItem
x:Uid="SettingsPageDesktopLyrics"
Icon="{ui:FontIcon Glyph=&#xEB41;}"
Tag="DesktopLyrics" />
Tag="Lyrics" />
<NavigationViewItem
x:Uid="SettingsPageAbout"
Icon="{ui:FontIcon Glyph=&#xE946;}"
@@ -297,12 +293,8 @@
</StackPanel>
</controls:Case>
<controls:Case Value="InAppLyrics">
<uc:LyricsSettingsControl ViewModel="{x:Bind InAppLyricsSettingsControlViewModel}" />
</controls:Case>
<controls:Case Value="DesktopLyrics">
<uc:LyricsSettingsControl ViewModel="{x:Bind DesktopLyricsSettingsControlViewModel}" />
<controls:Case Value="Lyrics">
<uc:LyricsSettingsControl ViewModel="{x:Bind LyricsSettingsControlViewModel}" />
</controls:Case>
<controls:Case Value="About">

View File

@@ -15,11 +15,8 @@ namespace BetterLyrics.WinUI3.Views
public sealed partial class SettingsPage : Page
{
public SettingsViewModel ViewModel => (SettingsViewModel)DataContext;
public InAppLyricsSettingsControlViewModel InAppLyricsSettingsControlViewModel =>
Ioc.Default.GetRequiredService<InAppLyricsSettingsControlViewModel>();
public DesktopLyricsSettingsControlViewModel DesktopLyricsSettingsControlViewModel =>
Ioc.Default.GetRequiredService<DesktopLyricsSettingsControlViewModel>();
public LyricsSettingsControlViewModel LyricsSettingsControlViewModel =>
Ioc.Default.GetRequiredService<LyricsSettingsControlViewModel>();
public SettingsPage()
{

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<Page
x:Class="BetterLyrics.WinUI3.Views.SystemTrayPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tb="using:H.NotifyIcon"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<tb:TaskbarIcon
x:Name="TrayIcon"
x:FieldModifier="public"
ContextMenuMode="SecondWindow"
IconSource="ms-appx:///Assets/Logo.ico"
NoLeftClickDelay="True"
ToolTipText="BetterLyrics">
<tb:TaskbarIcon.ContextFlyout>
<MenuFlyout AreOpenCloseAnimationsEnabled="True" LightDismissOverlayMode="On">
<MenuFlyoutItem Command="{x:Bind ViewModel.OpenSettingsWindowCommand}" Text="Settings" />
<MenuFlyoutItem Command="{x:Bind ViewModel.ExitAppCommand}" Text="Exit" />
</MenuFlyout>
</tb:TaskbarIcon.ContextFlyout>
</tb:TaskbarIcon>
</Page>

View File

@@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace BetterLyrics.WinUI3.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SystemTrayPage : Page
{
public SystemTrayPageViewModel ViewModel => (SystemTrayPageViewModel)DataContext;
public SystemTrayPage()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<SystemTrayPageViewModel>();
}
}
}