diff --git a/SpineViewer/Models/PreferenceModel.cs b/SpineViewer/Models/PreferenceModel.cs index da94a82..635ec10 100644 --- a/SpineViewer/Models/PreferenceModel.cs +++ b/SpineViewer/Models/PreferenceModel.cs @@ -73,6 +73,9 @@ namespace SpineViewer.Models #region 程序选项 + [ObservableProperty] + private bool _wallpaperView; + [ObservableProperty] private bool _renderSelectedOnly; diff --git a/SpineViewer/Resources/Strings/en.xaml b/SpineViewer/Resources/Strings/en.xaml index 7cdda02..9d7a0d5 100644 --- a/SpineViewer/Resources/Strings/en.xaml +++ b/SpineViewer/Resources/Strings/en.xaml @@ -118,6 +118,7 @@ Max FPS Maximum frame rate of the preview. Set to 0 for no limit. Playback Speed + Wallpaper View Render Selected Only Show Axis Background Color diff --git a/SpineViewer/Resources/Strings/ja.xaml b/SpineViewer/Resources/Strings/ja.xaml index 5f4ef84..e1fb869 100644 --- a/SpineViewer/Resources/Strings/ja.xaml +++ b/SpineViewer/Resources/Strings/ja.xaml @@ -118,6 +118,7 @@ 最大FPS プレビュー画面の最大フレームレート。0 に設定すると制限なし。 再生速度 + 壁紙表示 選択のみレンダリング 座標軸を表示 背景色 diff --git a/SpineViewer/Resources/Strings/zh.xaml b/SpineViewer/Resources/Strings/zh.xaml index 793fd7e..05f485d 100644 --- a/SpineViewer/Resources/Strings/zh.xaml +++ b/SpineViewer/Resources/Strings/zh.xaml @@ -118,6 +118,7 @@ 最大帧率 预览画面的最大帧率,设置为 0 时则无帧率限制 播放速度 + 桌面投影 仅渲染选中 显示坐标轴 背景颜色 diff --git a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs index 656f36c..044e33a 100644 --- a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs @@ -77,7 +77,15 @@ namespace SpineViewer.ViewModels.MainWindow public SFMLRendererViewModel SFMLRendererViewModel => _sfmlRendererViewModel; private readonly SFMLRendererViewModel _sfmlRendererViewModel; - public RelayCommand Cmd_Exit => new(App.Current.Shutdown); + public RelayCommand Cmd_SwitchWallpaperView => _cmd_SwitchWallpaperView ??= new(() => + { + _preferenceViewModel.WallpaperView = !_preferenceViewModel.WallpaperView; + _preferenceViewModel.SavePreference(); + }); + private RelayCommand _cmd_SwitchWallpaperView; + + public RelayCommand Cmd_Exit => _cmd_Exit ??= new(App.Current.Shutdown); + private RelayCommand? _cmd_Exit; /// /// 打开工作区 @@ -137,5 +145,6 @@ namespace SpineViewer.ViewModels.MainWindow _spineObjectListViewModel.LoadedSpineObjects = value.LoadedSpineObjects; } } + } } \ No newline at end of file diff --git a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs index a5e3adc..bcdefb7 100644 --- a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs @@ -111,6 +111,7 @@ namespace SpineViewer.ViewModels.MainWindow DebugPoints = DebugPoints, DebugClippings = DebugClippings, + WallpaperView = WallpaperView, RenderSelectedOnly = RenderSelectedOnly, AssociateFileSuffix = AssociateFileSuffix, AppLanguage = AppLanguage, @@ -136,6 +137,7 @@ namespace SpineViewer.ViewModels.MainWindow DebugPoints = value.DebugPoints; DebugClippings = value.DebugClippings; + WallpaperView = value.WallpaperView; RenderSelectedOnly = value.RenderSelectedOnly; AssociateFileSuffix = value.AssociateFileSuffix; AppLanguage = value.AppLanguage; @@ -244,6 +246,19 @@ namespace SpineViewer.ViewModels.MainWindow public static ImmutableArray AppLanguageOptions { get; } = Enum.GetValues().ToImmutableArray(); + public bool AutoRun + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public bool WallpaperView + { + get => _wallpaperView; + set => SetProperty(ref _wallpaperView, value); + } + private bool _wallpaperView; // UI 变化通过 PropertyChanged 事件交由 View 层处理 + public bool RenderSelectedOnly { get => _vmMain.SFMLRendererViewModel.RenderSelectedOnly; diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml index d6d2e6d..ffde3f7 100644 --- a/SpineViewer/Views/MainWindow.xaml +++ b/SpineViewer/Views/MainWindow.xaml @@ -940,7 +940,7 @@ MouseDoubleClick="_notifyIcon_MouseDoubleClick"> - + diff --git a/SpineViewer/Views/MainWindow.xaml.cs b/SpineViewer/Views/MainWindow.xaml.cs index b65e3cf..65e2b60 100644 --- a/SpineViewer/Views/MainWindow.xaml.cs +++ b/SpineViewer/Views/MainWindow.xaml.cs @@ -12,6 +12,7 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.IO; +using System.Reflection.Metadata; using System.Text; using System.Windows; using System.Windows.Controls; @@ -47,67 +48,28 @@ public partial class MainWindow : Window InitializeComponent(); InitializeLogConfiguration(); - _wallpaperRenderWindow = new(new(500, 500), "SpineViewerWallpaper", SFML.Window.Styles.None); + // Initialize Wallpaper RenderWindow + _wallpaperRenderWindow = new(new(1, 1), "SpineViewerWallpaper", SFML.Window.Styles.None); _wallpaperRenderWindow.SetVisible(false); + var handle = _wallpaperRenderWindow.SystemHandle; + var style = User32.GetWindowLong(handle, User32.GWL_STYLE) | User32.WS_POPUP; + var exStyle = User32.GetWindowLong(handle, User32.GWL_EXSTYLE) | User32.WS_EX_LAYERED | User32.WS_EX_TOOLWINDOW; + User32.SetWindowLong(handle, User32.GWL_STYLE, style); + User32.SetWindowLong(handle, User32.GWL_EXSTYLE, exStyle); + User32.SetLayeredWindowAttributes(handle, 0, byte.MaxValue, User32.LWA_ALPHA); + DataContext = _vm = new(_renderPanel, _wallpaperRenderWindow); // XXX: hc 的 NotifyIcon 的 Text 似乎没法双向绑定 _notifyIcon.Text = _vm.Title; - _vm.SpineObjectListViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging; - _vm.SFMLRendererViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging; Loaded += MainWindow_Loaded; ContentRendered += MainWindow_ContentRendered; Closed += MainWindow_Closed; - } - private void MainWindow_Loaded(object sender, RoutedEventArgs e) - { - var vm = _vm.SFMLRendererViewModel; - _renderPanel.CanvasMouseWheelScrolled += vm.CanvasMouseWheelScrolled; - _renderPanel.CanvasMouseButtonPressed += (s, e) => { vm.CanvasMouseButtonPressed(s, e); _spinesListView.Focus(); }; // 用户点击画布后强制转移焦点至列表 - _renderPanel.CanvasMouseMove += vm.CanvasMouseMove; - _renderPanel.CanvasMouseButtonReleased += vm.CanvasMouseButtonReleased; - - // 设置默认参数并启动渲染 - vm.SetResolution(1500, 1000); - vm.Zoom = 0.75f; - vm.CenterX = 0; - vm.CenterY = 0; - vm.FlipY = true; - vm.MaxFps = 30; - vm.StartRender(); - - // 加载首选项 - _vm.PreferenceViewModel.LoadPreference(); - - LoadLastState(); - } - - private void MainWindow_ContentRendered(object? sender, EventArgs e) - { - string[] args = Environment.GetCommandLineArgs(); - if (args.Length > 1) - { - string[] filePaths = args.Skip(1).ToArray(); - _vm.SpineObjectListViewModel.AddSpineObjectFromFileList(filePaths); - } - } - - private void MainWindow_Closed(object? sender, EventArgs e) - { - SaveLastState(); - - var vm = _vm.SFMLRendererViewModel; - vm.StopRender(); - } - - /// - /// 给管道通信提供的打开文件外部调用方法 - /// - public void OpenFiles(IEnumerable filePaths) - { - _vm.SpineObjectListViewModel.AddSpineObjectFromFileList(filePaths); + _vm.SpineObjectListViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging; + _vm.SFMLRendererViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging; + _vm.PreferenceViewModel.PropertyChanged += PreferenceViewModel_PropertyChanged; } /// @@ -195,6 +157,95 @@ public partial class MainWindow : Window JsonHelper.Serialize(m, LastStateFilePath); } + /// + /// 给管道通信提供的打开文件外部调用方法 + /// + public void OpenFiles(IEnumerable filePaths) + { + _vm.SpineObjectListViewModel.AddSpineObjectFromFileList(filePaths); + } + + #region MainWindow 事件处理 + + private void MainWindow_Loaded(object sender, RoutedEventArgs e) + { + var vm = _vm.SFMLRendererViewModel; + _renderPanel.CanvasMouseWheelScrolled += vm.CanvasMouseWheelScrolled; + _renderPanel.CanvasMouseButtonPressed += (s, e) => { vm.CanvasMouseButtonPressed(s, e); _spinesListView.Focus(); }; // 用户点击画布后强制转移焦点至列表 + _renderPanel.CanvasMouseMove += vm.CanvasMouseMove; + _renderPanel.CanvasMouseButtonReleased += vm.CanvasMouseButtonReleased; + + // 设置默认参数并启动渲染 + vm.SetResolution(1500, 1000); + vm.Zoom = 0.75f; + vm.CenterX = 0; + vm.CenterY = 0; + vm.FlipY = true; + vm.MaxFps = 30; + vm.StartRender(); + + // 加载首选项 + _vm.PreferenceViewModel.LoadPreference(); + + LoadLastState(); + } + + private void MainWindow_ContentRendered(object? sender, EventArgs e) + { + string[] args = Environment.GetCommandLineArgs(); + if (args.Length > 1) + { + string[] filePaths = args.Skip(1).ToArray(); + _vm.SpineObjectListViewModel.AddSpineObjectFromFileList(filePaths); + } + } + + private void MainWindow_Closed(object? sender, EventArgs e) + { + SaveLastState(); + + var vm = _vm.SFMLRendererViewModel; + vm.StopRender(); + } + + #endregion + + #region PreferenceViewModel 事件处理 + + private void PreferenceViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(PreferenceViewModel.WallpaperView)) + { + if (_vm.PreferenceViewModel.WallpaperView) + { + var workerw = User32.GetWorkerW(); + if (workerw == IntPtr.Zero) + { + _logger.Error("Failed to enable wallpaper view, WorkerW not found"); + return; + } + var wnd = _wallpaperRenderWindow; + var handle = wnd.SystemHandle; + + User32.GetPrimaryScreenResolution(out var sw, out var sh); + + User32.SetParent(handle, workerw); + User32.SetLayeredWindowAttributes(handle, 0, byte.MaxValue, User32.LWA_ALPHA); + + wnd.Position = new(0, 0); + wnd.Size = new(sw + 1, sh); + wnd.Size = new(sw, sh); + wnd.SetVisible(true); + } + else + { + _wallpaperRenderWindow.SetVisible(false); + } + } + } + + #endregion + #region _spinesListView 事件处理 private void SpinesListView_RequestSelectionChanging(object? sender, NotifyCollectionChangedEventArgs e) @@ -609,29 +660,7 @@ public partial class MainWindow : Window private void DebugMenuItem_Click(object sender, RoutedEventArgs e) { #if DEBUG - var www = _wallpaperRenderWindow; - User32.GetPrimaryScreenResolution(out var sw, out var sh); - www.Position = new(0, 0); - www.Size = new(sw, sh); - var handle = www.SystemHandle; - Debug.WriteLine($"Handle: {handle:x8}"); - - var style = User32.GetWindowLong(handle, User32.GWL_STYLE) | User32.WS_POPUP; - var exStyle = User32.GetWindowLong(handle, User32.GWL_EXSTYLE) | User32.WS_EX_LAYERED | User32.WS_EX_TOOLWINDOW | User32.WS_EX_TOPMOST; - User32.SetWindowLong(handle, User32.GWL_STYLE, style); - User32.SetWindowLong(handle, User32.GWL_EXSTYLE, exStyle); - User32.SetLayeredWindowAttributes(handle, 0, 200, User32.LWA_ALPHA); - - var workerw = User32.GetWorkerW(); - var res = User32.SetParent(handle, workerw); - Debug.WriteLine($"SetParent: {res:x8}"); - - User32.SetLayeredWindowAttributes(handle, 0, 255, User32.LWA_ALPHA); - var s = www.Size; - www.Size = new(s.X + 1, s.Y + 1); - www.Size = s; - www.SetVisible(true); #endif } } \ No newline at end of file diff --git a/SpineViewer/Views/PreferenceDialog.xaml b/SpineViewer/Views/PreferenceDialog.xaml index 37723e4..644cf8a 100644 --- a/SpineViewer/Views/PreferenceDialog.xaml +++ b/SpineViewer/Views/PreferenceDialog.xaml @@ -144,16 +144,20 @@ + -