Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40bde84648 | ||
|
|
ebb2593526 | ||
|
|
6a74204ba1 | ||
|
|
78c6c47300 | ||
|
|
746a3decc8 | ||
|
|
6dfd25b760 |
@@ -1,5 +1,9 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## v0.15.10
|
||||||
|
|
||||||
|
- 增加插槽可见性参数, 允许任何情况下对插槽启用和禁用对插槽的渲染
|
||||||
|
|
||||||
## v0.15.9
|
## v0.15.9
|
||||||
|
|
||||||
- 添加 V34 和 V35 版本支持
|
- 添加 V34 和 V35 版本支持
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ A simple and user-friendly Spine file viewer and exporter with multi-language su
|
|||||||
* Batch adjustment of skeleton parameters using multi-selection.
|
* Batch adjustment of skeleton parameters using multi-selection.
|
||||||
* Multi-track animation settings.
|
* Multi-track animation settings.
|
||||||
* Skin and custom slot attachment settings.
|
* Skin and custom slot attachment settings.
|
||||||
|
* Custom slot visibility settings.
|
||||||
* Debug rendering support.
|
* Debug rendering support.
|
||||||
* Fullscreen preview mode.
|
* Fullscreen preview mode.
|
||||||
* Export to single frame/image sequence/animated GIF/video formats.
|
* Export to single frame/image sequence/animated GIF/video formats.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
- 支持列表多选批量设置骨骼参数
|
- 支持列表多选批量设置骨骼参数
|
||||||
- 支持多轨道动画设置
|
- 支持多轨道动画设置
|
||||||
- 支持皮肤/自定义插槽附件设置
|
- 支持皮肤/自定义插槽附件设置
|
||||||
|
- 支持自定义插槽可见性
|
||||||
- 支持调试渲染
|
- 支持调试渲染
|
||||||
- 支持全屏预览
|
- 支持全屏预览
|
||||||
- 支持单帧/动图/视频文件导出
|
- 支持单帧/动图/视频文件导出
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ namespace Spine.Implementations.SpineWrappers.V21
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ namespace Spine.Implementations.SpineWrappers.V34
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ namespace Spine.Implementations.SpineWrappers.V35
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ namespace Spine.Implementations.SpineWrappers.V36
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ namespace Spine.Implementations.SpineWrappers.V37
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ namespace Spine.Implementations.SpineWrappers.V38
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ namespace Spine.Implementations.SpineWrappers.V40
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ namespace Spine.Implementations.SpineWrappers.V41
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ namespace Spine.Implementations.SpineWrappers.V42
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
public override string ToString() => _o.ToString();
|
public override string ToString() => _o.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||||
<Version>0.15.9</Version>
|
<Version>0.15.10</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -172,9 +172,12 @@ namespace Spine
|
|||||||
_skinLoadStatus = other._skinLoadStatus.ToDictionary();
|
_skinLoadStatus = other._skinLoadStatus.ToDictionary();
|
||||||
ReloadSkins();
|
ReloadSkins();
|
||||||
|
|
||||||
// 拷贝自定义插槽附件加载情况
|
// 拷贝插槽属性值
|
||||||
for (int i = 0; i < other._skeleton.Slots.Length; i++)
|
for (int i = 0; i < other._skeleton.Slots.Length; i++)
|
||||||
|
{
|
||||||
_skeleton.Slots[i].Attachment = other._skeleton.Slots[i].Attachment;
|
_skeleton.Slots[i].Attachment = other._skeleton.Slots[i].Attachment;
|
||||||
|
_skeleton.Slots[i].Disabled = other._skeleton.Slots[i].Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
// 拷贝调试属性
|
// 拷贝调试属性
|
||||||
EnableDebug = other.EnableDebug;
|
EnableDebug = other.EnableDebug;
|
||||||
@@ -299,6 +302,30 @@ namespace Spine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DebugClippings { get; set; }
|
public bool DebugClippings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取插槽可见性, 如果不存在则默认返回 false
|
||||||
|
/// </summary>
|
||||||
|
public bool GetSlotVisible(string slotName)
|
||||||
|
{
|
||||||
|
if (_skeleton.SlotsByName.TryGetValue(slotName, out var slot))
|
||||||
|
return !slot.Disabled;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置插槽可见性, 插槽不可见后将不会在任何渲染中出现, 插槽不存在则忽略操作
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>操作是否成功, 插槽不存在则返回 false</returns>
|
||||||
|
public bool SetSlotVisible(string slotName, bool visible)
|
||||||
|
{
|
||||||
|
if (_skeleton.SlotsByName.TryGetValue(slotName, out var slot))
|
||||||
|
{
|
||||||
|
slot.Disabled = !visible;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取某个插槽上的附件名, 插槽不存在或者无附件均返回 null
|
/// 获取某个插槽上的附件名, 插槽不存在或者无附件均返回 null
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -310,7 +337,7 @@ namespace Spine
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置某个插槽的附件, 如果不存在则忽略, 可以使用 null 来清除附件
|
/// 设置某个插槽的附件, 如果不存在则忽略, 可以使用 null 来尝试清除附件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>是否操作成功</returns>
|
/// <returns>是否操作成功</returns>
|
||||||
public bool SetAttachment(string slotName, string? attachmentName)
|
public bool SetAttachment(string slotName, string? attachmentName)
|
||||||
@@ -471,7 +498,7 @@ namespace Spine
|
|||||||
|
|
||||||
foreach (var slot in _skeleton.IterDrawOrder())
|
foreach (var slot in _skeleton.IterDrawOrder())
|
||||||
{
|
{
|
||||||
if (slot.A <= 0 || !slot.Bone.Active)
|
if (slot.A <= 0 || !slot.Bone.Active || slot.Disabled)
|
||||||
{
|
{
|
||||||
_clipping.ClipEnd(slot);
|
_clipping.ClipEnd(slot);
|
||||||
continue;
|
continue;
|
||||||
@@ -602,7 +629,7 @@ namespace Spine
|
|||||||
if (DebugRegions)
|
if (DebugRegions)
|
||||||
{
|
{
|
||||||
vt.Color = AttachmentLineColor;
|
vt.Color = AttachmentLineColor;
|
||||||
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active))
|
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active && !s.Disabled))
|
||||||
{
|
{
|
||||||
if (slot.Attachment is IRegionAttachment regionAttachment)
|
if (slot.Attachment is IRegionAttachment regionAttachment)
|
||||||
{
|
{
|
||||||
@@ -634,7 +661,7 @@ namespace Spine
|
|||||||
if (DebugMeshes)
|
if (DebugMeshes)
|
||||||
{
|
{
|
||||||
vt.Color = MeshLineColor;
|
vt.Color = MeshLineColor;
|
||||||
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active))
|
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active && !s.Disabled))
|
||||||
{
|
{
|
||||||
if (slot.Attachment is IMeshAttachment meshAttachment)
|
if (slot.Attachment is IMeshAttachment meshAttachment)
|
||||||
{
|
{
|
||||||
@@ -698,7 +725,7 @@ namespace Spine
|
|||||||
if (DebugMeshHulls)
|
if (DebugMeshHulls)
|
||||||
{
|
{
|
||||||
vt.Color = AttachmentLineColor;
|
vt.Color = AttachmentLineColor;
|
||||||
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active))
|
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active && !s.Disabled))
|
||||||
{
|
{
|
||||||
if (slot.Attachment is IMeshAttachment meshAttachment)
|
if (slot.Attachment is IMeshAttachment meshAttachment)
|
||||||
{
|
{
|
||||||
@@ -767,7 +794,7 @@ namespace Spine
|
|||||||
if (DebugClippings)
|
if (DebugClippings)
|
||||||
{
|
{
|
||||||
vt.Color = ClippingLineColor;
|
vt.Color = ClippingLineColor;
|
||||||
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active))
|
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active && !s.Disabled))
|
||||||
{
|
{
|
||||||
if (slot.Attachment is IClippingAttachment clippingAttachment)
|
if (slot.Attachment is IClippingAttachment clippingAttachment)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,5 +53,10 @@ namespace Spine.SpineWrappers
|
|||||||
/// 使用的附件, 可以设置为 null 清空附件
|
/// 使用的附件, 可以设置为 null 清空附件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IAttachment? Attachment { get; set; }
|
public IAttachment? Attachment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已禁用渲染该插槽
|
||||||
|
/// </summary>
|
||||||
|
public bool Disabled { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ namespace SpineViewer.Models
|
|||||||
|
|
||||||
public Dictionary<string, string?> SlotAttachment { get; set; } = [];
|
public Dictionary<string, string?> SlotAttachment { get; set; } = [];
|
||||||
|
|
||||||
|
public List<string> DisabledSlots { get; set; } = [];
|
||||||
|
|
||||||
public List<string?> Animations { get; set; } = [];
|
public List<string?> Animations { get; set; } = [];
|
||||||
|
|
||||||
public bool DebugTexture { get; set; } = true;
|
public bool DebugTexture { get; set; } = true;
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ namespace SpineViewer.Models
|
|||||||
|
|
||||||
public event EventHandler<SkinStatusChangedEventArgs>? SkinStatusChanged;
|
public event EventHandler<SkinStatusChangedEventArgs>? SkinStatusChanged;
|
||||||
|
|
||||||
|
public event EventHandler<SlotVisibleChangedEventArgs>? SlotVisibleChanged;
|
||||||
|
|
||||||
public event EventHandler<SlotAttachmentChangedEventArgs>? SlotAttachmentChanged;
|
public event EventHandler<SlotAttachmentChangedEventArgs>? SlotAttachmentChanged;
|
||||||
|
|
||||||
public event EventHandler<AnimationChangedEventArgs>? AnimationChanged;
|
public event EventHandler<AnimationChangedEventArgs>? AnimationChanged;
|
||||||
@@ -200,6 +202,19 @@ namespace SpineViewer.Models
|
|||||||
|
|
||||||
public FrozenDictionary<string, ImmutableArray<string>> SlotAttachments => _slotAttachments;
|
public FrozenDictionary<string, ImmutableArray<string>> SlotAttachments => _slotAttachments;
|
||||||
|
|
||||||
|
public bool GetSlotVisible(string slotName)
|
||||||
|
{
|
||||||
|
lock (_lock) return _spineObject.GetSlotVisible(slotName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetSlotVisible(string slotName, bool visible)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
lock (_lock) changed = _spineObject.SetSlotVisible(slotName, visible);
|
||||||
|
if (changed) SlotVisibleChanged?.Invoke(this, new(slotName, visible));
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
public string? GetAttachment(string slotName)
|
public string? GetAttachment(string slotName)
|
||||||
{
|
{
|
||||||
lock (_lock) return _spineObject.GetAttachment(slotName);
|
lock (_lock) return _spineObject.GetAttachment(slotName);
|
||||||
@@ -390,6 +405,8 @@ namespace SpineViewer.Models
|
|||||||
|
|
||||||
foreach (var slot in _spineObject.Skeleton.Slots) config.SlotAttachment[slot.Name] = slot.Attachment?.Name;
|
foreach (var slot in _spineObject.Skeleton.Slots) config.SlotAttachment[slot.Name] = slot.Attachment?.Name;
|
||||||
|
|
||||||
|
config.DisabledSlots = _spineObject.Skeleton.Slots.Where(it => it.Disabled).Select(it => it.Name).ToList();
|
||||||
|
|
||||||
// XXX: 处理空动画
|
// XXX: 处理空动画
|
||||||
config.Animations.AddRange(_spineObject.AnimationState.IterTracks().Select(tr => tr?.Animation.Name));
|
config.Animations.AddRange(_spineObject.AnimationState.IterTracks().Select(tr => tr?.Animation.Name));
|
||||||
|
|
||||||
@@ -422,6 +439,10 @@ namespace SpineViewer.Models
|
|||||||
if (_spineObject.SetAttachment(slotName, attachmentName))
|
if (_spineObject.SetAttachment(slotName, attachmentName))
|
||||||
SlotAttachmentChanged?.Invoke(this, new(slotName, attachmentName));
|
SlotAttachmentChanged?.Invoke(this, new(slotName, attachmentName));
|
||||||
|
|
||||||
|
foreach (var slotName in value.DisabledSlots)
|
||||||
|
if (_spineObject.SetSlotVisible(slotName, false))
|
||||||
|
SlotVisibleChanged?.Invoke(this, new(slotName, false));
|
||||||
|
|
||||||
// XXX: 处理空动画
|
// XXX: 处理空动画
|
||||||
_spineObject.AnimationState.ClearTracks();
|
_spineObject.AnimationState.ClearTracks();
|
||||||
int trackIndex = 0;
|
int trackIndex = 0;
|
||||||
@@ -507,6 +528,12 @@ namespace SpineViewer.Models
|
|||||||
public bool Status { get; } = status;
|
public bool Status { get; } = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SlotVisibleChangedEventArgs(string slotName, bool visible) : EventArgs
|
||||||
|
{
|
||||||
|
public string SlotName { get; } = slotName;
|
||||||
|
public bool Visible { get; } = visible;
|
||||||
|
}
|
||||||
|
|
||||||
public class SlotAttachmentChangedEventArgs(string slotName, string? attachmentName) : EventArgs
|
public class SlotAttachmentChangedEventArgs(string slotName, string? attachmentName) : EventArgs
|
||||||
{
|
{
|
||||||
public string SlotName { get; } = slotName;
|
public string SlotName { get; } = slotName;
|
||||||
|
|||||||
@@ -78,6 +78,8 @@
|
|||||||
|
|
||||||
<s:String x:Key="Str_Slot">Slot</s:String>
|
<s:String x:Key="Str_Slot">Slot</s:String>
|
||||||
<s:String x:Key="Str_ClearSlotsAttachment">Clear Slots Attachment</s:String>
|
<s:String x:Key="Str_ClearSlotsAttachment">Clear Slots Attachment</s:String>
|
||||||
|
<s:String x:Key="Str_EnableSlots">Enable Slots</s:String>
|
||||||
|
<s:String x:Key="Str_DisableSlots">Disable Slots</s:String>
|
||||||
|
|
||||||
<s:String x:Key="Str_Animation">Animation</s:String>
|
<s:String x:Key="Str_Animation">Animation</s:String>
|
||||||
<s:String x:Key="Str_AppendTrack">Add</s:String>
|
<s:String x:Key="Str_AppendTrack">Add</s:String>
|
||||||
|
|||||||
@@ -78,6 +78,8 @@
|
|||||||
|
|
||||||
<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_EnableSlots">有効</s:String>
|
||||||
|
<s:String x:Key="Str_DisableSlots">無効</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>
|
||||||
|
|||||||
@@ -78,6 +78,8 @@
|
|||||||
|
|
||||||
<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_EnableSlots">启用</s:String>
|
||||||
|
<s:String x:Key="Str_DisableSlots">禁用</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>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||||
<Version>0.15.9</Version>
|
<Version>0.15.10</Version>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
{
|
{
|
||||||
private SpineObjectModel[] _selectedObjects = [];
|
private SpineObjectModel[] _selectedObjects = [];
|
||||||
private readonly ObservableCollection<SkinViewModel> _skins = [];
|
private readonly ObservableCollection<SkinViewModel> _skins = [];
|
||||||
private readonly ObservableCollection<SlotAttachmentViewModel> _slots = [];
|
private readonly ObservableCollection<SlotViewModel> _slots = [];
|
||||||
private readonly ObservableCollection<AnimationTrackViewModel> _animationTracks = [];
|
private readonly ObservableCollection<AnimationTrackViewModel> _animationTracks = [];
|
||||||
|
|
||||||
public ImmutableArray<ISkeleton.Physics> PhysicsOptions { get; } = Enum.GetValues<ISkeleton.Physics>().ToImmutableArray();
|
public ImmutableArray<ISkeleton.Physics> PhysicsOptions { get; } = Enum.GetValues<ISkeleton.Physics>().ToImmutableArray();
|
||||||
@@ -324,11 +324,16 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
args => { return args is not null && args.OfType<SkinViewModel>().Any(); }
|
args => { return args is not null && args.OfType<SkinViewModel>().Any(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
public ObservableCollection<SlotAttachmentViewModel> Slots => _slots;
|
public ObservableCollection<SlotViewModel> Slots => _slots;
|
||||||
|
|
||||||
public RelayCommand<IList?> Cmd_ClearSlotsAttachment { get; } = new(
|
public RelayCommand<IList?> Cmd_EnableSlots { get; } = new(
|
||||||
args => { if (args is null) return; foreach (var s in args.OfType<SlotAttachmentViewModel>()) s.AttachmentName = null; },
|
args => { if (args is null) return; foreach (var s in args.OfType<SlotViewModel>()) s.Visible = true; },
|
||||||
args => { return args is not null && args.OfType<SlotAttachmentViewModel>().Any(); }
|
args => { return args is not null && args.OfType<SlotViewModel>().Any(); }
|
||||||
|
);
|
||||||
|
|
||||||
|
public RelayCommand<IList?> Cmd_DisableSlots { get; } = new(
|
||||||
|
args => { if (args is null) return; foreach (var s in args.OfType<SlotViewModel>()) s.Visible = false; },
|
||||||
|
args => { return args is not null && args.OfType<SlotViewModel>().Any(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
public ObservableCollection<AnimationTrackViewModel> AnimationTracks => _animationTracks;
|
public ObservableCollection<AnimationTrackViewModel> AnimationTracks => _animationTracks;
|
||||||
@@ -672,13 +677,13 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SlotAttachmentViewModel : ObservableObject
|
public class SlotViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly SpineObjectModel[] _spines;
|
private readonly SpineObjectModel[] _spines;
|
||||||
private readonly string[] _attachmentNames = [];
|
private readonly string[] _attachmentNames = [];
|
||||||
private readonly string _slotName;
|
private readonly string _slotName;
|
||||||
|
|
||||||
public SlotAttachmentViewModel(string slotName, SpineObjectModel[] spines)
|
public SlotViewModel(string slotName, SpineObjectModel[] spines)
|
||||||
{
|
{
|
||||||
_spines = spines;
|
_spines = spines;
|
||||||
_slotName = slotName;
|
_slotName = slotName;
|
||||||
@@ -694,6 +699,11 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
// 使用弱引用, 则此 ViewModel 被释放时无需显式退订事件
|
// 使用弱引用, 则此 ViewModel 被释放时无需显式退订事件
|
||||||
foreach (var sp in _spines)
|
foreach (var sp in _spines)
|
||||||
{
|
{
|
||||||
|
WeakEventManager<SpineObjectModel, SlotVisibleChangedEventArgs>.AddHandler(
|
||||||
|
sp,
|
||||||
|
nameof(sp.SlotVisibleChanged),
|
||||||
|
SingleModel_SlotVisibleChanged
|
||||||
|
);
|
||||||
WeakEventManager<SpineObjectModel, SlotAttachmentChangedEventArgs>.AddHandler(
|
WeakEventManager<SpineObjectModel, SlotAttachmentChangedEventArgs>.AddHandler(
|
||||||
sp,
|
sp,
|
||||||
nameof(sp.SlotAttachmentChanged),
|
nameof(sp.SlotAttachmentChanged),
|
||||||
@@ -707,9 +717,6 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RelayCommand Cmd_ClearAttachment => _cmd_ClearAttachment ??= new(() => AttachmentName = null);
|
|
||||||
private RelayCommand? _cmd_ClearAttachment;
|
|
||||||
|
|
||||||
public ReadOnlyCollection<string> AttachmentNames => _attachmentNames.AsReadOnly();
|
public ReadOnlyCollection<string> AttachmentNames => _attachmentNames.AsReadOnly();
|
||||||
|
|
||||||
public string SlotName => _slotName;
|
public string SlotName => _slotName;
|
||||||
@@ -733,6 +740,30 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool? Visible
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_spines.Length <= 0) return null;
|
||||||
|
var val = _spines[0].GetSlotVisible(_slotName);
|
||||||
|
if (_spines.Skip(1).Any(it => it.GetSlotVisible(_slotName) != val)) return null;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_spines.Length <= 0) return;
|
||||||
|
if (value is null) return;
|
||||||
|
foreach (var sp in _spines) sp.SetSlotVisible(_slotName, (bool)value);
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SingleModel_SlotVisibleChanged(object? sender, SlotVisibleChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.SlotName == _slotName) OnPropertyChanged(nameof(Visible));
|
||||||
|
}
|
||||||
|
|
||||||
private void SingleModel_SlotAttachmentChanged(object? sender, SlotAttachmentChangedEventArgs e)
|
private void SingleModel_SlotAttachmentChanged(object? sender, SlotAttachmentChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.SlotName == _slotName) OnPropertyChanged(nameof(AttachmentName));
|
if (e.SlotName == _slotName) OnPropertyChanged(nameof(AttachmentName));
|
||||||
|
|||||||
@@ -524,8 +524,11 @@
|
|||||||
|
|
||||||
<ListBox.ContextMenu>
|
<ListBox.ContextMenu>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<MenuItem Header="{DynamicResource Str_ClearSlotsAttachment}"
|
<MenuItem Header="{DynamicResource Str_EnableSlots}"
|
||||||
Command="{Binding Cmd_ClearSlotsAttachment}"
|
Command="{Binding Cmd_EnableSlots}"
|
||||||
|
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
|
||||||
|
<MenuItem Header="{DynamicResource Str_DisableSlots}"
|
||||||
|
Command="{Binding Cmd_DisableSlots}"
|
||||||
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
|
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</ListBox.ContextMenu>
|
</ListBox.ContextMenu>
|
||||||
@@ -540,9 +543,7 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Label Grid.Column="0" Content="{Binding SlotName}" HorizontalAlignment="Left"/>
|
<Label Grid.Column="0" Content="{Binding SlotName}" HorizontalAlignment="Left"/>
|
||||||
<ComboBox Grid.Column="1" SelectedValue="{Binding AttachmentName}" ItemsSource="{Binding AttachmentNames}"/>
|
<ComboBox Grid.Column="1" SelectedValue="{Binding AttachmentName}" ItemsSource="{Binding AttachmentNames}"/>
|
||||||
<Button Grid.Column="2"
|
<ToggleButton Grid.Column="2" IsChecked="{Binding Visible}"/>
|
||||||
Command="{Binding Cmd_ClearAttachment}"
|
|
||||||
hc:IconElement.Geometry="{StaticResource Geo_Ban}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
|
|||||||
Reference in New Issue
Block a user