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