修复插槽禁用功能
This commit is contained in:
@@ -31,6 +31,8 @@ namespace SpineViewer.Models
|
||||
|
||||
public Dictionary<string, string?> SlotAttachment { get; set; } = [];
|
||||
|
||||
public List<string> DisabledSlots { get; set; } = [];
|
||||
|
||||
public List<string?> Animations { get; set; } = [];
|
||||
|
||||
public bool DebugTexture { get; set; } = true;
|
||||
|
||||
@@ -85,6 +85,8 @@ namespace SpineViewer.Models
|
||||
|
||||
public event EventHandler<SkinStatusChangedEventArgs>? SkinStatusChanged;
|
||||
|
||||
public event EventHandler<SlotVisibleChangedEventArgs>? SlotVisibleChanged;
|
||||
|
||||
public event EventHandler<SlotAttachmentChangedEventArgs>? SlotAttachmentChanged;
|
||||
|
||||
public event EventHandler<AnimationChangedEventArgs>? AnimationChanged;
|
||||
@@ -200,6 +202,19 @@ namespace SpineViewer.Models
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
config.DisabledSlots = _spineObject.Skeleton.Slots.Where(it => it.Disabled).Select(it => it.Name).ToList();
|
||||
|
||||
// XXX: 处理空动画
|
||||
config.Animations.AddRange(_spineObject.AnimationState.IterTracks().Select(tr => tr?.Animation.Name));
|
||||
|
||||
@@ -422,6 +439,10 @@ namespace SpineViewer.Models
|
||||
if (_spineObject.SetAttachment(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: 处理空动画
|
||||
_spineObject.AnimationState.ClearTracks();
|
||||
int trackIndex = 0;
|
||||
@@ -507,6 +528,12 @@ namespace SpineViewer.Models
|
||||
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 string SlotName { get; } = slotName;
|
||||
|
||||
@@ -78,6 +78,8 @@
|
||||
|
||||
<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_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_AppendTrack">Add</s:String>
|
||||
|
||||
@@ -78,6 +78,8 @@
|
||||
|
||||
<s:String x:Key="Str_Slot">スロット</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_AppendTrack">追加</s:String>
|
||||
|
||||
@@ -78,6 +78,8 @@
|
||||
|
||||
<s:String x:Key="Str_Slot">插槽</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_AppendTrack">添加</s:String>
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
{
|
||||
private SpineObjectModel[] _selectedObjects = [];
|
||||
private readonly ObservableCollection<SkinViewModel> _skins = [];
|
||||
private readonly ObservableCollection<SlotAttachmentViewModel> _slots = [];
|
||||
private readonly ObservableCollection<SlotViewModel> _slots = [];
|
||||
private readonly ObservableCollection<AnimationTrackViewModel> _animationTracks = [];
|
||||
|
||||
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(); }
|
||||
);
|
||||
|
||||
public ObservableCollection<SlotAttachmentViewModel> Slots => _slots;
|
||||
public ObservableCollection<SlotViewModel> Slots => _slots;
|
||||
|
||||
public RelayCommand<IList?> Cmd_ClearSlotsAttachment { get; } = new(
|
||||
args => { if (args is null) return; foreach (var s in args.OfType<SlotAttachmentViewModel>()) s.AttachmentName = null; },
|
||||
args => { return args is not null && args.OfType<SlotAttachmentViewModel>().Any(); }
|
||||
public RelayCommand<IList?> Cmd_EnableSlots { get; } = new(
|
||||
args => { if (args is null) return; foreach (var s in args.OfType<SlotViewModel>()) s.Visible = true; },
|
||||
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;
|
||||
@@ -672,13 +677,13 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
}
|
||||
}
|
||||
|
||||
public class SlotAttachmentViewModel : ObservableObject
|
||||
public class SlotViewModel : ObservableObject
|
||||
{
|
||||
private readonly SpineObjectModel[] _spines;
|
||||
private readonly string[] _attachmentNames = [];
|
||||
private readonly string _slotName;
|
||||
|
||||
public SlotAttachmentViewModel(string slotName, SpineObjectModel[] spines)
|
||||
public SlotViewModel(string slotName, SpineObjectModel[] spines)
|
||||
{
|
||||
_spines = spines;
|
||||
_slotName = slotName;
|
||||
@@ -694,6 +699,11 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
// 使用弱引用, 则此 ViewModel 被释放时无需显式退订事件
|
||||
foreach (var sp in _spines)
|
||||
{
|
||||
WeakEventManager<SpineObjectModel, SlotVisibleChangedEventArgs>.AddHandler(
|
||||
sp,
|
||||
nameof(sp.SlotVisibleChanged),
|
||||
SingleModel_SlotVisibleChanged
|
||||
);
|
||||
WeakEventManager<SpineObjectModel, SlotAttachmentChangedEventArgs>.AddHandler(
|
||||
sp,
|
||||
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 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)
|
||||
{
|
||||
if (e.SlotName == _slotName) OnPropertyChanged(nameof(AttachmentName));
|
||||
|
||||
@@ -524,8 +524,11 @@
|
||||
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{DynamicResource Str_ClearSlotsAttachment}"
|
||||
Command="{Binding Cmd_ClearSlotsAttachment}"
|
||||
<MenuItem Header="{DynamicResource Str_EnableSlots}"
|
||||
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}}}"/>
|
||||
</ContextMenu>
|
||||
</ListBox.ContextMenu>
|
||||
@@ -540,9 +543,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Grid.Column="0" Content="{Binding SlotName}" HorizontalAlignment="Left"/>
|
||||
<ComboBox Grid.Column="1" SelectedValue="{Binding AttachmentName}" ItemsSource="{Binding AttachmentNames}"/>
|
||||
<Button Grid.Column="2"
|
||||
Command="{Binding Cmd_ClearAttachment}"
|
||||
hc:IconElement.Geometry="{StaticResource Geo_Ban}"/>
|
||||
<ToggleButton Grid.Column="2" IsChecked="{Binding Visible}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
|
||||
Reference in New Issue
Block a user