增加首选项对话框

This commit is contained in:
ww-rm
2025-06-13 23:12:15 +08:00
parent 125ce6fa86
commit b3010360b4
12 changed files with 565 additions and 30 deletions

View 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
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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,4 +195,14 @@
<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>

View File

@@ -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>

View File

@@ -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>

View 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
}
}

View File

@@ -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}";

View File

@@ -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}"/>-->

View File

@@ -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)

View 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>

View 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;
}
}
}