Compare commits

...

21 Commits

Author SHA1 Message Date
ww-rm
3442ace981 更新至v0.10.6 2025-03-21 14:57:50 +08:00
ww-rm
547cebf5a9 update readme 2025-03-21 14:57:30 +08:00
ww-rm
7a24d22bc6 update changelog 2025-03-21 14:57:25 +08:00
ww-rm
8f5728afe4 fix bug 2025-03-21 14:45:45 +08:00
ww-rm
41b5ac2c61 优化渲染 2025-03-21 14:31:20 +08:00
ww-rm
694ca3bf25 refactor 2025-03-21 13:32:03 +08:00
ww-rm
674d314b55 增加文件夹查找 2025-03-21 13:24:18 +08:00
ww-rm
08a35cc5d1 增加ctrlV导入 2025-03-21 11:14:31 +08:00
ww-rm
176e5db4d9 fix bug 2025-03-21 01:40:42 +08:00
ww-rm
2535a9ebf9 增加运行时标识 2025-03-21 01:05:08 +08:00
ww-rm
8ff99ee925 small optimize 2025-03-21 00:27:38 +08:00
ww-rm
abc8218487 add log 2025-03-21 00:23:31 +08:00
ww-rm
e4765750c3 更新至v0.10.5 2025-03-21 00:04:32 +08:00
ww-rm
02cddf556b update changelog 2025-03-21 00:04:07 +08:00
ww-rm
e1e6d3c72d fix bug 2025-03-21 00:03:31 +08:00
ww-rm
b401a16002 更新至v0.10.4 2025-03-20 23:50:04 +08:00
ww-rm
523b0ce295 update changelog 2025-03-20 23:49:54 +08:00
ww-rm
abb06726f0 fix bug 2025-03-20 23:49:45 +08:00
ww-rm
d9190e9418 修复图标显示问题 2025-03-20 20:30:04 +08:00
ww-rm
9fe3761eca 修改默认大小 2025-03-20 20:22:07 +08:00
ww-rm
51824afba6 优化列表使用 2025-03-20 20:02:25 +08:00
18 changed files with 328 additions and 174 deletions

View File

@@ -1,24 +1,38 @@
# CHANGELOG # CHANGELOG
## v0.10.6
- 增加文件夹检测
- 增加从剪贴板添加(可复制本地文件/文件夹直接打开)
- 修复预览图导致的批量添加可能卡死
## v0.10.5
- 修复一些问题
## v0.10.4
- 修复一些问题
## v0.10.3 ## v0.10.3
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> - 增加自动版本检测
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD>ϷŴ<EFBFBD><EFBFBD><EFBFBD> - 增加文件拖放打开
## v0.10.2 ## v0.10.2
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><EFBFBD>Ҽ<EFBFBD><EFBFBD>˵<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݼ<EFBFBD> - 增加列表右键菜单快捷键
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> - 增加预览缩略图复制
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD>л<EFBFBD> - 增加列表视图切换
## v0.10.1 ## v0.10.1
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>Ԥ<EFBFBD><EFBFBD>ͼ - 增加列表预览图
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>Ԥ<EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> - 增加列表预览图导出
## v0.10.0 ## v0.10.0
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˻<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><EFBFBD><EFBFBD>ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><EFBFBD>Χ<EFBFBD><EFBFBD>ѡ<EFBFBD><EFBFBD> - 增加了画面和列表的选择联动,并删除了预览画面显示包围盒选项
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˹<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD>ʽת<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܣ<EFBFBD>Ŀǰ<EFBFBD><EFBFBD>֧<EFBFBD>ֲ<EFBFBD><EFBFBD>ְ汾<EFBFBD>IJ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> - 增加了骨骼文件格式转换功能,目前仅支持部分版本的不完整功能
- <EFBFBD>Ż<EFBFBD><EFBFBD>˲<EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> - 优化了部分使用体验

View File

@@ -33,7 +33,7 @@
| `4.2.x` | :white_check_mark: | | | `4.2.x` | :white_check_mark: | |
| `4.3.x` | | | | `4.3.x` | | |
- 支持文件拖放打开 - 支持文件拖放/复制到剪贴板打开
- 支持自动检测版本 - 支持自动检测版本
- 支持列表缩略图预览 - 支持列表缩略图预览
- 支持多骨骼文件动画预览 - 支持多骨骼文件动画预览

View File

@@ -129,8 +129,8 @@ namespace SpineRuntime38 {
if (skeletonData.hash.Length == 0) skeletonData.hash = null; if (skeletonData.hash.Length == 0) skeletonData.hash = null;
skeletonData.version = input.ReadString(); skeletonData.version = input.ReadString();
if (skeletonData.version.Length == 0) skeletonData.version = null; if (skeletonData.version.Length == 0) skeletonData.version = null;
if ("3.8.75" == skeletonData.version) //if ("3.8.75" == skeletonData.version)
throw new Exception("Unsupported skeleton data, please export with a newer version of Spine."); // throw new Exception("Unsupported skeleton data, please export with a newer version of Spine.");
skeletonData.x = input.ReadFloat(); skeletonData.x = input.ReadFloat();
skeletonData.y = input.ReadFloat(); skeletonData.y = input.ReadFloat();
skeletonData.width = input.ReadFloat(); skeletonData.width = input.ReadFloat();

View File

@@ -100,8 +100,8 @@ namespace SpineRuntime38 {
var skeletonMap = (Dictionary<string, Object>)root["skeleton"]; var skeletonMap = (Dictionary<string, Object>)root["skeleton"];
skeletonData.hash = (string)skeletonMap["hash"]; skeletonData.hash = (string)skeletonMap["hash"];
skeletonData.version = (string)skeletonMap["spine"]; skeletonData.version = (string)skeletonMap["spine"];
if ("3.8.75" == skeletonData.version) //if ("3.8.75" == skeletonData.version)
throw new Exception("Unsupported skeleton data, please export with a newer version of Spine."); // throw new Exception("Unsupported skeleton data, please export with a newer version of Spine.");
skeletonData.x = GetFloat(skeletonMap, "x", 0); skeletonData.x = GetFloat(skeletonMap, "x", 0);
skeletonData.y = GetFloat(skeletonMap, "y", 0); skeletonData.y = GetFloat(skeletonMap, "y", 0);
skeletonData.width = GetFloat(skeletonMap, "width", 0); skeletonData.width = GetFloat(skeletonMap, "width", 0);

View File

@@ -49,15 +49,17 @@
toolStripSeparator4 = new ToolStripSeparator(); toolStripSeparator4 = new ToolStripSeparator();
toolStripMenuItem_ChangeView = new ToolStripMenuItem(); toolStripMenuItem_ChangeView = new ToolStripMenuItem();
toolStripMenuItem_LargeIconView = new ToolStripMenuItem(); toolStripMenuItem_LargeIconView = new ToolStripMenuItem();
toolStripMenuItem_SmallIconView = new ToolStripMenuItem(); toolStripMenuItem_ListView = new ToolStripMenuItem();
toolStripMenuItem_DetailsView = new ToolStripMenuItem(); toolStripMenuItem_DetailsView = new ToolStripMenuItem();
imageList_LargeIcon = new ImageList(components); imageList_LargeIcon = new ImageList(components);
imageList_SmallIcon = new ImageList(components); imageList_SmallIcon = new ImageList(components);
toolStripMenuItem_AddFromClipboard = new ToolStripMenuItem();
contextMenuStrip.SuspendLayout(); contextMenuStrip.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// listView // listView
// //
listView.Alignment = ListViewAlignment.Left;
listView.AllowDrop = true; listView.AllowDrop = true;
listView.Columns.AddRange(new ColumnHeader[] { columnHeader_Name }); listView.Columns.AddRange(new ColumnHeader[] { columnHeader_Name });
listView.ContextMenuStrip = contextMenuStrip; listView.ContextMenuStrip = contextMenuStrip;
@@ -87,9 +89,9 @@
// contextMenuStrip // contextMenuStrip
// //
contextMenuStrip.ImageScalingSize = new Size(24, 24); contextMenuStrip.ImageScalingSize = new Size(24, 24);
contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_Add, toolStripMenuItem_Insert, toolStripMenuItem_Remove, toolStripSeparator1, toolStripMenuItem_BatchAdd, toolStripMenuItem_RemoveAll, toolStripSeparator2, toolStripMenuItem_MoveUp, toolStripMenuItem_MoveDown, toolStripMenuItem_MoveTop, toolStripMenuItem_MoveBottom, toolStripSeparator3, toolStripMenuItem_SelectAll, toolStripMenuItem_CopyPreview, toolStripSeparator4, toolStripMenuItem_ChangeView }); contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_Add, toolStripMenuItem_Insert, toolStripMenuItem_Remove, toolStripSeparator1, toolStripMenuItem_BatchAdd, toolStripMenuItem_RemoveAll, toolStripSeparator2, toolStripMenuItem_MoveUp, toolStripMenuItem_MoveDown, toolStripMenuItem_MoveTop, toolStripMenuItem_MoveBottom, toolStripSeparator3, toolStripMenuItem_CopyPreview, toolStripMenuItem_AddFromClipboard, toolStripMenuItem_SelectAll, toolStripSeparator4, toolStripMenuItem_ChangeView });
contextMenuStrip.Name = "contextMenuStrip"; contextMenuStrip.Name = "contextMenuStrip";
contextMenuStrip.Size = new Size(329, 388); contextMenuStrip.Size = new Size(329, 451);
contextMenuStrip.Closed += contextMenuStrip_Closed; contextMenuStrip.Closed += contextMenuStrip_Closed;
contextMenuStrip.Opening += contextMenuStrip_Opening; contextMenuStrip.Opening += contextMenuStrip_Opening;
// //
@@ -110,6 +112,7 @@
// toolStripMenuItem_Remove // toolStripMenuItem_Remove
// //
toolStripMenuItem_Remove.Name = "toolStripMenuItem_Remove"; toolStripMenuItem_Remove.Name = "toolStripMenuItem_Remove";
toolStripMenuItem_Remove.ShortcutKeys = Keys.Delete;
toolStripMenuItem_Remove.Size = new Size(328, 30); toolStripMenuItem_Remove.Size = new Size(328, 30);
toolStripMenuItem_Remove.Text = "移除"; toolStripMenuItem_Remove.Text = "移除";
toolStripMenuItem_Remove.Click += toolStripMenuItem_Remove_Click; toolStripMenuItem_Remove.Click += toolStripMenuItem_Remove_Click;
@@ -198,7 +201,7 @@
// //
// toolStripMenuItem_ChangeView // toolStripMenuItem_ChangeView
// //
toolStripMenuItem_ChangeView.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_LargeIconView, toolStripMenuItem_SmallIconView, toolStripMenuItem_DetailsView }); toolStripMenuItem_ChangeView.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_LargeIconView, toolStripMenuItem_ListView, toolStripMenuItem_DetailsView });
toolStripMenuItem_ChangeView.Name = "toolStripMenuItem_ChangeView"; toolStripMenuItem_ChangeView.Name = "toolStripMenuItem_ChangeView";
toolStripMenuItem_ChangeView.Size = new Size(328, 30); toolStripMenuItem_ChangeView.Size = new Size(328, 30);
toolStripMenuItem_ChangeView.Text = "切换视图"; toolStripMenuItem_ChangeView.Text = "切换视图";
@@ -207,24 +210,24 @@
// //
toolStripMenuItem_LargeIconView.Name = "toolStripMenuItem_LargeIconView"; toolStripMenuItem_LargeIconView.Name = "toolStripMenuItem_LargeIconView";
toolStripMenuItem_LargeIconView.ShortcutKeys = Keys.Alt | Keys.D1; toolStripMenuItem_LargeIconView.ShortcutKeys = Keys.Alt | Keys.D1;
toolStripMenuItem_LargeIconView.Size = new Size(223, 34); toolStripMenuItem_LargeIconView.Size = new Size(241, 34);
toolStripMenuItem_LargeIconView.Text = "大图标"; toolStripMenuItem_LargeIconView.Text = "大图标";
toolStripMenuItem_LargeIconView.Click += toolStripMenuItem_LargeIconView_Click; toolStripMenuItem_LargeIconView.Click += toolStripMenuItem_LargeIconView_Click;
// //
// toolStripMenuItem_SmallIconView // toolStripMenuItem_ListView
// //
toolStripMenuItem_SmallIconView.Name = "toolStripMenuItem_SmallIconView"; toolStripMenuItem_ListView.Name = "toolStripMenuItem_ListView";
toolStripMenuItem_SmallIconView.ShortcutKeys = Keys.Alt | Keys.D2; toolStripMenuItem_ListView.ShortcutKeys = Keys.Alt | Keys.D2;
toolStripMenuItem_SmallIconView.Size = new Size(223, 34); toolStripMenuItem_ListView.Size = new Size(241, 34);
toolStripMenuItem_SmallIconView.Text = "小图标"; toolStripMenuItem_ListView.Text = "列表";
toolStripMenuItem_SmallIconView.Click += toolStripMenuItem_SmallIconView_Click; toolStripMenuItem_ListView.Click += toolStripMenuItem_ListView_Click;
// //
// toolStripMenuItem_DetailsView // toolStripMenuItem_DetailsView
// //
toolStripMenuItem_DetailsView.Name = "toolStripMenuItem_DetailsView"; toolStripMenuItem_DetailsView.Name = "toolStripMenuItem_DetailsView";
toolStripMenuItem_DetailsView.ShortcutKeys = Keys.Alt | Keys.D3; toolStripMenuItem_DetailsView.ShortcutKeys = Keys.Alt | Keys.D3;
toolStripMenuItem_DetailsView.Size = new Size(223, 34); toolStripMenuItem_DetailsView.Size = new Size(241, 34);
toolStripMenuItem_DetailsView.Text = "列表"; toolStripMenuItem_DetailsView.Text = "详细信息";
toolStripMenuItem_DetailsView.Click += toolStripMenuItem_DetailsView_Click; toolStripMenuItem_DetailsView.Click += toolStripMenuItem_DetailsView_Click;
// //
// imageList_LargeIcon // imageList_LargeIcon
@@ -239,6 +242,14 @@
imageList_SmallIcon.ImageSize = new Size(48, 48); imageList_SmallIcon.ImageSize = new Size(48, 48);
imageList_SmallIcon.TransparentColor = Color.Transparent; imageList_SmallIcon.TransparentColor = Color.Transparent;
// //
// toolStripMenuItem_AddFromClipboard
//
toolStripMenuItem_AddFromClipboard.Name = "toolStripMenuItem_AddFromClipboard";
toolStripMenuItem_AddFromClipboard.ShortcutKeys = Keys.Control | Keys.V;
toolStripMenuItem_AddFromClipboard.Size = new Size(328, 30);
toolStripMenuItem_AddFromClipboard.Text = "从剪贴板添加";
toolStripMenuItem_AddFromClipboard.Click += toolStripMenuItem_AddFromClipboard_Click;
//
// SpineListView // SpineListView
// //
AutoScaleDimensions = new SizeF(11F, 24F); AutoScaleDimensions = new SizeF(11F, 24F);
@@ -268,12 +279,13 @@
private ToolStripSeparator toolStripSeparator3; private ToolStripSeparator toolStripSeparator3;
private ToolStripMenuItem toolStripMenuItem_ChangeView; private ToolStripMenuItem toolStripMenuItem_ChangeView;
private ToolStripMenuItem toolStripMenuItem_LargeIconView; private ToolStripMenuItem toolStripMenuItem_LargeIconView;
private ToolStripMenuItem toolStripMenuItem_SmallIconView; private ToolStripMenuItem toolStripMenuItem_ListView;
private ToolStripMenuItem toolStripMenuItem_DetailsView; private ToolStripMenuItem toolStripMenuItem_DetailsView;
private ToolStripMenuItem toolStripMenuItem_MoveTop; private ToolStripMenuItem toolStripMenuItem_MoveTop;
private ToolStripMenuItem toolStripMenuItem_MoveBottom; private ToolStripMenuItem toolStripMenuItem_MoveBottom;
private ToolStripMenuItem toolStripMenuItem_CopyPreview; private ToolStripMenuItem toolStripMenuItem_CopyPreview;
private ToolStripMenuItem toolStripMenuItem_SelectAll; private ToolStripMenuItem toolStripMenuItem_SelectAll;
private ToolStripSeparator toolStripSeparator4; private ToolStripSeparator toolStripSeparator4;
private ToolStripMenuItem toolStripMenuItem_AddFromClipboard;
} }
} }

View File

@@ -64,30 +64,6 @@ namespace SpineViewer.Controls
progressDialog.ShowDialog(); progressDialog.ShowDialog();
} }
/// <summary>
/// 弹出对话框导出列表预览图
/// </summary>
public void ExportPreviews()
{
lock (Spines)
{
if (spines.Count <= 0)
{
MessageBox.Show("请至少打开一个骨骼文件", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
}
var saveDialog = new Dialogs.ExportPreviewDialog();
if (saveDialog.ShowDialog() != DialogResult.OK)
return;
var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += ExportPreview_Work;
progressDialog.RunWorkerAsync(saveDialog);
progressDialog.ShowDialog();
}
private void listView_SelectedIndexChanged(object sender, EventArgs e) private void listView_SelectedIndexChanged(object sender, EventArgs e)
{ {
if (PropertyGrid is not null) if (PropertyGrid is not null)
@@ -106,6 +82,18 @@ namespace SpineViewer.Controls
spines[i].IsSelected = listView.SelectedIndices.Contains(i); spines[i].IsSelected = listView.SelectedIndices.Contains(i);
} }
} }
// XXX: 图标显示的时候没法自动刷新顺序, 只能切换视图刷新, 不知道什么原理
if (listView.View == View.LargeIcon)
{
listView.BeginUpdate();
listView.View = View.List;
listView.View = View.LargeIcon;
listView.EndUpdate();
}
if (listView.SelectedItems.Count > 0)
listView.SelectedItems[0].EnsureVisible();
} }
private void listView_ItemDrag(object sender, ItemDragEventArgs e) private void listView_ItemDrag(object sender, ItemDragEventArgs e)
@@ -185,23 +173,7 @@ namespace SpineViewer.Controls
} }
else if (e.Data.GetDataPresent(DataFormats.FileDrop)) else if (e.Data.GetDataPresent(DataFormats.FileDrop))
{ {
var validPaths = ((string[])e.Data.GetData(DataFormats.FileDrop)).Where( AddFromFileDrop((string[])e.Data.GetData(DataFormats.FileDrop));
path => File.Exists(path) &&
(Path.GetExtension(path).Equals(".skel", StringComparison.OrdinalIgnoreCase) ||
Path.GetExtension(path).Equals(".json", StringComparison.OrdinalIgnoreCase))
).ToArray();
if (validPaths.Length > 1)
{
var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += BatchAdd_Work;
progressDialog.RunWorkerAsync(new Dialogs.BatchOpenSpineDialogResult(Spine.Version.Auto, validPaths));
progressDialog.ShowDialog();
}
else if (validPaths.Length > 0)
{
Insert(new Dialogs.OpenSpineDialogResult(Spine.Version.Auto, validPaths[0]));
}
} }
} }
@@ -220,7 +192,7 @@ namespace SpineViewer.Controls
// 视图选项 // 视图选项
toolStripMenuItem_LargeIconView.Checked = listView.View == View.LargeIcon; toolStripMenuItem_LargeIconView.Checked = listView.View == View.LargeIcon;
toolStripMenuItem_SmallIconView.Checked = listView.View == View.SmallIcon; toolStripMenuItem_ListView.Checked = listView.View == View.List;
toolStripMenuItem_DetailsView.Checked = listView.View == View.Details; toolStripMenuItem_DetailsView.Checked = listView.View == View.Details;
} }
@@ -303,8 +275,10 @@ namespace SpineViewer.Controls
{ {
lock (Spines) { (spines[index - 1], spines[index]) = (spines[index], spines[index - 1]); } lock (Spines) { (spines[index - 1], spines[index]) = (spines[index], spines[index - 1]); }
var item = listView.Items[index]; var item = listView.Items[index];
listView.BeginUpdate();
listView.Items.RemoveAt(index); listView.Items.RemoveAt(index);
listView.Items.Insert(index - 1, item); listView.Items.Insert(index - 1, item);
listView.EndUpdate();
} }
} }
@@ -318,8 +292,10 @@ namespace SpineViewer.Controls
{ {
lock (Spines) { (spines[index], spines[index + 1]) = (spines[index + 1], spines[index]); } lock (Spines) { (spines[index], spines[index + 1]) = (spines[index + 1], spines[index]); }
var item = listView.Items[index]; var item = listView.Items[index];
listView.BeginUpdate();
listView.Items.RemoveAt(index); listView.Items.RemoveAt(index);
listView.Items.Insert(index + 1, item); listView.Items.Insert(index + 1, item);
listView.EndUpdate();
} }
} }
@@ -333,12 +309,9 @@ namespace SpineViewer.Controls
{ {
lock (Spines) lock (Spines)
{ {
lock (Spines) var spine = spines[index];
{ spines.RemoveAt(index);
var spine = spines[index]; spines.Add(spine);
spines.RemoveAt(index);
spines.Add(spine);
}
} }
var item = listView.Items[index]; var item = listView.Items[index];
listView.Items.RemoveAt(index); listView.Items.RemoveAt(index);
@@ -367,14 +340,6 @@ namespace SpineViewer.Controls
PropertyGrid.SelectedObject = null; PropertyGrid.SelectedObject = null;
} }
private void toolStripMenuItem_SelectAll_Click(object sender, EventArgs e)
{
listView.BeginUpdate();
foreach (ListViewItem item in listView.Items)
item.Selected = true;
listView.EndUpdate();
}
private void toolStripMenuItem_CopyPreview_Click(object sender, EventArgs e) private void toolStripMenuItem_CopyPreview_Click(object sender, EventArgs e)
{ {
var fileDropList = new StringCollection(); var fileDropList = new StringCollection();
@@ -395,14 +360,33 @@ namespace SpineViewer.Controls
Clipboard.SetFileDropList(fileDropList); Clipboard.SetFileDropList(fileDropList);
} }
private void toolStripMenuItem_AddFromClipboard_Click(object sender, EventArgs e)
{
if (Clipboard.ContainsFileDropList())
{
var fileDropList = Clipboard.GetFileDropList();
var paths = new string[fileDropList.Count];
fileDropList.CopyTo(paths, 0);
AddFromFileDrop(paths);
}
}
private void toolStripMenuItem_SelectAll_Click(object sender, EventArgs e)
{
listView.BeginUpdate();
foreach (ListViewItem item in listView.Items)
item.Selected = true;
listView.EndUpdate();
}
private void toolStripMenuItem_LargeIconView_Click(object sender, EventArgs e) private void toolStripMenuItem_LargeIconView_Click(object sender, EventArgs e)
{ {
listView.View = View.LargeIcon; listView.View = View.LargeIcon;
} }
private void toolStripMenuItem_SmallIconView_Click(object sender, EventArgs e) private void toolStripMenuItem_ListView_Click(object sender, EventArgs e)
{ {
listView.View = View.SmallIcon; listView.View = View.List;
} }
private void toolStripMenuItem_DetailsView_Click(object sender, EventArgs e) private void toolStripMenuItem_DetailsView_Click(object sender, EventArgs e)
@@ -512,57 +496,42 @@ namespace SpineViewer.Controls
Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB"); Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB");
} }
private void ExportPreview_Work(object? sender, DoWorkEventArgs e) private void AddFromFileDrop(string[] paths)
{ {
var worker = sender as BackgroundWorker; List<string> validPaths = [];
var arguments = e.Argument as Dialogs.ExportPreviewDialog; foreach (var path in paths)
var outputDir = arguments.OutputDir;
var width = arguments.PreviewWidth;
var height = arguments.PreviewHeight;
int success = 0;
int error = 0;
lock (Spines)
{ {
int totalCount = spines.Count; if (File.Exists(path))
worker.ReportProgress(0, $"已处理 0/{totalCount}");
for (int i = 0; i < totalCount; i++)
{ {
if (worker.CancellationPending) if (Spine.Spine.CommonSkelSuffix.Contains(Path.GetExtension(path).ToLower()))
validPaths.Add(path);
}
else if (Directory.Exists(path))
{
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{ {
e.Cancel = true; if (Spine.Spine.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
break; validPaths.Add(file);
} }
var spine = spines[i];
try
{
var preview = spine.GetPreview(width, height);
var savePath = Path.Combine(outputDir, $"{spine.Name}.png");
preview.SaveToFile(savePath);
success++;
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to save preview {}", spine.SkelPath);
error++;
}
worker.ReportProgress((int)((i + 1) * 100.0) / totalCount, $"已处理 {i + 1}/{totalCount}");
} }
} }
if (error > 0) if (validPaths.Count > 1)
{ {
Program.Logger.Warn("Preview save {} successfully, {} failed", success, error); if (validPaths.Count > 100)
{
if (MessageBox.Show($"共发现 {validPaths.Count} 个可加载骨骼,数量较大,是否一次性全部加载?", "操作确认", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel)
return;
}
var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += BatchAdd_Work;
progressDialog.RunWorkerAsync(new Dialogs.BatchOpenSpineDialogResult(Spine.Version.Auto, validPaths.ToArray()));
progressDialog.ShowDialog();
} }
else else if (validPaths.Count > 0)
{ {
Program.Logger.Info("{} preview saved successfully", success); Insert(new Dialogs.OpenSpineDialogResult(Spine.Version.Auto, validPaths[0]));
} }
Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB");
} }
} }
} }

View File

@@ -458,8 +458,13 @@ namespace SpineViewer.Controls
{ {
lock (SpineListView.Spines) lock (SpineListView.Spines)
{ {
foreach (var spine in SpineListView.Spines.Reverse()) var spines = SpineListView.Spines;
for (int i = spines.Count - 1; i >= 0; i--)
{ {
if (cancelToken is not null && cancelToken.IsCancellationRequested)
break; // 提前中止
var spine = spines[i];
spine.Update(delta); spine.Update(delta);
RenderWindow.Draw(spine); RenderWindow.Draw(spine);

View File

@@ -18,7 +18,7 @@ namespace SpineViewer.Dialogs
public BatchOpenSpineDialog() public BatchOpenSpineDialog()
{ {
InitializeComponent(); InitializeComponent();
comboBox_Version.DataSource = VersionHelper.Versions.ToList(); comboBox_Version.DataSource = VersionHelper.Names.ToList();
comboBox_Version.DisplayMember = "Value"; comboBox_Version.DisplayMember = "Value";
comboBox_Version.ValueMember = "Key"; comboBox_Version.ValueMember = "Key";
comboBox_Version.SelectedValue = Spine.Version.Auto; comboBox_Version.SelectedValue = Spine.Version.Auto;
@@ -61,7 +61,7 @@ namespace SpineViewer.Dialogs
if (version != Spine.Version.Auto && !Spine.Spine.ImplementedVersions.Contains(version)) if (version != Spine.Version.Auto && !Spine.Spine.ImplementedVersions.Contains(version))
{ {
MessageBox.Show($"{version.String()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show($"{version.GetName()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }

View File

@@ -24,7 +24,7 @@ namespace SpineViewer.Dialogs
InitializeComponent(); InitializeComponent();
// XXX: 文件格式转换暂时不支持自动检测版本 // XXX: 文件格式转换暂时不支持自动检测版本
var impVersions = VersionHelper.Versions.ToDictionary(); var impVersions = VersionHelper.Names.ToDictionary();
impVersions.Remove(Spine.Version.Auto); impVersions.Remove(Spine.Version.Auto);
comboBox_SourceVersion.DataSource = impVersions.ToList(); comboBox_SourceVersion.DataSource = impVersions.ToList();
@@ -77,13 +77,13 @@ namespace SpineViewer.Dialogs
if (!SkeletonConverter.ImplementedVersions.Contains(sourceVersion)) if (!SkeletonConverter.ImplementedVersions.Contains(sourceVersion))
{ {
MessageBox.Show($"{sourceVersion.String()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show($"{sourceVersion.GetName()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
if (!SkeletonConverter.ImplementedVersions.Contains(targetVersion)) if (!SkeletonConverter.ImplementedVersions.Contains(targetVersion))
{ {
MessageBox.Show($"{targetVersion.String()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show($"{targetVersion.GetName()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }

View File

@@ -17,7 +17,7 @@ namespace SpineViewer.Dialogs
public OpenSpineDialog() public OpenSpineDialog()
{ {
InitializeComponent(); InitializeComponent();
comboBox_Version.DataSource = VersionHelper.Versions.ToList(); comboBox_Version.DataSource = VersionHelper.Names.ToList();
comboBox_Version.DisplayMember = "Value"; comboBox_Version.DisplayMember = "Value";
comboBox_Version.ValueMember = "Key"; comboBox_Version.ValueMember = "Key";
comboBox_Version.SelectedValue = Spine.Version.Auto; comboBox_Version.SelectedValue = Spine.Version.Auto;
@@ -78,7 +78,7 @@ namespace SpineViewer.Dialogs
if (version != Spine.Version.Auto && !Spine.Spine.ImplementedVersions.Contains(version)) if (version != Spine.Version.Auto && !Spine.Spine.ImplementedVersions.Contains(version))
{ {
MessageBox.Show($"{version.String()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show($"{version.GetName()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }

View File

@@ -95,7 +95,7 @@
menuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_File, toolStripMenuItem_Function, toolStripMenuItem_Tool, toolStripMenuItem_Download, toolStripMenuItem_Help }); menuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_File, toolStripMenuItem_Function, toolStripMenuItem_Tool, toolStripMenuItem_Download, toolStripMenuItem_Help });
menuStrip.Location = new Point(0, 0); menuStrip.Location = new Point(0, 0);
menuStrip.Name = "menuStrip"; menuStrip.Name = "menuStrip";
menuStrip.Size = new Size(1741, 32); menuStrip.Size = new Size(1748, 32);
menuStrip.TabIndex = 0; menuStrip.TabIndex = 0;
menuStrip.Text = "菜单"; menuStrip.Text = "菜单";
// //
@@ -164,7 +164,7 @@
// toolStripMenuItem_ResetAnimation // toolStripMenuItem_ResetAnimation
// //
toolStripMenuItem_ResetAnimation.Name = "toolStripMenuItem_ResetAnimation"; toolStripMenuItem_ResetAnimation.Name = "toolStripMenuItem_ResetAnimation";
toolStripMenuItem_ResetAnimation.Size = new Size(270, 34); toolStripMenuItem_ResetAnimation.Size = new Size(242, 34);
toolStripMenuItem_ResetAnimation.Text = "重置动画时间(&R)"; toolStripMenuItem_ResetAnimation.Text = "重置动画时间(&R)";
toolStripMenuItem_ResetAnimation.Click += toolStripMenuItem_ResetAnimation_Click; toolStripMenuItem_ResetAnimation.Click += toolStripMenuItem_ResetAnimation_Click;
// //
@@ -232,7 +232,7 @@
rtbLog.Margin = new Padding(3, 2, 3, 2); rtbLog.Margin = new Padding(3, 2, 3, 2);
rtbLog.Name = "rtbLog"; rtbLog.Name = "rtbLog";
rtbLog.ReadOnly = true; rtbLog.ReadOnly = true;
rtbLog.Size = new Size(1721, 106); rtbLog.Size = new Size(1728, 114);
rtbLog.TabIndex = 0; rtbLog.TabIndex = 0;
rtbLog.Text = ""; rtbLog.Text = "";
rtbLog.WordWrap = false; rtbLog.WordWrap = false;
@@ -254,8 +254,8 @@
// //
splitContainer_MainForm.Panel2.Controls.Add(rtbLog); splitContainer_MainForm.Panel2.Controls.Add(rtbLog);
splitContainer_MainForm.Panel2.Cursor = Cursors.Default; splitContainer_MainForm.Panel2.Cursor = Cursors.Default;
splitContainer_MainForm.Size = new Size(1721, 958); splitContainer_MainForm.Size = new Size(1728, 997);
splitContainer_MainForm.SplitterDistance = 848; splitContainer_MainForm.SplitterDistance = 879;
splitContainer_MainForm.TabIndex = 3; splitContainer_MainForm.TabIndex = 3;
splitContainer_MainForm.TabStop = false; splitContainer_MainForm.TabStop = false;
splitContainer_MainForm.SplitterMoved += splitContainer_SplitterMoved; splitContainer_MainForm.SplitterMoved += splitContainer_SplitterMoved;
@@ -277,8 +277,8 @@
// //
splitContainer_Functional.Panel2.Controls.Add(groupBox_Preview); splitContainer_Functional.Panel2.Controls.Add(groupBox_Preview);
splitContainer_Functional.Panel2.Cursor = Cursors.Default; splitContainer_Functional.Panel2.Cursor = Cursors.Default;
splitContainer_Functional.Size = new Size(1721, 848); splitContainer_Functional.Size = new Size(1728, 879);
splitContainer_Functional.SplitterDistance = 744; splitContainer_Functional.SplitterDistance = 747;
splitContainer_Functional.TabIndex = 2; splitContainer_Functional.TabIndex = 2;
splitContainer_Functional.TabStop = false; splitContainer_Functional.TabStop = false;
splitContainer_Functional.SplitterMoved += splitContainer_SplitterMoved; splitContainer_Functional.SplitterMoved += splitContainer_SplitterMoved;
@@ -300,8 +300,8 @@
// //
splitContainer_Information.Panel2.Controls.Add(splitContainer_Config); splitContainer_Information.Panel2.Controls.Add(splitContainer_Config);
splitContainer_Information.Panel2.Cursor = Cursors.Default; splitContainer_Information.Panel2.Cursor = Cursors.Default;
splitContainer_Information.Size = new Size(744, 848); splitContainer_Information.Size = new Size(747, 879);
splitContainer_Information.SplitterDistance = 398; splitContainer_Information.SplitterDistance = 399;
splitContainer_Information.TabIndex = 1; splitContainer_Information.TabIndex = 1;
splitContainer_Information.TabStop = false; splitContainer_Information.TabStop = false;
splitContainer_Information.SplitterMoved += splitContainer_SplitterMoved; splitContainer_Information.SplitterMoved += splitContainer_SplitterMoved;
@@ -313,7 +313,7 @@
groupBox_SkelList.Dock = DockStyle.Fill; groupBox_SkelList.Dock = DockStyle.Fill;
groupBox_SkelList.Location = new Point(0, 0); groupBox_SkelList.Location = new Point(0, 0);
groupBox_SkelList.Name = "groupBox_SkelList"; groupBox_SkelList.Name = "groupBox_SkelList";
groupBox_SkelList.Size = new Size(398, 848); groupBox_SkelList.Size = new Size(399, 879);
groupBox_SkelList.TabIndex = 0; groupBox_SkelList.TabIndex = 0;
groupBox_SkelList.TabStop = false; groupBox_SkelList.TabStop = false;
groupBox_SkelList.Text = "模型列表"; groupBox_SkelList.Text = "模型列表";
@@ -324,7 +324,7 @@
spineListView.Location = new Point(3, 26); spineListView.Location = new Point(3, 26);
spineListView.Name = "spineListView"; spineListView.Name = "spineListView";
spineListView.PropertyGrid = propertyGrid_Spine; spineListView.PropertyGrid = propertyGrid_Spine;
spineListView.Size = new Size(392, 819); spineListView.Size = new Size(393, 850);
spineListView.TabIndex = 0; spineListView.TabIndex = 0;
// //
// propertyGrid_Spine // propertyGrid_Spine
@@ -333,7 +333,7 @@
propertyGrid_Spine.HelpVisible = false; propertyGrid_Spine.HelpVisible = false;
propertyGrid_Spine.Location = new Point(3, 26); propertyGrid_Spine.Location = new Point(3, 26);
propertyGrid_Spine.Name = "propertyGrid_Spine"; propertyGrid_Spine.Name = "propertyGrid_Spine";
propertyGrid_Spine.Size = new Size(336, 470); propertyGrid_Spine.Size = new Size(338, 485);
propertyGrid_Spine.TabIndex = 0; propertyGrid_Spine.TabIndex = 0;
propertyGrid_Spine.ToolbarVisible = false; propertyGrid_Spine.ToolbarVisible = false;
propertyGrid_Spine.PropertyValueChanged += propertyGrid_PropertyValueChanged; propertyGrid_Spine.PropertyValueChanged += propertyGrid_PropertyValueChanged;
@@ -355,8 +355,8 @@
// //
splitContainer_Config.Panel2.Controls.Add(groupBox_PreviewConfig); splitContainer_Config.Panel2.Controls.Add(groupBox_PreviewConfig);
splitContainer_Config.Panel2.Cursor = Cursors.Default; splitContainer_Config.Panel2.Cursor = Cursors.Default;
splitContainer_Config.Size = new Size(342, 848); splitContainer_Config.Size = new Size(344, 879);
splitContainer_Config.SplitterDistance = 499; splitContainer_Config.SplitterDistance = 514;
splitContainer_Config.TabIndex = 0; splitContainer_Config.TabIndex = 0;
splitContainer_Config.TabStop = false; splitContainer_Config.TabStop = false;
splitContainer_Config.SplitterMoved += splitContainer_SplitterMoved; splitContainer_Config.SplitterMoved += splitContainer_SplitterMoved;
@@ -368,7 +368,7 @@
groupBox_SkelConfig.Dock = DockStyle.Fill; groupBox_SkelConfig.Dock = DockStyle.Fill;
groupBox_SkelConfig.Location = new Point(0, 0); groupBox_SkelConfig.Location = new Point(0, 0);
groupBox_SkelConfig.Name = "groupBox_SkelConfig"; groupBox_SkelConfig.Name = "groupBox_SkelConfig";
groupBox_SkelConfig.Size = new Size(342, 499); groupBox_SkelConfig.Size = new Size(344, 514);
groupBox_SkelConfig.TabIndex = 0; groupBox_SkelConfig.TabIndex = 0;
groupBox_SkelConfig.TabStop = false; groupBox_SkelConfig.TabStop = false;
groupBox_SkelConfig.Text = "模型参数"; groupBox_SkelConfig.Text = "模型参数";
@@ -379,7 +379,7 @@
groupBox_PreviewConfig.Dock = DockStyle.Fill; groupBox_PreviewConfig.Dock = DockStyle.Fill;
groupBox_PreviewConfig.Location = new Point(0, 0); groupBox_PreviewConfig.Location = new Point(0, 0);
groupBox_PreviewConfig.Name = "groupBox_PreviewConfig"; groupBox_PreviewConfig.Name = "groupBox_PreviewConfig";
groupBox_PreviewConfig.Size = new Size(342, 345); groupBox_PreviewConfig.Size = new Size(344, 361);
groupBox_PreviewConfig.TabIndex = 1; groupBox_PreviewConfig.TabIndex = 1;
groupBox_PreviewConfig.TabStop = false; groupBox_PreviewConfig.TabStop = false;
groupBox_PreviewConfig.Text = "画面参数"; groupBox_PreviewConfig.Text = "画面参数";
@@ -390,7 +390,7 @@
propertyGrid_Previewer.HelpVisible = false; propertyGrid_Previewer.HelpVisible = false;
propertyGrid_Previewer.Location = new Point(3, 26); propertyGrid_Previewer.Location = new Point(3, 26);
propertyGrid_Previewer.Name = "propertyGrid_Previewer"; propertyGrid_Previewer.Name = "propertyGrid_Previewer";
propertyGrid_Previewer.Size = new Size(336, 316); propertyGrid_Previewer.Size = new Size(338, 332);
propertyGrid_Previewer.TabIndex = 1; propertyGrid_Previewer.TabIndex = 1;
propertyGrid_Previewer.ToolbarVisible = false; propertyGrid_Previewer.ToolbarVisible = false;
propertyGrid_Previewer.PropertyValueChanged += propertyGrid_PropertyValueChanged; propertyGrid_Previewer.PropertyValueChanged += propertyGrid_PropertyValueChanged;
@@ -401,7 +401,7 @@
groupBox_Preview.Dock = DockStyle.Fill; groupBox_Preview.Dock = DockStyle.Fill;
groupBox_Preview.Location = new Point(0, 0); groupBox_Preview.Location = new Point(0, 0);
groupBox_Preview.Name = "groupBox_Preview"; groupBox_Preview.Name = "groupBox_Preview";
groupBox_Preview.Size = new Size(973, 848); groupBox_Preview.Size = new Size(977, 879);
groupBox_Preview.TabIndex = 1; groupBox_Preview.TabIndex = 1;
groupBox_Preview.TabStop = false; groupBox_Preview.TabStop = false;
groupBox_Preview.Text = "预览画面"; groupBox_Preview.Text = "预览画面";
@@ -413,7 +413,7 @@
spinePreviewer.Location = new Point(3, 26); spinePreviewer.Location = new Point(3, 26);
spinePreviewer.Name = "spinePreviewer"; spinePreviewer.Name = "spinePreviewer";
spinePreviewer.PropertyGrid = propertyGrid_Previewer; spinePreviewer.PropertyGrid = propertyGrid_Previewer;
spinePreviewer.Size = new Size(967, 819); spinePreviewer.Size = new Size(971, 850);
spinePreviewer.SpineListView = spineListView; spinePreviewer.SpineListView = spineListView;
spinePreviewer.TabIndex = 0; spinePreviewer.TabIndex = 0;
spinePreviewer.MouseUp += spinePreviewer_MouseUp; spinePreviewer.MouseUp += spinePreviewer_MouseUp;
@@ -425,7 +425,7 @@
panel_MainForm.Location = new Point(0, 32); panel_MainForm.Location = new Point(0, 32);
panel_MainForm.Name = "panel_MainForm"; panel_MainForm.Name = "panel_MainForm";
panel_MainForm.Padding = new Padding(10, 5, 10, 10); panel_MainForm.Padding = new Padding(10, 5, 10, 10);
panel_MainForm.Size = new Size(1741, 973); panel_MainForm.Size = new Size(1748, 1012);
panel_MainForm.TabIndex = 4; panel_MainForm.TabIndex = 4;
// //
// toolTip // toolTip
@@ -436,7 +436,7 @@
// //
AutoScaleDimensions = new SizeF(11F, 24F); AutoScaleDimensions = new SizeF(11F, 24F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1741, 1005); ClientSize = new Size(1748, 1044);
Controls.Add(panel_MainForm); Controls.Add(panel_MainForm);
Controls.Add(menuStrip); Controls.Add(menuStrip);
Icon = (Icon)resources.GetObject("$this.Icon"); Icon = (Icon)resources.GetObject("$this.Icon");

View File

@@ -86,7 +86,23 @@ namespace SpineViewer
private void toolStripMenuItem_ExportPreview_Click(object sender, EventArgs e) private void toolStripMenuItem_ExportPreview_Click(object sender, EventArgs e)
{ {
spineListView.ExportPreviews(); lock (spineListView.Spines)
{
if (spineListView.Spines.Count <= 0)
{
MessageBox.Show("请至少打开一个骨骼文件", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
}
var saveDialog = new Dialogs.ExportPreviewDialog();
if (saveDialog.ShowDialog() != DialogResult.OK)
return;
var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += ExportPreview_Work;
progressDialog.RunWorkerAsync(saveDialog);
progressDialog.ShowDialog();
} }
private void toolStripMenuItem_Exit_Click(object sender, EventArgs e) private void toolStripMenuItem_Exit_Click(object sender, EventArgs e)
@@ -200,6 +216,62 @@ namespace SpineViewer
spinePreviewer.StartPreview(); spinePreviewer.StartPreview();
} }
private void ExportPreview_Work(object? sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
var arguments = e.Argument as Dialogs.ExportPreviewDialog;
var outputDir = arguments.OutputDir;
var width = arguments.PreviewWidth;
var height = arguments.PreviewHeight;
int success = 0;
int error = 0;
spinePreviewer.StopPreview();
lock (spineListView.Spines)
{
var spines = spineListView.Spines;
int totalCount = spines.Count;
worker.ReportProgress(0, $"已处理 0/{totalCount}");
for (int i = 0; i < totalCount; i++)
{
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
var spine = spines[i];
try
{
var preview = spine.GetPreview(width, height);
var savePath = Path.Combine(outputDir, $"{spine.Name}.png");
preview.SaveToFile(savePath);
success++;
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to save preview {}", spine.SkelPath);
error++;
}
worker.ReportProgress((int)((i + 1) * 100.0) / totalCount, $"已处理 {i + 1}/{totalCount}");
}
}
spinePreviewer.StartPreview();
if (error > 0)
{
Program.Logger.Warn("Preview save {} successfully, {} failed", success, error);
}
else
{
Program.Logger.Info("{} preview saved successfully", success);
}
Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB");
}
private void ConvertFileFormat_Work(object? sender, DoWorkEventArgs e) private void ConvertFileFormat_Work(object? sender, DoWorkEventArgs e)
{ {
var worker = sender as BackgroundWorker; var worker = sender as BackgroundWorker;

View File

@@ -51,7 +51,9 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
{ {
JsonObject skeleton = []; JsonObject skeleton = [];
skeleton["hash"] = reader.ReadString(); skeleton["hash"] = reader.ReadString();
skeleton["spine"] = reader.ReadString(); var version = reader.ReadString();
if (version == "3.8.75") version = "3.8.76"; // replace 3.8.75 to another version to avoid detection in official runtime
skeleton["spine"] = version;
skeleton["x"] = reader.ReadFloat(); skeleton["x"] = reader.ReadFloat();
skeleton["y"] = reader.ReadFloat(); skeleton["y"] = reader.ReadFloat();
skeleton["width"] = reader.ReadFloat(); skeleton["width"] = reader.ReadFloat();
@@ -842,7 +844,9 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
{ {
JsonObject skeleton = root["skeleton"].AsObject(); JsonObject skeleton = root["skeleton"].AsObject();
writer.WriteString((string)skeleton["hash"]); writer.WriteString((string)skeleton["hash"]);
writer.WriteString((string)skeleton["spine"]); var version = (string)skeleton["spine"];
if (version == "3.8.75") version = "3.8.76"; // replace 3.8.75 to another version to avoid detection in official runtime
writer.WriteString(version);
if (skeleton.TryGetPropertyValue("x", out var x)) writer.WriteFloat((float)x); else writer.WriteFloat(0); if (skeleton.TryGetPropertyValue("x", out var x)) writer.WriteFloat((float)x); else writer.WriteFloat(0);
if (skeleton.TryGetPropertyValue("y", out var y)) writer.WriteFloat((float)y); else writer.WriteFloat(0); if (skeleton.TryGetPropertyValue("y", out var y)) writer.WriteFloat((float)y); else writer.WriteFloat(0);
if (skeleton.TryGetPropertyValue("width", out var width)) writer.WriteFloat((float)width); else writer.WriteFloat(0); if (skeleton.TryGetPropertyValue("width", out var width)) writer.WriteFloat((float)width); else writer.WriteFloat(0);
@@ -1089,6 +1093,25 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
writer.WriteVarInt(name2idx[(string)name]); writer.WriteVarInt(name2idx[(string)name]);
} }
public override JsonObject ReadJson(string jsonPath)
{
// replace 3.8.75 to another version to avoid detection in official runtime
var root = base.ReadJson(jsonPath);
var skeleton = root["skeleton"].AsObject();
var version = (string)skeleton["spine"];
if (version == "3.8.75") skeleton["spine"] = "3.8.76";
return root;
}
public override void WriteJson(JsonObject root, string jsonPath)
{
// replace 3.8.75 to another version to avoid detection in official runtime
var skeleton = root["skeleton"].AsObject();
var version = (string)skeleton["spine"];
if (version == "3.8.75") skeleton["spine"] = "3.8.76";
base.WriteJson(root, jsonPath);
}
public override JsonObject ToVersion(JsonObject root, Version version) public override JsonObject ToVersion(JsonObject root, Version version)
{ {
root = version switch root = version switch

View File

@@ -92,7 +92,7 @@ namespace SpineViewer.Spine
/// <summary> /// <summary>
/// 读取 Json 对象 /// 读取 Json 对象
/// </summary> /// </summary>
public JsonObject ReadJson(string jsonPath) public virtual JsonObject ReadJson(string jsonPath)
{ {
using var input = File.OpenRead(jsonPath); using var input = File.OpenRead(jsonPath);
if (JsonNode.Parse(input) is JsonObject root) if (JsonNode.Parse(input) is JsonObject root)
@@ -104,7 +104,7 @@ namespace SpineViewer.Spine
/// <summary> /// <summary>
/// 写入 Json 对象 /// 写入 Json 对象
/// </summary> /// </summary>
public void WriteJson(JsonObject root, string jsonPath) public virtual void WriteJson(JsonObject root, string jsonPath)
{ {
using var output = File.Create(jsonPath); using var output = File.Create(jsonPath);
using var writer = new Utf8JsonWriter(output, jsonWriterOptions); using var writer = new Utf8JsonWriter(output, jsonWriterOptions);

View File

@@ -15,6 +15,7 @@ using System.Reflection;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Collections.Immutable;
namespace SpineViewer.Spine namespace SpineViewer.Spine
{ {
@@ -37,6 +38,11 @@ namespace SpineViewer.Spine
/// </summary> /// </summary>
public abstract class Spine : SFML.Graphics.Drawable, IDisposable public abstract class Spine : SFML.Graphics.Drawable, IDisposable
{ {
/// <summary>
/// 常规骨骼文件后缀集合
/// </summary>
public static readonly ImmutableHashSet<string> CommonSkelSuffix = [".skel", ".json"];
/// <summary> /// <summary>
/// 空动画标记 /// 空动画标记
/// </summary> /// </summary>
@@ -120,8 +126,9 @@ namespace SpineViewer.Spine
// try json format // try json format
try try
{ {
if (JsonNode.Parse(input) is JsonObject root && root.TryGetPropertyValue("spine", out var node)) if (JsonNode.Parse(input) is JsonObject root && root.TryGetPropertyValue("skeleton", out var node) &&
versionString = (string)node; node is JsonObject _skeleton && _skeleton.TryGetPropertyValue("spine", out var _version))
versionString = (string)_version;
} }
catch { } catch { }
@@ -163,6 +170,7 @@ namespace SpineViewer.Spine
else if (versionString.StartsWith("4.1.")) version = Version.V41; else if (versionString.StartsWith("4.1.")) version = Version.V41;
else if (versionString.StartsWith("4.2.")) version = Version.V42; else if (versionString.StartsWith("4.2.")) version = Version.V42;
else if (versionString.StartsWith("4.3.")) version = Version.V43; else if (versionString.StartsWith("4.3.")) version = Version.V43;
else Program.Logger.Error("Unknown verison: {}, {}", versionString, skelPath);
} }
return version; return version;
@@ -209,6 +217,7 @@ namespace SpineViewer.Spine
// 设置 Version // 设置 Version
Version = attr.Version; Version = attr.Version;
AssetsDir = Directory.GetParent(skelPath).FullName;
SkelPath = Path.GetFullPath(skelPath); SkelPath = Path.GetFullPath(skelPath);
AtlasPath = Path.GetFullPath(atlasPath); AtlasPath = Path.GetFullPath(atlasPath);
Name = Path.GetFileNameWithoutExtension(skelPath); Name = Path.GetFileNameWithoutExtension(skelPath);
@@ -225,6 +234,12 @@ namespace SpineViewer.Spine
[Category("基本信息"), DisplayName("运行时版本")] [Category("基本信息"), DisplayName("运行时版本")]
public Version Version { get; } public Version Version { get; }
/// <summary>
/// 资源所在完整目录
/// </summary>
[Category("基本信息"), DisplayName("资源目录")]
public string AssetsDir { get; }
/// <summary> /// <summary>
/// skel 文件完整路径 /// skel 文件完整路径
/// </summary> /// </summary>
@@ -237,6 +252,9 @@ namespace SpineViewer.Spine
[Category("基本信息"), DisplayName("atlas文件路径")] [Category("基本信息"), DisplayName("atlas文件路径")]
public string AtlasPath { get; } public string AtlasPath { get; }
/// <summary>
/// 名称
/// </summary>
[Category("基本信息"), DisplayName("名称")] [Category("基本信息"), DisplayName("名称")]
public string Name { get; } public string Name { get; }
@@ -352,7 +370,9 @@ namespace SpineViewer.Spine
viewX *= scale; viewX *= scale;
viewY *= scale; viewY *= scale;
using var tex = new SFML.Graphics.RenderTexture(width, height); // XXX: 貌似无法使用 using 或者 Dispose 主动释放 tex 资源
// 在批量添加的中途, 如果触发 GC? 会卡死, 目前未知原因
var tex = new SFML.Graphics.RenderTexture(width, height);
var view = tex.GetView(); var view = tex.GetView();
view.Center = new(bounds.X + viewX / 2, bounds.Y + viewY / 2); view.Center = new(bounds.X + viewX / 2, bounds.Y + viewY / 2);
view.Size = new(viewX, -viewY); view.Size = new(viewX, -viewY);
@@ -395,5 +415,23 @@ namespace SpineViewer.Spine
/// </summary> /// </summary>
[Browsable(false)] [Browsable(false)]
public bool IsSelected { get; set; } = false; public bool IsSelected { get; set; } = false;
/// <summary>
/// 显示调试
/// </summary>
[Browsable(false)]
public bool IsDebug { get; set; } = false;
/// <summary>
/// 显示包围盒
/// </summary>
[Browsable(false)]
public bool DebugBounds { get; set; } = true;
/// <summary>
/// 显示骨骼
/// </summary>
[Browsable(false)]
public bool DebugBones { get; set; } = true;
} }
} }

View File

@@ -18,7 +18,7 @@ namespace SpineViewer.Spine
if (destinationType == typeof(string) && value is Version version) if (destinationType == typeof(string) && value is Version version)
{ {
// 调用自定义的 String() 方法 // 调用自定义的 String() 方法
return version.String(); return version.GetName();
} }
return base.ConvertTo(context, culture, value, destinationType); return base.ConvertTo(context, culture, value, destinationType);

View File

@@ -12,10 +12,15 @@ namespace SpineViewer.Spine
public static class VersionHelper public static class VersionHelper
{ {
/// <summary> /// <summary>
/// 描述缓存 /// 版本名称
/// </summary> /// </summary>
public static readonly ReadOnlyDictionary<Version, string> Versions; public static readonly ReadOnlyDictionary<Version, string> Names;
private static readonly Dictionary<Version, string> versions = []; private static readonly Dictionary<Version, string> names = [];
/// <summary>
/// Runtime 版本字符串
/// </summary>
private static readonly Dictionary<Version, string> runtimes = [];
static VersionHelper() static VersionHelper()
{ {
@@ -24,17 +29,33 @@ namespace SpineViewer.Spine
{ {
var field = typeof(Version).GetField(value.ToString()); var field = typeof(Version).GetField(value.ToString());
var attribute = field?.GetCustomAttribute<DescriptionAttribute>(); var attribute = field?.GetCustomAttribute<DescriptionAttribute>();
versions[(Version)value] = attribute?.Description ?? value.ToString(); names[(Version)value] = attribute?.Description ?? value.ToString();
} }
Versions = versions.AsReadOnly(); Names = names.AsReadOnly();
runtimes[Version.V21] = Assembly.GetAssembly(typeof(SpineRuntime21.Skeleton)).GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
runtimes[Version.V36] = Assembly.GetAssembly(typeof(SpineRuntime36.Skeleton)).GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
runtimes[Version.V37] = Assembly.GetAssembly(typeof(SpineRuntime37.Skeleton)).GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
runtimes[Version.V38] = Assembly.GetAssembly(typeof(SpineRuntime38.Skeleton)).GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
runtimes[Version.V40] = Assembly.GetAssembly(typeof(SpineRuntime40.Skeleton)).GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
runtimes[Version.V41] = Assembly.GetAssembly(typeof(SpineRuntime41.Skeleton)).GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
runtimes[Version.V42] = Assembly.GetAssembly(typeof(SpineRuntime42.Skeleton)).GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
} }
/// <summary> /// <summary>
/// 版本字符串 /// 版本字符串名称
/// </summary> /// </summary>
public static string String(this Version version) public static string GetName(this Version version)
{ {
return Versions.TryGetValue(version, out var description) ? description : version.ToString(); return Names.TryGetValue(version, out var val) ? val : version.ToString();
}
/// <summary>
/// Runtime 版本字符串名称
/// </summary>
public static string GetRuntime(this Version version)
{
return runtimes.TryGetValue(version, out var val) ? val : GetName(version);
} }
} }

View File

@@ -8,7 +8,7 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath> <BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.10.3</Version> <Version>0.10.6</Version>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>appicon.ico</ApplicationIcon> <ApplicationIcon>appicon.ico</ApplicationIcon>