diff --git a/SpineViewer/Controls/SpineListView.Designer.cs b/SpineViewer/Controls/SpineListView.Designer.cs
index 422c977..4ddc1a4 100644
--- a/SpineViewer/Controls/SpineListView.Designer.cs
+++ b/SpineViewer/Controls/SpineListView.Designer.cs
@@ -41,6 +41,13 @@
toolStripSeparator2 = new ToolStripSeparator();
toolStripMenuItem_BatchAdd = new ToolStripMenuItem();
toolStripMenuItem_RemoveAll = new ToolStripMenuItem();
+ toolStripSeparator3 = new ToolStripSeparator();
+ toolStripMenuItem_ChangeView = new ToolStripMenuItem();
+ toolStripMenuItem_LargeIconView = new ToolStripMenuItem();
+ toolStripMenuItem_SmallIconView = new ToolStripMenuItem();
+ toolStripMenuItem_DetailsView = new ToolStripMenuItem();
+ imageList_LargeIcon = new ImageList(components);
+ imageList_SmallIcon = new ImageList(components);
contextMenuStrip.SuspendLayout();
SuspendLayout();
//
@@ -52,10 +59,12 @@
listView.Dock = DockStyle.Fill;
listView.FullRowSelect = true;
listView.GridLines = true;
+ listView.LargeImageList = imageList_LargeIcon;
listView.Location = new Point(0, 0);
listView.Name = "listView";
listView.ShowItemToolTips = true;
listView.Size = new Size(336, 445);
+ listView.SmallImageList = imageList_SmallIcon;
listView.TabIndex = 1;
listView.UseCompatibleStateImageBehavior = false;
listView.View = View.Details;
@@ -73,9 +82,9 @@
// contextMenuStrip
//
contextMenuStrip.ImageScalingSize = new Size(24, 24);
- contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_Add, toolStripMenuItem_Insert, toolStripMenuItem_Remove, toolStripSeparator1, toolStripMenuItem_MoveUp, toolStripMenuItem_MoveDown, toolStripSeparator2, toolStripMenuItem_BatchAdd, toolStripMenuItem_RemoveAll });
+ contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_Add, toolStripMenuItem_Insert, toolStripMenuItem_Remove, toolStripSeparator1, toolStripMenuItem_MoveUp, toolStripMenuItem_MoveDown, toolStripSeparator2, toolStripMenuItem_BatchAdd, toolStripMenuItem_RemoveAll, toolStripSeparator3, toolStripMenuItem_ChangeView });
contextMenuStrip.Name = "contextMenuStrip";
- contextMenuStrip.Size = new Size(188, 226);
+ contextMenuStrip.Size = new Size(188, 262);
contextMenuStrip.Opening += contextMenuStrip_Opening;
//
// toolStripMenuItem_Add
@@ -140,6 +149,51 @@
toolStripMenuItem_RemoveAll.Text = "移除全部(&X)";
toolStripMenuItem_RemoveAll.Click += toolStripMenuItem_RemoveAll_Click;
//
+ // toolStripSeparator3
+ //
+ toolStripSeparator3.Name = "toolStripSeparator3";
+ toolStripSeparator3.Size = new Size(184, 6);
+ //
+ // toolStripMenuItem_ChangeView
+ //
+ toolStripMenuItem_ChangeView.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_LargeIconView, toolStripMenuItem_SmallIconView, toolStripMenuItem_DetailsView });
+ toolStripMenuItem_ChangeView.Name = "toolStripMenuItem_ChangeView";
+ toolStripMenuItem_ChangeView.Size = new Size(187, 30);
+ toolStripMenuItem_ChangeView.Text = "切换视图";
+ //
+ // toolStripMenuItem_LargeIconView
+ //
+ toolStripMenuItem_LargeIconView.Name = "toolStripMenuItem_LargeIconView";
+ toolStripMenuItem_LargeIconView.Size = new Size(164, 34);
+ toolStripMenuItem_LargeIconView.Text = "大图标";
+ toolStripMenuItem_LargeIconView.Click += toolStripMenuItem_LargeIconView_Click;
+ //
+ // toolStripMenuItem_SmallIconView
+ //
+ toolStripMenuItem_SmallIconView.Name = "toolStripMenuItem_SmallIconView";
+ toolStripMenuItem_SmallIconView.Size = new Size(164, 34);
+ toolStripMenuItem_SmallIconView.Text = "小图标";
+ toolStripMenuItem_SmallIconView.Click += toolStripMenuItem_SmallIconView_Click;
+ //
+ // toolStripMenuItem_DetailsView
+ //
+ toolStripMenuItem_DetailsView.Name = "toolStripMenuItem_DetailsView";
+ toolStripMenuItem_DetailsView.Size = new Size(164, 34);
+ toolStripMenuItem_DetailsView.Text = "列表";
+ toolStripMenuItem_DetailsView.Click += toolStripMenuItem_DetailsView_Click;
+ //
+ // imageList_LargeIcon
+ //
+ imageList_LargeIcon.ColorDepth = ColorDepth.Depth32Bit;
+ imageList_LargeIcon.ImageSize = new Size(96, 96);
+ imageList_LargeIcon.TransparentColor = Color.Transparent;
+ //
+ // imageList_SmallIcon
+ //
+ imageList_SmallIcon.ColorDepth = ColorDepth.Depth32Bit;
+ imageList_SmallIcon.ImageSize = new Size(48, 48);
+ imageList_SmallIcon.TransparentColor = Color.Transparent;
+ //
// SpineListView
//
AutoScaleDimensions = new SizeF(11F, 24F);
@@ -164,5 +218,12 @@
private ToolStripMenuItem toolStripMenuItem_MoveDown;
private ToolStripSeparator toolStripSeparator2;
private ColumnHeader columnHeader_Name;
+ private ImageList imageList_SmallIcon;
+ private ImageList imageList_LargeIcon;
+ private ToolStripSeparator toolStripSeparator3;
+ private ToolStripMenuItem toolStripMenuItem_ChangeView;
+ private ToolStripMenuItem toolStripMenuItem_LargeIconView;
+ private ToolStripMenuItem toolStripMenuItem_SmallIconView;
+ private ToolStripMenuItem toolStripMenuItem_DetailsView;
}
}
diff --git a/SpineViewer/Controls/SpineListView.cs b/SpineViewer/Controls/SpineListView.cs
index e252109..e95f514 100644
--- a/SpineViewer/Controls/SpineListView.cs
+++ b/SpineViewer/Controls/SpineListView.cs
@@ -58,8 +58,13 @@ namespace SpineViewer.Controls
index = listView.Items.Count;
// 锁定外部的读操作
- lock (Spines) { spines.Insert(index, spine); }
- listView.Items.Insert(index, new ListViewItem(spine.Name) { ToolTipText = spine.SkelPath });
+ lock (Spines)
+ {
+ spines.Insert(index, spine);
+ listView.SmallImageList.Images.Add(spine.ID, spine.Preview);
+ listView.LargeImageList.Images.Add(spine.ID, spine.Preview);
+ }
+ listView.Items.Insert(index, new ListViewItem(spine.Name, spine.ID) { ToolTipText = spine.SkelPath });
// 选中新增项
listView.SelectedIndices.Clear();
@@ -123,8 +128,14 @@ namespace SpineViewer.Controls
try
{
var spine = Spine.Spine.New(version, skelPath);
+ var preview = spine.Preview;
lock (Spines) { spines.Add(spine); }
- listView.Invoke(() => listView.Items.Add(new ListViewItem(spine.Name) { ToolTipText = spine.SkelPath }));
+ listView.Invoke(() =>
+ {
+ listView.SmallImageList.Images.Add(spine.ID, preview);
+ listView.LargeImageList.Images.Add(spine.ID, preview);
+ listView.Items.Add(new ListViewItem(spine.Name, spine.ID) { ToolTipText = spine.SkelPath });
+ });
success++;
}
catch (Exception ex)
@@ -255,6 +266,11 @@ namespace SpineViewer.Controls
toolStripMenuItem_MoveUp.Enabled = selectedCount == 1 && listView.SelectedIndices[0] != 0;
toolStripMenuItem_MoveDown.Enabled = selectedCount == 1 && listView.SelectedIndices[0] != itemsCount - 1;
toolStripMenuItem_RemoveAll.Enabled = itemsCount > 0;
+
+ // 视图选项
+ toolStripMenuItem_LargeIconView.Checked = listView.View == View.LargeIcon;
+ toolStripMenuItem_SmallIconView.Checked = listView.View == View.SmallIcon;
+ toolStripMenuItem_DetailsView.Checked = listView.View == View.Details;
}
private void toolStripMenuItem_Add_Click(object sender, EventArgs e)
@@ -288,8 +304,11 @@ namespace SpineViewer.Controls
{
lock (Spines)
{
- spines[i].Dispose();
+ var spine = spines[i];
spines.RemoveAt(i);
+ listView.SmallImageList.Images.RemoveByKey(spine.ID);
+ listView.LargeImageList.Images.RemoveByKey(spine.ID);
+ spine.Dispose();
}
listView.Items.RemoveAt(i);
}
@@ -333,15 +352,32 @@ namespace SpineViewer.Controls
if (MessageBox.Show($"确认移除所有 {listView.Items.Count} 项吗?", "操作确认", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK)
return;
- lock (Spines)
+ lock (Spines)
{
foreach (var spine in spines)
spine.Dispose();
- spines.Clear();
+ spines.Clear();
+ listView.SmallImageList.Images.Clear();
+ listView.LargeImageList.Images.Clear();
}
listView.Items.Clear();
if (PropertyGrid is not null)
PropertyGrid.SelectedObject = null;
}
+
+ private void toolStripMenuItem_LargeIconView_Click(object sender, EventArgs e)
+ {
+ listView.View = View.LargeIcon;
+ }
+
+ private void toolStripMenuItem_SmallIconView_Click(object sender, EventArgs e)
+ {
+ listView.View = View.SmallIcon;
+ }
+
+ private void toolStripMenuItem_DetailsView_Click(object sender, EventArgs e)
+ {
+ listView.View = View.Details;
+ }
}
}
diff --git a/SpineViewer/Controls/SpineListView.resx b/SpineViewer/Controls/SpineListView.resx
index efb6fba..a813d55 100644
--- a/SpineViewer/Controls/SpineListView.resx
+++ b/SpineViewer/Controls/SpineListView.resx
@@ -120,4 +120,10 @@
17, 17
+
+ 552, 29
+
+
+ 267, 34
+
\ No newline at end of file
diff --git a/SpineViewer/Spine/Spine.cs b/SpineViewer/Spine/Spine.cs
index 9e0a2d7..fc4b73d 100644
--- a/SpineViewer/Spine/Spine.cs
+++ b/SpineViewer/Spine/Spine.cs
@@ -41,6 +41,16 @@ namespace SpineViewer.Spine
///
public const string EMPTY_ANIMATION = "";
+ ///
+ /// 预览图大小
+ ///
+ public static readonly Size PREVIEW_SIZE = new(256, 256);
+
+ ///
+ /// 缩放最小值
+ ///
+ public const float SCALE_MIN = 0.001f;
+
///
/// 实现类缓存
///
@@ -108,6 +118,11 @@ namespace SpineViewer.Spine
return (Spine)Activator.CreateInstance(spineType, skelPath, atlasPath);
}
+ ///
+ /// 标识符
+ ///
+ public readonly string ID = Guid.NewGuid().ToString();
+
///
/// 构造函数
///
@@ -132,13 +147,7 @@ namespace SpineViewer.Spine
~Spine() { Dispose(false); }
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
- protected virtual void Dispose(bool disposing) { }
-
- ///
- /// 缩放最小值
- ///
- [Browsable(false)]
- public const float SCALE_MIN = 0.001f;
+ protected virtual void Dispose(bool disposing) { preview?.Dispose(); }
///
/// 获取所属版本
@@ -231,6 +240,48 @@ namespace SpineViewer.Spine
[Browsable(false)]
public abstract RectangleF Bounds { get; }
+ ///
+ /// 骨骼预览图
+ ///
+ [Browsable(false)]
+ public Image Preview { get => preview ??= GetPreview(PREVIEW_SIZE); }
+ private Image preview = null;
+
+ public Image GetPreview(Size size)
+ {
+ var curAnimation = CurrentAnimation;
+ CurrentAnimation = EMPTY_ANIMATION;
+ var bounds = Bounds;
+
+ float viewX = size.Width;
+ float viewY = size.Height;
+ float sizeX = bounds.Width;
+ float sizeY = bounds.Height;
+
+ var scale = 1f;
+ if ((sizeY / sizeX) < (viewY / viewX))
+ scale = sizeX / viewX;// 相同的 X, 视窗 Y 更大
+ else
+ scale = sizeY / viewY;// 相同的 Y, 视窗 X 更大
+
+ viewX *= scale;
+ viewY *= scale;
+
+ var tex = new SFML.Graphics.RenderTexture((uint)size.Width, (uint)size.Height);
+ var view = tex.GetView();
+ view.Center = new(bounds.X + viewX / 2, bounds.Y + viewY / 2);
+ view.Size = new(viewX, -viewY);
+ tex.SetView(view);
+ tex.Clear(SFML.Graphics.Color.Transparent);
+ tex.Draw(this);
+ tex.Display();
+ CurrentAnimation = curAnimation;
+ using var img = tex.Texture.CopyToImage();
+ img.SaveToMemory(out var imgBuffer, "bmp");
+ using var stream = new MemoryStream(imgBuffer);
+ return new Bitmap(stream);
+ }
+
///
/// 获取动画时长, 如果动画不存在则返回 0
///