Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bca1d1205 |
0
.github/workflows/dotnet-desktop.yml
vendored
Normal file
@@ -11,7 +11,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=Zhe"
|
||||
Version="1.0.4.0" />
|
||||
Version="1.0.3.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -71,12 +71,12 @@ namespace BetterLyrics.WinUI3
|
||||
loggingBuilder.ClearProviders();
|
||||
loggingBuilder.AddSerilog();
|
||||
})
|
||||
// Services
|
||||
// Services (Singleton)
|
||||
.AddSingleton<ISettingsService, SettingsService>()
|
||||
.AddSingleton<IDatabaseService, DatabaseService>()
|
||||
.AddSingleton<IPlaybackService, PlaybackService>()
|
||||
// ViewModels
|
||||
.AddTransient<HostWindowViewModel>()
|
||||
// ViewModels (Singleton)
|
||||
.AddSingleton<HostWindowViewModel>()
|
||||
.AddSingleton<SettingsViewModel>()
|
||||
.AddSingleton<LyricsPageViewModel>()
|
||||
.AddSingleton<LyricsRendererViewModel>()
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\CustomTransform.bin" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Logo.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -50,9 +50,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private static string TestMusicFileName => "AI - 甜度爆表.mp3";
|
||||
public static string TestMusicPath => Path.Combine(AssetsFolder, TestMusicFileName);
|
||||
|
||||
private static string CustomShaderFileName => "CustomTransform.bin";
|
||||
public static string CustomShaderPath => Path.Combine(AssetsFolder, CustomShaderFileName);
|
||||
|
||||
public static void EnsureDirectories()
|
||||
{
|
||||
Directory.CreateDirectory(LogDirectory);
|
||||
|
||||
@@ -177,44 +177,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
int cy,
|
||||
uint uFlags
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// 更改已注册 AppBar 的高度。
|
||||
/// </summary>
|
||||
/// <param name="window">目标窗口</param>
|
||||
/// <param name="newHeight">新的高度</param>
|
||||
public static void UpdateAppBarHeight(IntPtr hwnd, int newHeight)
|
||||
{
|
||||
if (!_registered.Contains(hwnd))
|
||||
return;
|
||||
|
||||
APPBARDATA abd = new()
|
||||
{
|
||||
cbSize = Marshal.SizeOf<APPBARDATA>(),
|
||||
hWnd = hwnd,
|
||||
uEdge = ABE_TOP,
|
||||
rc = new RECT
|
||||
{
|
||||
left = 0,
|
||||
top = 0,
|
||||
right = GetSystemMetrics(SM_CXSCREEN),
|
||||
bottom = newHeight,
|
||||
},
|
||||
};
|
||||
|
||||
SHAppBarMessage(ABM_SETPOS, ref abd);
|
||||
|
||||
// 同步窗口实际高度
|
||||
SetWindowPos(
|
||||
hwnd,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
0,
|
||||
GetSystemMetrics(SM_CXSCREEN),
|
||||
newHeight,
|
||||
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_SHOWWINDOW
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
27
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/MathHelper.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class MathHelper
|
||||
{
|
||||
public static List<int> GetAllFactors(int n)
|
||||
{
|
||||
var result = new SortedSet<int>();
|
||||
|
||||
for (int i = 1; i <= Math.Sqrt(n); i++)
|
||||
{
|
||||
if (n % i == 0)
|
||||
{
|
||||
result.Add(i);
|
||||
result.Add(n / i);
|
||||
}
|
||||
}
|
||||
|
||||
return [.. result];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
internal class NativeHelper
|
||||
{
|
||||
public const int ERROR_SUCCESS = 0;
|
||||
public const int ERROR_INSUFFICIENT_BUFFER = 122;
|
||||
public const int APPMODEL_ERROR_NO_PACKAGE = 15700;
|
||||
|
||||
[DllImport("api-ms-win-appmodel-runtime-l1-1-1", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
internal static extern uint GetCurrentPackageId(ref int pBufferLength, out byte pBuffer);
|
||||
|
||||
public static bool IsAppPackaged
|
||||
{
|
||||
get
|
||||
{
|
||||
int bufferSize = 0;
|
||||
byte byteBuffer = 0;
|
||||
uint lastError = NativeHelper.GetCurrentPackageId(ref bufferSize, out byteBuffer);
|
||||
bool isPackaged = true;
|
||||
|
||||
if (lastError == NativeHelper.APPMODEL_ERROR_NO_PACKAGE)
|
||||
{
|
||||
isPackaged = false;
|
||||
}
|
||||
return isPackaged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
@@ -41,13 +40,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
if (_windowCache.TryGetValue(pageType, out var window))
|
||||
{
|
||||
if (window is HostWindow hostWindow)
|
||||
{
|
||||
hostWindow.Navigate(pageType);
|
||||
}
|
||||
window.TryShow();
|
||||
}
|
||||
else
|
||||
{
|
||||
var newWindow = new HostWindow();
|
||||
TrackWindow(newWindow, pageType);
|
||||
newWindow.ViewModel.FramePageType = pageType;
|
||||
newWindow.Navigate(pageType);
|
||||
newWindow.Activate();
|
||||
}
|
||||
@@ -117,17 +119,5 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
window.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
public static Window GetWindowByFramePageType(Type type)
|
||||
{
|
||||
foreach (var cachedWindow in _windowCache)
|
||||
{
|
||||
if (cachedWindow.Key == type)
|
||||
{
|
||||
return cachedWindow.Value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ namespace BetterLyrics.WinUI3.Services.Database
|
||||
{
|
||||
_connection.DeleteAll<MetadataIndex>();
|
||||
|
||||
HashSet<string> insertedPaths = new();
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
@@ -43,19 +41,16 @@ namespace BetterLyrics.WinUI3.Services.Database
|
||||
var file in Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
|
||||
)
|
||||
{
|
||||
if (!insertedPaths.Contains(file))
|
||||
{
|
||||
var track = new Track(file);
|
||||
_connection.Insert(
|
||||
new MetadataIndex
|
||||
{
|
||||
Path = file,
|
||||
Title = track.Title,
|
||||
Artist = track.Artist,
|
||||
}
|
||||
);
|
||||
insertedPaths.Add(file);
|
||||
}
|
||||
var fileExtension = Path.GetExtension(file);
|
||||
var track = new Track(file);
|
||||
_connection.Insert(
|
||||
new MetadataIndex
|
||||
{
|
||||
Path = file,
|
||||
Title = track.Title,
|
||||
Artist = track.Artist,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,16 @@ namespace BetterLyrics.WinUI3.Services.Settings
|
||||
private const string LyricsLineSpacingFactorKey = "LyricsLineSpacingFactor";
|
||||
private const string LyricsFontSizeKey = "LyricsFontSize";
|
||||
private const string IsLyricsGlowEffectEnabledKey = "IsLyricsGlowEffectEnabled";
|
||||
private const string IsLyricsDynamicGlowEffectEnabledKey =
|
||||
"IsLyricsDynamicGlowEffectEnabled";
|
||||
private const string LyricsFontColorTypeKey = "LyricsFontColorType";
|
||||
private const string LyricsGlowEffectScopeKey = "LyricsGlowEffectScope";
|
||||
|
||||
// Notification
|
||||
private const string NeverShowEnterFullScreenMessageKey = "NeverShowEnterFullScreenMessage";
|
||||
private const string NeverShowEnterImmersiveModeMessageKey =
|
||||
"NeverShowEnterImmersiveModeMessage";
|
||||
|
||||
public bool IsFirstRun
|
||||
{
|
||||
get => GetValue<bool>(IsFirstRunKey);
|
||||
@@ -200,8 +207,11 @@ namespace BetterLyrics.WinUI3.Services.Settings
|
||||
SetDefault(LyricsFontSizeKey, 28);
|
||||
SetDefault(LyricsLineSpacingFactorKey, 0.5f);
|
||||
SetDefault(LyricsVerticalEdgeOpacityKey, 0);
|
||||
SetDefault(IsLyricsGlowEffectEnabledKey, true);
|
||||
SetDefault(IsLyricsGlowEffectEnabledKey, false);
|
||||
SetDefault(LyricsGlowEffectScopeKey, (int)LyricsGlowEffectScope.CurrentChar);
|
||||
// Notification
|
||||
SetDefault(NeverShowEnterFullScreenMessageKey, false);
|
||||
SetDefault(NeverShowEnterImmersiveModeMessageKey, false);
|
||||
}
|
||||
|
||||
private T? GetValue<T>(string key)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BetterInAppLyrics.WinUI3.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Messages;
|
||||
@@ -11,27 +9,16 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using H.NotifyIcon.Interop;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Windows.UI;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
|
||||
namespace BetterLyrics.WinUI3
|
||||
{
|
||||
public partial class HostWindowViewModel
|
||||
: BaseViewModel,
|
||||
IRecipient<PropertyChangedMessage<TitleBarType>>,
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<BackdropType>>,
|
||||
IRecipient<PropertyChangedMessage<int>>
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>
|
||||
{
|
||||
private ForegroundWindowWatcherHelper? _watcherHelper = null;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Type FramePageType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ElementTheme ThemeType { get; set; }
|
||||
|
||||
@@ -89,39 +76,6 @@ namespace BetterLyrics.WinUI3
|
||||
);
|
||||
}
|
||||
|
||||
private void StartWatchWindowColorChange()
|
||||
{
|
||||
var hwnd = WindowNative.GetWindowHandle(
|
||||
WindowHelper.GetWindowByFramePageType(FramePageType)
|
||||
);
|
||||
_watcherHelper = new ForegroundWindowWatcherHelper(
|
||||
hwnd,
|
||||
onWindowChanged =>
|
||||
{
|
||||
UpdateAccentColor(hwnd);
|
||||
}
|
||||
);
|
||||
_watcherHelper.Start();
|
||||
UpdateAccentColor(hwnd);
|
||||
}
|
||||
|
||||
private void StopWatchWindowColorChange()
|
||||
{
|
||||
_watcherHelper?.Stop();
|
||||
_watcherHelper = null;
|
||||
}
|
||||
|
||||
partial void OnFramePageTypeChanged(Type value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
var window = WindowHelper.GetWindowByFramePageType(FramePageType);
|
||||
window.SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(
|
||||
_settingsService.BackdropType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateAccentColor(nint hwnd)
|
||||
{
|
||||
ActivatedWindowAccentColor = WindowColorHelper
|
||||
@@ -162,24 +116,6 @@ namespace BetterLyrics.WinUI3
|
||||
return null;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ToggleDockMode()
|
||||
{
|
||||
var window = WindowHelper.GetWindowByFramePageType(FramePageType);
|
||||
|
||||
IsDockMode = !IsDockMode;
|
||||
if (IsDockMode)
|
||||
{
|
||||
DockHelper.Enable(window, _settingsService.LyricsFontSize * 3);
|
||||
StartWatchWindowColorChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
DockHelper.Disable(window);
|
||||
StopWatchWindowColorChange();
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<TitleBarType> message)
|
||||
{
|
||||
if (message.Sender is SettingsViewModel)
|
||||
@@ -195,30 +131,5 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
ThemeType = message.NewValue;
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<BackdropType> message)
|
||||
{
|
||||
WindowHelper.GetWindowByFramePageType(FramePageType).SystemBackdrop =
|
||||
SystemBackdropHelper.CreateSystemBackdrop(message.NewValue);
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<int> message)
|
||||
{
|
||||
if (message.Sender is LyricsSettingsControlViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsSettingsControlViewModel.LyricsFontSize))
|
||||
{
|
||||
if (IsDockMode)
|
||||
{
|
||||
DockHelper.UpdateAppBarHeight(
|
||||
WindowNative.GetWindowHandle(
|
||||
WindowHelper.GetWindowByFramePageType(FramePageType)
|
||||
),
|
||||
message.NewValue * 3
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
@@ -53,12 +52,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
HorizontalAlignment = CanvasHorizontalAlignment.Left,
|
||||
VerticalAlignment = CanvasVerticalAlignment.Top,
|
||||
//FontFamily = "Segoe UI Mono",
|
||||
};
|
||||
|
||||
public LyricsDisplayType DisplayType { get; set; }
|
||||
|
||||
private float _rotateAngle = 0f;
|
||||
private byte[] _shaderByteCode = File.ReadAllBytes(AppInfo.CustomShaderPath);
|
||||
|
||||
private Color ActivatedWindowAccentColor { get; set; } = Colors.Transparent;
|
||||
|
||||
@@ -345,8 +344,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
var (displayStartLineIndex, displayEndLineIndex) =
|
||||
GetVisibleLyricsLineIndexBoundaries();
|
||||
|
||||
var currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
|
||||
for (
|
||||
int i = displayStartLineIndex;
|
||||
source?.Count > 0 && i >= 0 && i < source?.Count && i <= displayEndLineIndex;
|
||||
@@ -466,8 +463,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
(float)(control.Size.Width - _rightMargin - LimitedLineWidth),
|
||||
_totalYScroll + (float)(control.Size.Height / 2)
|
||||
);
|
||||
// _logger.LogDebug(_totalYScroll);
|
||||
|
||||
ds.DrawTextLayout(textLayout, position, Colors.Transparent);
|
||||
|
||||
// Reset scale
|
||||
ds.Transform = Matrix3x2.Identity;
|
||||
}
|
||||
@@ -516,81 +515,52 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
};
|
||||
}
|
||||
|
||||
private void DrawImmersiveBackground(
|
||||
ICanvasAnimatedControl control,
|
||||
CanvasDrawingSession ds,
|
||||
bool withGradient
|
||||
)
|
||||
{
|
||||
ds.FillRectangle(
|
||||
new Rect(0, 0, control.Size.Width, control.Size.Height),
|
||||
new CanvasLinearGradientBrush(
|
||||
control,
|
||||
[
|
||||
new CanvasGradientStop
|
||||
{
|
||||
Position = 0f,
|
||||
Color = withGradient
|
||||
? Color.FromArgb(
|
||||
211,
|
||||
_currentBgColor.R,
|
||||
_currentBgColor.G,
|
||||
_currentBgColor.B
|
||||
)
|
||||
: _currentBgColor,
|
||||
},
|
||||
new CanvasGradientStop { Position = 1, Color = _currentBgColor },
|
||||
]
|
||||
)
|
||||
{
|
||||
StartPoint = new Vector2(0, 0),
|
||||
EndPoint = new Vector2(0, (float)control.Size.Height),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void DrawAlbumArtBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
bool isAlbumArtOverlayDrawn = IsCoverOverlayEnabled && SoftwareBitmap != null;
|
||||
if (isAlbumArtOverlayDrawn)
|
||||
{
|
||||
DrawAlbumArtBackground(control, ds);
|
||||
}
|
||||
|
||||
if (IsDockMode)
|
||||
{
|
||||
DrawImmersiveBackground(control, ds, isAlbumArtOverlayDrawn);
|
||||
ds.FillRectangle(
|
||||
new Rect(0, 0, control.Size.Width, control.Size.Height),
|
||||
_currentBgColor
|
||||
);
|
||||
}
|
||||
else if (IsCoverOverlayEnabled && SoftwareBitmap != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Original lyrics only layer
|
||||
@@ -667,11 +637,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
using (var glowedLyricsDs = glowedLyrics.CreateDrawingSession())
|
||||
{
|
||||
glowedLyricsDs.DrawImage(
|
||||
new ShadowEffect
|
||||
new GaussianBlurEffect
|
||||
{
|
||||
Source = modifiedLyrics,
|
||||
BlurAmount = _lyricsGlowEffectAmount,
|
||||
ShadowColor = _fontColor,
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
}
|
||||
);
|
||||
@@ -679,11 +649,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
|
||||
// Mock gradient blurred lyrics layer
|
||||
using var blurredLyrics = new CanvasCommandList(control);
|
||||
using var blurredLyricsDs = blurredLyrics.CreateDrawingSession();
|
||||
using var combinedBlurredLyrics = new CanvasCommandList(control);
|
||||
using var combinedBlurredLyricsDs = combinedBlurredLyrics.CreateDrawingSession();
|
||||
if (LyricsBlurAmount == 0)
|
||||
{
|
||||
blurredLyricsDs.DrawImage(glowedLyrics);
|
||||
combinedBlurredLyricsDs.DrawImage(glowedLyrics);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -691,7 +661,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
double overlapFactor = 0;
|
||||
for (double i = 0; i <= 0.5 - step; i += step)
|
||||
{
|
||||
using var halfBlurredLyrics = new GaussianBlurEffect
|
||||
using var blurredLyrics = new GaussianBlurEffect
|
||||
{
|
||||
Source = glowedLyrics,
|
||||
BlurAmount = (float)(LyricsBlurAmount * (1 - i / (0.5 - step))),
|
||||
@@ -700,7 +670,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
};
|
||||
using var topCropped = new CropEffect
|
||||
{
|
||||
Source = halfBlurredLyrics,
|
||||
Source = blurredLyrics,
|
||||
SourceRectangle = new Rect(
|
||||
0,
|
||||
control.Size.Height * i,
|
||||
@@ -710,7 +680,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
};
|
||||
using var bottomCropped = new CropEffect
|
||||
{
|
||||
Source = halfBlurredLyrics,
|
||||
Source = blurredLyrics,
|
||||
SourceRectangle = new Rect(
|
||||
0,
|
||||
control.Size.Height * (1 - i - step * (1 + overlapFactor)),
|
||||
@@ -718,18 +688,21 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
control.Size.Height * step * (1 + overlapFactor)
|
||||
),
|
||||
};
|
||||
blurredLyricsDs.DrawImage(topCropped);
|
||||
blurredLyricsDs.DrawImage(bottomCropped);
|
||||
combinedBlurredLyricsDs.DrawImage(topCropped);
|
||||
combinedBlurredLyricsDs.DrawImage(bottomCropped);
|
||||
}
|
||||
}
|
||||
|
||||
// Masked mock gradient blurred lyrics layer
|
||||
using var maskedBlurredLyrics = new CanvasCommandList(control);
|
||||
using (var maskedBlurredLyricsDs = maskedBlurredLyrics.CreateDrawingSession())
|
||||
using var maskedCombinedBlurredLyrics = new CanvasCommandList(control);
|
||||
using (
|
||||
var maskedCombinedBlurredLyricsDs =
|
||||
maskedCombinedBlurredLyrics.CreateDrawingSession()
|
||||
)
|
||||
{
|
||||
if (LyricsVerticalEdgeOpacity == 100)
|
||||
{
|
||||
maskedBlurredLyricsDs.DrawImage(blurredLyrics);
|
||||
maskedCombinedBlurredLyricsDs.DrawImage(combinedBlurredLyrics);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -738,14 +711,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
DrawGradientOpacityMask(control, maskDs);
|
||||
}
|
||||
maskedBlurredLyricsDs.DrawImage(
|
||||
new AlphaMaskEffect { Source = blurredLyrics, AlphaMask = mask }
|
||||
maskedCombinedBlurredLyricsDs.DrawImage(
|
||||
new AlphaMaskEffect { Source = combinedBlurredLyrics, AlphaMask = mask }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the final composed layer
|
||||
ds.DrawImage(maskedBlurredLyrics);
|
||||
ds.DrawImage(maskedCombinedBlurredLyrics);
|
||||
}
|
||||
|
||||
private void DrawGradientOpacityMask(
|
||||
@@ -858,8 +831,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
int currentPlayingLineIndex = GetCurrentPlayingLineIndex();
|
||||
|
||||
CalculateLinesProps(SongInfo?.LyricsLines, currentPlayingLineIndex, _defaultOpacity);
|
||||
CalculateCanvasYScrollOffset(control, currentPlayingLineIndex);
|
||||
CalculateScaleAndOpacity(
|
||||
SongInfo?.LyricsLines,
|
||||
currentPlayingLineIndex,
|
||||
_defaultOpacity
|
||||
);
|
||||
CalculatePosition(control, currentPlayingLineIndex);
|
||||
|
||||
if (IsLyricsGlowEffectEnabled)
|
||||
{
|
||||
@@ -870,10 +847,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
case LyricsGlowEffectScope.WholeLyrics:
|
||||
break;
|
||||
case LyricsGlowEffectScope.CurrentLine:
|
||||
CalculateLinesProps(_lyricsForGlowEffect, currentPlayingLineIndex, 0);
|
||||
CalculateScaleAndOpacity(_lyricsForGlowEffect, currentPlayingLineIndex, 0);
|
||||
break;
|
||||
case LyricsGlowEffectScope.CurrentChar:
|
||||
CalculateLinesProps(_lyricsForGlowEffect, currentPlayingLineIndex, 0);
|
||||
CalculateScaleAndOpacity(_lyricsForGlowEffect, currentPlayingLineIndex, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -881,7 +858,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateLinesProps(
|
||||
private void CalculateScaleAndOpacity(
|
||||
List<LyricsLine>? source,
|
||||
int currentPlayingLineIndex,
|
||||
float defaultOpacity
|
||||
@@ -979,10 +956,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateCanvasYScrollOffset(
|
||||
ICanvasAnimatedControl control,
|
||||
int currentPlayingLineIndex
|
||||
)
|
||||
private void CalculatePosition(ICanvasAnimatedControl control, int currentPlayingLineIndex)
|
||||
{
|
||||
if (currentPlayingLineIndex < 0)
|
||||
{
|
||||
@@ -1046,7 +1020,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
_startVisibleLineIndex = _endVisibleLineIndex = -1;
|
||||
|
||||
// Update visible line indices
|
||||
// Update Positions
|
||||
for (
|
||||
int i = startLineIndex;
|
||||
i >= 0 && i <= endLineIndex && i < SongInfo?.LyricsLines?.Count;
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="RootGrid" RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
Loaded="RootGrid_Loaded"
|
||||
RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
|
||||
|
||||
<Frame
|
||||
x:Name="RootFrame"
|
||||
@@ -82,8 +85,7 @@
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="DockFlyoutItem"
|
||||
x:Uid="HostWindowDockFlyoutItem"
|
||||
Command="{x:Bind ViewModel.ToggleDockModeCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsDockMode, Mode=OneWay}" />
|
||||
Click="DockFlyoutItem_Click" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="MiniFlyoutItem"
|
||||
x:Uid="BaseWindowMiniFlyoutItem"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using BetterInAppLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Messages;
|
||||
@@ -26,18 +25,31 @@ 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 HostWindow : Window
|
||||
public sealed partial class HostWindow
|
||||
: Window,
|
||||
IRecipient<PropertyChangedMessage<BackdropType>>
|
||||
{
|
||||
public HostWindowViewModel ViewModel { get; private set; } =
|
||||
Ioc.Default.GetRequiredService<HostWindowViewModel>();
|
||||
public HostWindowViewModel ViewModel { get; set; }
|
||||
|
||||
private ForegroundWindowWatcherHelper? _watcherHelper = null;
|
||||
|
||||
private readonly ISettingsService _settingsService =
|
||||
Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
public HostWindow(bool alwaysOnTop = false, bool clickThrough = false)
|
||||
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;
|
||||
|
||||
this.HideSystemTitleBarAndSetCustomTitleBar(TopCommandGrid);
|
||||
@@ -47,8 +59,37 @@ namespace BetterLyrics.WinUI3.Views
|
||||
ExtendedWindowStyle.Transparent | ExtendedWindowStyle.Layered
|
||||
);
|
||||
|
||||
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(
|
||||
_settingsService.BackdropType
|
||||
);
|
||||
|
||||
if (alwaysOnTop)
|
||||
((OverlappedPresenter)AppWindow.Presenter).IsAlwaysOnTop = true;
|
||||
|
||||
if (listenOnActivatedWindowChange)
|
||||
{
|
||||
StartWatchWindowColorChange();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartWatchWindowColorChange()
|
||||
{
|
||||
var hwnd = WindowNative.GetWindowHandle(this);
|
||||
_watcherHelper = new ForegroundWindowWatcherHelper(
|
||||
hwnd,
|
||||
onWindowChanged =>
|
||||
{
|
||||
ViewModel.UpdateAccentColor(hwnd);
|
||||
}
|
||||
);
|
||||
_watcherHelper.Start();
|
||||
ViewModel.UpdateAccentColor(hwnd);
|
||||
}
|
||||
|
||||
private void StopWatchWindowColorChange()
|
||||
{
|
||||
_watcherHelper?.Stop();
|
||||
_watcherHelper = null;
|
||||
}
|
||||
|
||||
private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
|
||||
@@ -178,7 +219,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
if (_settingsService.AutoStartWindowType == AutoStartWindowType.DockMode)
|
||||
{
|
||||
DockFlyoutItem.IsChecked = true;
|
||||
ViewModel.ToggleDockModeCommand.Execute(null);
|
||||
DockFlyoutItem_Click(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,6 +261,36 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
StartWatchWindowColorChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
DockHelper.Disable(this);
|
||||
StopWatchWindowColorChange();
|
||||
}
|
||||
ViewModel.IsDockMode = DockFlyoutItem.IsChecked;
|
||||
UpdateTitleBarWindowButtonsVisibility();
|
||||
}
|
||||
|
||||
private void TopCommandGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (TopCommandGrid.Opacity == 0)
|
||||
|
||||
34
README.md
@@ -36,24 +36,40 @@ We provide more than one setting item to better align with your preference
|
||||
|
||||
- Language (English, Simplified Chinese, Traditional Chinese)
|
||||
|
||||
## Live demonstration
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
> **Highlighted feature**: Immersive lyrics shown on top of the screen (automatically follow current activated windows's accent color)
|
||||
|
||||
Or watch our introduction video「BetterLyrics 阶段性开发成果展示」(uploaded on 31 May 2025) on Bilibili [here](https://b23.tv/QjKkYmL).
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
### In-app lyrics
|
||||
|
||||

|
||||
Non-immersive mode
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
Immersive mode
|
||||

|
||||
|
||||

|
||||
Lyrics only
|
||||
|
||||

|
||||

|
||||
|
||||
## Demonstration
|
||||
Fullscreen
|
||||
|
||||
Watch our introduction video「BetterLyrics 阶段性开发成果展示」(uploaded on 31 May 2025) on Bilibili [here](https://b23.tv/QjKkYmL).
|
||||

|
||||
|
||||
### Desktop lyrics
|
||||
|
||||

|
||||
|
||||
## Try it now
|
||||
|
||||
@@ -110,7 +126,7 @@ So technically, as long as you are using the music apps (like
|
||||
|
||||
## Inspired by
|
||||
|
||||
- [refined-now-playing-netease](https://github.com/solstice23/refined-now-playing-netease)
|
||||
- [BetterNCM](https://github.com/std-microblock/BetterNCM)
|
||||
- [Lyricify-App](https://github.com/WXRIW/Lyricify-App)
|
||||
- [椒盐音乐 Salt Player](https://moriafly.com/program/salt-player)
|
||||
- [MyToolBar](https://github.com/TwilightLemon/MyToolBar)
|
||||
|
||||
|
After Width: | Height: | Size: 6.1 MiB |
BIN
Screenshots/BetterLyrics.WinUI3_igDdnc4rzW.gif
Normal file
|
After Width: | Height: | Size: 44 MiB |
BIN
Screenshots/Code_HhDCqJZYEZ.gif
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
Screenshots/Snipaste_2025-06-03_16-47-43.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
Screenshots/Snipaste_2025-06-03_17-51-22.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
Screenshots/Snipaste_2025-06-03_17-52-51.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
Screenshots/Snipaste_2025-06-03_17-53-07.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
Screenshots/Snipaste_2025-06-03_18-36-05.png
Normal file
|
After Width: | Height: | Size: 459 KiB |
BIN
Screenshots/Snipaste_2025-06-07_17-32-02.png
Normal file
|
After Width: | Height: | Size: 372 KiB |
BIN
Screenshots/Snipaste_2025-06-07_17-32-17.png
Normal file
|
After Width: | Height: | Size: 394 KiB |
BIN
Screenshots/Snipaste_2025-06-07_17-32-23.png
Normal file
|
After Width: | Height: | Size: 403 KiB |
BIN
Screenshots/Snipaste_2025-06-07_17-36-26.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 544 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
BIN
Screenshots/image.png
Normal file
|
After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 947 KiB |