增加首选项对话框
This commit is contained in:
106
SpineViewer/Models/PreferenceModel.cs
Normal file
106
SpineViewer/Models/PreferenceModel.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 首选项参数模型, 用于对话框修改以及本地保存
|
||||||
|
/// </summary>
|
||||||
|
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 序列化与反序列
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存 Json 文件的格式参数
|
||||||
|
/// </summary>
|
||||||
|
private static readonly JsonSerializerOptions _jsonOptions = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||||
|
AllowTrailingCommas = true,
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从文件反序列对象, 可能抛出异常
|
||||||
|
/// </summary>
|
||||||
|
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<PreferenceModel>(json, _jsonOptions);
|
||||||
|
return model ?? throw new JsonException($"null data in file '{path}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存至文件, 可能抛出异常
|
||||||
|
/// </summary>
|
||||||
|
public void Serialize(string path)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
var json = JsonSerializer.Serialize(this, _jsonOptions);
|
||||||
|
File.WriteAllText(path, json, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,8 @@
|
|||||||
<s:String x:Key="Str_Experiment">Experimental Features</s:String>
|
<s:String x:Key="Str_Experiment">Experimental Features</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Open">Open...</s:String>
|
<s:String x:Key="Str_Open">Open...</s:String>
|
||||||
<s:String x:Key="Str_Preference">Preferences...</s:String>
|
<s:String x:Key="Str_Preference">Preferences</s:String>
|
||||||
|
<s:String x:Key="Str_PreferenceWithDots">Preferences...</s:String>
|
||||||
<s:String x:Key="Str_Exit">Exit</s:String>
|
<s:String x:Key="Str_Exit">Exit</s:String>
|
||||||
|
|
||||||
<!-- 标签页 -->
|
<!-- 标签页 -->
|
||||||
@@ -194,4 +195,14 @@
|
|||||||
<s:String x:Key="Str_ProgremVersion">Program Version</s:String>
|
<s:String x:Key="Str_ProgremVersion">Program Version</s:String>
|
||||||
<s:String x:Key="Str_ProjectUrl">Project URL</s:String>
|
<s:String x:Key="Str_ProjectUrl">Project URL</s:String>
|
||||||
|
|
||||||
|
<!-- 首选项对话框 -->
|
||||||
|
<s:String x:Key="Str_TextureLoadPreference">Texture Loading Options</s:String>
|
||||||
|
<s:String x:Key="Str_ForcePremul">Force Premultiplied Channels</s:String>
|
||||||
|
<s:String x:Key="Str_ForcePremulTooltip">When enabled, this applies premultiplied operations to pixels during texture loading, helping to resolve black edge issues at some connections.</s:String>
|
||||||
|
<s:String x:Key="Str_ForceNearest">Force Nearest Interpolation</s:String>
|
||||||
|
<s:String x:Key="Str_ForceMipmap">Force Mipmap</s:String>
|
||||||
|
<s:String x:Key="Str_ForceMipmapTooltip">When enabled, this helps reduce aliasing when textures are scaled down, at the cost of slightly higher video memory usage.</s:String>
|
||||||
|
|
||||||
|
<s:String x:Key="Str_SpineLoadPreference">Model Loading Options</s:String>
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
<s:String x:Key="Str_Experiment">実験機能</s:String>
|
<s:String x:Key="Str_Experiment">実験機能</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Open">開く...</s:String>
|
<s:String x:Key="Str_Open">開く...</s:String>
|
||||||
<s:String x:Key="Str_Preference">設定...</s:String>
|
<s:String x:Key="Str_Preference">設定</s:String>
|
||||||
|
<s:String x:Key="Str_PreferenceWithDots">設定...</s:String>
|
||||||
<s:String x:Key="Str_Exit">終了</s:String>
|
<s:String x:Key="Str_Exit">終了</s:String>
|
||||||
|
|
||||||
<!-- 标签页 -->
|
<!-- 标签页 -->
|
||||||
@@ -194,5 +195,15 @@
|
|||||||
<s:String x:Key="Str_ProgremVersion">プログラムバージョン</s:String>
|
<s:String x:Key="Str_ProgremVersion">プログラムバージョン</s:String>
|
||||||
<s:String x:Key="Str_ProjectUrl">プロジェクトURL</s:String>
|
<s:String x:Key="Str_ProjectUrl">プロジェクトURL</s:String>
|
||||||
|
|
||||||
|
<!-- 首选项对话框 -->
|
||||||
|
<s:String x:Key="Str_TextureLoadPreference">テクスチャ読み込みオプション</s:String>
|
||||||
|
<s:String x:Key="Str_ForcePremul">強制プリマルチチャンネル</s:String>
|
||||||
|
<s:String x:Key="Str_ForcePremulTooltip">有効にすると、テクスチャ読み込み時にピクセルにプリマルチ処理を適用し、一部の接続部分で発生する黒い縁の問題を解決します。</s:String>
|
||||||
|
<s:String x:Key="Str_ForceNearest">Nearest補間を強制使用</s:String>
|
||||||
|
<s:String x:Key="Str_ForceMipmap">Mipmapを強制使用</s:String>
|
||||||
|
<s:String x:Key="Str_ForceMipmapTooltip">有効にすると、テクスチャ縮小時のジャギーを軽減しますが、ビデオメモリの使用量が若干増加します。</s:String>
|
||||||
|
|
||||||
|
<s:String x:Key="Str_SpineLoadPreference">モデル読み込みオプション</s:String>
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
<s:String x:Key="Str_Experiment">实验性功能</s:String>
|
<s:String x:Key="Str_Experiment">实验性功能</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Open">打开...</s:String>
|
<s:String x:Key="Str_Open">打开...</s:String>
|
||||||
<s:String x:Key="Str_Preference">首选项...</s:String>
|
<s:String x:Key="Str_Preference">首选项</s:String>
|
||||||
|
<s:String x:Key="Str_PreferenceWithDots">首选项...</s:String>
|
||||||
<s:String x:Key="Str_Exit">退出</s:String>
|
<s:String x:Key="Str_Exit">退出</s:String>
|
||||||
|
|
||||||
<!-- 标签页 -->
|
<!-- 标签页 -->
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
|
|
||||||
<s:String x:Key="Str_Slot">插槽</s:String>
|
<s:String x:Key="Str_Slot">插槽</s:String>
|
||||||
<s:String x:Key="Str_ClearSlotsAttachment">清除附件</s:String>
|
<s:String x:Key="Str_ClearSlotsAttachment">清除附件</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Animation">动画</s:String>
|
<s:String x:Key="Str_Animation">动画</s:String>
|
||||||
<s:String x:Key="Str_AppendTrack">添加</s:String>
|
<s:String x:Key="Str_AppendTrack">添加</s:String>
|
||||||
<s:String x:Key="Str_InsertTrack">插入</s:String>
|
<s:String x:Key="Str_InsertTrack">插入</s:String>
|
||||||
@@ -141,7 +142,7 @@
|
|||||||
<s:String x:Key="Str_InvalidMaxResolution">使用自动分辨率时需要提供有效的最大分辨率</s:String>
|
<s:String x:Key="Str_InvalidMaxResolution">使用自动分辨率时需要提供有效的最大分辨率</s:String>
|
||||||
<s:String x:Key="Str_InvalidDuration">导出单个时导出时长不能为负数</s:String>
|
<s:String x:Key="Str_InvalidDuration">导出单个时导出时长不能为负数</s:String>
|
||||||
<s:String x:Key="Str_FFmpegFormatRequired">必须指定 FFmpeg 导出格式</s:String>
|
<s:String x:Key="Str_FFmpegFormatRequired">必须指定 FFmpeg 导出格式</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_ResolutionTooltip">画面分辨率,相关参数请在画面参数面板进行调整</s:String>
|
<s:String x:Key="Str_ResolutionTooltip">画面分辨率,相关参数请在画面参数面板进行调整</s:String>
|
||||||
<s:String x:Key="Str_ExportSingle">导出单个</s:String>
|
<s:String x:Key="Str_ExportSingle">导出单个</s:String>
|
||||||
<s:String x:Key="Str_ExportSingleTooltip">勾选后将所选模型在同一个画面上进行导出,且必须提供输出文件夹</s:String>
|
<s:String x:Key="Str_ExportSingleTooltip">勾选后将所选模型在同一个画面上进行导出,且必须提供输出文件夹</s:String>
|
||||||
@@ -185,7 +186,7 @@
|
|||||||
<s:String x:Key="Str_FFmpegFilterTooltip">FFmpeg 滤镜,等价于参数 “-vf”</s:String>
|
<s:String x:Key="Str_FFmpegFilterTooltip">FFmpeg 滤镜,等价于参数 “-vf”</s:String>
|
||||||
<s:String x:Key="Str_FFmpegCustomArgs">自定义参数</s:String>
|
<s:String x:Key="Str_FFmpegCustomArgs">自定义参数</s:String>
|
||||||
<s:String x:Key="Str_FFmpegCustomArgsTooltip">FFmpeg 自定义参数,与命令行提供方式相同,例如 “-crf 23”</s:String>
|
<s:String x:Key="Str_FFmpegCustomArgsTooltip">FFmpeg 自定义参数,与命令行提供方式相同,例如 “-crf 23”</s:String>
|
||||||
|
|
||||||
<!-- 诊断信息对话框 -->
|
<!-- 诊断信息对话框 -->
|
||||||
<s:String x:Key="Str_CopyDiagnosticsInfo">复制到剪贴板</s:String>
|
<s:String x:Key="Str_CopyDiagnosticsInfo">复制到剪贴板</s:String>
|
||||||
<s:String x:Key="Str_Copied">已复制</s:String>
|
<s:String x:Key="Str_Copied">已复制</s:String>
|
||||||
@@ -193,5 +194,15 @@
|
|||||||
<!-- 关于对话框 -->
|
<!-- 关于对话框 -->
|
||||||
<s:String x:Key="Str_ProgremVersion">程序版本</s:String>
|
<s:String x:Key="Str_ProgremVersion">程序版本</s:String>
|
||||||
<s:String x:Key="Str_ProjectUrl">项目地址</s:String>
|
<s:String x:Key="Str_ProjectUrl">项目地址</s:String>
|
||||||
|
|
||||||
|
<!-- 首选项对话框 -->
|
||||||
|
<s:String x:Key="Str_TextureLoadPreference">纹理加载选项</s:String>
|
||||||
|
<s:String x:Key="Str_ForcePremul">强制预乘通道</s:String>
|
||||||
|
<s:String x:Key="Str_ForcePremulTooltip">开启后,会在加载纹理时对像素进行预乘操作,有助于解决某些情况下的连接处黑边问题</s:String>
|
||||||
|
<s:String x:Key="Str_ForceNearest">强制使用 Nearest 插值</s:String>
|
||||||
|
<s:String x:Key="Str_ForceMipmap">强制使用 Mipmap</s:String>
|
||||||
|
<s:String x:Key="Str_ForceMipmapTooltip">开启后有助于改善纹理缩小时的锯齿现象,但是会略微增加显存占用</s:String>
|
||||||
|
|
||||||
|
<s:String x:Key="Str_SpineLoadPreference">模型加载选项</s:String>
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using SpineViewer.Models;
|
||||||
using SpineViewer.ViewModels.Exporters;
|
using SpineViewer.ViewModels.Exporters;
|
||||||
using SpineViewer.Views;
|
using SpineViewer.Views;
|
||||||
using SpineViewer.Views.ExporterDialogs;
|
using SpineViewer.Views.ExporterDialogs;
|
||||||
@@ -51,6 +52,15 @@ namespace SpineViewer.Services
|
|||||||
return dialog.ShowDialog() ?? false;
|
return dialog.ShowDialog() ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将给定的首选项参数在对话框上进行显示, 返回值表示是否确认修改
|
||||||
|
/// </summary>
|
||||||
|
public static bool ShowPreferenceDialog(PreferenceModel m)
|
||||||
|
{
|
||||||
|
var dialog = new PreferenceDialog() { DataContext = m, Owner = App.Current.MainWindow };
|
||||||
|
return dialog.ShowDialog() ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取用户选择的文件夹
|
/// 获取用户选择的文件夹
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
_explorerListViewModel = new(this);
|
_explorerListViewModel = new(this);
|
||||||
_spineObjectListViewModel = new(this);
|
_spineObjectListViewModel = new(this);
|
||||||
_sfmlRendererViewModel = new(this);
|
_sfmlRendererViewModel = new(this);
|
||||||
|
_preferenceViewModel = new(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Title => $"SpineViewer - v{App.Version}";
|
public string Title => $"SpineViewer - v{App.Version}";
|
||||||
@@ -56,6 +57,9 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
public ObservableCollectionWithLock<SpineObjectModel> SpineObjects => _spineObjectModels;
|
public ObservableCollectionWithLock<SpineObjectModel> SpineObjects => _spineObjectModels;
|
||||||
private readonly ObservableCollectionWithLock<SpineObjectModel> _spineObjectModels = [];
|
private readonly ObservableCollectionWithLock<SpineObjectModel> _spineObjectModels = [];
|
||||||
|
|
||||||
|
public PreferenceViewModel PreferenceViewModel => _preferenceViewModel;
|
||||||
|
private readonly PreferenceViewModel _preferenceViewModel;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 浏览页列表 ViewModel
|
/// 浏览页列表 ViewModel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
191
SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs
Normal file
191
SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 文件保存路径
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示首选项对话框
|
||||||
|
/// </summary>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存首选项, 保存失败会有日志提示
|
||||||
|
/// </summary>
|
||||||
|
public void SavePreference() => SavePreference(Preference);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载首选项, 加载失败会有日志提示
|
||||||
|
/// </summary>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取参数副本或者进行设置
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -121,7 +121,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
|
|
||||||
lock (_spineObjectModels.Lock)
|
lock (_spineObjectModels.Lock)
|
||||||
{
|
{
|
||||||
// XXX: 这里必须要浅拷贝一次, 不能直接对会被修改的绑定数据 args 进行 foreach 遍历
|
// NOTE: 这里必须要浅拷贝一次, 不能直接对会被修改的绑定数据 args 进行 foreach 遍历
|
||||||
foreach (var sp in args.Cast<SpineObjectModel>().ToArray())
|
foreach (var sp in args.Cast<SpineObjectModel>().ToArray())
|
||||||
{
|
{
|
||||||
_spineObjectModels.Remove(sp);
|
_spineObjectModels.Remove(sp);
|
||||||
@@ -249,6 +249,41 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地在末尾添加一个模型, 发生错误会输出日志
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>是否添加成功</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从路径列表添加对象
|
/// 从路径列表添加对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -289,17 +324,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
}
|
}
|
||||||
else if (validPaths.Count > 0)
|
else if (validPaths.Count > 0)
|
||||||
{
|
{
|
||||||
var skelPath = validPaths[0];
|
AddSpineObject(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);
|
|
||||||
}
|
|
||||||
_logger.LogCurrentProcessMemoryUsage();
|
_logger.LogCurrentProcessMemoryUsage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,18 +351,10 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
var skelPath = paths[i];
|
var skelPath = paths[i];
|
||||||
reporter.ProgressText = $"[{i}/{totalCount}] {skelPath}";
|
reporter.ProgressText = $"[{i}/{totalCount}] {skelPath}";
|
||||||
|
|
||||||
try
|
if (AddSpineObject(skelPath))
|
||||||
{
|
|
||||||
var sp = new SpineObjectModel(skelPath);
|
|
||||||
lock (_spineObjectModels.Lock) _spineObjectModels.Add(sp);
|
|
||||||
success++;
|
success++;
|
||||||
}
|
else
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Trace(ex.ToString());
|
|
||||||
_logger.Error("Failed to load: {0}, {1}", skelPath, ex.Message);
|
|
||||||
error++;
|
error++;
|
||||||
}
|
|
||||||
|
|
||||||
reporter.Done = i + 1;
|
reporter.Done = i + 1;
|
||||||
reporter.ProgressText = $"[{i + 1}/{totalCount}] {skelPath}";
|
reporter.ProgressText = $"[{i + 1}/{totalCount}] {skelPath}";
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
<MenuItem Header="{DynamicResource Str_File}">
|
<MenuItem Header="{DynamicResource Str_File}">
|
||||||
<MenuItem Header="{DynamicResource Str_Open}" InputGestureText="Ctrl+O"/>
|
<MenuItem Header="{DynamicResource Str_Open}" InputGestureText="Ctrl+O"/>
|
||||||
<Separator/>
|
<Separator/>
|
||||||
<MenuItem Header="{DynamicResource Str_Preference}"/>
|
<MenuItem Header="{DynamicResource Str_PreferenceWithDots}" Command="{Binding PreferenceViewModel.Cmd_ShowPreferenceDialog}"/>
|
||||||
<!--<MenuItem Header="{DynamicResource Str_Exit}" InputGestureText="Alt+F4"/>-->
|
<!--<MenuItem Header="{DynamicResource Str_Exit}" InputGestureText="Alt+F4"/>-->
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<!--<MenuItem Header="{DynamicResource Str_Tool}"/>-->
|
<!--<MenuItem Header="{DynamicResource Str_Tool}"/>-->
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ public partial class MainWindow : Window
|
|||||||
vm.FlipY = true;
|
vm.FlipY = true;
|
||||||
vm.MaxFps = 30;
|
vm.MaxFps = 30;
|
||||||
vm.StartRender();
|
vm.StartRender();
|
||||||
|
|
||||||
|
// 加载首选项
|
||||||
|
_vm.PreferenceViewModel.LoadPreference();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainWindow_Closed(object? sender, EventArgs e)
|
private void MainWindow_Closed(object? sender, EventArgs e)
|
||||||
|
|||||||
132
SpineViewer/Views/PreferenceDialog.xaml
Normal file
132
SpineViewer/Views/PreferenceDialog.xaml
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<Window xmlns:hc="https://handyorg.github.io/handycontrol" x:Class="SpineViewer.Views.PreferenceDialog"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:SpineViewer.Views"
|
||||||
|
xmlns:models="clr-namespace:SpineViewer.Models"
|
||||||
|
d:DataContext="{d:DesignInstance Type=models:PreferenceModel}"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="{DynamicResource Str_Preference}"
|
||||||
|
Height="650"
|
||||||
|
Width="600"
|
||||||
|
ShowInTaskbar="False"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<DockPanel>
|
||||||
|
<Border DockPanel.Dock="Bottom">
|
||||||
|
<WrapPanel HorizontalAlignment="Center">
|
||||||
|
<WrapPanel.Resources>
|
||||||
|
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource ButtonDefault}">
|
||||||
|
<Setter Property="Margin" Value="5"/>
|
||||||
|
<Setter Property="Width" Value="100"/>
|
||||||
|
</Style>
|
||||||
|
</WrapPanel.Resources>
|
||||||
|
<Button Content="{DynamicResource Str_OK}" Click="ButtonOK_Click"/>
|
||||||
|
<Button Content="{DynamicResource Str_Cancel}" Click="ButtonCancel_Click"/>
|
||||||
|
</WrapPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border>
|
||||||
|
<Border.Resources>
|
||||||
|
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource LabelDefault}">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource TextBoxBaseStyle}">
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ComboBoxBaseStyle}">
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButton}">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource GroupBoxTab}">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="hc:TitleElement.Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
</Border.Resources>
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
|
<StackPanel Grid.IsSharedSizeScope="True">
|
||||||
|
<GroupBox Header="{DynamicResource Str_TextureLoadPreference}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ForcePremul}" ToolTip="{DynamicResource Str_ForcePremulTooltip}"/>
|
||||||
|
<ToggleButton Grid.Row="0" Grid.Column="1" IsChecked="{Binding ForcePremul}" ToolTip="{DynamicResource Str_ForcePremulTooltip}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_ForceNearest}"/>
|
||||||
|
<ToggleButton Grid.Row="1" Grid.Column="1" IsChecked="{Binding ForceNearest}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_ForceMipmap}" ToolTip="{DynamicResource Str_ForceMipmapTooltip}"/>
|
||||||
|
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding ForceMipmap}" ToolTip="{DynamicResource Str_ForceMipmapTooltip}"/>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Header="{DynamicResource Str_SpineLoadPreference}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_UsePma}"/>
|
||||||
|
<ToggleButton Grid.Row="0" Grid.Column="1" IsChecked="{Binding UsePma}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_DebugTexture}"/>
|
||||||
|
<ToggleButton Grid.Row="1" Grid.Column="1" IsChecked="{Binding DebugTexture}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_DebugBounds}"/>
|
||||||
|
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding DebugBounds}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_DebugBones}"/>
|
||||||
|
<ToggleButton Grid.Row="3" Grid.Column="1" IsChecked="{Binding DebugBones}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_DebugRegions}"/>
|
||||||
|
<ToggleButton Grid.Row="4" Grid.Column="1" IsChecked="{Binding DebugRegions}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_DebugMeshHulls}"/>
|
||||||
|
<ToggleButton Grid.Row="5" Grid.Column="1" IsChecked="{Binding DebugMeshHulls}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource Str_DebugMeshes}"/>
|
||||||
|
<ToggleButton Grid.Row="6" Grid.Column="1" IsChecked="{Binding DebugMeshes}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="7" Grid.Column="0" Content="{DynamicResource Str_DebugBoundingBoxes}"/>
|
||||||
|
<ToggleButton Grid.Row="7" Grid.Column="1" IsChecked="{Binding DebugBoundingBoxes}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="8" Grid.Column="0" Content="{DynamicResource Str_DebugPaths}"/>
|
||||||
|
<ToggleButton Grid.Row="8" Grid.Column="1" IsChecked="{Binding DebugPaths}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="9" Grid.Column="0" Content="{DynamicResource Str_DebugPoints}"/>
|
||||||
|
<ToggleButton Grid.Row="9" Grid.Column="1" IsChecked="{Binding DebugPoints}"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_DebugClippings}"/>
|
||||||
|
<ToggleButton Grid.Row="10" Grid.Column="1" IsChecked="{Binding DebugClippings}"/>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</DockPanel>
|
||||||
|
</Window>
|
||||||
39
SpineViewer/Views/PreferenceDialog.xaml.cs
Normal file
39
SpineViewer/Views/PreferenceDialog.xaml.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using SpineViewer.Services;
|
||||||
|
using SpineViewer.ViewModels.Exporters;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// PreferenceDialog.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class PreferenceDialog : Window
|
||||||
|
{
|
||||||
|
public PreferenceDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonOK_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonCancel_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user