From 5aaca437afa1b60b2761994a67fe8e89ba532577 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Wed, 28 May 2025 18:43:42 +0800 Subject: [PATCH 01/27] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E5=8F=91=E5=B8=83=E5=B7=A5=E4=BD=9C=E6=B5=81=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E7=89=88=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/dotnet-desktop.yml | 70 +++++++++++++++++++++------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index 2d10d62..b2b3ff6 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -1,46 +1,80 @@ name: Build & Release on: - push: - tags: - - 'v*.*.*' + pull_request: + branches: + - main + types: + - closed jobs: build-release: + if: ${{ github.event.pull_request.merged == true }} runs-on: windows-latest env: PROJECT_NAME: SpineViewer - VERSION: ${{ github.ref_name }} + steps: - name: Checkout code uses: actions/checkout@v3 - + with: + fetch-tags: true + - name: Setup .NET SDK uses: actions/setup-dotnet@v3 with: - dotnet-version: '8.0.x' + dotnet-version: "8.0.x" + + - name: Extract version from csproj + shell: pwsh + run: | + [xml]$proj = Get-Content "$env:PROJECT_NAME\$env:PROJECT_NAME.csproj" + $VERSION_NUM = $proj.Project.PropertyGroup.Version + $VERSION_TAG = "v$VERSION_NUM" + "VERSION=$VERSION_TAG" >> $env:GITHUB_ENV + + - name: Check Version Tag + shell: pwsh + run: | + if (-not $env:VERSION) { + Write-Error "Version tag not found in csproj file." + exit 1 + } + Write-Host "Version tag found: $env:VERSION" + + - name: Tag merge commit + shell: pwsh + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag $env:VERSION + git push --tags - name: Publish FrameworkDependent version + shell: pwsh run: | - dotnet publish ${{ env.PROJECT_NAME }}/${{ env.PROJECT_NAME }}.csproj -c Release -r win-x64 --sc false -o publish/${{ env.PROJECT_NAME }}-${{ env.VERSION }} - + dotnet publish "$env:PROJECT_NAME\$env:PROJECT_NAME.csproj" -c Release -r win-x64 --sc false -o "publish\$env:PROJECT_NAME-$env:VERSION" + - name: Publish SelfContained version + shell: pwsh run: | - dotnet publish ${{ env.PROJECT_NAME }}/${{ env.PROJECT_NAME }}.csproj -c Release -r win-x64 --sc true -o publish/${{ env.PROJECT_NAME }}-${{ env.VERSION }}-SelfContained - + dotnet publish "$env:PROJECT_NAME\$env:PROJECT_NAME.csproj" -c Release -r win-x64 --sc true -o "publish\$env:PROJECT_NAME-$env:VERSION-SelfContained" + - name: Create release directory - run: mkdir release - + shell: pwsh + run: | + New-Item -ItemType Directory -Path release -Force | Out-Null + - name: Compress FrameworkDependent version shell: pwsh run: | - Compress-Archive -Path "publish/${env:PROJECT_NAME}-${env:VERSION}" -DestinationPath "release/${env:PROJECT_NAME}-${env:VERSION}.zip" -Force - + Compress-Archive -Path "publish\$env:PROJECT_NAME-$env:VERSION\*" -DestinationPath "release\$env:PROJECT_NAME-$env:VERSION.zip" -Force + - name: Compress SelfContained version shell: pwsh run: | - Compress-Archive -Path "publish/${env:PROJECT_NAME}-${env:VERSION}-SelfContained" -DestinationPath "release/${env:PROJECT_NAME}-${env:VERSION}-SelfContained.zip" -Force - + Compress-Archive -Path "publish\$env:PROJECT_NAME-$env:VERSION-SelfContained\*" -DestinationPath "release\$env:PROJECT_NAME-$env:VERSION-SelfContained.zip" -Force + - name: Create GitHub Release id: create_release uses: actions/create-release@v1 @@ -51,7 +85,7 @@ jobs: release_name: Release ${{ env.VERSION }} draft: false prerelease: false - + - name: Upload FrameworkDependent zip uses: actions/upload-release-asset@v1 env: @@ -61,7 +95,7 @@ jobs: asset_path: release/${{ env.PROJECT_NAME }}-${{ env.VERSION }}.zip asset_name: ${{ env.PROJECT_NAME }}-${{ env.VERSION }}.zip asset_content_type: application/zip - + - name: Upload SelfContained zip uses: actions/upload-release-asset@v1 env: From 550dafb2c21409f471437429cf4ecf44b22e1045 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Wed, 28 May 2025 19:37:22 +0800 Subject: [PATCH 02/27] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E9=80=BB=E8=BE=91=E5=92=8C=E9=80=89=E4=B8=AD?= =?UTF-8?q?=E6=97=B6=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Spine/SpineObject.cs | 10 ++------- SpineViewer/Models/SpineObjectModel.cs | 4 ++-- .../ViewModels/SFMLRendererViewModel.cs | 22 +++++++++++++++++++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Spine/SpineObject.cs b/Spine/SpineObject.cs index d5735d7..1c13496 100644 --- a/Spine/SpineObject.cs +++ b/Spine/SpineObject.cs @@ -177,7 +177,6 @@ namespace Spine // 拷贝调试属性 EnableDebug = other.EnableDebug; DebugTexture = other.DebugTexture; - DebugNonTexture = other.DebugNonTexture; DebugBounds = other.DebugBounds; DebugBones = other.DebugBones; DebugRegions = other.DebugRegions; @@ -253,15 +252,10 @@ namespace Spine /// public bool DebugTexture { get; set; } = true; - /// - /// 是否显示非纹理内容, 一个总开关 - /// - public bool DebugNonTexture { get; set; } = true; - /// /// 显示包围盒 /// - public bool DebugBounds { get; set; } = true; + public bool DebugBounds { get; set; } = false; /// /// 显示骨骼 @@ -864,7 +858,7 @@ namespace Spine else { if (DebugTexture) DrawTexture(target, states); - if (DebugNonTexture) DrawNonTexture(target); + DrawNonTexture(target); } } diff --git a/SpineViewer/Models/SpineObjectModel.cs b/SpineViewer/Models/SpineObjectModel.cs index 3a8c549..116d266 100644 --- a/SpineViewer/Models/SpineObjectModel.cs +++ b/SpineViewer/Models/SpineObjectModel.cs @@ -42,7 +42,7 @@ namespace SpineViewer.Models /// public SpineObjectModel(string skelPath, string? atlasPath = null, SpineVersion? version = null) { - _spineObject = new(skelPath, atlasPath, version) { DebugNonTexture = false }; + _spineObject = new(skelPath, atlasPath, version); _skins = _spineObject.Data.Skins.Select(v => v.Name).ToImmutableArray(); _slotAttachments = _spineObject.Data.SlotAttachments.ToFrozenDictionary(it => it.Key, it => it.Value.Keys); _animations = _spineObject.Data.Animations.Select(v => v.Name).ToImmutableArray(); @@ -73,7 +73,7 @@ namespace SpineViewer.Models public bool IsSelected { get { lock (_lock) return _isSelected; } - set { lock (_lock) if (SetProperty(ref _isSelected, value)) _spineObject.DebugNonTexture = _isSelected; } + set { lock (_lock) SetProperty(ref _isSelected, value); } } private bool _isSelected = false; diff --git a/SpineViewer/ViewModels/SFMLRendererViewModel.cs b/SpineViewer/ViewModels/SFMLRendererViewModel.cs index 244d6e6..15f53ac 100644 --- a/SpineViewer/ViewModels/SFMLRendererViewModel.cs +++ b/SpineViewer/ViewModels/SFMLRendererViewModel.cs @@ -31,6 +31,16 @@ namespace SpineViewer.ViewModels private readonly ObservableCollectionWithLock _models; private readonly ISFMLRenderer _renderer; + /// + /// 被选中对象的背景颜色 + /// + private static readonly SFML.Graphics.Color _selectedBackgroundColor = new(255, 255, 255, 50); + + /// + /// 被选中对象背景顶点缓冲区 + /// + private readonly SFML.Graphics.VertexArray _selectedBackgroundVertices = new(SFML.Graphics.PrimitiveType.Quads, 4); // XXX: 暂时未使用 Dispose 释放 + /// /// 预览画面坐标轴颜色 /// @@ -381,6 +391,18 @@ namespace SpineViewer.ViewModels sp.Update(0); // 避免物理效果出现问题 sp.Update(delta); + // 为选中对象绘制一个半透明背景 + if (sp.IsSelected) + { + var rc = sp.GetCurrentBounds().ToFloatRect(); + _selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor); + _selectedBackgroundVertices[1] = new(new(rc.Left + rc.Width, rc.Top), _selectedBackgroundColor); + _selectedBackgroundVertices[2] = new(new(rc.Left + rc.Width, rc.Top + rc.Height), _selectedBackgroundColor); + _selectedBackgroundVertices[3] = new(new(rc.Left, rc.Top + rc.Height), _selectedBackgroundColor); + _renderer.Draw(_selectedBackgroundVertices); + } + + // 仅在预览画面临时启用调试模式 sp.EnableDebug = true; _renderer.Draw(sp); sp.EnableDebug = false; From 54f9a054cf1e229031de0121971c0d508ed1773d Mon Sep 17 00:00:00 2001 From: ww-rm Date: Wed, 28 May 2025 19:47:43 +0800 Subject: [PATCH 03/27] =?UTF-8?q?=E6=A0=87=E9=A2=98=E5=A2=9E=E5=8A=A0v?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/ViewModels/MainWindowViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpineViewer/ViewModels/MainWindowViewModel.cs b/SpineViewer/ViewModels/MainWindowViewModel.cs index 532a0d9..3a13a29 100644 --- a/SpineViewer/ViewModels/MainWindowViewModel.cs +++ b/SpineViewer/ViewModels/MainWindowViewModel.cs @@ -36,7 +36,7 @@ namespace SpineViewer.ViewModels _sfmlRendererViewModel = new(this); } - public string Title => $"SpineViewer - {App.Version}"; + public string Title => $"SpineViewer - v{App.Version}"; /// /// SFML 渲染对象 From 707bdf7d33c9434fee77b09aedc4d222aca61f48 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 29 May 2025 19:36:15 +0800 Subject: [PATCH 04/27] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BC=B9=E6=A1=86?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/SpineViewer.csproj | 1 + SpineViewer/Views/MessageBoxDialog.xaml | 12 ---- SpineViewer/Views/MessageBoxDialog.xaml.cs | 27 -------- SpineViewer/app.manifest | 79 ++++++++++++++++++++++ 4 files changed, 80 insertions(+), 39 deletions(-) delete mode 100644 SpineViewer/Views/MessageBoxDialog.xaml delete mode 100644 SpineViewer/Views/MessageBoxDialog.xaml.cs create mode 100644 SpineViewer/app.manifest diff --git a/SpineViewer/SpineViewer.csproj b/SpineViewer/SpineViewer.csproj index a76949f..eebcf1d 100644 --- a/SpineViewer/SpineViewer.csproj +++ b/SpineViewer/SpineViewer.csproj @@ -16,6 +16,7 @@ $(NoWarn);NETSDK1206 appicon.ico + app.manifest diff --git a/SpineViewer/Views/MessageBoxDialog.xaml b/SpineViewer/Views/MessageBoxDialog.xaml deleted file mode 100644 index 571b190..0000000 --- a/SpineViewer/Views/MessageBoxDialog.xaml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/SpineViewer/Views/MessageBoxDialog.xaml.cs b/SpineViewer/Views/MessageBoxDialog.xaml.cs deleted file mode 100644 index 4180e0d..0000000 --- a/SpineViewer/Views/MessageBoxDialog.xaml.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; - -namespace SpineViewer.Views -{ - /// - /// MessageBoxWindow.xaml 的交互逻辑 - /// - public partial class MessageBoxWindow : Window - { - public MessageBoxWindow() - { - InitializeComponent(); - } - } -} diff --git a/SpineViewer/app.manifest b/SpineViewer/app.manifest new file mode 100644 index 0000000..7271022 --- /dev/null +++ b/SpineViewer/app.manifest @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From fffe69c49f94903c9e45f850aaacba8db602f85e Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 29 May 2025 19:49:06 +0800 Subject: [PATCH 05/27] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Exporters/BaseExporterViewModel.cs | 1 + .../CustomFFmpegExporterViewModel.cs | 1 + .../Exporters/FFmpegVideoExporterViewModel.cs | 1 + .../Exporters/FrameExporterViewModel.cs | 1 + .../FrameSequenceExporterViewModel.cs | 1 + .../Exporters/VideoExporterViewModel.cs | 1 + .../{ => MainWindow}/ExplorerListViewModel.cs | 3 +-- .../{ => MainWindow}/MainWindowViewModel.cs | 27 ++----------------- .../{ => MainWindow}/SFMLRendererViewModel.cs | 2 +- .../SpineObjectListViewModel.cs | 2 +- .../SpineObjectTabViewModel.cs | 4 +-- SpineViewer/Views/MainWindow.xaml | 2 +- SpineViewer/Views/MainWindow.xaml.cs | 2 +- 13 files changed, 15 insertions(+), 33 deletions(-) rename SpineViewer/ViewModels/{ => MainWindow}/ExplorerListViewModel.cs (99%) rename SpineViewer/ViewModels/{ => MainWindow}/MainWindowViewModel.cs (77%) rename SpineViewer/ViewModels/{ => MainWindow}/SFMLRendererViewModel.cs (99%) rename SpineViewer/ViewModels/{ => MainWindow}/SpineObjectListViewModel.cs (99%) rename SpineViewer/ViewModels/{ => MainWindow}/SpineObjectTabViewModel.cs (99%) diff --git a/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs index 0c3bbd2..a53c7e8 100644 --- a/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs @@ -6,6 +6,7 @@ using Spine; using Spine.Exporters; using SpineViewer.Extensions; using SpineViewer.Resources; +using SpineViewer.ViewModels.MainWindow; using System; using System.Collections; using System.Collections.Generic; diff --git a/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs index 685e84d..f846bd3 100644 --- a/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; +using SpineViewer.ViewModels.MainWindow; namespace SpineViewer.ViewModels.Exporters { diff --git a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs index 653dc16..860265c 100644 --- a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs @@ -12,6 +12,7 @@ using System.Text; using System.Threading.Tasks; using System.IO; using System.Collections.Immutable; +using SpineViewer.ViewModels.MainWindow; namespace SpineViewer.ViewModels.Exporters { diff --git a/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs index 16b0b0b..f65bee5 100644 --- a/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs @@ -6,6 +6,7 @@ using SpineViewer.Extensions; using SpineViewer.Models; using SpineViewer.Resources; using SpineViewer.Services; +using SpineViewer.ViewModels.MainWindow; using System; using System.Collections; using System.Collections.Generic; diff --git a/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs index 4c23ef3..09f00fd 100644 --- a/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs @@ -4,6 +4,7 @@ using SpineViewer.Extensions; using SpineViewer.Models; using SpineViewer.Resources; using SpineViewer.Services; +using SpineViewer.ViewModels.MainWindow; using System; using System.Collections; using System.Collections.Generic; diff --git a/SpineViewer/ViewModels/Exporters/VideoExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/VideoExporterViewModel.cs index 3d302b5..fa6e9b2 100644 --- a/SpineViewer/ViewModels/Exporters/VideoExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/VideoExporterViewModel.cs @@ -1,4 +1,5 @@ using SpineViewer.Resources; +using SpineViewer.ViewModels.MainWindow; using System; using System.Collections.Generic; using System.Linq; diff --git a/SpineViewer/ViewModels/ExplorerListViewModel.cs b/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs similarity index 99% rename from SpineViewer/ViewModels/ExplorerListViewModel.cs rename to SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs index 92f8800..ce40a8d 100644 --- a/SpineViewer/ViewModels/ExplorerListViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs @@ -8,7 +8,6 @@ using SpineViewer.Extensions; using SpineViewer.Models; using SpineViewer.Resources; using SpineViewer.Services; -using SpineViewer.ViewModels; using System; using System.Collections; using System.Collections.Generic; @@ -22,7 +21,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shell; -namespace SpineViewer.ViewModels +namespace SpineViewer.ViewModels.MainWindow { public class ExplorerListViewModel : ObservableObject { diff --git a/SpineViewer/ViewModels/MainWindowViewModel.cs b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs similarity index 77% rename from SpineViewer/ViewModels/MainWindowViewModel.cs rename to SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs index 3a13a29..f6156e4 100644 --- a/SpineViewer/ViewModels/MainWindowViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs @@ -19,7 +19,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Shell; -namespace SpineViewer.ViewModels +namespace SpineViewer.ViewModels.MainWindow { /// /// MainWindow 上下文对象 @@ -101,31 +101,8 @@ namespace SpineViewer.ViewModels private void Debug_Execute() { #if DEBUG - var path = @"C:\Users\ljh\Desktop\a.mp4"; - using var exporter = new FFmpegVideoExporter(_sfmlRenderer.Resolution); - using var view = _sfmlRenderer.GetView(); - exporter.Center = view.Center; - exporter.Size = view.Size; - exporter.Rotation = view.Rotation; - exporter.Viewport = view.Viewport; - - SpineObject[] spines; - lock (_spineObjectModels.Lock) - { - spines = _spineObjectModels.Select(it => it.GetSpineObject(true)).ToArray(); - } - - exporter.Fps = 60; - exporter.Format = FFmpegVideoExporter.VideoFormat.Webm; - exporter.Duration = 3; - exporter.BackgroundColor = new(0, 0, 0, 0); - ProgressService.RunAsync((pr, ct) => - { - exporter.ProgressReporter = (total, done, text) => - { pr.Total = total; pr.Done = done; pr.ProgressText = text; }; - exporter.Export(path, ct, spines); - }, "测试一下"); + MessagePopupService.Quest("测试一下"); #endif } } diff --git a/SpineViewer/ViewModels/SFMLRendererViewModel.cs b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs similarity index 99% rename from SpineViewer/ViewModels/SFMLRendererViewModel.cs rename to SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs index 15f53ac..6441782 100644 --- a/SpineViewer/ViewModels/SFMLRendererViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs @@ -18,7 +18,7 @@ using System.Windows; using System.Windows.Input; using System.Windows.Media; -namespace SpineViewer.ViewModels +namespace SpineViewer.ViewModels.MainWindow { public class SFMLRendererViewModel : ObservableObject { diff --git a/SpineViewer/ViewModels/SpineObjectListViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs similarity index 99% rename from SpineViewer/ViewModels/SpineObjectListViewModel.cs rename to SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs index 4e1d7b3..3904f41 100644 --- a/SpineViewer/ViewModels/SpineObjectListViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs @@ -17,7 +17,7 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Shell; -namespace SpineViewer.ViewModels +namespace SpineViewer.ViewModels.MainWindow { public class SpineObjectListViewModel : ObservableObject { diff --git a/SpineViewer/ViewModels/SpineObjectTabViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs similarity index 99% rename from SpineViewer/ViewModels/SpineObjectTabViewModel.cs rename to SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs index cf4f4b3..c092a14 100644 --- a/SpineViewer/ViewModels/SpineObjectTabViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs @@ -9,7 +9,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; -namespace SpineViewer.ViewModels +namespace SpineViewer.ViewModels.MainWindow { public class SpineObjectTabViewModel : ObservableObject { @@ -611,7 +611,7 @@ namespace SpineViewer.ViewModels // 但是目前无法识别是否增加了轨道, 因此总是重建列表 // 由于某些原因, 直接使用 Clear 会和 UI 逻辑冲突产生报错, 因此需要放到 Dispatcher 里延迟执行 - App.Current.Dispatcher.BeginInvoke( + Application.Current.Dispatcher.BeginInvoke( () => { _animationTracks.Clear(); diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml index 29c9f64..9df47f3 100644 --- a/SpineViewer/Views/MainWindow.xaml +++ b/SpineViewer/Views/MainWindow.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:hc="https://handyorg.github.io/handycontrol" - xmlns:vm="clr-namespace:SpineViewer.ViewModels" + xmlns:vm="clr-namespace:SpineViewer.ViewModels.MainWindow" xmlns:ext="clr-namespace:SpineViewer.Extensions" xmlns:SFMLRenderer="clr-namespace:SFMLRenderer;assembly=SFMLRenderer" mc:Ignorable="d" diff --git a/SpineViewer/Views/MainWindow.xaml.cs b/SpineViewer/Views/MainWindow.xaml.cs index 8c3b8f5..e83f61d 100644 --- a/SpineViewer/Views/MainWindow.xaml.cs +++ b/SpineViewer/Views/MainWindow.xaml.cs @@ -5,7 +5,7 @@ using NLog.Targets; using Spine; using SpineViewer.Natives; using SpineViewer.Resources; -using SpineViewer.ViewModels; +using SpineViewer.ViewModels.MainWindow; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; From 42cb782a969ebc3b5b3eec1e2a5bb4d918446b9f Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 29 May 2025 20:10:15 +0800 Subject: [PATCH 06/27] =?UTF-8?q?=E7=A7=BB=E9=99=A4winforms=E5=BC=95?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Services/AboutDialogService.cs | 18 ---- .../Services/DiagnosticsDialogService.cs | 18 ---- SpineViewer/Services/DialogService.cs | 85 +++++++++++++++++++ SpineViewer/Services/ExporterDialogService.cs | 38 --------- SpineViewer/Services/OpenFolderService.cs | 29 ------- SpineViewer/Services/SaveFileService.cs | 25 ------ SpineViewer/SpineViewer.csproj | 6 -- .../ViewModels/DiagnosticsDialogViewModel.cs | 16 +++- .../CustomFFmpegExporterViewModel.cs | 2 +- .../Exporters/FFmpegVideoExporterViewModel.cs | 2 +- .../Exporters/FrameExporterViewModel.cs | 2 +- .../FrameSequenceExporterViewModel.cs | 2 +- .../MainWindow/ExplorerListViewModel.cs | 2 +- .../MainWindow/MainWindowViewModel.cs | 4 +- .../CustomFFmpegExporterDialog.xaml.cs | 2 +- .../FFmpegVideoExporterDialog.xaml.cs | 2 +- .../FrameExporterDialog.xaml.cs | 2 +- .../FrameSequenceExporterDialog.xaml.cs | 2 +- 18 files changed, 111 insertions(+), 146 deletions(-) delete mode 100644 SpineViewer/Services/AboutDialogService.cs delete mode 100644 SpineViewer/Services/DiagnosticsDialogService.cs create mode 100644 SpineViewer/Services/DialogService.cs delete mode 100644 SpineViewer/Services/ExporterDialogService.cs delete mode 100644 SpineViewer/Services/OpenFolderService.cs delete mode 100644 SpineViewer/Services/SaveFileService.cs diff --git a/SpineViewer/Services/AboutDialogService.cs b/SpineViewer/Services/AboutDialogService.cs deleted file mode 100644 index f8cdf30..0000000 --- a/SpineViewer/Services/AboutDialogService.cs +++ /dev/null @@ -1,18 +0,0 @@ -using SpineViewer.Views; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpineViewer.Services -{ - public static class AboutDialogService - { - public static bool ShowAboutDialog() - { - var dialog = new AboutDialog() { Owner = App.Current.MainWindow }; - return dialog.ShowDialog() ?? false; - } - } -} diff --git a/SpineViewer/Services/DiagnosticsDialogService.cs b/SpineViewer/Services/DiagnosticsDialogService.cs deleted file mode 100644 index c14300c..0000000 --- a/SpineViewer/Services/DiagnosticsDialogService.cs +++ /dev/null @@ -1,18 +0,0 @@ -using SpineViewer.Views; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpineViewer.Services -{ - public static class DiagnosticsDialogService - { - public static bool ShowDiagnosticsDialog() - { - var dialog = new DiagnosticsDialog() { Owner = App.Current.MainWindow }; - return dialog.ShowDialog() ?? false; - } - } -} diff --git a/SpineViewer/Services/DialogService.cs b/SpineViewer/Services/DialogService.cs new file mode 100644 index 0000000..120fc89 --- /dev/null +++ b/SpineViewer/Services/DialogService.cs @@ -0,0 +1,85 @@ +using Microsoft.Win32; +using SpineViewer.ViewModels.Exporters; +using SpineViewer.Views; +using SpineViewer.Views.ExporterDialogs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpineViewer.Services +{ + /// + /// 用于弹出各种对话框的服务 + /// + public static class DialogService + { + public static bool ShowDiagnosticsDialog() + { + var dialog = new DiagnosticsDialog() { Owner = App.Current.MainWindow }; + return dialog.ShowDialog() ?? false; + } + + public static bool ShowAboutDialog() + { + var dialog = new AboutDialog() { Owner = App.Current.MainWindow }; + return dialog.ShowDialog() ?? false; + } + + public static bool ShowFrameExporterDialog(FrameExporterViewModel vm) + { + var dialog = new FrameExporterDialog() { DataContext = vm, Owner = App.Current.MainWindow }; + return dialog.ShowDialog() ?? false; + } + + public static bool ShowFrameSequenceExporterDialog(FrameSequenceExporterViewModel vm) + { + var dialog = new FrameSequenceExporterDialog() { DataContext = vm, Owner = App.Current.MainWindow }; + return dialog.ShowDialog() ?? false; + } + + public static bool ShowFFmpegVideoExporterDialog(FFmpegVideoExporterViewModel vm) + { + var dialog = new FFmpegVideoExporterDialog() { DataContext = vm, Owner = App.Current.MainWindow }; + return dialog.ShowDialog() ?? false; + } + + public static bool ShowCustomFFmpegExporterDialog(CustomFFmpegExporterViewModel vm) + { + var dialog = new CustomFFmpegExporterDialog() { DataContext = vm, Owner = App.Current.MainWindow }; + return dialog.ShowDialog() ?? false; + } + + /// + /// 获取用户选择的文件夹 + /// + /// + /// 是否确认了选择 + public static bool ShowOpenFolderDialog(out string? folderName) + { + // XXX: 此处使用了 System.Windows.Forms 的文件夹浏览对话框 + var folderDialog = new OpenFolderDialog() { Multiselect = false }; + if (folderDialog.ShowDialog() is true) + { + folderName = folderDialog.FolderName; + return true; + } + folderName = null; + return false; + } + + /// + /// 获取用户选择的文件夹 + /// + /// + /// 是否确认了选择 + public static bool ShowSaveFileDialog(out string? selectedPath) + { + var dialog = new SaveFileDialog() { }; + selectedPath = null; + // TODO + return false; + } + } +} diff --git a/SpineViewer/Services/ExporterDialogService.cs b/SpineViewer/Services/ExporterDialogService.cs deleted file mode 100644 index 2088d1b..0000000 --- a/SpineViewer/Services/ExporterDialogService.cs +++ /dev/null @@ -1,38 +0,0 @@ -using SpineViewer.ViewModels.Exporters; -using SpineViewer.Views; -using SpineViewer.Views.ExporterDialogs; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpineViewer.Services -{ - public static class ExporterDialogService - { - public static bool ShowFrameExporterDialog(FrameExporterViewModel vm) - { - var dialog = new FrameExporterDialog() { DataContext = vm, Owner = App.Current.MainWindow }; - return dialog.ShowDialog() ?? false; - } - - public static bool ShowFrameSequenceExporterDialog(FrameSequenceExporterViewModel vm) - { - var dialog = new FrameSequenceExporterDialog() { DataContext = vm, Owner = App.Current.MainWindow }; - return dialog.ShowDialog() ?? false; - } - - public static bool ShowFFmpegVideoExporterDialog(FFmpegVideoExporterViewModel vm) - { - var dialog = new FFmpegVideoExporterDialog() { DataContext = vm, Owner = App.Current.MainWindow }; - return dialog.ShowDialog() ?? false; - } - - public static bool ShowCustomFFmpegExporterDialog(CustomFFmpegExporterViewModel vm) - { - var dialog = new CustomFFmpegExporterDialog() { DataContext = vm, Owner = App.Current.MainWindow }; - return dialog.ShowDialog() ?? false; - } - } -} diff --git a/SpineViewer/Services/OpenFolderService.cs b/SpineViewer/Services/OpenFolderService.cs deleted file mode 100644 index 58973eb..0000000 --- a/SpineViewer/Services/OpenFolderService.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpineViewer.Services -{ - public static class OpenFolderService - { - /// - /// 获取用户选择的文件夹 - /// - /// - /// 是否确认了选择 - public static bool OpenFolder(out string? selectedPath) - { - // XXX: 此处使用了 System.Windows.Forms 的文件夹浏览对话框 - using var folderDialog = new System.Windows.Forms.FolderBrowserDialog(); - if (folderDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) - { - selectedPath = folderDialog.SelectedPath; - return true; - } - selectedPath = null; - return false; - } - } -} diff --git a/SpineViewer/Services/SaveFileService.cs b/SpineViewer/Services/SaveFileService.cs deleted file mode 100644 index 640bad7..0000000 --- a/SpineViewer/Services/SaveFileService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpineViewer.Services -{ - public static class SaveService - { - /// - /// 获取用户选择的文件夹 - /// - /// - /// 是否确认了选择 - public static bool SaveFile(out string? selectedPath) - { - var dialog = new SaveFileDialog() { }; - selectedPath = null; - // TODO - return false; - } - } -} diff --git a/SpineViewer/SpineViewer.csproj b/SpineViewer/SpineViewer.csproj index eebcf1d..1e28920 100644 --- a/SpineViewer/SpineViewer.csproj +++ b/SpineViewer/SpineViewer.csproj @@ -10,7 +10,6 @@ 0.15.0 WinExe true - true @@ -18,11 +17,6 @@ appicon.ico app.manifest - - - - - diff --git a/SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs b/SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs index b6c2863..aff49af 100644 --- a/SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs +++ b/SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs @@ -12,6 +12,7 @@ using System.Text; using System.Threading.Tasks; using System.Windows; + namespace SpineViewer.ViewModels { public class DiagnosticsDialogViewModel : ObservableObject @@ -31,7 +32,20 @@ namespace SpineViewer.ViewModels } } - public string Memory => $"{new Microsoft.VisualBasic.Devices.ComputerInfo().TotalPhysicalMemory / 1024f / 1024f / 1024f:F1} GB"; + public string Memory + { + get + { + var searcher = new ManagementObjectSearcher("SELECT TotalPhysicalMemory FROM Win32_ComputerSystem"); + foreach (ManagementObject obj in searcher.Get()) + { + ulong bytes = (ulong)obj["TotalPhysicalMemory"]; + float gb = bytes / 1024f / 1024f / 1024f; + return $"{gb:F1} GB"; + } + return "N/A"; + } + } public string WindowsVersion { diff --git a/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs index f846bd3..2ea1cae 100644 --- a/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs @@ -50,7 +50,7 @@ namespace SpineViewer.ViewModels.Exporters protected override void Export_Execute(IList? args) { if (args is null || args.Count <= 0) return; - if (!ExporterDialogService.ShowCustomFFmpegExporterDialog(this)) return; + if (!DialogService.ShowCustomFFmpegExporterDialog(this)) return; SpineObject[] spines = args.Cast().Select(m => m.GetSpineObject()).ToArray(); ProgressService.RunAsync((pr, ct) => ExportTask(spines, pr, ct), AppResource.Str_CustomFFmpegExporterTitle); foreach (var sp in spines) sp.Dispose(); diff --git a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs index 860265c..55322ac 100644 --- a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs @@ -37,7 +37,7 @@ namespace SpineViewer.ViewModels.Exporters protected override void Export_Execute(IList? args) { if (args is null || args.Count <= 0) return; - if (!ExporterDialogService.ShowFFmpegVideoExporterDialog(this)) return; + if (!DialogService.ShowFFmpegVideoExporterDialog(this)) return; SpineObject[] spines = args.Cast().Select(m => m.GetSpineObject()).ToArray(); ProgressService.RunAsync((pr, ct) => ExportTask(spines, pr, ct), AppResource.Str_FFmpegVideoExporterTitle); foreach (var sp in spines) sp.Dispose(); diff --git a/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs index f65bee5..dd1a59e 100644 --- a/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs @@ -41,7 +41,7 @@ namespace SpineViewer.ViewModels.Exporters protected override void Export_Execute(IList? args) { if (args is null || args.Count <= 0) return; - if (!ExporterDialogService.ShowFrameExporterDialog(this)) return; + if (!DialogService.ShowFrameExporterDialog(this)) return; SpineObject[] spines = args.Cast().Select(m => m.GetSpineObject(true)).ToArray(); ProgressService.RunAsync((pr, ct) => ExportTask(spines, pr, ct), AppResource.Str_FrameExporterTitle); foreach (var sp in spines) sp.Dispose(); diff --git a/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs index 09f00fd..d692379 100644 --- a/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs @@ -20,7 +20,7 @@ namespace SpineViewer.ViewModels.Exporters protected override void Export_Execute(IList? args) { if (args is null || args.Count <= 0) return; - if (!ExporterDialogService.ShowFrameSequenceExporterDialog(this)) return; + if (!DialogService.ShowFrameSequenceExporterDialog(this)) return; SpineObject[] spines = args.Cast().Select(m => m.GetSpineObject()).ToArray(); ProgressService.RunAsync((pr, ct) => ExportTask(spines, pr, ct), AppResource.Str_FrameSequenceExporterTitle); foreach (var sp in spines) sp.Dispose(); diff --git a/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs b/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs index ce40a8d..6992cf0 100644 --- a/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs @@ -94,7 +94,7 @@ namespace SpineViewer.ViewModels.MainWindow /// public RelayCommand Cmd_ChangeCurrentDirectory => _cmd_ChangeCurrentDirectory ??= new(() => { - if (OpenFolderService.OpenFolder(out var selectedPath)) + if (DialogService.ShowOpenFolderDialog(out var selectedPath)) { _currentDirectory = selectedPath; RefreshItems(); diff --git a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs index f6156e4..80d8968 100644 --- a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs @@ -83,13 +83,13 @@ namespace SpineViewer.ViewModels.MainWindow /// /// 显示诊断信息对话框 /// - public RelayCommand Cmd_ShowDiagnosticsDialog => _cmd_ShowDiagnosticsDialog ??= new(() => { DiagnosticsDialogService.ShowDiagnosticsDialog(); }); + public RelayCommand Cmd_ShowDiagnosticsDialog => _cmd_ShowDiagnosticsDialog ??= new(() => { DialogService.ShowDiagnosticsDialog(); }); private RelayCommand? _cmd_ShowDiagnosticsDialog; /// /// 显示关于对话框 /// - public RelayCommand Cmd_ShowAboutDialog => _cmd_ShowAboutDialog ??= new(() => { AboutDialogService.ShowAboutDialog(); }); + public RelayCommand Cmd_ShowAboutDialog => _cmd_ShowAboutDialog ??= new(() => { DialogService.ShowAboutDialog(); }); private RelayCommand? _cmd_ShowAboutDialog; /// diff --git a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs index e8e0c7d..8b45daf 100644 --- a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs +++ b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs @@ -44,7 +44,7 @@ namespace SpineViewer.Views.ExporterDialogs private void ButtonSelectOutputDir_Click(object sender, RoutedEventArgs e) { - if (OpenFolderService.OpenFolder(out var selectedPath)) + if (DialogService.ShowOpenFolderDialog(out var selectedPath)) { var vm = (CustomFFmpegExporterViewModel)DataContext; vm.OutputDir = selectedPath; diff --git a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs index 9cc1e44..4c58a55 100644 --- a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs +++ b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs @@ -44,7 +44,7 @@ namespace SpineViewer.Views.ExporterDialogs private void ButtonSelectOutputDir_Click(object sender, RoutedEventArgs e) { - if (OpenFolderService.OpenFolder(out var selectedPath)) + if (DialogService.ShowOpenFolderDialog(out var selectedPath)) { var vm = (FFmpegVideoExporterViewModel)DataContext; vm.OutputDir = selectedPath; diff --git a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs index 308fc06..f4f9e31 100644 --- a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs +++ b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs @@ -44,7 +44,7 @@ namespace SpineViewer.Views.ExporterDialogs private void ButtonSelectOutputDir_Click(object sender, RoutedEventArgs e) { - if (OpenFolderService.OpenFolder(out var selectedPath)) + if (DialogService.ShowOpenFolderDialog(out var selectedPath)) { var vm = (FrameExporterViewModel)DataContext; vm.OutputDir = selectedPath; diff --git a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs index c3bad17..b4e72fe 100644 --- a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs +++ b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs @@ -44,7 +44,7 @@ namespace SpineViewer.Views.ExporterDialogs private void ButtonSelectOutputDir_Click(object sender, RoutedEventArgs e) { - if (OpenFolderService.OpenFolder(out var selectedPath)) + if (DialogService.ShowOpenFolderDialog(out var selectedPath)) { var vm = (FrameSequenceExporterViewModel)DataContext; vm.OutputDir = selectedPath; From 16d4388f3e154e26d120246cfcb0c72e65309316 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 29 May 2025 21:55:47 +0800 Subject: [PATCH 07/27] =?UTF-8?q?=E4=BF=AE=E4=B8=80=E4=B8=8B=E7=BC=BA?= =?UTF-8?q?=E7=9C=81=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs b/SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs index aff49af..cb8dbec 100644 --- a/SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs +++ b/SpineViewer/ViewModels/DiagnosticsDialogViewModel.cs @@ -43,7 +43,7 @@ namespace SpineViewer.ViewModels float gb = bytes / 1024f / 1024f / 1024f; return $"{gb:F1} GB"; } - return "N/A"; + return "Unknown"; } } From 28fd11cf3ef4d1f3fc48603a1e87203f15e10669 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 6 Jun 2025 22:02:22 +0800 Subject: [PATCH 08/27] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=AA=A8=E9=AA=BC?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=B0=9D=E8=AF=95=E6=80=A7=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SpineWrappers/V21/SpineObjectData21.cs | 22 +++++++++++++++++-- .../SpineWrappers/V36/SpineObjectData36.cs | 22 +++++++++++++++++-- .../SpineWrappers/V37/SpineObjectData37.cs | 22 +++++++++++++++++-- .../SpineWrappers/V38/SpineObjectData38.cs | 22 +++++++++++++++++-- .../SpineWrappers/V40/SpineObjectData40.cs | 22 +++++++++++++++++-- .../SpineWrappers/V41/SpineObjectData41.cs | 22 +++++++++++++++++-- .../SpineWrappers/V42/SpineObjectData42.cs | 22 +++++++++++++++++-- 7 files changed, 140 insertions(+), 14 deletions(-) diff --git a/Spine/Implementations/SpineWrappers/V21/SpineObjectData21.cs b/Spine/Implementations/SpineWrappers/V21/SpineObjectData21.cs index abe247d..6959e60 100644 --- a/Spine/Implementations/SpineWrappers/V21/SpineObjectData21.cs +++ b/Spine/Implementations/SpineWrappers/V21/SpineObjectData21.cs @@ -35,9 +35,27 @@ namespace Spine.Implementations.SpineWrappers.V21 try { if (Utf8Validator.IsUtf8(skelPath)) - _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + } else - _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + } } catch (Exception ex) { diff --git a/Spine/Implementations/SpineWrappers/V36/SpineObjectData36.cs b/Spine/Implementations/SpineWrappers/V36/SpineObjectData36.cs index 159063c..f38bcd0 100644 --- a/Spine/Implementations/SpineWrappers/V36/SpineObjectData36.cs +++ b/Spine/Implementations/SpineWrappers/V36/SpineObjectData36.cs @@ -35,9 +35,27 @@ namespace Spine.Implementations.SpineWrappers.V36 try { if (Utf8Validator.IsUtf8(skelPath)) - _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + } else - _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + } } catch (Exception ex) { diff --git a/Spine/Implementations/SpineWrappers/V37/SpineObjectData37.cs b/Spine/Implementations/SpineWrappers/V37/SpineObjectData37.cs index 4fdfee2..8774bff 100644 --- a/Spine/Implementations/SpineWrappers/V37/SpineObjectData37.cs +++ b/Spine/Implementations/SpineWrappers/V37/SpineObjectData37.cs @@ -35,9 +35,27 @@ namespace Spine.Implementations.SpineWrappers.V37 try { if (Utf8Validator.IsUtf8(skelPath)) - _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + } else - _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + } } catch (Exception ex) { diff --git a/Spine/Implementations/SpineWrappers/V38/SpineObjectData38.cs b/Spine/Implementations/SpineWrappers/V38/SpineObjectData38.cs index b46076a..1138177 100644 --- a/Spine/Implementations/SpineWrappers/V38/SpineObjectData38.cs +++ b/Spine/Implementations/SpineWrappers/V38/SpineObjectData38.cs @@ -36,9 +36,27 @@ namespace Spine.Implementations.SpineWrappers.V38 try { if (Utf8Validator.IsUtf8(skelPath)) - _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + } else - _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + } } catch (Exception ex) { diff --git a/Spine/Implementations/SpineWrappers/V40/SpineObjectData40.cs b/Spine/Implementations/SpineWrappers/V40/SpineObjectData40.cs index 3c8370f..b3852f4 100644 --- a/Spine/Implementations/SpineWrappers/V40/SpineObjectData40.cs +++ b/Spine/Implementations/SpineWrappers/V40/SpineObjectData40.cs @@ -36,9 +36,27 @@ namespace Spine.Implementations.SpineWrappers.V40 try { if (Utf8Validator.IsUtf8(skelPath)) - _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + } else - _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + } } catch (Exception ex) { diff --git a/Spine/Implementations/SpineWrappers/V41/SpineObjectData41.cs b/Spine/Implementations/SpineWrappers/V41/SpineObjectData41.cs index 49ed2b7..b5755b0 100644 --- a/Spine/Implementations/SpineWrappers/V41/SpineObjectData41.cs +++ b/Spine/Implementations/SpineWrappers/V41/SpineObjectData41.cs @@ -36,9 +36,27 @@ namespace Spine.Implementations.SpineWrappers.V41 try { if (Utf8Validator.IsUtf8(skelPath)) - _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + } else - _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + } } catch (Exception ex) { diff --git a/Spine/Implementations/SpineWrappers/V42/SpineObjectData42.cs b/Spine/Implementations/SpineWrappers/V42/SpineObjectData42.cs index e826704..acd41ee 100644 --- a/Spine/Implementations/SpineWrappers/V42/SpineObjectData42.cs +++ b/Spine/Implementations/SpineWrappers/V42/SpineObjectData42.cs @@ -36,9 +36,27 @@ namespace Spine.Implementations.SpineWrappers.V42 try { if (Utf8Validator.IsUtf8(skelPath)) - _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + } else - _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + { + try + { + _skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath); + } + catch + { + _skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath); + } + } } catch (Exception ex) { From 9be77ba8bdb1215fb9f414f3d2e27aa9050abc61 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 12 Jun 2025 23:47:46 +0800 Subject: [PATCH 09/27] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BA=B9=E7=90=86?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SpineWrappers/V21/SpineObjectData21.cs | 5 +- .../SpineWrappers/V36/SpineObjectData36.cs | 5 +- .../SpineWrappers/V37/SpineObjectData37.cs | 5 +- .../SpineWrappers/V38/SpineObjectData38.cs | 5 +- .../SpineWrappers/V40/SpineObjectData40.cs | 5 +- .../SpineWrappers/V41/SpineObjectData41.cs | 5 +- .../SpineWrappers/V42/SpineObjectData42.cs | 5 +- Spine/SpineObject.cs | 8 +- Spine/SpineWrappers/SpineObjectData.cs | 11 +- Spine/SpineWrappers/TextureLoader.cs | 112 ++++++++++++------ 10 files changed, 104 insertions(+), 62 deletions(-) diff --git a/Spine/Implementations/SpineWrappers/V21/SpineObjectData21.cs b/Spine/Implementations/SpineWrappers/V21/SpineObjectData21.cs index 6959e60..4a7c9a1 100644 --- a/Spine/Implementations/SpineWrappers/V21/SpineObjectData21.cs +++ b/Spine/Implementations/SpineWrappers/V21/SpineObjectData21.cs @@ -26,10 +26,11 @@ namespace Spine.Implementations.SpineWrappers.V21 private readonly ImmutableArray _animations; private readonly FrozenDictionary _animationsByName; - public SpineObjectData21(string skelPath, string atlasPath) : base(skelPath, atlasPath) + public SpineObjectData21(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader) + : base(skelPath, atlasPath, textureLoader) { // 加载 atlas - try { _atlas = new Atlas(atlasPath, _textureLoader); } + try { _atlas = new Atlas(atlasPath, textureLoader); } catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); } try diff --git a/Spine/Implementations/SpineWrappers/V36/SpineObjectData36.cs b/Spine/Implementations/SpineWrappers/V36/SpineObjectData36.cs index f38bcd0..a389679 100644 --- a/Spine/Implementations/SpineWrappers/V36/SpineObjectData36.cs +++ b/Spine/Implementations/SpineWrappers/V36/SpineObjectData36.cs @@ -26,10 +26,11 @@ namespace Spine.Implementations.SpineWrappers.V36 private readonly ImmutableArray _animations; private readonly FrozenDictionary _animationsByName; - public SpineObjectData36(string skelPath, string atlasPath) : base(skelPath, atlasPath) + public SpineObjectData36(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader) + : base(skelPath, atlasPath, textureLoader) { // 加载 atlas - try { _atlas = new Atlas(atlasPath, _textureLoader); } + try { _atlas = new Atlas(atlasPath, textureLoader); } catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); } try diff --git a/Spine/Implementations/SpineWrappers/V37/SpineObjectData37.cs b/Spine/Implementations/SpineWrappers/V37/SpineObjectData37.cs index 8774bff..470eec7 100644 --- a/Spine/Implementations/SpineWrappers/V37/SpineObjectData37.cs +++ b/Spine/Implementations/SpineWrappers/V37/SpineObjectData37.cs @@ -26,10 +26,11 @@ namespace Spine.Implementations.SpineWrappers.V37 private readonly ImmutableArray _animations; private readonly FrozenDictionary _animationsByName; - public SpineObjectData37(string skelPath, string atlasPath) : base(skelPath, atlasPath) + public SpineObjectData37(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader) + : base(skelPath, atlasPath, textureLoader) { // 加载 atlas - try { _atlas = new Atlas(atlasPath, _textureLoader); } + try { _atlas = new Atlas(atlasPath, textureLoader); } catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); } try diff --git a/Spine/Implementations/SpineWrappers/V38/SpineObjectData38.cs b/Spine/Implementations/SpineWrappers/V38/SpineObjectData38.cs index 1138177..0204041 100644 --- a/Spine/Implementations/SpineWrappers/V38/SpineObjectData38.cs +++ b/Spine/Implementations/SpineWrappers/V38/SpineObjectData38.cs @@ -27,10 +27,11 @@ namespace Spine.Implementations.SpineWrappers.V38 private readonly ImmutableArray _animations; private readonly FrozenDictionary _animationsByName; - public SpineObjectData38(string skelPath, string atlasPath) : base(skelPath, atlasPath) + public SpineObjectData38(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader) + : base(skelPath, atlasPath, textureLoader) { // 加载 atlas - try { _atlas = new Atlas(atlasPath, _textureLoader); } + try { _atlas = new Atlas(atlasPath, textureLoader); } catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); } try diff --git a/Spine/Implementations/SpineWrappers/V40/SpineObjectData40.cs b/Spine/Implementations/SpineWrappers/V40/SpineObjectData40.cs index b3852f4..bc035d7 100644 --- a/Spine/Implementations/SpineWrappers/V40/SpineObjectData40.cs +++ b/Spine/Implementations/SpineWrappers/V40/SpineObjectData40.cs @@ -26,10 +26,11 @@ namespace Spine.Implementations.SpineWrappers.V40 private readonly ImmutableArray _animations; private readonly FrozenDictionary _animationsByName; - public SpineObjectData40(string skelPath, string atlasPath) : base(skelPath, atlasPath) + public SpineObjectData40(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader) + : base(skelPath, atlasPath, textureLoader) { // 加载 atlas - try { _atlas = new Atlas(atlasPath, _textureLoader); } + try { _atlas = new Atlas(atlasPath, textureLoader); } catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); } // 加载 skel diff --git a/Spine/Implementations/SpineWrappers/V41/SpineObjectData41.cs b/Spine/Implementations/SpineWrappers/V41/SpineObjectData41.cs index b5755b0..18ed63b 100644 --- a/Spine/Implementations/SpineWrappers/V41/SpineObjectData41.cs +++ b/Spine/Implementations/SpineWrappers/V41/SpineObjectData41.cs @@ -26,10 +26,11 @@ namespace Spine.Implementations.SpineWrappers.V41 private readonly ImmutableArray _animations; private readonly FrozenDictionary _animationsByName; - public SpineObjectData41(string skelPath, string atlasPath) : base(skelPath, atlasPath) + public SpineObjectData41(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader) + : base(skelPath, atlasPath, textureLoader) { // 加载 atlas - try { _atlas = new Atlas(atlasPath, _textureLoader); } + try { _atlas = new Atlas(atlasPath, textureLoader); } catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); } // 加载 skel diff --git a/Spine/Implementations/SpineWrappers/V42/SpineObjectData42.cs b/Spine/Implementations/SpineWrappers/V42/SpineObjectData42.cs index acd41ee..83a7365 100644 --- a/Spine/Implementations/SpineWrappers/V42/SpineObjectData42.cs +++ b/Spine/Implementations/SpineWrappers/V42/SpineObjectData42.cs @@ -26,10 +26,11 @@ namespace Spine.Implementations.SpineWrappers.V42 private readonly ImmutableArray _animations; private readonly FrozenDictionary _animationsByName; - public SpineObjectData42(string skelPath, string atlasPath) : base(skelPath, atlasPath) + public SpineObjectData42(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader) + : base(skelPath, atlasPath, textureLoader) { // 加载 atlas - try { _atlas = new Atlas(atlasPath, _textureLoader); } + try { _atlas = new Atlas(atlasPath, textureLoader); } catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); } // 加载 skel diff --git a/Spine/SpineObject.cs b/Spine/SpineObject.cs index 1c13496..d270488 100644 --- a/Spine/SpineObject.cs +++ b/Spine/SpineObject.cs @@ -45,10 +45,12 @@ namespace Spine /// skel 文件路径 /// atlas 文件路径, 为空时会根据 进行自动检测 /// 要使用的运行时版本, 为空时会自动检测 - public SpineObject(string skelPath, string? atlasPath = null, SpineVersion? version = null) + public SpineObject(string skelPath, string? atlasPath = null, SpineVersion? version = null, TextureLoader? textureLoader = null) { if (string.IsNullOrWhiteSpace(skelPath)) throw new ArgumentException(skelPath, nameof(skelPath)); if (!File.Exists(skelPath)) throw new FileNotFoundException($"{nameof(skelPath)} not found", skelPath); + textureLoader ??= TextureLoader.DefaultLoader; + SkelPath = Path.GetFullPath(skelPath); AssetsDir = Directory.GetParent(skelPath).FullName; Name = Path.GetFileNameWithoutExtension(skelPath); @@ -91,7 +93,7 @@ namespace Spine { try { - _data = SpineObjectData.New(v, skelPath, atlasPath); + _data = SpineObjectData.New(v, skelPath, atlasPath, textureLoader); Version = v; break; } @@ -109,7 +111,7 @@ namespace Spine { // 根据版本实例化对象 Version = version; - _data = SpineObjectData.New(Version, skelPath, atlasPath); + _data = SpineObjectData.New(Version, skelPath, atlasPath, textureLoader); } // 创建状态实例 diff --git a/Spine/SpineWrappers/SpineObjectData.cs b/Spine/SpineWrappers/SpineObjectData.cs index a6c8e2a..0e8f0dc 100644 --- a/Spine/SpineWrappers/SpineObjectData.cs +++ b/Spine/SpineWrappers/SpineObjectData.cs @@ -20,18 +20,13 @@ namespace Spine.SpineWrappers /// /// 构建版本对象 /// - public static SpineObjectData New(SpineVersion version, string skelPath, string atlasPath) => CreateInstance(version.Tag, skelPath, atlasPath); - - /// - /// 纹理加载器, 可以设置一些预置参数 - /// - public static TextureLoader TextureLoader => _textureLoader; - protected static readonly TextureLoader _textureLoader = new(); + public static SpineObjectData New(SpineVersion version, string skelPath, string atlasPath, TextureLoader textureLoader) + => CreateInstance(version.Tag, skelPath, atlasPath, textureLoader); /// /// 构造函数, 继承的子类应当实现一个相同签名的构造函数 /// - public SpineObjectData(string skelPath, string atlasPath) { } + public SpineObjectData(string skelPath, string atlasPath, TextureLoader textureLoader) { } public abstract string SkeletonVersion { get; } diff --git a/Spine/SpineWrappers/TextureLoader.cs b/Spine/SpineWrappers/TextureLoader.cs index 9aebcff..c07654d 100644 --- a/Spine/SpineWrappers/TextureLoader.cs +++ b/Spine/SpineWrappers/TextureLoader.cs @@ -19,23 +19,62 @@ namespace Spine.SpineWrappers SpineRuntime42.TextureLoader { /// - /// 强制启用 Smooth + /// 默认的全局纹理加载器 /// - public bool ForceSmooth { get; set; } = false; + public static TextureLoader DefaultLoader { get; } = new(); /// - /// 强制启用 Repeated + /// 在读取纹理时强制进行通道预乘操作 /// - public bool ForceRepeated { get; set; } = false; + public bool ForcePremul { get; set; } = false; + + /// + /// 强制使用 Nearest + /// + public bool ForceNearest { get; set; } = false; /// /// 强制启用 Mipmap /// public bool ForceMipmap { get; set; } = false; - public void Load(SpineRuntime21.AtlasPage page, string path) + private SFML.Graphics.Texture ReadTexture(string path) { - var texture = new SFML.Graphics.Texture(path); + if (ForcePremul) + { + using var image = new SFML.Graphics.Image(path); + var width = image.Size.X; + var height = image.Size.Y; + var pixels = image.Pixels; + var size = width * height * 4; + for (int i = 0; i < size; i += 4) + { + byte a = pixels[i + 3]; + if (a == 0) + { + pixels[i + 0] = 0; + pixels[i + 1] = 0; + pixels[i + 2] = 0; + } + else if (a != 255) + { + float f = a / 255f; + pixels[i + 0] = (byte)(pixels[i + 0] * f); + pixels[i + 1] = (byte)(pixels[i + 1] * f); + pixels[i + 2] = (byte)(pixels[i + 2] * f); + } + } + var tex = new SFML.Graphics.Texture(width, height); + tex.Update(pixels); + return tex; + } + return new(path); + } + + public virtual void Load(SpineRuntime21.AtlasPage page, string path) + { + var texture = ReadTexture(path); + if (page.magFilter == SpineRuntime21.TextureFilter.Linear) { texture.Smooth = true; @@ -61,16 +100,16 @@ namespace Spine.SpineWrappers break; } - if (ForceSmooth) texture.Smooth = true; - if (ForceRepeated) texture.Repeated = true; + if (ForceNearest) texture.Smooth = false; if (ForceMipmap) texture.GenerateMipmap(); page.rendererObject = texture; } - public void Load(SpineRuntime36.AtlasPage page, string path) + public virtual void Load(SpineRuntime36.AtlasPage page, string path) { - var texture = new SFML.Graphics.Texture(path); + var texture = ReadTexture(path); + if (page.magFilter == SpineRuntime36.TextureFilter.Linear) { texture.Smooth = true; @@ -96,16 +135,16 @@ namespace Spine.SpineWrappers break; } - if (ForceSmooth) texture.Smooth = true; - if (ForceRepeated) texture.Repeated = true; + if (ForceNearest) texture.Smooth = false; if (ForceMipmap) texture.GenerateMipmap(); page.rendererObject = texture; } - public void Load(SpineRuntime37.AtlasPage page, string path) + public virtual void Load(SpineRuntime37.AtlasPage page, string path) { - var texture = new SFML.Graphics.Texture(path); + var texture = ReadTexture(path); + if (page.magFilter == SpineRuntime37.TextureFilter.Linear) { texture.Smooth = true; @@ -131,16 +170,16 @@ namespace Spine.SpineWrappers break; } - if (ForceSmooth) texture.Smooth = true; - if (ForceRepeated) texture.Repeated = true; + if (ForceNearest) texture.Smooth = false; if (ForceMipmap) texture.GenerateMipmap(); page.rendererObject = texture; } - public void Load(SpineRuntime38.AtlasPage page, string path) + public virtual void Load(SpineRuntime38.AtlasPage page, string path) { - var texture = new SFML.Graphics.Texture(path); + var texture = ReadTexture(path); + if (page.magFilter == SpineRuntime38.TextureFilter.Linear) { texture.Smooth = true; @@ -166,16 +205,20 @@ namespace Spine.SpineWrappers break; } - if (ForceSmooth) texture.Smooth = true; - if (ForceRepeated) texture.Repeated = true; + if (ForceNearest) texture.Smooth = false; if (ForceMipmap) texture.GenerateMipmap(); page.rendererObject = texture; + + // 似乎是不需要设置的, 因为存在某些 png 和 atlas 大小不同的情况, 一般是有一些缩放, 如果设置了反而渲染异常 + // page.width = (int)texture.Size.X; + // page.height = (int)texture.Size.Y; } - public void Load(SpineRuntime40.AtlasPage page, string path) + public virtual void Load(SpineRuntime40.AtlasPage page, string path) { - var texture = new SFML.Graphics.Texture(path); + var texture = ReadTexture(path); + if (page.magFilter == SpineRuntime40.TextureFilter.Linear) { texture.Smooth = true; @@ -201,16 +244,16 @@ namespace Spine.SpineWrappers break; } - if (ForceSmooth) texture.Smooth = true; - if (ForceRepeated) texture.Repeated = true; + if (ForceNearest) texture.Smooth = false; if (ForceMipmap) texture.GenerateMipmap(); page.rendererObject = texture; } - public void Load(SpineRuntime41.AtlasPage page, string path) + public virtual void Load(SpineRuntime41.AtlasPage page, string path) { - var texture = new SFML.Graphics.Texture(path); + var texture = ReadTexture(path); + if (page.magFilter == SpineRuntime41.TextureFilter.Linear) { texture.Smooth = true; @@ -236,16 +279,16 @@ namespace Spine.SpineWrappers break; } - if (ForceSmooth) texture.Smooth = true; - if (ForceRepeated) texture.Repeated = true; + if (ForceNearest) texture.Smooth = false; if (ForceMipmap) texture.GenerateMipmap(); page.rendererObject = texture; } - public void Load(SpineRuntime42.AtlasPage page, string path) + public virtual void Load(SpineRuntime42.AtlasPage page, string path) { - var texture = new SFML.Graphics.Texture(path); + var texture = ReadTexture(path); + if (page.magFilter == SpineRuntime42.TextureFilter.Linear) { texture.Smooth = true; @@ -271,18 +314,13 @@ namespace Spine.SpineWrappers break; } - if (ForceSmooth) texture.Smooth = true; - if (ForceRepeated) texture.Repeated = true; + if (ForceNearest) texture.Smooth = false; if (ForceMipmap) texture.GenerateMipmap(); page.rendererObject = texture; - - // 似乎是不需要设置的, 因为存在某些 png 和 atlas 大小不同的情况, 一般是有一些缩放, 如果设置了反而渲染异常 - // page.width = (int)texture.Size.X; - // page.height = (int)texture.Size.Y; } - public void Unload(object texture) + public virtual void Unload(object texture) { ((SFML.Graphics.Texture)texture).Dispose(); } From 1092f37a02a6760618226c72f23a30c45c21ff73 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 13 Jun 2025 00:11:03 +0800 Subject: [PATCH 10/27] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E9=9D=9E=E5=BF=85=E8=A6=81=E9=BB=98=E8=AE=A4=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Spine/SpineObject.cs | 22 ++--- Spine/SpineWrappers/TextureLoader.cs | 6 +- SpineViewer/Models/SpineObjectConfigModel.cs | 84 ++++++++++---------- 3 files changed, 58 insertions(+), 54 deletions(-) diff --git a/Spine/SpineObject.cs b/Spine/SpineObject.cs index d270488..471f9cb 100644 --- a/Spine/SpineObject.cs +++ b/Spine/SpineObject.cs @@ -237,7 +237,7 @@ namespace Spine /// /// 是否使用预乘 Alpha /// - public bool UsePma { get; set; } = false; + public bool UsePma { get; set; } /// /// 物理约束更新方式 @@ -247,7 +247,7 @@ namespace Spine /// /// 启用渲染调试, 将会使所有 DebugXXX 属性生效 /// - public bool EnableDebug { get; set; } = false; + public bool EnableDebug { get; set; } /// /// 显示纹理 @@ -257,47 +257,47 @@ namespace Spine /// /// 显示包围盒 /// - public bool DebugBounds { get; set; } = false; + public bool DebugBounds { get; set; } /// /// 显示骨骼 /// - public bool DebugBones { get; set; } = false; + public bool DebugBones { get; set; } /// /// 显示区域附件边框 /// - public bool DebugRegions { get; set; } = false; + public bool DebugRegions { get; set; } /// /// 显示网格附件边框线 /// - public bool DebugMeshHulls { get; set; } = false; + public bool DebugMeshHulls { get; set; } /// /// 显示网格附件网格线 /// - public bool DebugMeshes { get; set; } = false; + public bool DebugMeshes { get; set; } /// /// 显示碰撞盒附件边框线 /// - public bool DebugBoundingBoxes { get; set; } = false; + public bool DebugBoundingBoxes { get; set; } /// /// 显示路径附件网格线 /// - public bool DebugPaths { get; set; } = false; + public bool DebugPaths { get; set; } /// /// 显示点附件 /// - public bool DebugPoints { get; set; } = false; + public bool DebugPoints { get; set; } /// /// 显示剪裁附件网格线 /// - public bool DebugClippings { get; set; } = false; + public bool DebugClippings { get; set; } /// /// 获取某个插槽上的附件名, 插槽不存在或者无附件均返回 null diff --git a/Spine/SpineWrappers/TextureLoader.cs b/Spine/SpineWrappers/TextureLoader.cs index c07654d..34c23c7 100644 --- a/Spine/SpineWrappers/TextureLoader.cs +++ b/Spine/SpineWrappers/TextureLoader.cs @@ -26,17 +26,17 @@ namespace Spine.SpineWrappers /// /// 在读取纹理时强制进行通道预乘操作 /// - public bool ForcePremul { get; set; } = false; + public bool ForcePremul { get; set; } /// /// 强制使用 Nearest /// - public bool ForceNearest { get; set; } = false; + public bool ForceNearest { get; set; } /// /// 强制启用 Mipmap /// - public bool ForceMipmap { get; set; } = false; + public bool ForceMipmap { get; set; } private SFML.Graphics.Texture ReadTexture(string path) { diff --git a/SpineViewer/Models/SpineObjectConfigModel.cs b/SpineViewer/Models/SpineObjectConfigModel.cs index 270013b..bf83ac7 100644 --- a/SpineViewer/Models/SpineObjectConfigModel.cs +++ b/SpineViewer/Models/SpineObjectConfigModel.cs @@ -13,6 +13,48 @@ namespace SpineViewer.Models { public class SpineObjectConfigModel { + public bool UsePma { get; set; } + + public string Physics { get; set; } = ISkeleton.Physics.Update.ToString(); + + public float Scale { get; set; } = 1f; + + public bool FlipX { get; set; } + + public bool FlipY { get; set; } + + public float X { get; set; } + + public float Y { get; set; } + + public List LoadedSkins { get; set; } = []; + + public Dictionary SlotAttachment { get; set; } = []; + + public List Animations { get; set; } = []; + + public bool DebugTexture { get; set; } = true; + + public bool DebugBounds { get; set; } + + public bool DebugBones { get; set; } + + public bool DebugRegions { get; set; } + + public bool DebugMeshHulls { get; set; } + + public bool DebugMeshes { get; set; } + + public bool DebugBoundingBoxes { get; set; } + + public bool DebugPaths { get; set; } + + public bool DebugPoints { get; set; } + + public bool DebugClippings { get; set; } + + #region 序列化与反序列 + /// /// 保存 Json 文件的格式参数 /// @@ -36,7 +78,7 @@ namespace SpineViewer.Models } /// - /// 保存预设至文件, 概率抛出异常 + /// 保存至文件, 可能抛出异常 /// public void Serialize(string path) { @@ -45,44 +87,6 @@ namespace SpineViewer.Models File.WriteAllText(path, json, Encoding.UTF8); } - public bool UsePma { get; set; } - - public string Physics { get; set; } = ISkeleton.Physics.Update.ToString(); - - public float Scale { get; set; } = 1f; - - public bool FlipX { get; set; } - - public bool FlipY { get; set; } - - public float X { get; set; } - - public float Y { get; set; } - - public List LoadedSkins { get; set; } = []; - - public Dictionary SlotAttachment { get; set; } = []; - - public List Animations { get; set; } = []; - - public bool DebugTexture { get; set; } = true; - - public bool DebugBounds { get; set; } = true; - - public bool DebugBones { get; set; } - - public bool DebugRegions { get; set; } - - public bool DebugMeshHulls { get; set; } - - public bool DebugMeshes { get; set; } - - public bool DebugBoundingBoxes { get; set; } - - public bool DebugPaths { get; set; } - - public bool DebugPoints { get; set; } - - public bool DebugClippings { get; set; } + #endregion } } From 7f61ebda7896d97685d9fa9dde0bccb69a68ba19 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 13 Jun 2025 00:35:59 +0800 Subject: [PATCH 11/27] =?UTF-8?q?=E4=BF=AE=E6=94=B9setproperty=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Models/SpineObjectModel.cs | 66 +++++++++---------- .../MainWindow/SFMLRendererViewModel.cs | 20 +++--- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/SpineViewer/Models/SpineObjectModel.cs b/SpineViewer/Models/SpineObjectModel.cs index 116d266..6501865 100644 --- a/SpineViewer/Models/SpineObjectModel.cs +++ b/SpineViewer/Models/SpineObjectModel.cs @@ -87,13 +87,13 @@ namespace SpineViewer.Models public bool UsePma { get { lock (_lock) return _spineObject.UsePma; } - set { lock (_lock) SetProperty(_spineObject.UsePma, value, _spineObject, (m, v) => m.UsePma = v); } + set { lock (_lock) SetProperty(_spineObject.UsePma, value, v => _spineObject.UsePma = v); } } public ISkeleton.Physics Physics { get { lock (_lock) return _spineObject.Physics; } - set { lock (_lock) SetProperty(_spineObject.Physics, value, _spineObject, (m, v) => m.Physics = v); } + set { lock (_lock) SetProperty(_spineObject.Physics, value, v => _spineObject.Physics = v); } } /// @@ -128,7 +128,7 @@ namespace SpineViewer.Models public bool FlipX { get { lock (_lock) return _spineObject.Skeleton.ScaleX < 0; } - set { lock (_lock) SetProperty(_spineObject.Skeleton.ScaleX < 0, value, _spineObject, (m, v) => m.Skeleton.ScaleX *= -1); } + set { lock (_lock) SetProperty(_spineObject.Skeleton.ScaleX < 0, value, v => _spineObject.Skeleton.ScaleX *= -1); } } /// @@ -137,19 +137,19 @@ namespace SpineViewer.Models public bool FlipY { get { lock (_lock) return _spineObject.Skeleton.ScaleY < 0; } - set { lock (_lock) SetProperty(_spineObject.Skeleton.ScaleY < 0, value, _spineObject, (m, v) => m.Skeleton.ScaleY *= -1); } + set { lock (_lock) SetProperty(_spineObject.Skeleton.ScaleY < 0, value, v => _spineObject.Skeleton.ScaleY *= -1); } } public float X { get { lock (_lock) return _spineObject.Skeleton.X; } - set { lock (_lock) SetProperty(_spineObject.Skeleton.X, value, _spineObject, (m, v) => m.Skeleton.X = v); } + set { lock (_lock) SetProperty(_spineObject.Skeleton.X, value, v => _spineObject.Skeleton.X = v); } } public float Y { get { lock (_lock) return _spineObject.Skeleton.Y; } - set { lock (_lock) SetProperty(_spineObject.Skeleton.Y, value, _spineObject, (m, v) => m.Skeleton.Y = v); } + set { lock (_lock) SetProperty(_spineObject.Skeleton.Y, value, v => _spineObject.Skeleton.Y = v); } } public ImmutableArray Skins => _skins; @@ -242,67 +242,67 @@ namespace SpineViewer.Models public bool EnableDebug { get { lock (_lock) return _spineObject.EnableDebug; } - set { lock (_lock) SetProperty(_spineObject.EnableDebug, value, _spineObject, (m, v) => m.EnableDebug = v); } + set { lock (_lock) SetProperty(_spineObject.EnableDebug, value, v => _spineObject.EnableDebug = v); } } public bool DebugTexture { get { lock (_lock) return _spineObject.DebugTexture; } - set { lock (_lock) SetProperty(_spineObject.DebugTexture, value, _spineObject, (m, v) => m.DebugTexture = v); } + set { lock (_lock) SetProperty(_spineObject.DebugTexture, value, v => _spineObject.DebugTexture = v); } } public bool DebugBounds { get { lock (_lock) return _spineObject.DebugBounds; } - set { lock (_lock) SetProperty(_spineObject.DebugBounds, value, _spineObject, (m, v) => m.DebugBounds = v); } + set { lock (_lock) SetProperty(_spineObject.DebugBounds, value, v => _spineObject.DebugBounds = v); } } public bool DebugBones { get { lock (_lock) return _spineObject.DebugBones; } - set { lock (_lock) SetProperty(_spineObject.DebugBones, value, _spineObject, (m, v) => m.DebugBones = v); } + set { lock (_lock) SetProperty(_spineObject.DebugBones, value, v => _spineObject.DebugBones = v); } } public bool DebugRegions { get { lock (_lock) return _spineObject.DebugRegions; } - set { lock (_lock) SetProperty(_spineObject.DebugRegions, value, _spineObject, (m, v) => m.DebugRegions = v); } + set { lock (_lock) SetProperty(_spineObject.DebugRegions, value, v => _spineObject.DebugRegions = v); } } public bool DebugMeshHulls { get { lock (_lock) return _spineObject.DebugMeshHulls; } - set { lock (_lock) SetProperty(_spineObject.DebugMeshHulls, value, _spineObject, (m, v) => m.DebugMeshHulls = v); } + set { lock (_lock) SetProperty(_spineObject.DebugMeshHulls, value, v => _spineObject.DebugMeshHulls = v); } } public bool DebugMeshes { get { lock (_lock) return _spineObject.DebugMeshes; } - set { lock (_lock) SetProperty(_spineObject.DebugMeshes, value, _spineObject, (m, v) => m.DebugMeshes = v); } + set { lock (_lock) SetProperty(_spineObject.DebugMeshes, value, v => _spineObject.DebugMeshes = v); } } public bool DebugBoundingBoxes { get { lock (_lock) return _spineObject.DebugBoundingBoxes; } - set { lock (_lock) SetProperty(_spineObject.DebugBoundingBoxes, value, _spineObject, (m, v) => m.DebugBoundingBoxes = v); } + set { lock (_lock) SetProperty(_spineObject.DebugBoundingBoxes, value, v => _spineObject.DebugBoundingBoxes = v); } } public bool DebugPaths { get { lock (_lock) return _spineObject.DebugPaths; } - set { lock (_lock) SetProperty(_spineObject.DebugPaths, value, _spineObject, (m, v) => m.DebugPaths = v); } + set { lock (_lock) SetProperty(_spineObject.DebugPaths, value, v => _spineObject.DebugPaths = v); } } public bool DebugPoints { get { lock (_lock) return _spineObject.DebugPoints; } - set { lock (_lock) SetProperty(_spineObject.DebugPoints, value, _spineObject, (m, v) => m.DebugPoints = v); } + set { lock (_lock) SetProperty(_spineObject.DebugPoints, value, v => _spineObject.DebugPoints = v); } } public bool DebugClippings { get { lock (_lock) return _spineObject.DebugClippings; } - set { lock (_lock) SetProperty(_spineObject.DebugClippings, value, _spineObject, (m, v) => m.DebugClippings = v); } + set { lock (_lock) SetProperty(_spineObject.DebugClippings, value, v => _spineObject.DebugClippings = v); } } public void Update(float delta) @@ -376,12 +376,12 @@ namespace SpineViewer.Models _spineObject.Skeleton.ScaleX = config.Scale; _spineObject.Skeleton.ScaleY = config.Scale; OnPropertyChanged(nameof(Scale)); - SetProperty(_spineObject.Skeleton.ScaleX < 0, config.FlipX, _spineObject, (m, v) => m.Skeleton.ScaleX *= -1, nameof(FlipX)); - SetProperty(_spineObject.Skeleton.ScaleY < 0, config.FlipY, _spineObject, (m, v) => m.Skeleton.ScaleY *= -1, nameof(FlipY)); - SetProperty(_spineObject.Skeleton.X, config.X, _spineObject, (m, v) => m.Skeleton.X = v, nameof(X)); - SetProperty(_spineObject.Skeleton.Y, config.Y, _spineObject, (m, v) => m.Skeleton.Y = v, nameof(Y)); - SetProperty(_spineObject.UsePma, config.UsePma, _spineObject, (m, v) => m.UsePma = v, nameof(UsePma)); - SetProperty(_spineObject.Physics, Enum.Parse(config.Physics ?? "Update", true), _spineObject, (m, v) => m.Physics = v, nameof(Physics)); + SetProperty(_spineObject.Skeleton.ScaleX < 0, config.FlipX, v => _spineObject.Skeleton.ScaleX *= -1, nameof(FlipX)); + SetProperty(_spineObject.Skeleton.ScaleY < 0, config.FlipY, v => _spineObject.Skeleton.ScaleY *= -1, nameof(FlipY)); + SetProperty(_spineObject.Skeleton.X, config.X, v => _spineObject.Skeleton.X = v, nameof(X)); + SetProperty(_spineObject.Skeleton.Y, config.Y, v => _spineObject.Skeleton.Y = v, nameof(Y)); + SetProperty(_spineObject.UsePma, config.UsePma, v => _spineObject.UsePma = v, nameof(UsePma)); + SetProperty(_spineObject.Physics, Enum.Parse(config.Physics ?? "Update", true), v => _spineObject.Physics = v, nameof(Physics)); foreach (var name in _spineObject.Data.Skins.Select(v => v.Name).Except(config.LoadedSkins)) if (_spineObject.SetSkinStatus(name, false)) @@ -405,16 +405,16 @@ namespace SpineViewer.Models trackIndex++; } - SetProperty(_spineObject.DebugTexture, config.DebugTexture, _spineObject, (m, v) => m.DebugTexture = v, nameof(DebugTexture)); - SetProperty(_spineObject.DebugBounds, config.DebugBounds, _spineObject, (m, v) => m.DebugBounds = v, nameof(DebugBounds)); - SetProperty(_spineObject.DebugBones, config.DebugBones, _spineObject, (m, v) => m.DebugBones = v, nameof(DebugBones)); - SetProperty(_spineObject.DebugRegions, config.DebugRegions, _spineObject, (m, v) => m.DebugRegions = v, nameof(DebugRegions)); - SetProperty(_spineObject.DebugMeshHulls, config.DebugMeshHulls, _spineObject, (m, v) => m.DebugMeshHulls = v, nameof(DebugMeshHulls)); - SetProperty(_spineObject.DebugMeshes, config.DebugMeshes, _spineObject, (m, v) => m.DebugMeshes = v, nameof(DebugMeshes)); - SetProperty(_spineObject.DebugBoundingBoxes, config.DebugBoundingBoxes, _spineObject, (m, v) => m.DebugBoundingBoxes = v, nameof(DebugBoundingBoxes)); - SetProperty(_spineObject.DebugPaths, config.DebugPaths, _spineObject, (m, v) => m.DebugPaths = v, nameof(DebugPaths)); - SetProperty(_spineObject.DebugPoints, config.DebugPoints, _spineObject, (m, v) => m.DebugPoints = v, nameof(DebugPoints)); - SetProperty(_spineObject.DebugClippings, config.DebugClippings, _spineObject, (m, v) => m.DebugClippings = v, nameof(DebugClippings)); + SetProperty(_spineObject.DebugTexture, config.DebugTexture, v => _spineObject.DebugTexture = v, nameof(DebugTexture)); + SetProperty(_spineObject.DebugBounds, config.DebugBounds, v => _spineObject.DebugBounds = v, nameof(DebugBounds)); + SetProperty(_spineObject.DebugBones, config.DebugBones, v => _spineObject.DebugBones = v, nameof(DebugBones)); + SetProperty(_spineObject.DebugRegions, config.DebugRegions, v => _spineObject.DebugRegions = v, nameof(DebugRegions)); + SetProperty(_spineObject.DebugMeshHulls, config.DebugMeshHulls, v => _spineObject.DebugMeshHulls = v, nameof(DebugMeshHulls)); + SetProperty(_spineObject.DebugMeshes, config.DebugMeshes, v => _spineObject.DebugMeshes = v, nameof(DebugMeshes)); + SetProperty(_spineObject.DebugBoundingBoxes, config.DebugBoundingBoxes, v => _spineObject.DebugBoundingBoxes = v, nameof(DebugBoundingBoxes)); + SetProperty(_spineObject.DebugPaths, config.DebugPaths, v => _spineObject.DebugPaths = v, nameof(DebugPaths)); + SetProperty(_spineObject.DebugPoints, config.DebugPoints, v => _spineObject.DebugPoints = v, nameof(DebugPoints)); + SetProperty(_spineObject.DebugClippings, config.DebugClippings, v => _spineObject.DebugClippings = v, nameof(DebugClippings)); } } diff --git a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs index 6441782..7125d4d 100644 --- a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs @@ -88,55 +88,55 @@ namespace SpineViewer.ViewModels.MainWindow public uint ResolutionX { get => _renderer.Resolution.X; - set => SetProperty(_renderer.Resolution.X, value, _renderer, (r, v) => r.Resolution = new(v, r.Resolution.Y)); + set => SetProperty(_renderer.Resolution.X, value, v => _renderer.Resolution = new(v, _renderer.Resolution.Y)); } public uint ResolutionY { get => _renderer.Resolution.Y; - set => SetProperty(_renderer.Resolution.Y, value, _renderer, (r, v) => r.Resolution = new(r.Resolution.X, v)); + set => SetProperty(_renderer.Resolution.Y, value, v => _renderer.Resolution = new(_renderer.Resolution.X, v)); } public float CenterX { get => _renderer.Center.X; - set => SetProperty(_renderer.Center.X, value, _renderer, (r, v) => r.Center = new(v, r.Center.Y)); + set => SetProperty(_renderer.Center.X, value, v => _renderer.Center = new(v, _renderer.Center.Y)); } public float CenterY { get => _renderer.Center.Y; - set => SetProperty(_renderer.Center.Y, value, _renderer, (r, v) => r.Center = new(r.Center.X, v)); + set => SetProperty(_renderer.Center.Y, value, v => _renderer.Center = new(_renderer.Center.X, v)); } public float Zoom { get => _renderer.Zoom; - set => SetProperty(_renderer.Zoom, value, _renderer, (r, v) => r.Zoom = value); + set => SetProperty(_renderer.Zoom, value, v => _renderer.Zoom = value); } public float Rotation { get => _renderer.Rotation; - set => SetProperty(_renderer.Rotation, value, _renderer, (r, v) => r.Rotation = value); + set => SetProperty(_renderer.Rotation, value, v => _renderer.Rotation = value); } public bool FlipX { get => _renderer.FlipX; - set => SetProperty(_renderer.FlipX, value, _renderer, (r, v) => r.FlipX = value); + set => SetProperty(_renderer.FlipX, value, v => _renderer.FlipX = value); } public bool FlipY { get => _renderer.FlipY; - set => SetProperty(_renderer.FlipY, value, _renderer, (r, v) => r.FlipY = value); + set => SetProperty(_renderer.FlipY, value, v => _renderer.FlipY = value); } public uint MaxFps { get => _renderer.MaxFps; - set => SetProperty(_renderer.MaxFps, value, _renderer, (r, v) => r.MaxFps = value); + set => SetProperty(_renderer.MaxFps, value, v => _renderer.MaxFps = value); } public bool ShowAxis @@ -149,7 +149,7 @@ namespace SpineViewer.ViewModels.MainWindow public Color BackgroundColor { get => Color.FromRgb(_backgroundColor.R, _backgroundColor.G, _backgroundColor.B); - set => SetProperty(BackgroundColor, value, this, (m, v) => m._backgroundColor = new(value.R, value.G, value.B)); + set => SetProperty(BackgroundColor, value, v => _backgroundColor = new(value.R, value.G, value.B)); } private SFML.Graphics.Color _backgroundColor = new(105, 105, 105); From 125ce6fa86b22e79c887fc3f6365c6966ad66a62 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 13 Jun 2025 23:09:53 +0800 Subject: [PATCH 12/27] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E9=87=8C=E7=9A=84=E7=89=88=E6=9C=AC=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Models/SpineObjectModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SpineViewer/Models/SpineObjectModel.cs b/SpineViewer/Models/SpineObjectModel.cs index 6501865..069d267 100644 --- a/SpineViewer/Models/SpineObjectModel.cs +++ b/SpineViewer/Models/SpineObjectModel.cs @@ -40,9 +40,9 @@ namespace SpineViewer.Models /// /// 构造函数, 可能会抛出异常 /// - public SpineObjectModel(string skelPath, string? atlasPath = null, SpineVersion? version = null) + public SpineObjectModel(string skelPath, string? atlasPath = null) { - _spineObject = new(skelPath, atlasPath, version); + _spineObject = new(skelPath, atlasPath); _skins = _spineObject.Data.Skins.Select(v => v.Name).ToImmutableArray(); _slotAttachments = _spineObject.Data.SlotAttachments.ToFrozenDictionary(it => it.Key, it => it.Value.Keys); _animations = _spineObject.Data.Animations.Select(v => v.Name).ToImmutableArray(); From b3010360b42eb2a8f3231fb69d7095c1879a73a6 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 13 Jun 2025 23:12:15 +0800 Subject: [PATCH 13/27] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A6=96=E9=80=89?= =?UTF-8?q?=E9=A1=B9=E5=AF=B9=E8=AF=9D=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Models/PreferenceModel.cs | 106 ++++++++++ SpineViewer/Resources/Strings/en-us.xaml | 13 +- SpineViewer/Resources/Strings/ja-jp.xaml | 13 +- SpineViewer/Resources/Strings/zh-cn.xaml | 21 +- SpineViewer/Services/DialogService.cs | 10 + .../MainWindow/MainWindowViewModel.cs | 4 + .../MainWindow/PreferenceViewModel.cs | 191 ++++++++++++++++++ .../MainWindow/SpineObjectListViewModel.cs | 61 ++++-- SpineViewer/Views/MainWindow.xaml | 2 +- SpineViewer/Views/MainWindow.xaml.cs | 3 + SpineViewer/Views/PreferenceDialog.xaml | 132 ++++++++++++ SpineViewer/Views/PreferenceDialog.xaml.cs | 39 ++++ 12 files changed, 565 insertions(+), 30 deletions(-) create mode 100644 SpineViewer/Models/PreferenceModel.cs create mode 100644 SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs create mode 100644 SpineViewer/Views/PreferenceDialog.xaml create mode 100644 SpineViewer/Views/PreferenceDialog.xaml.cs diff --git a/SpineViewer/Models/PreferenceModel.cs b/SpineViewer/Models/PreferenceModel.cs new file mode 100644 index 0000000..0a4b87b --- /dev/null +++ b/SpineViewer/Models/PreferenceModel.cs @@ -0,0 +1,106 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using Spine.SpineWrappers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Threading.Tasks; +using System.Windows.Media; + +namespace SpineViewer.Models +{ + /// + /// 首选项参数模型, 用于对话框修改以及本地保存 + /// + public partial class PreferenceModel : ObservableObject + { + #region 纹理加载首选项 + + [ObservableProperty] + private bool _forcePremul; + + [ObservableProperty] + private bool _forceNearest; + + [ObservableProperty] + private bool _forceMipmap; + + #endregion + + #region 模型加载首选项 + + [ObservableProperty] + private bool _usePma; + + [ObservableProperty] + private bool _debugTexture = true; + + [ObservableProperty] + private bool _debugBounds; + + [ObservableProperty] + private bool _debugBones; + + [ObservableProperty] + private bool _debugRegions; + + [ObservableProperty] + private bool _debugMeshHulls; + + [ObservableProperty] + private bool _debugMeshes; + + [ObservableProperty] + private bool _debugBoundingBoxes; + + [ObservableProperty] + private bool _debugPaths; + + [ObservableProperty] + private bool _debugPoints; + + [ObservableProperty] + private bool _debugClippings; + + #endregion + + #region 序列化与反序列 + + /// + /// 保存 Json 文件的格式参数 + /// + private static readonly JsonSerializerOptions _jsonOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; + + /// + /// 从文件反序列对象, 可能抛出异常 + /// + public static PreferenceModel Deserialize(string path) + { + if (!File.Exists(path)) throw new FileNotFoundException("Preference file not found", path); + var json = File.ReadAllText(path, Encoding.UTF8); + var model = JsonSerializer.Deserialize(json, _jsonOptions); + return model ?? throw new JsonException($"null data in file '{path}'"); + } + + /// + /// 保存至文件, 可能抛出异常 + /// + public void Serialize(string path) + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); + var json = JsonSerializer.Serialize(this, _jsonOptions); + File.WriteAllText(path, json, Encoding.UTF8); + } + + #endregion + } +} diff --git a/SpineViewer/Resources/Strings/en-us.xaml b/SpineViewer/Resources/Strings/en-us.xaml index f46c305..48286f9 100644 --- a/SpineViewer/Resources/Strings/en-us.xaml +++ b/SpineViewer/Resources/Strings/en-us.xaml @@ -11,7 +11,8 @@ Experimental Features Open... - Preferences... + Preferences + Preferences... Exit @@ -194,4 +195,14 @@ Program Version Project URL + + Texture Loading Options + Force Premultiplied Channels + When enabled, this applies premultiplied operations to pixels during texture loading, helping to resolve black edge issues at some connections. + Force Nearest Interpolation + Force Mipmap + When enabled, this helps reduce aliasing when textures are scaled down, at the cost of slightly higher video memory usage. + + Model Loading Options + diff --git a/SpineViewer/Resources/Strings/ja-jp.xaml b/SpineViewer/Resources/Strings/ja-jp.xaml index 8eb4e4e..bbd9e7e 100644 --- a/SpineViewer/Resources/Strings/ja-jp.xaml +++ b/SpineViewer/Resources/Strings/ja-jp.xaml @@ -11,7 +11,8 @@ 実験機能 開く... - 設定... + 設定 + 設定... 終了 @@ -194,5 +195,15 @@ プログラムバージョン プロジェクトURL + + テクスチャ読み込みオプション + 強制プリマルチチャンネル + 有効にすると、テクスチャ読み込み時にピクセルにプリマルチ処理を適用し、一部の接続部分で発生する黒い縁の問題を解決します。 + Nearest補間を強制使用 + Mipmapを強制使用 + 有効にすると、テクスチャ縮小時のジャギーを軽減しますが、ビデオメモリの使用量が若干増加します。 + + モデル読み込みオプション + diff --git a/SpineViewer/Resources/Strings/zh-cn.xaml b/SpineViewer/Resources/Strings/zh-cn.xaml index 4c98840..ba76573 100644 --- a/SpineViewer/Resources/Strings/zh-cn.xaml +++ b/SpineViewer/Resources/Strings/zh-cn.xaml @@ -11,7 +11,8 @@ 实验性功能 打开... - 首选项... + 首选项 + 首选项... 退出 @@ -74,7 +75,7 @@ 插槽 清除附件 - + 动画 添加 插入 @@ -141,7 +142,7 @@ 使用自动分辨率时需要提供有效的最大分辨率 导出单个时导出时长不能为负数 必须指定 FFmpeg 导出格式 - + 画面分辨率,相关参数请在画面参数面板进行调整 导出单个 勾选后将所选模型在同一个画面上进行导出,且必须提供输出文件夹 @@ -185,7 +186,7 @@ FFmpeg 滤镜,等价于参数 “-vf” 自定义参数 FFmpeg 自定义参数,与命令行提供方式相同,例如 “-crf 23” - + 复制到剪贴板 已复制 @@ -193,5 +194,15 @@ 程序版本 项目地址 - + + + 纹理加载选项 + 强制预乘通道 + 开启后,会在加载纹理时对像素进行预乘操作,有助于解决某些情况下的连接处黑边问题 + 强制使用 Nearest 插值 + 强制使用 Mipmap + 开启后有助于改善纹理缩小时的锯齿现象,但是会略微增加显存占用 + + 模型加载选项 + \ No newline at end of file diff --git a/SpineViewer/Services/DialogService.cs b/SpineViewer/Services/DialogService.cs index 120fc89..02cb4c3 100644 --- a/SpineViewer/Services/DialogService.cs +++ b/SpineViewer/Services/DialogService.cs @@ -1,4 +1,5 @@ using Microsoft.Win32; +using SpineViewer.Models; using SpineViewer.ViewModels.Exporters; using SpineViewer.Views; using SpineViewer.Views.ExporterDialogs; @@ -51,6 +52,15 @@ namespace SpineViewer.Services return dialog.ShowDialog() ?? false; } + /// + /// 将给定的首选项参数在对话框上进行显示, 返回值表示是否确认修改 + /// + public static bool ShowPreferenceDialog(PreferenceModel m) + { + var dialog = new PreferenceDialog() { DataContext = m, Owner = App.Current.MainWindow }; + return dialog.ShowDialog() ?? false; + } + /// /// 获取用户选择的文件夹 /// diff --git a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs index 80d8968..04f8b81 100644 --- a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs @@ -34,6 +34,7 @@ namespace SpineViewer.ViewModels.MainWindow _explorerListViewModel = new(this); _spineObjectListViewModel = new(this); _sfmlRendererViewModel = new(this); + _preferenceViewModel = new(this); } public string Title => $"SpineViewer - v{App.Version}"; @@ -56,6 +57,9 @@ namespace SpineViewer.ViewModels.MainWindow public ObservableCollectionWithLock SpineObjects => _spineObjectModels; private readonly ObservableCollectionWithLock _spineObjectModels = []; + public PreferenceViewModel PreferenceViewModel => _preferenceViewModel; + private readonly PreferenceViewModel _preferenceViewModel; + /// /// 浏览页列表 ViewModel /// diff --git a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs new file mode 100644 index 0000000..e5f8529 --- /dev/null +++ b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs @@ -0,0 +1,191 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using NLog; +using Spine.SpineWrappers; +using SpineViewer.Models; +using SpineViewer.Services; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; + +namespace SpineViewer.ViewModels.MainWindow +{ + public class PreferenceViewModel : ObservableObject + { + /// + /// 文件保存路径 + /// + public static readonly string PreferenceFilePath = Path.Combine(Path.GetDirectoryName(Environment.ProcessPath), "preference.json"); + + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + private readonly MainWindowViewModel _vmMain; + + public PreferenceViewModel(MainWindowViewModel vmMain) + { + _vmMain = vmMain; + } + + /// + /// 显示首选项对话框 + /// + public RelayCommand Cmd_ShowPreferenceDialog => _cmd_ShowPreferenceDialog ??= new(ShowPreferenceDialog_Execute); + private RelayCommand? _cmd_ShowPreferenceDialog; + + private void ShowPreferenceDialog_Execute() + { + var m = Preference; + if (!DialogService.ShowPreferenceDialog(m)) + return; + + Preference = m; + SavePreference(m); + } + + private static void SavePreference(PreferenceModel m) + { + try + { + m.Serialize(PreferenceFilePath); + } + catch (Exception ex) + { + _logger.Error("Failed to save preference to {0}, {1}", PreferenceFilePath, ex.Message); + _logger.Trace(ex.ToString()); + } + } + + /// + /// 保存首选项, 保存失败会有日志提示 + /// + public void SavePreference() => SavePreference(Preference); + + /// + /// 加载首选项, 加载失败会有日志提示 + /// + public void LoadPreference() + { + if (!File.Exists(PreferenceFilePath)) return; + + try + { + var m = PreferenceModel.Deserialize(PreferenceFilePath); + Preference = m; + } + catch (Exception ex) + { + _logger.Error("Failed to load preference from {0}, {1}", PreferenceFilePath, ex.Message); + _logger.Trace(ex.ToString()); + } + } + + /// + /// 获取参数副本或者进行设置 + /// + private PreferenceModel Preference + { + get + { + return new() + { + ForcePremul = ForcePremul, + ForceNearest = ForceNearest, + ForceMipmap = ForceMipmap, + UsePma = UsePma, + DebugTexture = DebugTexture, + DebugBounds = DebugBounds, + DebugBones = DebugBones, + DebugRegions = DebugRegions, + DebugMeshHulls = DebugMeshHulls, + DebugMeshes = DebugMeshes, + DebugBoundingBoxes = DebugBoundingBoxes, + DebugPaths = DebugPaths, + DebugPoints = DebugPoints, + DebugClippings = DebugClippings + }; + } + set + { + ForcePremul = value.ForcePremul; + ForceNearest = value.ForceNearest; + ForceMipmap = value.ForceMipmap; + UsePma = value.UsePma; + DebugTexture = value.DebugTexture; + DebugBounds = value.DebugBounds; + DebugBones = value.DebugBones; + DebugRegions = value.DebugRegions; + DebugMeshHulls = value.DebugMeshHulls; + DebugMeshes = value.DebugMeshes; + DebugBoundingBoxes = value.DebugBoundingBoxes; + DebugPaths = value.DebugPaths; + DebugPoints = value.DebugPoints; + DebugClippings = value.DebugClippings; + } + } + + #region 纹理加载首选项 + + public bool ForcePremul + { + get => TextureLoader.DefaultLoader.ForcePremul; + set => SetProperty(TextureLoader.DefaultLoader.ForcePremul, value, v => TextureLoader.DefaultLoader.ForcePremul = v); + } + + public bool ForceNearest + { + get => TextureLoader.DefaultLoader.ForceNearest; + set => SetProperty(TextureLoader.DefaultLoader.ForceNearest, value, v => TextureLoader.DefaultLoader.ForceNearest = v); + } + + public bool ForceMipmap + { + get => TextureLoader.DefaultLoader.ForceMipmap; + set => SetProperty(TextureLoader.DefaultLoader.ForceMipmap, value, v => TextureLoader.DefaultLoader.ForceMipmap = v); + } + + #endregion + + // TODO: 是否自动记忆模型参数 + + #region 模型加载首选项 + + public bool UsePma { get => _usePma; set => SetProperty(ref _usePma, value); } + private bool _usePma; + + public bool DebugTexture { get => _debugTexture; set => SetProperty(ref _debugTexture, value); } + private bool _debugTexture = true; + + public bool DebugBounds { get => _debugBounds; set => SetProperty(ref _debugBounds, value); } + private bool _debugBounds; + + public bool DebugBones { get => _debugBones; set => SetProperty(ref _debugBones, value); } + private bool _debugBones; + + public bool DebugRegions { get => _debugRegions; set => SetProperty(ref _debugRegions, value); } + private bool _debugRegions; + + public bool DebugMeshHulls { get => _debugMeshHulls; set => SetProperty(ref _debugMeshHulls, value); } + private bool _debugMeshHulls; + + public bool DebugMeshes { get => _debugMeshes; set => SetProperty(ref _debugMeshes, value); } + private bool _debugMeshes; + + public bool DebugBoundingBoxes { get => _debugBoundingBoxes; set => SetProperty(ref _debugBoundingBoxes, value); } + private bool _debugBoundingBoxes; + + public bool DebugPaths { get => _debugPaths; set => SetProperty(ref _debugPaths, value); } + private bool _debugPaths; + + public bool DebugPoints { get => _debugPoints; set => SetProperty(ref _debugPoints, value); } + private bool _debugPoints; + + public bool DebugClippings { get => _debugClippings; set => SetProperty(ref _debugClippings, value); } + private bool _debugClippings; + + #endregion + } +} diff --git a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs index 3904f41..c860b0c 100644 --- a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs @@ -121,7 +121,7 @@ namespace SpineViewer.ViewModels.MainWindow lock (_spineObjectModels.Lock) { - // XXX: 这里必须要浅拷贝一次, 不能直接对会被修改的绑定数据 args 进行 foreach 遍历 + // NOTE: 这里必须要浅拷贝一次, 不能直接对会被修改的绑定数据 args 进行 foreach 遍历 foreach (var sp in args.Cast().ToArray()) { _spineObjectModels.Remove(sp); @@ -249,6 +249,41 @@ namespace SpineViewer.ViewModels.MainWindow return true; } + /// + /// 安全地在末尾添加一个模型, 发生错误会输出日志 + /// + /// 是否添加成功 + public bool AddSpineObject(string skelPath, string? atlasPath = null) + { + try + { + // TODO: 判断是否记忆参数 + var pre = _vmMain.PreferenceViewModel; + var sp = new SpineObjectModel(skelPath, atlasPath) + { + UsePma = pre.UsePma, + DebugTexture = pre.DebugTexture, + DebugBounds = pre.DebugBounds, + DebugRegions = pre.DebugRegions, + DebugMeshHulls = pre.DebugMeshHulls, + DebugMeshes = pre.DebugMeshes, + DebugBoundingBoxes = pre.DebugBoundingBoxes, + DebugPaths = pre.DebugPaths, + DebugPoints = pre.DebugPoints, + DebugClippings = pre.DebugClippings + }; + + lock (_spineObjectModels.Lock) _spineObjectModels.Add(sp); + return true; + } + catch (Exception ex) + { + _logger.Trace(ex.ToString()); + _logger.Error("Failed to load: {0}, {1}", skelPath, ex.Message); + } + return false; + } + /// /// 从路径列表添加对象 /// @@ -289,17 +324,7 @@ namespace SpineViewer.ViewModels.MainWindow } else if (validPaths.Count > 0) { - var skelPath = validPaths[0]; - try - { - var sp = new SpineObjectModel(skelPath); - lock (_spineObjectModels.Lock) _spineObjectModels.Add(sp); - } - catch (Exception ex) - { - _logger.Trace(ex.ToString()); - _logger.Error("Failed to load: {0}, {1}", skelPath, ex.Message); - } + AddSpineObject(validPaths[0]); _logger.LogCurrentProcessMemoryUsage(); } } @@ -326,18 +351,10 @@ namespace SpineViewer.ViewModels.MainWindow var skelPath = paths[i]; reporter.ProgressText = $"[{i}/{totalCount}] {skelPath}"; - try - { - var sp = new SpineObjectModel(skelPath); - lock (_spineObjectModels.Lock) _spineObjectModels.Add(sp); + if (AddSpineObject(skelPath)) success++; - } - catch (Exception ex) - { - _logger.Trace(ex.ToString()); - _logger.Error("Failed to load: {0}, {1}", skelPath, ex.Message); + else error++; - } reporter.Done = i + 1; reporter.ProgressText = $"[{i + 1}/{totalCount}] {skelPath}"; diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml index 9df47f3..d6a82c1 100644 --- a/SpineViewer/Views/MainWindow.xaml +++ b/SpineViewer/Views/MainWindow.xaml @@ -58,7 +58,7 @@ - + diff --git a/SpineViewer/Views/MainWindow.xaml.cs b/SpineViewer/Views/MainWindow.xaml.cs index e83f61d..31e92ce 100644 --- a/SpineViewer/Views/MainWindow.xaml.cs +++ b/SpineViewer/Views/MainWindow.xaml.cs @@ -61,6 +61,9 @@ public partial class MainWindow : Window vm.FlipY = true; vm.MaxFps = 30; vm.StartRender(); + + // 加载首选项 + _vm.PreferenceViewModel.LoadPreference(); } private void MainWindow_Closed(object? sender, EventArgs e) diff --git a/SpineViewer/Views/PreferenceDialog.xaml b/SpineViewer/Views/PreferenceDialog.xaml new file mode 100644 index 0000000..524db37 --- /dev/null +++ b/SpineViewer/Views/PreferenceDialog.xaml @@ -0,0 +1,132 @@ + + + + + + + + public SpineObjectModel(string skelPath, string? atlasPath = null) { - _spineObject = new(skelPath, atlasPath); + _spineObject = new(skelPath, atlasPath) + { + UsePma = _loadOptions.UsePma, + DebugTexture = _loadOptions.DebugTexture, + DebugBounds = _loadOptions.DebugBounds, + DebugRegions = _loadOptions.DebugRegions, + DebugMeshHulls = _loadOptions.DebugMeshHulls, + DebugMeshes = _loadOptions.DebugMeshes, + DebugBoundingBoxes = _loadOptions.DebugBoundingBoxes, + DebugPaths = _loadOptions.DebugPaths, + DebugPoints = _loadOptions.DebugPoints, + DebugClippings = _loadOptions.DebugClippings + }; + _skins = _spineObject.Data.Skins.Select(v => v.Name).ToImmutableArray(); _slotAttachments = _spineObject.Data.SlotAttachments.ToFrozenDictionary(it => it.Key, it => it.Value.Keys); _animations = _spineObject.Data.Animations.Select(v => v.Name).ToImmutableArray(); @@ -475,4 +494,20 @@ namespace SpineViewer.Models public int TrackIndex { get; } = trackIndex; public string? AnimationName { get; } = animationName; } + + public class SpineObjectLoadOptions + { + public bool AutoSave { get; set; } + public bool UsePma { get; set; } + public bool DebugTexture { get; set; } = true; + public bool DebugBounds { get; set; } + public bool DebugBones { get; set; } + public bool DebugRegions { get; set; } + public bool DebugMeshHulls { get; set; } + public bool DebugMeshes { get; set; } + public bool DebugBoundingBoxes { get; set; } + public bool DebugPaths { get; set; } + public bool DebugPoints { get; set; } + public bool DebugClippings { get; set; } + } } diff --git a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs index d8c35fd..32117b7 100644 --- a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs @@ -159,68 +159,68 @@ namespace SpineViewer.ViewModels.MainWindow public bool UsePma { - get => SpineObjectListViewModel.LoadOptions.UsePma; - set => SetProperty(SpineObjectListViewModel.LoadOptions.UsePma, value, v => SpineObjectListViewModel.LoadOptions.UsePma = v); + get => SpineObjectModel.LoadOptions.UsePma; + set => SetProperty(SpineObjectModel.LoadOptions.UsePma, value, v => SpineObjectModel.LoadOptions.UsePma = v); } public bool DebugTexture { - get => SpineObjectListViewModel.LoadOptions.DebugTexture; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugTexture, value, v => SpineObjectListViewModel.LoadOptions.DebugTexture = v); + get => SpineObjectModel.LoadOptions.DebugTexture; + set => SetProperty(SpineObjectModel.LoadOptions.DebugTexture, value, v => SpineObjectModel.LoadOptions.DebugTexture = v); } public bool DebugBounds { - get => SpineObjectListViewModel.LoadOptions.DebugBounds; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugBounds, value, v => SpineObjectListViewModel.LoadOptions.DebugBounds = v); + get => SpineObjectModel.LoadOptions.DebugBounds; + set => SetProperty(SpineObjectModel.LoadOptions.DebugBounds, value, v => SpineObjectModel.LoadOptions.DebugBounds = v); } public bool DebugBones { - get => SpineObjectListViewModel.LoadOptions.DebugBones; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugBones, value, v => SpineObjectListViewModel.LoadOptions.DebugBones = v); + get => SpineObjectModel.LoadOptions.DebugBones; + set => SetProperty(SpineObjectModel.LoadOptions.DebugBones, value, v => SpineObjectModel.LoadOptions.DebugBones = v); } public bool DebugRegions { - get => SpineObjectListViewModel.LoadOptions.DebugRegions; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugRegions, value, v => SpineObjectListViewModel.LoadOptions.DebugRegions = v); + get => SpineObjectModel.LoadOptions.DebugRegions; + set => SetProperty(SpineObjectModel.LoadOptions.DebugRegions, value, v => SpineObjectModel.LoadOptions.DebugRegions = v); } public bool DebugMeshHulls { - get => SpineObjectListViewModel.LoadOptions.DebugMeshHulls; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugMeshHulls, value, v => SpineObjectListViewModel.LoadOptions.DebugMeshHulls = v); + get => SpineObjectModel.LoadOptions.DebugMeshHulls; + set => SetProperty(SpineObjectModel.LoadOptions.DebugMeshHulls, value, v => SpineObjectModel.LoadOptions.DebugMeshHulls = v); } public bool DebugMeshes { - get => SpineObjectListViewModel.LoadOptions.DebugMeshes; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugMeshes, value, v => SpineObjectListViewModel.LoadOptions.DebugMeshes = v); + get => SpineObjectModel.LoadOptions.DebugMeshes; + set => SetProperty(SpineObjectModel.LoadOptions.DebugMeshes, value, v => SpineObjectModel.LoadOptions.DebugMeshes = v); } public bool DebugBoundingBoxes { - get => SpineObjectListViewModel.LoadOptions.DebugBoundingBoxes; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugBoundingBoxes, value, v => SpineObjectListViewModel.LoadOptions.DebugBoundingBoxes = v); + get => SpineObjectModel.LoadOptions.DebugBoundingBoxes; + set => SetProperty(SpineObjectModel.LoadOptions.DebugBoundingBoxes, value, v => SpineObjectModel.LoadOptions.DebugBoundingBoxes = v); } public bool DebugPaths { - get => SpineObjectListViewModel.LoadOptions.DebugPaths; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugPaths, value, v => SpineObjectListViewModel.LoadOptions.DebugPaths = v); + get => SpineObjectModel.LoadOptions.DebugPaths; + set => SetProperty(SpineObjectModel.LoadOptions.DebugPaths, value, v => SpineObjectModel.LoadOptions.DebugPaths = v); } public bool DebugPoints { - get => SpineObjectListViewModel.LoadOptions.DebugPoints; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugPoints, value, v => SpineObjectListViewModel.LoadOptions.DebugPoints = v); + get => SpineObjectModel.LoadOptions.DebugPoints; + set => SetProperty(SpineObjectModel.LoadOptions.DebugPoints, value, v => SpineObjectModel.LoadOptions.DebugPoints = v); } public bool DebugClippings { - get => SpineObjectListViewModel.LoadOptions.DebugClippings; - set => SetProperty(SpineObjectListViewModel.LoadOptions.DebugClippings, value, v => SpineObjectListViewModel.LoadOptions.DebugClippings = v); + get => SpineObjectModel.LoadOptions.DebugClippings; + set => SetProperty(SpineObjectModel.LoadOptions.DebugClippings, value, v => SpineObjectModel.LoadOptions.DebugClippings = v); } #endregion diff --git a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs index 031ead1..ac6db0f 100644 --- a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs @@ -21,12 +21,6 @@ namespace SpineViewer.ViewModels.MainWindow { public class SpineObjectListViewModel : ObservableObject { - /// - /// 加载选项 - /// - public static SpineLoadOptions LoadOptions => _loadOptions; - private static readonly SpineLoadOptions _loadOptions = new(); - private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); /// @@ -349,20 +343,7 @@ namespace SpineViewer.ViewModels.MainWindow { try { - var sp = new SpineObjectModel(skelPath, atlasPath) - { - UsePma = _loadOptions.UsePma, - DebugTexture = _loadOptions.DebugTexture, - DebugBounds = _loadOptions.DebugBounds, - DebugRegions = _loadOptions.DebugRegions, - DebugMeshHulls = _loadOptions.DebugMeshHulls, - DebugMeshes = _loadOptions.DebugMeshes, - DebugBoundingBoxes = _loadOptions.DebugBoundingBoxes, - DebugPaths = _loadOptions.DebugPaths, - DebugPoints = _loadOptions.DebugPoints, - DebugClippings = _loadOptions.DebugClippings - }; - + var sp = new SpineObjectModel(skelPath, atlasPath); lock (_spineObjectModels.Lock) _spineObjectModels.Add(sp); return true; } @@ -373,21 +354,5 @@ namespace SpineViewer.ViewModels.MainWindow } return false; } - - public class SpineLoadOptions - { - // TODO: 判断是否记忆参数 - public bool UsePma { get; set; } - public bool DebugTexture { get; set; } = true; - public bool DebugBounds { get; set; } - public bool DebugBones { get; set; } - public bool DebugRegions { get; set; } - public bool DebugMeshHulls { get; set; } - public bool DebugMeshes { get; set; } - public bool DebugBoundingBoxes { get; set; } - public bool DebugPaths { get; set; } - public bool DebugPoints { get; set; } - public bool DebugClippings { get; set; } - } } } From 333c5e99814e7d314704f57cd8171c76685f3eb3 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Sat, 14 Jun 2025 11:26:09 +0800 Subject: [PATCH 18/27] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E9=A1=B9=E8=BF=9B=E8=A1=8C=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Extensions/SpineObjectExtension.cs | 2 +- .../ViewModels/MainWindow/SpineObjectTabViewModel.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SpineViewer/Extensions/SpineObjectExtension.cs b/SpineViewer/Extensions/SpineObjectExtension.cs index 66a02b2..2d25ef8 100644 --- a/SpineViewer/Extensions/SpineObjectExtension.cs +++ b/SpineViewer/Extensions/SpineObjectExtension.cs @@ -49,7 +49,7 @@ namespace SpineViewer.Extensions public static Rect GetCurrentBounds(this SpineObject self) { self.Skeleton.GetBounds(out var x, out var y, out var w, out var h); - return new(x, y, w, h); + return new(x, y, Math.Max(w, 1e-6f), Math.Max(h, 1e-6f)); } /// diff --git a/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs index c092a14..6b24919 100644 --- a/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs @@ -49,15 +49,15 @@ namespace SpineViewer.ViewModels.MainWindow IEnumerable commonSkinNames = _selectedObjects[0].Skins; foreach (var obj in _selectedObjects.Skip(1)) commonSkinNames = commonSkinNames.Intersect(obj.Skins); - foreach (var name in commonSkinNames) _skins.Add(new(name, _selectedObjects)); + foreach (var name in commonSkinNames.Order()) _skins.Add(new(name, _selectedObjects)); IEnumerable commonSlotNames = _selectedObjects[0].SlotAttachments.Keys; foreach (var obj in _selectedObjects.Skip(1)) commonSlotNames = commonSlotNames.Intersect(obj.SlotAttachments.Keys); - foreach (var name in commonSlotNames) _slots.Add(new(name, _selectedObjects)); + foreach (var name in commonSlotNames.Order()) _slots.Add(new(name, _selectedObjects)); IEnumerable commonTrackIndices = _selectedObjects[0].GetTrackIndices(); foreach (var obj in _selectedObjects.Skip(1)) commonTrackIndices = commonTrackIndices.Intersect(obj.GetTrackIndices()); - foreach (var idx in commonTrackIndices) _animationTracks.Add(new(idx, _selectedObjects)); + foreach (var idx in commonTrackIndices.Order()) _animationTracks.Add(new(idx, _selectedObjects)); } OnPropertyChanged(); From 2287542522ae7620923ebb03c34ab8b2ec43a916 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Sat, 14 Jun 2025 11:33:00 +0800 Subject: [PATCH 19/27] small change --- SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs index 32117b7..49d806d 100644 --- a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs @@ -155,8 +155,6 @@ namespace SpineViewer.ViewModels.MainWindow #region 模型加载首选项 - // TODO: 是否自动记忆模型参数 - public bool UsePma { get => SpineObjectModel.LoadOptions.UsePma; From 5f189a066d802129874dfded4804866d68dddd3b Mon Sep 17 00:00:00 2001 From: ww-rm Date: Sat, 14 Jun 2025 20:00:25 +0800 Subject: [PATCH 20/27] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=9D=99=E6=80=81?= =?UTF-8?q?=E7=94=BB=E9=9D=A2=E7=9A=84=E8=BE=B9=E7=95=8C=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModels/Exporters/BaseExporterViewModel.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs index a53c7e8..4baff6e 100644 --- a/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs @@ -101,18 +101,19 @@ namespace SpineViewer.ViewModels.Exporters /// protected void SetAutoResolutionStatic(BaseExporter exporter, params SpineObject[] spines) { - var bounds = spines[0].GetAnimationBounds(); - foreach (var sp in spines.Skip(1)) bounds.Union(sp.GetAnimationBounds()); + var bounds = spines[0].GetCurrentBounds(); + foreach (var sp in spines.Skip(1)) bounds.Union(sp.GetCurrentBounds()); SetAutoResolution(exporter, bounds); } /// /// 使用提供的模型设置导出器的自动分辨率和视区参数, 动画画面 /// - protected void SetAutoResolutionAnimated(BaseExporter exporter, params SpineObject[] spines) + protected void SetAutoResolutionAnimated(VideoExporter exporter, params SpineObject[] spines) { - var bounds = spines[0].GetAnimationBounds(); - foreach (var sp in spines.Skip(1)) bounds.Union(sp.GetAnimationBounds()); + var fps = exporter.Fps; + var bounds = spines[0].GetAnimationBounds(fps); + foreach (var sp in spines.Skip(1)) bounds.Union(sp.GetAnimationBounds(fps)); SetAutoResolution(exporter, bounds); } From 9a62d7eb53c431af21a6a2bac59bbd4fb0585b66 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Sun, 15 Jun 2025 01:12:16 +0800 Subject: [PATCH 21/27] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=AA=A8=E9=AA=BC?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=87=8D=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Resources/AppResource.cs | 1 + SpineViewer/Resources/Strings/en.xaml | 4 +- SpineViewer/Resources/Strings/ja.xaml | 4 +- SpineViewer/Resources/Strings/zh.xaml | 4 +- .../MainWindow/SpineObjectListViewModel.cs | 143 +++++++++++++++--- .../ViewModels/ProgressDialogViewModel.cs | 1 + SpineViewer/Views/MainWindow.xaml | 14 +- 7 files changed, 141 insertions(+), 30 deletions(-) diff --git a/SpineViewer/Resources/AppResource.cs b/SpineViewer/Resources/AppResource.cs index 7080b3f..0f076b0 100644 --- a/SpineViewer/Resources/AppResource.cs +++ b/SpineViewer/Resources/AppResource.cs @@ -19,6 +19,7 @@ namespace SpineViewer.Resources public static string Str_GeneratePreviewsTitle => Get("Str_GeneratePreviewsTitle"); public static string Str_DeletePreviewsTitle => Get("Str_DeletePreviewsTitle"); public static string Str_AddSpineObjectsTitle => Get("Str_AddSpineObjectsTitle"); + public static string Str_ReloadSpineObjectsTitle => Get("Str_ReloadSpineObjectsTitle"); public static string Str_CustomFFmpegExporterTitle => Get("Str_CustomFFmpegExporterTitle"); public static string Str_InfoPopup => Get("Str_InfoPopup"); diff --git a/SpineViewer/Resources/Strings/en.xaml b/SpineViewer/Resources/Strings/en.xaml index 411d34d..2b1301e 100644 --- a/SpineViewer/Resources/Strings/en.xaml +++ b/SpineViewer/Resources/Strings/en.xaml @@ -36,9 +36,10 @@ {0} items, {1} selected Add... Remove + Add from Clipboard + Reload Move Up Move Down - Add from Clipboard Copy Config Apply Config Save Config to File... @@ -120,6 +121,7 @@ Generate Previews Delete Previews Batch Add Spine Files + Reload Spine Files Information Warning diff --git a/SpineViewer/Resources/Strings/ja.xaml b/SpineViewer/Resources/Strings/ja.xaml index 52c3263..50d9bab 100644 --- a/SpineViewer/Resources/Strings/ja.xaml +++ b/SpineViewer/Resources/Strings/ja.xaml @@ -36,9 +36,10 @@ 全{0}件、選択中{1}件 追加... 削除 + クリップボードから追加 + 再読み込み 上へ移動 下へ移動 - クリップボードから追加 パラメーターをコピー パラメーターを適用 パラメータファイルを保存... @@ -120,6 +121,7 @@ プレビューを生成 プレビューを削除 一括でスケルトンファイルを追加 + スケルトンファイルの再読み込み 情報 警告 diff --git a/SpineViewer/Resources/Strings/zh.xaml b/SpineViewer/Resources/Strings/zh.xaml index dd75549..789e986 100644 --- a/SpineViewer/Resources/Strings/zh.xaml +++ b/SpineViewer/Resources/Strings/zh.xaml @@ -36,9 +36,10 @@ 共 {0} 项,已选择 {1} 项 添加... 移除 + 从剪贴板添加 + 重新加载 上移 下移 - 从剪贴板添加 复制参数 应用参数 保存参数文件... @@ -120,6 +121,7 @@ 生成预览图 删除预览图 批量添加骨骼文件 + 重新加载骨骼文件 提示信息 警告信息 diff --git a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs index ac6db0f..c87bf5a 100644 --- a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; +using System.Windows.Shapes; using System.Windows.Shell; namespace SpineViewer.ViewModels.MainWindow @@ -111,7 +112,7 @@ namespace SpineViewer.ViewModels.MainWindow private void RemoveSpineObject_Execute(IList? args) { - if (args is null) return; + if (!RemoveSpineObject_CanExecute(args)) return; if (args.Count > 1) { @@ -137,6 +138,121 @@ namespace SpineViewer.ViewModels.MainWindow return true; } + /// + /// 从剪贴板文件列表添加模型 + /// + public RelayCommand Cmd_AddSpineObjectFromClipboard => _cmd_AddSpineObjectFromClipboard ??= new(AddSpineObjectFromClipboard_Execute); + private RelayCommand? _cmd_AddSpineObjectFromClipboard; + + private void AddSpineObjectFromClipboard_Execute() + { + if (!Clipboard.ContainsFileDropList()) return; + AddSpineObjectFromFileList(Clipboard.GetFileDropList().Cast().ToArray()); + } + + /// + /// 重新加载模型 + /// + public RelayCommand Cmd_ReloadSpineObject => _cmd_ReloadSpineObject ??= new(ReloadSpineObject_Execute, ReloadSpineObject_CanExecute); + private RelayCommand? _cmd_ReloadSpineObject; + + private void ReloadSpineObject_Execute(IList? args) + { + if (!ReloadSpineObject_CanExecute(args)) return; + + if (args.Count <= 1) + { + lock (_spineObjectModels.Lock) + { + var sp = (SpineObjectModel)args[0]; + var idx = _spineObjectModels.IndexOf(sp); + if (idx < 0) return; + + try + { + var spNew = new SpineObjectModel(sp.SkelPath, sp.AtlasPath); + spNew.Load(sp.Dump()); + _spineObjectModels[idx] = spNew; + sp.Dispose(); + } + catch (Exception ex) + { + _logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message); + _logger.Trace(ex.ToString()); + } + } + } + else + { + ProgressService.RunAsync((pr, ct) => ReloadSpineObjectsTask( + args.Cast().ToArray(), pr, ct), + AppResource.Str_ReloadSpineObjectsTitle + ); + } + } + + private bool ReloadSpineObject_CanExecute(IList? args) + { + if (args is null) return false; + if (args.Count <= 0) return false; + return true; + } + + private void ReloadSpineObjectsTask(SpineObjectModel[] spines, IProgressReporter reporter, CancellationToken ct) + { + int totalCount = spines.Length; + int success = 0; + int error = 0; + + _vmMain.ProgressState = TaskbarItemProgressState.Normal; + _vmMain.ProgressValue = 0; + + reporter.Total = totalCount; + reporter.Done = 0; + reporter.ProgressText = $"[0/{totalCount}]"; + for (int i = 0; i < totalCount; i++) + { + if (ct.IsCancellationRequested) break; + + var sp = spines[i]; + reporter.ProgressText = $"[{i}/{totalCount}] {sp.Name}"; + + lock (_spineObjectModels.Lock) + { + var idx = _spineObjectModels.IndexOf(sp); + if (idx >= 0) + { + try + { + var spNew = new SpineObjectModel(sp.SkelPath, sp.AtlasPath); + spNew.Load(sp.Dump()); + _spineObjectModels[idx] = spNew; + sp.Dispose(); + success++; + } + catch (Exception ex) + { + error++; + _logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message); + _logger.Trace(ex.ToString()); + } + } + } + + reporter.Done = i + 1; + reporter.ProgressText = $"[{i + 1}/{totalCount}] {sp.Name}"; + _vmMain.ProgressValue = (i + 1f) / totalCount; + } + _vmMain.ProgressState = TaskbarItemProgressState.None; + + if (error > 0) + _logger.Warn("Batch reload {0} successfully, {1} failed", success, error); + else + _logger.Info("{0} skel reloaded successfully", success); + + _logger.LogCurrentProcessMemoryUsage(); + } + /// /// 模型上移一位 /// @@ -145,8 +261,7 @@ namespace SpineViewer.ViewModels.MainWindow private void MoveUpSpineObject_Execute(IList? args) { - if (args is null) return; - if (args.Count != 1) return; + if (!MoveUpSpineObject_CanExecute(args)) return; var sp = (SpineObjectModel)args[0]; lock (_spineObjectModels.Lock) { @@ -171,8 +286,7 @@ namespace SpineViewer.ViewModels.MainWindow private void MoveDownSpineObject_Execute(IList? args) { - if (args is null) return; - if (args.Count != 1) return; + if (!MoveDownSpineObject_CanExecute(args)) return; var sp = (SpineObjectModel)args[0]; lock (_spineObjectModels.Lock) { @@ -189,18 +303,6 @@ namespace SpineViewer.ViewModels.MainWindow return true; } - /// - /// 从剪贴板文件列表添加模型 - /// - public RelayCommand Cmd_AddSpineObjectFromClipboard => _cmd_AddSpineObjectFromClipboard ??= new(AddSpineObjectFromClipboard_Execute); - private RelayCommand? _cmd_AddSpineObjectFromClipboard; - - private void AddSpineObjectFromClipboard_Execute() - { - if (!Clipboard.ContainsFileDropList()) return; - AddSpineObjectFromFileList(Clipboard.GetFileDropList().Cast().ToArray()); - } - /// /// 复制模型参数 /// @@ -209,8 +311,7 @@ namespace SpineViewer.ViewModels.MainWindow private void CopySpineObjectConfig_Execute(IList? args) { - if (args is null) return; - if (args.Count != 1) return; + if (!CopySpineObjectConfig_CanExecute(args)) return; var sp = (SpineObjectModel)args[0]; _copiedSpineObjectConfigModel = sp.Dump(); _logger.Info("Copy config from model: {0}", sp.Name); @@ -231,9 +332,7 @@ namespace SpineViewer.ViewModels.MainWindow private void ApplySpineObjectConfig_Execute(IList? args) { - if (_copiedSpineObjectConfigModel is null) return; - if (args is null) return; - if (args.Count <= 0) return; + if (!ApplySpineObjectConfig_CanExecute(args)) return; foreach (SpineObjectModel sp in args) { sp.Load(_copiedSpineObjectConfigModel); diff --git a/SpineViewer/ViewModels/ProgressDialogViewModel.cs b/SpineViewer/ViewModels/ProgressDialogViewModel.cs index 982d80f..a677704 100644 --- a/SpineViewer/ViewModels/ProgressDialogViewModel.cs +++ b/SpineViewer/ViewModels/ProgressDialogViewModel.cs @@ -63,6 +63,7 @@ namespace SpineViewer.ViewModels private void Cancel_Execute() { + if (!Cancel_CanExecute()) return; if (!MessagePopupService.Quest(AppResource.Str_CancelQuest)) return; _cts.Cancel(); Cmd_Cancel.NotifyCanExecuteChanged(); diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml index d6a82c1..12334c1 100644 --- a/SpineViewer/Views/MainWindow.xaml +++ b/SpineViewer/Views/MainWindow.xaml @@ -246,9 +246,10 @@ + + - @@ -262,6 +263,13 @@ InputGestureText="Delete" Command="{Binding Cmd_RemoveSpineObject}" CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/> + + - - Date: Sun, 15 Jun 2025 11:08:43 +0800 Subject: [PATCH 22/27] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=92=8C=E6=B8=B2=E6=9F=93=E9=80=89=E4=B8=AD=E9=A6=96=E9=80=89?= =?UTF-8?q?=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Models/PreferenceModel.cs | 6 ++ SpineViewer/Models/SpineObjectModel.cs | 4 +- .../MainWindow/PreferenceViewModel.cs | 22 ++++++- SpineViewer/Views/MainWindow.xaml | 13 ++-- SpineViewer/Views/PreferenceDialog.xaml | 59 +++++++++++-------- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/SpineViewer/Models/PreferenceModel.cs b/SpineViewer/Models/PreferenceModel.cs index a5ca444..2a90d8a 100644 --- a/SpineViewer/Models/PreferenceModel.cs +++ b/SpineViewer/Models/PreferenceModel.cs @@ -33,6 +33,9 @@ namespace SpineViewer.Models #region 模型加载首选项 + [ObservableProperty] + private bool _isShown = true; + [ObservableProperty] private bool _usePma; @@ -70,6 +73,9 @@ namespace SpineViewer.Models #region 程序选项 + [ObservableProperty] + private bool _renderSelectedOnly; + [ObservableProperty] private AppLanguage _appLanguage; diff --git a/SpineViewer/Models/SpineObjectModel.cs b/SpineViewer/Models/SpineObjectModel.cs index 461211f..3f1bff6 100644 --- a/SpineViewer/Models/SpineObjectModel.cs +++ b/SpineViewer/Models/SpineObjectModel.cs @@ -101,7 +101,7 @@ namespace SpineViewer.Models get { lock (_lock) return _isShown; } set { lock (_lock) SetProperty(ref _isShown, value); } } - private bool _isShown = true; + private bool _isShown = _loadOptions.IsShown; public bool UsePma { @@ -497,7 +497,7 @@ namespace SpineViewer.Models public class SpineObjectLoadOptions { - public bool AutoSave { get; set; } + public bool IsShown { get; set; } = true; public bool UsePma { get; set; } public bool DebugTexture { get; set; } = true; public bool DebugBounds { get; set; } diff --git a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs index 49d806d..257662b 100644 --- a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs @@ -96,6 +96,8 @@ namespace SpineViewer.ViewModels.MainWindow ForcePremul = ForcePremul, ForceNearest = ForceNearest, ForceMipmap = ForceMipmap, + + IsShown = IsShown, UsePma = UsePma, DebugTexture = DebugTexture, DebugBounds = DebugBounds, @@ -107,6 +109,8 @@ namespace SpineViewer.ViewModels.MainWindow DebugPaths = DebugPaths, DebugPoints = DebugPoints, DebugClippings = DebugClippings, + + RenderSelectedOnly = RenderSelectedOnly, AppLanguage = AppLanguage, }; } @@ -115,7 +119,10 @@ namespace SpineViewer.ViewModels.MainWindow ForcePremul = value.ForcePremul; ForceNearest = value.ForceNearest; ForceMipmap = value.ForceMipmap; + + IsShown = value.IsShown; UsePma = value.UsePma; + DebugTexture = value.DebugTexture; DebugBounds = value.DebugBounds; DebugBones = value.DebugBones; @@ -126,6 +133,8 @@ namespace SpineViewer.ViewModels.MainWindow DebugPaths = value.DebugPaths; DebugPoints = value.DebugPoints; DebugClippings = value.DebugClippings; + + RenderSelectedOnly = value.RenderSelectedOnly; AppLanguage = value.AppLanguage; } } @@ -152,9 +161,14 @@ namespace SpineViewer.ViewModels.MainWindow #endregion - #region 模型加载首选项 + public bool IsShown + { + get => SpineObjectModel.LoadOptions.IsShown; + set => SetProperty(SpineObjectModel.LoadOptions.IsShown, value, v => SpineObjectModel.LoadOptions.IsShown = v); + } + public bool UsePma { get => SpineObjectModel.LoadOptions.UsePma; @@ -227,6 +241,12 @@ namespace SpineViewer.ViewModels.MainWindow public static ImmutableArray AppLanguageOptions { get; } = Enum.GetValues().ToImmutableArray(); + public bool RenderSelectedOnly + { + get => _vmMain.SFMLRendererViewModel.RenderSelectedOnly; + set => SetProperty(_vmMain.SFMLRendererViewModel.RenderSelectedOnly, value, v => _vmMain.SFMLRendererViewModel.RenderSelectedOnly = v); + } + public AppLanguage AppLanguage { get => ((App)App.Current).Language; diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml index 12334c1..5f996ee 100644 --- a/SpineViewer/Views/MainWindow.xaml +++ b/SpineViewer/Views/MainWindow.xaml @@ -691,7 +691,6 @@ - @@ -730,17 +729,13 @@