Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b23c779d3 | ||
|
|
f5684a50dc | ||
|
|
579ce9f944 | ||
|
|
7aa88089b8 | ||
|
|
be983f8407 | ||
|
|
249b930602 | ||
|
|
6472f378b7 | ||
|
|
8672f0571c | ||
|
|
e7a990c1bd | ||
|
|
6727fa8e8f | ||
|
|
66d8c489b5 | ||
|
|
1931c4713a | ||
|
|
f19f172e7c | ||
|
|
092fa76124 | ||
|
|
a0b7db0a70 | ||
|
|
6438b46ea0 | ||
|
|
2bf73db9d3 | ||
|
|
03c4974c9f | ||
|
|
760fa3a451 | ||
|
|
018d8f5330 | ||
|
|
c9730e1a11 | ||
|
|
1f6e19e544 | ||
|
|
a1a0777791 | ||
|
|
887e3f76d2 | ||
|
|
8b622050fa | ||
|
|
20369aaf43 | ||
|
|
07c0e84b7d | ||
|
|
6770acaffd | ||
|
|
6201ccc7d1 | ||
|
|
965d1c469e | ||
|
|
b448ca8cb0 | ||
|
|
2204eb6c75 | ||
|
|
0abe063899 | ||
|
|
6f9b357473 | ||
|
|
152d842043 | ||
|
|
d16f97d574 | ||
|
|
d28eabaca5 | ||
|
|
b730f677be | ||
|
|
8f8806417a | ||
|
|
06694c9e89 | ||
|
|
e9b0ce3db2 | ||
|
|
4c72608398 | ||
|
|
7e99882fbf | ||
|
|
0d72d8749a | ||
|
|
d5b7a74520 | ||
|
|
0202027edb |
27
CHANGELOG.md
27
CHANGELOG.md
@@ -1,5 +1,32 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.16.7
|
||||
|
||||
- 修复空帧导致的包围盒计算错误
|
||||
- 修复重复启动程序无法唤出界面的问题
|
||||
|
||||
## v0.16.6
|
||||
|
||||
- 修复控件尺寸为0时导致的画面缩放错误
|
||||
|
||||
## v0.16.5
|
||||
|
||||
- 修复对于无 size 行的旧 atlas 格式读取错误
|
||||
- 修复托盘化之后无法联动显示窗口的问题
|
||||
|
||||
## v0.16.4
|
||||
|
||||
- 增加 apng 导出格式
|
||||
- 增加颜色拾取器面板
|
||||
- 增加程序皮肤(主题颜色)首选项
|
||||
- 优化部分使用体验
|
||||
|
||||
## v0.16.3
|
||||
|
||||
- 修复加载工作区时的顺序错误
|
||||
- 调整部分调试渲染的逻辑
|
||||
- 完善命中检测逻辑
|
||||
|
||||
## v0.16.2
|
||||
|
||||
- 修复批量添加时的添加顺序错误
|
||||
|
||||
@@ -240,8 +240,8 @@ namespace SFMLRenderer
|
||||
if (RenderWindow is null) return;
|
||||
float parentW = (float)sizeInfo.NewSize.Width;
|
||||
float parentH = (float)sizeInfo.NewSize.Height;
|
||||
float renderW = (float)_hwndHost.ActualWidth;
|
||||
float renderH = (float)_hwndHost.ActualHeight;
|
||||
float renderW = _resolution.X;
|
||||
float renderH = _resolution.Y;
|
||||
float scale = Math.Min(parentW / renderW, parentH / renderH); // 两方向取较小值, 保证 parent 覆盖 render
|
||||
renderW *= scale;
|
||||
renderH *= scale;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<Version>0.16.0</Version>
|
||||
<Version>0.16.6</Version>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Spine.Exporters
|
||||
{
|
||||
Gif,
|
||||
Webp,
|
||||
Apng,
|
||||
Mp4,
|
||||
Webm,
|
||||
Mkv,
|
||||
@@ -41,31 +42,37 @@ namespace Spine.Exporters
|
||||
private VideoFormat _format = VideoFormat.Mp4;
|
||||
|
||||
/// <summary>
|
||||
/// 动图是否循环 [Gif/Webp]
|
||||
/// [Gif/Webp/Apng] 动图是否循环
|
||||
/// </summary>
|
||||
public bool Loop { get => _loop; set => _loop = value; }
|
||||
private bool _loop = true;
|
||||
|
||||
/// <summary>
|
||||
/// 质量 [Webp]
|
||||
/// [Webp] 质量
|
||||
/// </summary>
|
||||
public int Quality { get => _quality; set => _quality = Math.Clamp(value, 0, 100); }
|
||||
private int _quality = 75;
|
||||
|
||||
/// <summary>
|
||||
/// 无损压缩 [Webp]
|
||||
/// [Webp] 无损压缩
|
||||
/// </summary>
|
||||
public bool Lossless { get => _lossless; set => _lossless = value; }
|
||||
private bool _lossless = false;
|
||||
|
||||
/// <summary>
|
||||
/// CRF [Mp4/Webm/Mkv]
|
||||
/// [Apng] 预测器算法, 取值范围 0-5, 分别对应 none, sub, up, avg, paeth, mixed
|
||||
/// </summary>
|
||||
public int ApngPred { get => _apngPred; set => _apngPred = Math.Clamp(value, 0, 5); }
|
||||
private int _apngPred = 5;
|
||||
|
||||
/// <summary>
|
||||
/// [Mp4/Webm/Mkv] CRF
|
||||
/// </summary>
|
||||
public int Crf { get => _crf; set => _crf = Math.Clamp(value, 0, 63); }
|
||||
private int _crf = 23;
|
||||
|
||||
/// <summary>
|
||||
/// prores_ks 编码器的配置等级, -1 是自动, 越高质量越好, 只有 4 及以上才有透明通道 [Mov]
|
||||
/// [Mov] prores_ks 编码器的配置等级, -1 是自动, 越高质量越好, 只有 4 及以上才有透明通道
|
||||
/// </summary>
|
||||
public int Profile { get => _profile; set => _profile = Math.Clamp(value, -1, 5); }
|
||||
private int _profile = 5;
|
||||
@@ -93,6 +100,7 @@ namespace Spine.Exporters
|
||||
{
|
||||
VideoFormat.Gif => SetGifOptions,
|
||||
VideoFormat.Webp => SetWebpOptions,
|
||||
VideoFormat.Apng => SetApngOptions,
|
||||
VideoFormat.Mp4 => SetMp4Options,
|
||||
VideoFormat.Webm => SetWebmOptions,
|
||||
VideoFormat.Mkv => SetMkvOptions,
|
||||
@@ -132,6 +140,13 @@ namespace Spine.Exporters
|
||||
.WithCustomArgument(customArgs);
|
||||
}
|
||||
|
||||
private void SetApngOptions(FFMpegArgumentOptions options)
|
||||
{
|
||||
var customArgs = $"-vf unpremultiply=inplace=1 -plays {(_loop ? 0 : 1)} -pred {_apngPred}";
|
||||
options.ForceFormat("apng").WithVideoCodec("apng").ForcePixelFormat("rgba")
|
||||
.WithCustomArgument(customArgs);
|
||||
}
|
||||
|
||||
private void SetMp4Options(FFMpegArgumentOptions options)
|
||||
{
|
||||
// XXX: windows 默认播放器在播放 MP4 格式时对于 libx264 编码器只支持 yuv420p 的像素格式
|
||||
|
||||
@@ -112,6 +112,14 @@ namespace Spine.Implementations
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime34.AtlasPage page, string path)
|
||||
@@ -147,6 +155,14 @@ namespace Spine.Implementations
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime35.AtlasPage page, string path)
|
||||
@@ -182,6 +198,14 @@ namespace Spine.Implementations
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime36.AtlasPage page, string path)
|
||||
@@ -217,6 +241,14 @@ namespace Spine.Implementations
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime37.AtlasPage page, string path)
|
||||
@@ -252,6 +284,14 @@ namespace Spine.Implementations
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime38.AtlasPage page, string path)
|
||||
@@ -288,9 +328,13 @@ namespace Spine.Implementations
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 似乎是不需要设置的, 因为存在某些 png 和 atlas 大小不同的情况, 一般是有一些缩放, 如果设置了反而渲染异常
|
||||
// page.width = (int)texture.Size.X;
|
||||
// page.height = (int)texture.Size.Y;
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime40.AtlasPage page, string path)
|
||||
@@ -326,6 +370,14 @@ namespace Spine.Implementations
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime41.AtlasPage page, string path)
|
||||
@@ -361,6 +413,14 @@ namespace Spine.Implementations
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime42.AtlasPage page, string path)
|
||||
@@ -396,6 +456,14 @@ namespace Spine.Implementations
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Unload(object texture)
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.V21
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.V34
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.V35
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.V36
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.V37
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.V38
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.V40
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.V41
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace Spine.Implementations.V42
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
|
||||
@@ -15,6 +15,11 @@ namespace Spine.Interfaces
|
||||
/// </summary>
|
||||
public enum Physics { None, Reset, Update, Pose }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// R
|
||||
/// </summary>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<Version>0.16.2</Version>
|
||||
<Version>0.16.7</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using SkiaSharp;
|
||||
using NLog;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,10 +7,35 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Interfaces
|
||||
namespace Spine
|
||||
{
|
||||
/// <summary>
|
||||
/// 命中测试等级枚举值
|
||||
/// </summary>
|
||||
public enum HitTestLevel { None, Bounds, Meshes, Pixels }
|
||||
|
||||
public static class SpineExtension
|
||||
{
|
||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private static readonly SFML.Graphics.RenderTexture _renderTex; // XXX: 在此保留一个静态变量, 并且没有使用 Dispose 进行资源释放
|
||||
|
||||
static SpineExtension()
|
||||
{
|
||||
_renderTex = new(1, 1);
|
||||
_renderTex.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 命中检测精确度等级
|
||||
/// </summary>
|
||||
public static HitTestLevel HitTestLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 命中测试时输出命中的插槽名称
|
||||
/// </summary>
|
||||
public static bool LogHitSlots { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前状态包围盒
|
||||
/// </summary>
|
||||
@@ -84,38 +110,58 @@ namespace Spine.Interfaces
|
||||
break;
|
||||
}
|
||||
|
||||
for (int ii = 0; ii + 1 < verticesLength; ii += 2)
|
||||
if (verticesLength > 0)
|
||||
{
|
||||
float vx = vertices[ii];
|
||||
float vy = vertices[ii + 1];
|
||||
minX = Math.Min(minX, vx);
|
||||
minY = Math.Min(minY, vy);
|
||||
maxX = Math.Max(maxX, vx);
|
||||
maxY = Math.Max(maxY, vy);
|
||||
for (int ii = 0; ii + 1 < verticesLength; ii += 2)
|
||||
{
|
||||
float vx = vertices[ii];
|
||||
float vy = vertices[ii + 1];
|
||||
minX = Math.Min(minX, vx);
|
||||
minY = Math.Min(minY, vy);
|
||||
maxX = Math.Max(maxX, vx);
|
||||
maxY = Math.Max(maxY, vy);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var boneX = slot.Bone.WorldX;
|
||||
var boneY = slot.Bone.WorldY;
|
||||
minX = Math.Min(minX, boneX);
|
||||
minY = Math.Min(minY, boneY);
|
||||
maxX = Math.Max(maxX, boneX);
|
||||
maxY = Math.Max(maxY, boneY);
|
||||
}
|
||||
}
|
||||
x = minX;
|
||||
y = minY;
|
||||
w = maxX - minX;
|
||||
h = maxY - minY;
|
||||
if (minX >= int.MaxValue || minY >= int.MaxValue || maxX <= int.MinValue || maxY <= int.MinValue)
|
||||
{
|
||||
x = self.X;
|
||||
y = self.Y;
|
||||
w = 0;
|
||||
h = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = minX;
|
||||
y = minY;
|
||||
w = maxX - minX;
|
||||
h = maxY - minY;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 命中测试, 当插槽全透明或者处于禁用或者骨骼处于未激活则无法命中
|
||||
/// </summary>
|
||||
/// <param name="precise">是否精确命中检测, 否则仅使用包围盒进行命中检测</param>
|
||||
/// <param name="cache">调用方管理的缓存表</param>
|
||||
public static bool HitTest(this ISlot self, float x, float y, bool precise = false, Dictionary<SFML.Graphics.Texture, SFML.Graphics.Image> cache = null)
|
||||
public static bool HitTest(this ISlot self, float x, float y)
|
||||
{
|
||||
if (self.A <= 0 || !self.Bone.Active || self.Disabled)
|
||||
return false;
|
||||
|
||||
if (!precise)
|
||||
if (HitTestLevel == HitTestLevel.None || HitTestLevel == HitTestLevel.Bounds)
|
||||
{
|
||||
self.GetBounds(out var bx, out var by, out var bw, out var bh);
|
||||
return x >= bx && x <= (bx + bw) && y >= by && y <= (by + bh);
|
||||
return x >= bx && x <= bx + bw && y >= by && y <= by + bh;
|
||||
}
|
||||
else
|
||||
else if (HitTestLevel == HitTestLevel.Meshes || HitTestLevel == HitTestLevel.Pixels)
|
||||
{
|
||||
float[] vertices = new float[8];
|
||||
int[] triangles;
|
||||
@@ -140,22 +186,7 @@ namespace Spine.Interfaces
|
||||
return false;
|
||||
}
|
||||
|
||||
SFML.Graphics.Image img = null;
|
||||
if (cache is not null)
|
||||
{
|
||||
if (!cache.TryGetValue(tex, out img))
|
||||
{
|
||||
img = cache[tex] = tex.CopyToImage();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
img = tex.CopyToImage();
|
||||
}
|
||||
|
||||
bool hit = false;
|
||||
var trianglesLength = triangles.Length;
|
||||
var texSize = img.Size;
|
||||
for (int i = 0; i + 2 < trianglesLength; i += 3)
|
||||
{
|
||||
var idx0 = triangles[i] << 1;
|
||||
@@ -171,8 +202,11 @@ namespace Spine.Interfaces
|
||||
float c2 = Cross(x2, y2, x0, y0);
|
||||
|
||||
// 判断是否全部同号 (或为 0, 点在边上)
|
||||
if ((c0 >= 0 && c1 >= 0 && c2 >= 0) || (c0 <= 0 && c1 <= 0 && c2 <= 0))
|
||||
if (c0 >= 0 && c1 >= 0 && c2 >= 0 || c0 <= 0 && c1 <= 0 && c2 <= 0)
|
||||
{
|
||||
if (HitTestLevel == HitTestLevel.Meshes)
|
||||
return true;
|
||||
|
||||
float u0 = uvs[idx0], v0 = uvs[idx0 + 1];
|
||||
float u1 = uvs[idx1], v1 = uvs[idx1 + 1];
|
||||
float u2 = uvs[idx2], v2 = uvs[idx2 + 1];
|
||||
@@ -182,44 +216,62 @@ namespace Spine.Interfaces
|
||||
float w2 = c0 * inv;
|
||||
float u = u0 * w0 + u1 * w1 + u2 * w2;
|
||||
float v = v0 * w0 + v1 * w1 + v2 * w2;
|
||||
var texW = tex.Size.X;
|
||||
var texH = tex.Size.Y;
|
||||
|
||||
var pixel = img.GetPixel((uint)(u * texSize.X), (uint)(v * texSize.Y));
|
||||
hit = pixel.A > 0;
|
||||
break;
|
||||
// 把要判断的那个像素点渲出来
|
||||
using var view = _renderTex.GetView();
|
||||
using var vertexArray = new SFML.Graphics.VertexArray(SFML.Graphics.PrimitiveType.Points, 1);
|
||||
vertexArray[0] = new(view.Center, new SFML.System.Vector2f(u * texW, v * texH));
|
||||
|
||||
// XXX: 此处 RenderTexture 不能临时用临时释放, 由于未知原因如果短时间快速创建释放 RenderTexture 可能让程序卡死
|
||||
_renderTex.SetActive(true);
|
||||
_renderTex.Clear(SFML.Graphics.Color.Transparent);
|
||||
_renderTex.Draw(vertexArray, new(tex));
|
||||
_renderTex.Display();
|
||||
_renderTex.SetActive(false);
|
||||
|
||||
using var img = _renderTex.Texture.CopyToImage();
|
||||
var pixel = img.GetPixel(0, 0);
|
||||
return pixel.A > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 无缓存需要立即释放资源
|
||||
if (cache is null)
|
||||
{
|
||||
img.Dispose();
|
||||
}
|
||||
|
||||
return hit;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(HitTestLevel.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 逐插槽的命中测试, 命中后会提前返回结果中止计算
|
||||
/// </summary>
|
||||
public static bool HitTest(this ISkeleton self, float x, float y, bool precise = false)
|
||||
public static bool HitTest(this ISkeleton self, float x, float y)
|
||||
{
|
||||
var cache = new Dictionary<SFML.Graphics.Texture, SFML.Graphics.Image>();
|
||||
bool hit = self.IterDrawOrder().Any(st => st.HitTest(x, y, precise, cache));
|
||||
foreach (var img in cache.Values) img.Dispose();
|
||||
return hit;
|
||||
}
|
||||
if (HitTestLevel == HitTestLevel.None)
|
||||
{
|
||||
self.GetBounds(out var bx, out var by, out var bw, out var bh);
|
||||
return x >= bx && x <= bx + bw && y >= by && y <= by + bh;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 逐插槽的命中测试, 会完整计算所有插槽的命中情况并按顶层至底层的顺序返回命中的插槽
|
||||
/// /// <param name="precise">是否精确命中检测, 否则仅使用每个插槽的包围盒进行命中检测</param>
|
||||
/// </summary>
|
||||
public static ISlot[] HitTestFull(this ISkeleton self, float x, float y, bool precise = false)
|
||||
{
|
||||
var cache = new Dictionary<SFML.Graphics.Texture, SFML.Graphics.Image>();
|
||||
var hitSlots = self.IterDrawOrder().Where(st => st.HitTest(x, y, precise, cache)).Reverse().ToArray();
|
||||
foreach (var img in cache.Values) img.Dispose();
|
||||
return hitSlots;
|
||||
bool hit = false;
|
||||
string hitSlotName = "";
|
||||
foreach (var st in self.IterDrawOrder().Reverse())
|
||||
{
|
||||
if (st.HitTest(x, y))
|
||||
{
|
||||
hit = true;
|
||||
hitSlotName = st.Name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit && LogHitSlots)
|
||||
{
|
||||
_logger.Debug("Hit ({0}): [{1}]", self.Name, hitSlotName);
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -628,7 +628,7 @@ namespace Spine
|
||||
if (DebugRegions)
|
||||
{
|
||||
vt.Color = AttachmentLineColor;
|
||||
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active && !s.Disabled))
|
||||
foreach (var slot in _skeleton.IterDrawOrder().Where(s => s.A > 0 && s.Bone.Active && !s.Disabled))
|
||||
{
|
||||
if (slot.Attachment is IRegionAttachment regionAttachment)
|
||||
{
|
||||
@@ -660,7 +660,7 @@ namespace Spine
|
||||
if (DebugMeshes)
|
||||
{
|
||||
vt.Color = MeshLineColor;
|
||||
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active && !s.Disabled))
|
||||
foreach (var slot in _skeleton.IterDrawOrder().Where(s => s.A > 0 && s.Bone.Active && !s.Disabled))
|
||||
{
|
||||
if (slot.Attachment is IMeshAttachment meshAttachment)
|
||||
{
|
||||
@@ -696,7 +696,7 @@ namespace Spine
|
||||
if (DebugMeshHulls)
|
||||
{
|
||||
vt.Color = AttachmentLineColor;
|
||||
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active && !s.Disabled))
|
||||
foreach (var slot in _skeleton.IterDrawOrder().Where(s => s.A > 0 && s.Bone.Active && !s.Disabled))
|
||||
{
|
||||
if (slot.Attachment is IMeshAttachment meshAttachment)
|
||||
{
|
||||
@@ -742,7 +742,7 @@ namespace Spine
|
||||
if (DebugClippings)
|
||||
{
|
||||
vt.Color = ClippingLineColor;
|
||||
foreach (var slot in _skeleton.Slots.Where(s => s.Bone.Active && !s.Disabled))
|
||||
foreach (var slot in _skeleton.IterDrawOrder().Where(s => s.A > 0 && s.Bone.Active && !s.Disabled))
|
||||
{
|
||||
if (slot.Attachment is IClippingAttachment clippingAttachment)
|
||||
{
|
||||
@@ -799,7 +799,7 @@ namespace Spine
|
||||
if (DebugBones)
|
||||
{
|
||||
var width = Math.Max(Math.Abs(_skeleton.ScaleX), Math.Abs(_skeleton.ScaleY));
|
||||
foreach (var bone in _skeleton.Bones.Where(b => b.Active))
|
||||
foreach (var bone in _skeleton.IterDrawOrder().Where(s => s.A > 0 && s.Bone.Active && !s.Disabled).Select(st => st.Bone))
|
||||
{
|
||||
var boneLength = bone.Length;
|
||||
var p1 = new SFML.System.Vector2f(bone.WorldX, bone.WorldY);
|
||||
@@ -815,7 +815,7 @@ namespace Spine
|
||||
if (DebugBones)
|
||||
{
|
||||
var radius = Math.Max(Math.Abs(_skeleton.ScaleX), Math.Abs(_skeleton.ScaleY));
|
||||
foreach (var bone in _skeleton.Bones.Where(b => b.Active))
|
||||
foreach (var bone in _skeleton.IterDrawOrder().Where(s => s.A > 0 && s.Bone.Active && !s.Disabled).Select(st => st.Bone))
|
||||
{
|
||||
DrawCirclePoint(target, new(bone.WorldX, bone.WorldY), BonePointColor, radius);
|
||||
}
|
||||
|
||||
@@ -8,28 +8,11 @@
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
|
||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
|
||||
<ResourceDictionary Source="/Resources/Geometries.xaml"/>
|
||||
<ResourceDictionary Source="/Resources/Strings/zh.xaml"/>
|
||||
<ResourceDictionary Source="/Resources/Skins/Light.xaml"/>
|
||||
<ResourceDictionary Source="/Resources/Theme.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style x:Key="MyToggleButton" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource ToggleButtonSwitch}">
|
||||
<Setter Property="hc:VisualElement.HighlightBrush" Value="{StaticResource DarkSuccessBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ListBox}" BasedOn="{StaticResource ListBoxBaseStyle}">
|
||||
<Setter Property="SelectionMode" Value="Extended"/>
|
||||
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="False"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Visible"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ListView}" BasedOn="{StaticResource ListViewBaseStyle}">
|
||||
<Setter Property="SelectionMode" Value="Extended"/>
|
||||
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="False"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Win32;
|
||||
using NLog;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.ViewModels.MainWindow;
|
||||
using SpineViewer.Views;
|
||||
using System.Collections.Frozen;
|
||||
@@ -12,6 +13,7 @@ using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
|
||||
namespace SpineViewer
|
||||
{
|
||||
@@ -67,8 +69,7 @@ namespace SpineViewer
|
||||
_instanceMutex = new Mutex(true, MutexName, out var createdNew);
|
||||
if (!createdNew)
|
||||
{
|
||||
ShowExistedInstance();
|
||||
SendCommandLineArgs();
|
||||
ConnectAndSendArgs();
|
||||
Environment.Exit(0); // 不再启动新实例
|
||||
return;
|
||||
}
|
||||
@@ -98,55 +99,27 @@ namespace SpineViewer
|
||||
LogManager.Configuration = config;
|
||||
}
|
||||
|
||||
private static void ShowExistedInstance()
|
||||
private static void ConnectAndSendArgs()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 遍历同名进程
|
||||
var processes = Process.GetProcessesByName(ProcessName);
|
||||
foreach (var p in processes)
|
||||
{
|
||||
// 跳过当前进程
|
||||
if (p.Id == Process.GetCurrentProcess().Id)
|
||||
continue;
|
||||
|
||||
IntPtr hWnd = p.MainWindowHandle;
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
// 3. 显示并置顶窗口
|
||||
if (User32.IsIconic(hWnd))
|
||||
{
|
||||
User32.ShowWindow(hWnd, User32.SW_RESTORE);
|
||||
}
|
||||
User32.SetForegroundWindow(hWnd);
|
||||
break; // 找到一个就可以退出
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略异常,不影响当前进程退出
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendCommandLineArgs()
|
||||
{
|
||||
var args = Environment.GetCommandLineArgs().Skip(1).ToArray();
|
||||
if (args.Length <= 0)
|
||||
return;
|
||||
|
||||
_logger.Info("Send command line args to existed instance, \"{0}\"", string.Join(", ", args));
|
||||
try
|
||||
{
|
||||
// 已有实例在运行,把参数通过命名管道发过去
|
||||
using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
|
||||
{
|
||||
client.Connect(10000); // 10 秒超时
|
||||
using (var writer = new StreamWriter(client))
|
||||
// 只要启动了实例就要进行连接, 10 秒超时
|
||||
client.Connect(10000);
|
||||
|
||||
// 但是只有有参数的时候才发送参数
|
||||
var args = Environment.GetCommandLineArgs().Skip(1).ToArray();
|
||||
if (args.Length > 0)
|
||||
{
|
||||
foreach (var v in args)
|
||||
_logger.Info("Send command line args to existed instance, \"{0}\"", string.Join(", ", args));
|
||||
using (var writer = new StreamWriter(client))
|
||||
{
|
||||
writer.WriteLine(v);
|
||||
foreach (var v in args)
|
||||
{
|
||||
writer.WriteLine(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,6 +135,7 @@ namespace SpineViewer
|
||||
{
|
||||
var t = new Task(() =>
|
||||
{
|
||||
// 防止实例和窗口还没创建好
|
||||
while (Current is null) Thread.Sleep(10);
|
||||
while (true)
|
||||
{
|
||||
@@ -174,26 +148,48 @@ namespace SpineViewer
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
using (var server = new NamedPipeServerStream(PipeName, PipeDirection.In))
|
||||
try
|
||||
{
|
||||
server.WaitForConnection();
|
||||
using (var reader = new StreamReader(server))
|
||||
using (var server = new NamedPipeServerStream(PipeName, PipeDirection.In))
|
||||
{
|
||||
var args = new List<string>();
|
||||
string? line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
args.Add(line);
|
||||
server.WaitForConnection();
|
||||
|
||||
if (args.Count > 0)
|
||||
// 只要收到连接就可以显示窗口了
|
||||
Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Current.Dispatcher.Invoke(() =>
|
||||
var window = (MainWindow)Current.MainWindow;
|
||||
window.Show();
|
||||
if (window.WindowState == WindowState.Minimized)
|
||||
{
|
||||
var vm = (MainWindowViewModel)((MainWindow)Current.MainWindow).DataContext;
|
||||
vm.SpineObjectListViewModel.AddSpineObjectFromFileList(args);
|
||||
});
|
||||
window.WindowState = WindowState.Normal;
|
||||
}
|
||||
window.Activate();
|
||||
});
|
||||
using (var reader = new StreamReader(server))
|
||||
{
|
||||
var args = new List<string>();
|
||||
string? line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
args.Add(line);
|
||||
|
||||
if (args.Count > 0)
|
||||
{
|
||||
Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
// 尝试加载参数内容
|
||||
var window = (MainWindow)Current.MainWindow;
|
||||
var vm = (MainWindowViewModel)window.DataContext;
|
||||
vm.SpineObjectListViewModel.AddSpineObjectFromFileList(args);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to process arguments, {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}, default, TaskCreationOptions.LongRunning);
|
||||
t.Start();
|
||||
@@ -231,8 +227,8 @@ namespace SpineViewer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error("Failed to query autorun registry key, {0}", ex.Message);
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to query autorun registry key, {0}", ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -259,8 +255,8 @@ namespace SpineViewer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error("Failed to set autorun registry key, {0}", ex.Message);
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to set autorun registry key, {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,12 +339,36 @@ namespace SpineViewer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error("Failed to switch language to {0}, {1}", value, ex.Message);
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to switch language to {0}, {1}", value, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
private AppLanguage _language = AppLanguage.ZH;
|
||||
|
||||
public AppSkin Skin
|
||||
{
|
||||
get => _skin;
|
||||
set
|
||||
{
|
||||
var uri = $"Resources/Skins/{value.ToString().ToLower()}.xaml";
|
||||
try
|
||||
{
|
||||
Resources.MergedDictionaries.Add(new() { Source = new(uri, UriKind.Relative) });
|
||||
Resources.MergedDictionaries.Add(new() { Source = new("Resources/Theme.xaml", UriKind.Relative) });
|
||||
var hwnd = new WindowInteropHelper(Current.MainWindow).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
_skin = value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to switch skin to {0}, {1}", value, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
private AppSkin _skin = AppSkin.Light;
|
||||
}
|
||||
|
||||
public enum AppLanguage
|
||||
@@ -357,4 +377,11 @@ namespace SpineViewer
|
||||
EN,
|
||||
JA
|
||||
}
|
||||
|
||||
public enum AppSkin
|
||||
{
|
||||
Light,
|
||||
Dark,
|
||||
Violet,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Spine;
|
||||
using Spine.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Spine;
|
||||
using SpineViewer.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -85,11 +86,14 @@ namespace SpineViewer.Models
|
||||
[ObservableProperty]
|
||||
private AppLanguage _appLanguage;
|
||||
|
||||
[ObservableProperty]
|
||||
private AppSkin _appSkin;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _renderSelectedOnly;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _usePreciseHitTest;
|
||||
private HitTestLevel _hitTestLevel;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _logHitSlots;
|
||||
|
||||
@@ -429,19 +429,11 @@ namespace SpineViewer.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 命中检测, 可选是否使用精确检测, 会有性能损失
|
||||
/// 命中检测
|
||||
/// </summary>
|
||||
public bool HitTest(float x, float y, bool precise = false)
|
||||
public bool HitTest(float x, float y)
|
||||
{
|
||||
lock (_lock) return _spineObject.Skeleton.HitTest(x, y, precise);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 完整的命中检测, 会检测所有插槽是否命中并返回命中的插槽名称
|
||||
/// </summary>
|
||||
public string[] HitTestFull(float x, float y, bool precise = false)
|
||||
{
|
||||
lock (_lock) return _spineObject.Skeleton.HitTestFull(x, y, precise).Select(v => v.Name).ToArray();
|
||||
lock (_lock) return _spineObject.Skeleton.HitTest(x, y);
|
||||
}
|
||||
|
||||
public SpineObjectConfigModel ObjectConfig
|
||||
|
||||
48
SpineViewer/Natives/Dwmapi.cs
Normal file
48
SpineViewer/Natives/Dwmapi.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SpineViewer.Natives
|
||||
{
|
||||
/// <summary>
|
||||
/// dwmapi.dll 包装类
|
||||
/// </summary>
|
||||
public static class Dwmapi
|
||||
{
|
||||
private const uint DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
|
||||
private const uint DWMWA_CAPTION_COLOR = 35;
|
||||
private const uint DWMWA_TEXT_COLOR = 36;
|
||||
private const uint DWMWA_COLOR_DEFAULT = 0xFFFFFFFF;
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, uint dwAttribute, ref int pvAttribute, int cbAttribute);
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, uint dwAttribute, ref uint pvAttribute, int cbAttribute);
|
||||
|
||||
public static bool SetWindowCaptionColor(IntPtr hwnd, Color color)
|
||||
{
|
||||
int c = color.R | (color.G << 8) | (color.B << 16);
|
||||
return 0 == DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, ref c, sizeof(uint));
|
||||
}
|
||||
|
||||
public static bool SetWindowTextColor(IntPtr hwnd, Color color)
|
||||
{
|
||||
int c = color.R | (color.G << 8) | (color.B << 16);
|
||||
return 0 == DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, ref c, sizeof(uint));
|
||||
}
|
||||
|
||||
public static bool SetWindowDarkMode(IntPtr hwnd, bool darkMode)
|
||||
{
|
||||
int b = darkMode ? 1 : 0;
|
||||
uint c = DWMWA_COLOR_DEFAULT;
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, ref c, sizeof(uint));
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, ref c, sizeof(uint));
|
||||
return 0 == DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, ref b, sizeof(int));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,102 @@ namespace SpineViewer.Resources
|
||||
/// </summary>
|
||||
public static class AppResource
|
||||
{
|
||||
public static T Get<T>(string key) => (T)App.Current.FindResource(key);
|
||||
private static T Get<T>(string key) => (T)App.Current.FindResource(key);
|
||||
|
||||
#region Colors
|
||||
|
||||
public static Color Color_LightPrimary => Get<Color>("LightPrimaryColor");
|
||||
public static Color Color_Primary => Get<Color>("PrimaryColor");
|
||||
public static Color Color_DarkPrimary => Get<Color>("DarkPrimaryColor");
|
||||
|
||||
public static Color Color_LightDanger => Get<Color>("LightDangerColor");
|
||||
public static Color Color_Danger => Get<Color>("DangerColor");
|
||||
public static Color Color_DarkDanger => Get<Color>("DarkDangerColor");
|
||||
|
||||
public static Color Color_LightWarning => Get<Color>("LightWarningColor");
|
||||
public static Color Color_Warning => Get<Color>("WarningColor");
|
||||
public static Color Color_DarkWarning => Get<Color>("DarkWarningColor");
|
||||
|
||||
public static Color Color_LightInfo => Get<Color>("LightInfoColor");
|
||||
public static Color Color_Info => Get<Color>("InfoColor");
|
||||
public static Color Color_DarkInfo => Get<Color>("DarkInfoColor");
|
||||
|
||||
public static Color Color_LightSuccess => Get<Color>("LightSuccessColor");
|
||||
public static Color Color_Success => Get<Color>("SuccessColor");
|
||||
public static Color Color_DarkSuccess => Get<Color>("DarkSuccessColor");
|
||||
|
||||
public static Color Color_PrimaryText => Get<Color>("PrimaryTextColor");
|
||||
public static Color Color_SecondaryText => Get<Color>("SecondaryTextColor");
|
||||
public static Color Color_ThirdlyText => Get<Color>("ThirdlyTextColor");
|
||||
public static Color Color_ReverseText => Get<Color>("ReverseTextColor");
|
||||
public static Color Color_TextIcon => Get<Color>("TextIconColor");
|
||||
|
||||
public static Color Color_Border => Get<Color>("BorderColor");
|
||||
public static Color Color_SecondaryBorder => Get<Color>("SecondaryBorderColor");
|
||||
public static Color Color_Background => Get<Color>("BackgroundColor");
|
||||
public static Color Color_Region => Get<Color>("RegionColor");
|
||||
public static Color Color_SecondaryRegion => Get<Color>("SecondaryRegionColor");
|
||||
public static Color Color_ThirdlyRegion => Get<Color>("ThirdlyRegionColor");
|
||||
public static Color Color_Title => Get<Color>("TitleColor");
|
||||
public static Color Color_SecondaryTitle => Get<Color>("SecondaryTitleColor");
|
||||
|
||||
public static Color Color_Default => Get<Color>("DefaultColor");
|
||||
public static Color Color_DarkDefault => Get<Color>("DarkDefaultColor");
|
||||
|
||||
public static Color Color_Accent => Get<Color>("AccentColor");
|
||||
public static Color Color_DarkAccent => Get<Color>("DarkAccentColor");
|
||||
|
||||
public static Color Color_DarkMask => Get<Color>("DarkMaskColor");
|
||||
public static Color Color_DarkOpacity => Get<Color>("DarkOpacityColor");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Brushes
|
||||
|
||||
public static SolidColorBrush Brush_LightPrimary => Get<SolidColorBrush>("LightPrimaryBrush");
|
||||
public static LinearGradientBrush Brush_Primary => Get<LinearGradientBrush>("PrimaryBrush");
|
||||
public static SolidColorBrush Brush_DarkPrimary => Get<SolidColorBrush>("DarkPrimaryBrush");
|
||||
|
||||
public static SolidColorBrush Brush_PrimaryText => Get<SolidColorBrush>("PrimaryTextBrush");
|
||||
public static SolidColorBrush Brush_SecondaryText => Get<SolidColorBrush>("SecondaryTextBrush");
|
||||
public static SolidColorBrush Brush_ThirdlyText => Get<SolidColorBrush>("ThirdlyTextBrush");
|
||||
public static SolidColorBrush Brush_ReverseText => Get<SolidColorBrush>("ReverseTextBrush");
|
||||
public static SolidColorBrush Brush_TextIcon => Get<SolidColorBrush>("TextIconBrush");
|
||||
|
||||
public static SolidColorBrush Brush_Border => Get<SolidColorBrush>("BorderBrush");
|
||||
public static SolidColorBrush Brush_SecondaryBorder => Get<SolidColorBrush>("SecondaryBorderBrush");
|
||||
public static SolidColorBrush Brush_Background => Get<SolidColorBrush>("BackgroundBrush");
|
||||
public static SolidColorBrush Brush_Region => Get<SolidColorBrush>("RegionBrush");
|
||||
public static SolidColorBrush Brush_SecondaryRegion => Get<SolidColorBrush>("SecondaryRegionBrush");
|
||||
public static SolidColorBrush Brush_ThirdlyRegion => Get<SolidColorBrush>("ThirdlyRegionBrush");
|
||||
public static LinearGradientBrush Brush_Title => Get<LinearGradientBrush>("TitleBrush");
|
||||
|
||||
public static SolidColorBrush Brush_Default => Get<SolidColorBrush>("DefaultBrush");
|
||||
public static SolidColorBrush Brush_DarkDefault => Get<SolidColorBrush>("DarkDefaultBrush");
|
||||
|
||||
public static SolidColorBrush Brush_LightDanger => Get<SolidColorBrush>("LightDangerBrush");
|
||||
public static LinearGradientBrush Brush_Danger => Get<LinearGradientBrush>("DangerBrush");
|
||||
public static SolidColorBrush Brush_DarkDanger => Get<SolidColorBrush>("DarkDangerBrush");
|
||||
|
||||
public static SolidColorBrush Brush_LightWarning => Get<SolidColorBrush>("LightWarningBrush");
|
||||
public static LinearGradientBrush Brush_Warning => Get<LinearGradientBrush>("WarningBrush");
|
||||
public static SolidColorBrush Brush_DarkWarning => Get<SolidColorBrush>("DarkWarningBrush");
|
||||
|
||||
public static SolidColorBrush Brush_LightInfo => Get<SolidColorBrush>("LightInfoBrush");
|
||||
public static LinearGradientBrush Brush_Info => Get<LinearGradientBrush>("InfoBrush");
|
||||
public static SolidColorBrush Brush_DarkInfo => Get<SolidColorBrush>("DarkInfoBrush");
|
||||
|
||||
public static SolidColorBrush Brush_LightSuccess => Get<SolidColorBrush>("LightSuccessBrush");
|
||||
public static LinearGradientBrush Brush_Success => Get<LinearGradientBrush>("SuccessBrush");
|
||||
public static SolidColorBrush Brush_DarkSuccess => Get<SolidColorBrush>("DarkSuccessBrush");
|
||||
|
||||
public static SolidColorBrush Brush_Accent => Get<SolidColorBrush>("AccentBrush");
|
||||
public static SolidColorBrush Brush_DarkAccent => Get<SolidColorBrush>("DarkAccentBrush");
|
||||
|
||||
public static SolidColorBrush Brush_DarkMask => Get<SolidColorBrush>("DarkMaskBrush");
|
||||
public static SolidColorBrush Brush_DarkOpacity => Get<SolidColorBrush>("DarkOpacityBrush");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Strings
|
||||
|
||||
|
||||
2
SpineViewer/Resources/Skins/dark.xaml
Normal file
2
SpineViewer/Resources/Skins/dark.xaml
Normal file
@@ -0,0 +1,2 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
Source="pack://application:,,,/HandyControl;component/Themes/SkinDark.xaml"/>
|
||||
2
SpineViewer/Resources/Skins/light.xaml
Normal file
2
SpineViewer/Resources/Skins/light.xaml
Normal file
@@ -0,0 +1,2 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
|
||||
2
SpineViewer/Resources/Skins/violet.xaml
Normal file
2
SpineViewer/Resources/Skins/violet.xaml
Normal file
@@ -0,0 +1,2 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
Source="pack://application:,,,/HandyControl;component/Themes/SkinViolet.xaml"/>
|
||||
@@ -120,10 +120,9 @@
|
||||
<s:String x:Key="Str_PlaySpeed">Playback Speed</s:String>
|
||||
<s:String x:Key="Str_WallpaperView">Wallpaper View</s:String>
|
||||
<s:String x:Key="Str_RenderSelectedOnly">Render Selected Only</s:String>
|
||||
<s:String x:Key="Str_UsePreciseHitTest">Use Precise Hit Testing</s:String>
|
||||
<s:String x:Key="Str_UsePreciseHitTestTooltip">When enabled, click detection will be performed based on pixel transparency of the model.</s:String>
|
||||
<s:String x:Key="Str_LogHitSlots">Log Hit Slot Names</s:String>
|
||||
<s:String x:Key="Str_LogHitSlotsTooltip">When enabled, the log box will output the model and slot information hit by each click operation (will not output when using Ctrl for multi-selection).</s:String>
|
||||
<s:String x:Key="Str_HitTestLevel">Hit Test Accuracy Level</s:String>
|
||||
<s:String x:Key="Str_LogHitSlots">Output Hit Test Slot Names</s:String>
|
||||
<s:String x:Key="Str_LogHitSlotsTooltip">When enabled, the log box will output the model and slot information for each click hit test.</s:String>
|
||||
<s:String x:Key="Str_ShowAxis">Show Axis</s:String>
|
||||
<s:String x:Key="Str_BackgroundColor">Background Color</s:String>
|
||||
<s:String x:Key="Str_BackgroundImagePath">Background Image Path</s:String>
|
||||
@@ -198,11 +197,13 @@
|
||||
|
||||
<s:String x:Key="Str_VideoFormat">Video Format</s:String>
|
||||
<s:String x:Key="Str_LoopPlay">Loop Play</s:String>
|
||||
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp]
Whether the animation loops</s:String>
|
||||
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp/Apng]
Whether the animation loops</s:String>
|
||||
<s:String x:Key="Str_QualityParameter">Quality Parameter</s:String>
|
||||
<s:String x:Key="Str_QualityParameterTooltip" xml:space="preserve">[Webp]
Quality parameter, range 0-100, higher value means better quality</s:String>
|
||||
<s:String x:Key="Str_LosslessParam">Lossless Compression</s:String>
|
||||
<s:String x:Key="Str_LosslessParamTooltip" xml:space="preserve">[Webp]
Lossless compression, quality parameter will be ignored</s:String>
|
||||
<s:String x:Key="Str_ApngPred">Predictor Method</s:String>
|
||||
<s:String x:Key="Str_ApngPredTooltip" xml:space="preserve">[Apng]
Pred parameter, value range 0-5, corresponding to different encoding strategies: none, sub, up, avg, paeth, and mixed.
It affects encoding time and file size.</s:String>
|
||||
<s:String x:Key="Str_CrfParameter">CRF Parameter</s:String>
|
||||
<s:String x:Key="Str_CrfParameterTooltip" xml:space="preserve">[Mp4/Webm/Mkv]
CRF parameter, range 0-63, lower value means higher quality</s:String>
|
||||
<s:String x:Key="Str_ProfileParameter">Profile Parameter</s:String>
|
||||
@@ -243,6 +244,7 @@
|
||||
|
||||
<s:String x:Key="Str_AppPreference">Application Options</s:String>
|
||||
<s:String x:Key="Str_Language">Language</s:String>
|
||||
<s:String x:Key="Str_AppSkin">Skin</s:String>
|
||||
<s:String x:Key="Str_CloseToTray">Minimize to tray when closing</s:String>
|
||||
<s:String x:Key="Str_AutoRun">Auto Start</s:String>
|
||||
<s:String x:Key="Str_AutoRunWorkspaceConfigPath">Auto-load Workspace File on Startup</s:String>
|
||||
|
||||
@@ -120,10 +120,9 @@
|
||||
<s:String x:Key="Str_PlaySpeed">再生速度</s:String>
|
||||
<s:String x:Key="Str_WallpaperView">壁紙表示</s:String>
|
||||
<s:String x:Key="Str_RenderSelectedOnly">選択のみレンダリング</s:String>
|
||||
<s:String x:Key="Str_UsePreciseHitTest">精密ヒットテストを使用</s:String>
|
||||
<s:String x:Key="Str_UsePreciseHitTestTooltip">有効にすると、モデルのピクセル透過度に基づいてクリック判定を行います。</s:String>
|
||||
<s:String x:Key="Str_LogHitSlots">ヒットしたスロット名を出力</s:String>
|
||||
<s:String x:Key="Str_LogHitSlotsTooltip">有効にすると、ログボックスに各クリック操作でヒットしたモデルとスロットの情報を出力します(Ctrlを押しながら複数選択する場合は出力されません)。</s:String>
|
||||
<s:String x:Key="Str_HitTestLevel">ヒットテスト精度レベル</s:String>
|
||||
<s:String x:Key="Str_LogHitSlots">ヒットテスト結果のスロット名を出力</s:String>
|
||||
<s:String x:Key="Str_LogHitSlotsTooltip">有効にすると、ログボックスに各クリック操作で命中したモデルとスロットの情報が出力されます。</s:String>
|
||||
<s:String x:Key="Str_ShowAxis">座標軸を表示</s:String>
|
||||
<s:String x:Key="Str_BackgroundColor">背景色</s:String>
|
||||
<s:String x:Key="Str_BackgroundImagePath">背景画像のパス</s:String>
|
||||
@@ -198,11 +197,13 @@
|
||||
|
||||
<s:String x:Key="Str_VideoFormat">ビデオフォーマット</s:String>
|
||||
<s:String x:Key="Str_LoopPlay">ループ再生</s:String>
|
||||
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp]
アニメーションをループ再生するかどうか</s:String>
|
||||
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp/Apng]
アニメーションをループ再生するかどうか</s:String>
|
||||
<s:String x:Key="Str_QualityParameter">品質パラメータ</s:String>
|
||||
<s:String x:Key="Str_QualityParameterTooltip" xml:space="preserve">[Webp]
品質パラメータ、範囲は0-100。値が高いほど品質が良い</s:String>
|
||||
<s:String x:Key="Str_LosslessParam">無損失圧縮</s:String>
|
||||
<s:String x:Key="Str_LosslessParamTooltip" xml:space="preserve">[Webp]
無損失圧縮、品質パラメータは無視されます</s:String>
|
||||
<s:String x:Key="Str_ApngPred">予測器方式</s:String>
|
||||
<s:String x:Key="Str_ApngPredTooltip" xml:space="preserve">[Apng]
Pred パラメータ。値の範囲は 0~5 で、それぞれ none、sub、up、avg、paeth、mixed の異なるエンコード方式に対応します。
エンコード時間とファイルサイズに影響します。</s:String>
|
||||
<s:String x:Key="Str_CrfParameter">CRF パラメータ</s:String>
|
||||
<s:String x:Key="Str_CrfParameterTooltip" xml:space="preserve">[Mp4/Webm/Mkv]
CRF パラメータ、範囲0-63。値が小さいほど品質が高い</s:String>
|
||||
<s:String x:Key="Str_ProfileParameter">プロファイルパラメータ</s:String>
|
||||
@@ -243,6 +244,7 @@
|
||||
|
||||
<s:String x:Key="Str_AppPreference">アプリケーションプション</s:String>
|
||||
<s:String x:Key="Str_Language">言語</s:String>
|
||||
<s:String x:Key="Str_AppSkin">スキン</s:String>
|
||||
<s:String x:Key="Str_CloseToTray">閉じるときにトレイに最小化する</s:String>
|
||||
<s:String x:Key="Str_AutoRun">自動起動</s:String>
|
||||
<s:String x:Key="Str_AutoRunWorkspaceConfigPath">起動時にワークスペースファイルを自動読み込み</s:String>
|
||||
|
||||
@@ -120,10 +120,9 @@
|
||||
<s:String x:Key="Str_PlaySpeed">播放速度</s:String>
|
||||
<s:String x:Key="Str_WallpaperView">桌面投影</s:String>
|
||||
<s:String x:Key="Str_RenderSelectedOnly">仅渲染选中</s:String>
|
||||
<s:String x:Key="Str_UsePreciseHitTest">使用精确命中检测</s:String>
|
||||
<s:String x:Key="Str_UsePreciseHitTestTooltip">启用后将会按像素透明度来检测点击操作是否命中了模型</s:String>
|
||||
<s:String x:Key="Str_LogHitSlots">输出命中的插槽名称</s:String>
|
||||
<s:String x:Key="Str_LogHitSlotsTooltip">启用后将会在日志框内输出每一次点击操作命中的模型和插槽情况(按下 Ctrl 进行多选时不会输出)</s:String>
|
||||
<s:String x:Key="Str_HitTestLevel">命中检测准确度等级</s:String>
|
||||
<s:String x:Key="Str_LogHitSlots">输出命中检测结果的插槽名称</s:String>
|
||||
<s:String x:Key="Str_LogHitSlotsTooltip">启用后将会在日志框内输出每一次点击操作命中的模型和插槽情况</s:String>
|
||||
<s:String x:Key="Str_ShowAxis">显示坐标轴</s:String>
|
||||
<s:String x:Key="Str_BackgroundColor">背景颜色</s:String>
|
||||
<s:String x:Key="Str_BackgroundImagePath">背景图片路径</s:String>
|
||||
@@ -198,11 +197,13 @@
|
||||
|
||||
<s:String x:Key="Str_VideoFormat">视频格式</s:String>
|
||||
<s:String x:Key="Str_LoopPlay">循环播放</s:String>
|
||||
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp]
动图是否循环播放</s:String>
|
||||
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp/Apng]
动图是否循环播放</s:String>
|
||||
<s:String x:Key="Str_QualityParameter">质量参数</s:String>
|
||||
<s:String x:Key="Str_QualityParameterTooltip" xml:space="preserve">[Webp]
质量参数,取值范围 0-100,越高质量越好</s:String>
|
||||
<s:String x:Key="Str_LosslessParam">无损压缩</s:String>
|
||||
<s:String x:Key="Str_LosslessParamTooltip" xml:space="preserve">[Webp]
无损压缩,会忽略质量参数</s:String>
|
||||
<s:String x:Key="Str_ApngPred">预测器方法</s:String>
|
||||
<s:String x:Key="Str_ApngPredTooltip" xml:space="preserve">[Apng]
Pred 参数,取值范围 0-5,分别对应 none、sub、up、avg、paeth、mixed 几种不同的编码策略,
影响编码时间和文件大小</s:String>
|
||||
<s:String x:Key="Str_CrfParameter">CRF 参数</s:String>
|
||||
<s:String x:Key="Str_CrfParameterTooltip" xml:space="preserve">[Mp4/Webm/Mkv]
CRF 参数,取值范围 0-63,越小质量越高</s:String>
|
||||
<s:String x:Key="Str_ProfileParameter">Profile 参数</s:String>
|
||||
@@ -243,6 +244,7 @@
|
||||
|
||||
<s:String x:Key="Str_AppPreference">应用程序选项</s:String>
|
||||
<s:String x:Key="Str_Language">语言</s:String>
|
||||
<s:String x:Key="Str_AppSkin">皮肤</s:String>
|
||||
<s:String x:Key="Str_CloseToTray">关闭时最小化至托盘图标</s:String>
|
||||
<s:String x:Key="Str_AutoRun">开机自启</s:String>
|
||||
<s:String x:Key="Str_AutoRunWorkspaceConfigPath">自启动加载工作区文件</s:String>
|
||||
|
||||
85
SpineViewer/Resources/Theme.xaml
Normal file
85
SpineViewer/Resources/Theme.xaml
Normal file
@@ -0,0 +1,85 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:o="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
|
||||
xmlns:utils="clr-namespace:SpineViewer.Utils"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<utils:StringFormatMultiValueConverter x:Key="StrFmtCvter"/>
|
||||
<utils:BackgroundToForegroundConverter x:Key="Bg2FgCvter"/>
|
||||
|
||||
<Style x:Key="MyGridSplitterBaseStyle" TargetType="{x:Type GridSplitter}">
|
||||
<Setter Property="Background" Value="{DynamicResource SecondaryBorderBrush}"/>
|
||||
<Setter Property="ShowsPreview" Value="False"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="ResizeDirection" Value="Columns">
|
||||
<Setter Property="Width" Value="3"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalAlignment" Value="Stretch"/>
|
||||
</Trigger>
|
||||
<Trigger Property="ResizeDirection" Value="Rows">
|
||||
<Setter Property="Height" Value="3"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MyToggleButtonBaseStyle" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource ToggleButtonSwitch}">
|
||||
<Setter Property="hc:VisualElement.HighlightBrush" Value="{DynamicResource DarkSuccessBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MyListBoxBaseStyle" TargetType="{x:Type ListBox}" BasedOn="{StaticResource ListBoxBaseStyle}">
|
||||
<Setter Property="SelectionMode" Value="Extended"/>
|
||||
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="False"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Visible"/>
|
||||
<Setter Property="ItemContainerStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource ListBoxItemBaseStyle}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</Style>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MyListViewBaseStyle" TargetType="{x:Type ListView}" BasedOn="{StaticResource ListViewBaseStyle}">
|
||||
<Setter Property="SelectionMode" Value="Extended"/>
|
||||
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="False"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource ListViewItemBaseStyle.Small}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MyGroupBoxBaseStyle" TargetType="{x:Type GroupBox}" BasedOn="{StaticResource GroupBoxTab}">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="hc:TitleElement.Background" Value="Transparent"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MyLogRichTextBoxStyle" TargetType="{x:Type RichTextBox}" BasedOn="{StaticResource RichTextBoxBaseStyle}">
|
||||
<Setter Property="IsReadOnly" Value="True"/>
|
||||
<Setter Property="FontFamily" Value="Consolas"/>
|
||||
<Setter Property="Block.LineHeight" Value="3"/>
|
||||
<Setter Property="VerticalScrollBarVisibility" Value="Visible"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MyVerticalScrollViewerBaseStyle" TargetType="{x:Type ScrollViewer}" BasedOn="{StaticResource ScrollViewerNativeBaseStyle}">
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="ComputedVerticalScrollBarVisibility" Value="Visible">
|
||||
<Setter Property="Padding" Value="0 0 5 0"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type GridSplitter}" BasedOn="{StaticResource MyGridSplitterBaseStyle}"/>
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButtonBaseStyle}"/>
|
||||
<Style TargetType="{x:Type ListBox}" BasedOn="{StaticResource MyListBoxBaseStyle}"/>
|
||||
<Style TargetType="{x:Type ListView}" BasedOn="{StaticResource MyListViewBaseStyle}"/>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource MyGroupBoxBaseStyle}"/>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -7,7 +7,7 @@
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<Version>0.16.2</Version>
|
||||
<Version>0.16.7</Version>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
35
SpineViewer/Utils/BackgroundColorToForegroundColor.cs
Normal file
35
SpineViewer/Utils/BackgroundColorToForegroundColor.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SpineViewer.Utils
|
||||
{
|
||||
public class BackgroundToForegroundConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var color = Colors.White;
|
||||
if (value is SolidColorBrush brush)
|
||||
{
|
||||
color = brush.Color;
|
||||
}
|
||||
else if (value is Color c)
|
||||
{
|
||||
color = c;
|
||||
}
|
||||
|
||||
if (color.A < 128)
|
||||
return Brushes.Black;
|
||||
|
||||
// 计算亮度 (使用标准加权公式)
|
||||
double brightness = (0.299 * color.R + 0.587 * color.G + 0.114 * color.B) / 255.0;
|
||||
return brightness < 0.5 ? Brushes.White : Brushes.Black;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,8 +66,8 @@ namespace SpineViewer.Utils
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error("Failed to read json file {0}, {1}", path, ex.Message);
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to read json file {0}, {1}", path, ex.Message);
|
||||
MessagePopupService.Error($"Failed to read json file {path}, {ex.ToString()}");
|
||||
}
|
||||
}
|
||||
@@ -88,8 +88,8 @@ namespace SpineViewer.Utils
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error("Failed to save json file {0}, {1}", path, ex.Message);
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to save json file {0}, {1}", path, ex.Message);
|
||||
MessagePopupService.Error($"Failed to save json file {path}, {ex.ToString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Spine.Exporters;
|
||||
using SpineViewer.Extensions;
|
||||
using SpineViewer.Models;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.ViewModels.MainWindow;
|
||||
using System;
|
||||
using System.Collections;
|
||||
@@ -74,6 +75,16 @@ namespace SpineViewer.ViewModels.Exporters
|
||||
public uint MaxResolution { get => _maxResolution; set => SetProperty(ref _maxResolution, value); }
|
||||
protected uint _maxResolution = 2048;
|
||||
|
||||
public RelayCommand Cmd_SelectOutputDir => _cmd_SelectOutputDir ??= new(() =>
|
||||
{
|
||||
if (DialogService.ShowOpenFolderDialog(out var selectedPath))
|
||||
{
|
||||
_outputDir = selectedPath;
|
||||
OnPropertyChanged(nameof(OutputDir));
|
||||
}
|
||||
});
|
||||
protected RelayCommand _cmd_SelectOutputDir;
|
||||
|
||||
/// <summary>
|
||||
/// 使用提供的包围盒设置自动分辨率
|
||||
/// </summary>
|
||||
|
||||
@@ -18,26 +18,65 @@ namespace SpineViewer.ViewModels.Exporters
|
||||
{
|
||||
public class FFmpegVideoExporterViewModel(MainWindowViewModel vmMain) : VideoExporterViewModel(vmMain)
|
||||
{
|
||||
public ImmutableArray<FFmpegVideoExporter.VideoFormat> VideoFormatOptions { get; } = Enum.GetValues<FFmpegVideoExporter.VideoFormat>().ToImmutableArray();
|
||||
public static ImmutableArray<FFmpegVideoExporter.VideoFormat> VideoFormatOptions { get; } = Enum.GetValues<FFmpegVideoExporter.VideoFormat>().ToImmutableArray();
|
||||
|
||||
public FFmpegVideoExporter.VideoFormat Format { get => _format; set => SetProperty(ref _format, value); }
|
||||
public FFmpegVideoExporter.VideoFormat Format
|
||||
{
|
||||
get => _format;
|
||||
set
|
||||
{
|
||||
if (!SetProperty(ref _format, value))
|
||||
return;
|
||||
OnPropertyChanged(nameof(EnableParamLoop));
|
||||
OnPropertyChanged(nameof(EnableParamQuality));
|
||||
OnPropertyChanged(nameof(EnableParamLossless));
|
||||
OnPropertyChanged(nameof(EnableParamApngPred));
|
||||
OnPropertyChanged(nameof(EnableParamCrf));
|
||||
OnPropertyChanged(nameof(EnableParamProfile));
|
||||
}
|
||||
}
|
||||
protected FFmpegVideoExporter.VideoFormat _format = FFmpegVideoExporter.VideoFormat.Mp4;
|
||||
|
||||
public bool Loop { get => _loop; set => SetProperty(ref _loop, value); }
|
||||
protected bool _loop = true;
|
||||
|
||||
public bool EnableParamLoop =>
|
||||
_format == FFmpegVideoExporter.VideoFormat.Gif ||
|
||||
_format == FFmpegVideoExporter.VideoFormat.Webp ||
|
||||
_format == FFmpegVideoExporter.VideoFormat.Apng;
|
||||
|
||||
public int Quality { get => _quality; set => SetProperty(ref _quality, Math.Clamp(value, 0, 100)); }
|
||||
protected int _quality = 75;
|
||||
|
||||
public bool EnableParamQuality =>
|
||||
_format == FFmpegVideoExporter.VideoFormat.Webp;
|
||||
|
||||
public bool Lossless { get => _lossless; set => SetProperty(ref _lossless, value); }
|
||||
protected bool _lossless = false;
|
||||
|
||||
public bool EnableParamLossless =>
|
||||
_format == FFmpegVideoExporter.VideoFormat.Webp;
|
||||
|
||||
public int ApngPred { get => _apngPred; set => SetProperty(ref _apngPred, Math.Clamp(value, 0, 5)); }
|
||||
protected int _apngPred = 5;
|
||||
|
||||
public bool EnableParamApngPred =>
|
||||
_format == FFmpegVideoExporter.VideoFormat.Apng;
|
||||
|
||||
public int Crf { get => _crf; set => SetProperty(ref _crf, Math.Clamp(value, 0, 63)); }
|
||||
protected int _crf = 23;
|
||||
|
||||
public bool EnableParamCrf =>
|
||||
_format == FFmpegVideoExporter.VideoFormat.Mp4 ||
|
||||
_format == FFmpegVideoExporter.VideoFormat.Webm ||
|
||||
_format == FFmpegVideoExporter.VideoFormat.Mkv;
|
||||
|
||||
public int Profile { get => _profile; set => SetProperty(ref _profile, Math.Clamp(value, -1, 5)); }
|
||||
protected int _profile = 5;
|
||||
|
||||
public bool EnableParamProfile =>
|
||||
_format == FFmpegVideoExporter.VideoFormat.Mov;
|
||||
|
||||
private string FormatSuffix => $".{_format.ToString().ToLower()}";
|
||||
|
||||
protected override void Export(SpineObjectModel[] models)
|
||||
@@ -63,6 +102,7 @@ namespace SpineViewer.ViewModels.Exporters
|
||||
Loop = _loop,
|
||||
Quality = _quality,
|
||||
Lossless = _lossless,
|
||||
ApngPred = _apngPred,
|
||||
Crf = _crf,
|
||||
Profile = _profile,
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace SpineViewer.ViewModels.Exporters
|
||||
{
|
||||
public class FrameExporterViewModel(MainWindowViewModel vmMain) : BaseExporterViewModel(vmMain)
|
||||
{
|
||||
public ImmutableArray<SKEncodedImageFormat> FrameFormatOptions { get; } = Enum.GetValues<SKEncodedImageFormat>().ToImmutableArray();
|
||||
public static ImmutableArray<SKEncodedImageFormat> FrameFormatOptions { get; } = Enum.GetValues<SKEncodedImageFormat>().ToImmutableArray();
|
||||
|
||||
public SKEncodedImageFormat Format { get => _format; set => SetProperty(ref _format, value); }
|
||||
protected SKEncodedImageFormat _format = SKEncodedImageFormat.Png;
|
||||
|
||||
@@ -20,7 +20,6 @@ using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shell;
|
||||
using Spine.Interfaces;
|
||||
|
||||
namespace SpineViewer.ViewModels.MainWindow
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.Win32;
|
||||
using NLog;
|
||||
using Spine;
|
||||
using Spine.Implementations;
|
||||
using SpineViewer.Models;
|
||||
using SpineViewer.Natives;
|
||||
@@ -108,8 +109,9 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
DebugClippings = DebugClippings,
|
||||
|
||||
AppLanguage = AppLanguage,
|
||||
AppSkin = AppSkin,
|
||||
RenderSelectedOnly = RenderSelectedOnly,
|
||||
UsePreciseHitTest = UsePreciseHitTest,
|
||||
HitTestLevel = HitTestLevel,
|
||||
LogHitSlots = LogHitSlots,
|
||||
WallpaperView = WallpaperView,
|
||||
CloseToTray = CloseToTray,
|
||||
@@ -139,8 +141,9 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
DebugClippings = value.DebugClippings;
|
||||
|
||||
AppLanguage = value.AppLanguage;
|
||||
AppSkin = value.AppSkin;
|
||||
RenderSelectedOnly = value.RenderSelectedOnly;
|
||||
UsePreciseHitTest = value.UsePreciseHitTest;
|
||||
HitTestLevel = value.HitTestLevel;
|
||||
LogHitSlots = value.LogHitSlots;
|
||||
WallpaperView = value.WallpaperView;
|
||||
CloseToTray = value.CloseToTray;
|
||||
@@ -252,28 +255,38 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
|
||||
public static ImmutableArray<AppLanguage> AppLanguageOptions { get; } = Enum.GetValues<AppLanguage>().ToImmutableArray();
|
||||
|
||||
public static ImmutableArray<AppSkin> AppSkinOptions { get; } = Enum.GetValues<AppSkin>().ToImmutableArray();
|
||||
|
||||
public static ImmutableArray<HitTestLevel> HitTestLevelOptions { get; } = Enum.GetValues<HitTestLevel>().ToImmutableArray();
|
||||
|
||||
public AppLanguage AppLanguage
|
||||
{
|
||||
get => ((App)App.Current).Language;
|
||||
set => SetProperty(((App)App.Current).Language, value, v => ((App)App.Current).Language = v);
|
||||
}
|
||||
|
||||
public AppSkin AppSkin
|
||||
{
|
||||
get => ((App)App.Current).Skin;
|
||||
set => SetProperty(((App)App.Current).Skin, value, v => ((App)App.Current).Skin = v);
|
||||
}
|
||||
|
||||
public bool RenderSelectedOnly
|
||||
{
|
||||
get => _vmMain.SFMLRendererViewModel.RenderSelectedOnly;
|
||||
set => SetProperty(_vmMain.SFMLRendererViewModel.RenderSelectedOnly, value, v => _vmMain.SFMLRendererViewModel.RenderSelectedOnly = v);
|
||||
}
|
||||
|
||||
public bool UsePreciseHitTest
|
||||
public HitTestLevel HitTestLevel
|
||||
{
|
||||
get => _vmMain.SFMLRendererViewModel.UsePreciseHitTest;
|
||||
set => SetProperty(_vmMain.SFMLRendererViewModel.UsePreciseHitTest, value, v => _vmMain.SFMLRendererViewModel.UsePreciseHitTest = v);
|
||||
get => SpineExtension.HitTestLevel;
|
||||
set => SetProperty(SpineExtension.HitTestLevel, value, v => SpineExtension.HitTestLevel = v);
|
||||
}
|
||||
|
||||
public bool LogHitSlots
|
||||
{
|
||||
get => _vmMain.SFMLRendererViewModel.LogHitSlots;
|
||||
set => SetProperty(_vmMain.SFMLRendererViewModel.LogHitSlots, value, v => _vmMain.SFMLRendererViewModel.LogHitSlots = v);
|
||||
get => SpineExtension.LogHitSlots;
|
||||
set => SetProperty(SpineExtension.LogHitSlots, value, v => SpineExtension.LogHitSlots = v);
|
||||
}
|
||||
|
||||
public bool WallpaperView
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
{
|
||||
public class SFMLRendererViewModel : ObservableObject
|
||||
{
|
||||
public ImmutableArray<Stretch> StretchOptions { get; } = Enum.GetValues<Stretch>().ToImmutableArray();
|
||||
public static ImmutableArray<Stretch> StretchOptions { get; } = Enum.GetValues<Stretch>().ToImmutableArray();
|
||||
|
||||
/// <summary>
|
||||
/// 日志器
|
||||
@@ -47,11 +47,6 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
/// </summary>
|
||||
private readonly SFML.Graphics.VertexArray _selectedBackgroundVertices = new(SFML.Graphics.PrimitiveType.Quads, 4); // XXX: 暂时未使用 Dispose 释放
|
||||
|
||||
/// <summary>
|
||||
/// 预览画面坐标轴颜色
|
||||
/// </summary>
|
||||
private static readonly SFML.Graphics.Color _axisColor = new(220, 220, 220);
|
||||
|
||||
/// <summary>
|
||||
/// 坐标轴顶点缓冲区
|
||||
/// </summary>
|
||||
@@ -178,10 +173,21 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get => Color.FromRgb(_backgroundColor.R, _backgroundColor.G, _backgroundColor.B);
|
||||
set => SetProperty(BackgroundColor, value, v => _backgroundColor = new(value.R, value.G, value.B));
|
||||
set
|
||||
{
|
||||
if (!SetProperty(BackgroundColor, value, v => _backgroundColor = new(value.R, value.G, value.B)))
|
||||
return;
|
||||
var b = (0.299 * value.R + 0.587 * value.G + 0.114 * value.B) / 255.0;
|
||||
_axisColor = b < 0.5 ? SFML.Graphics.Color.White : SFML.Graphics.Color.Black;
|
||||
}
|
||||
}
|
||||
private SFML.Graphics.Color _backgroundColor = new(105, 105, 105);
|
||||
|
||||
/// <summary>
|
||||
/// 预览画面坐标轴颜色
|
||||
/// </summary>
|
||||
private SFML.Graphics.Color _axisColor = SFML.Graphics.Color.White;
|
||||
|
||||
public string BackgroundImagePath
|
||||
{
|
||||
get => _backgroundImagePath;
|
||||
@@ -250,26 +256,6 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
}
|
||||
private bool _renderSelectedOnly;
|
||||
|
||||
/// <summary>
|
||||
/// 启用精确命中测试
|
||||
/// </summary>
|
||||
public bool UsePreciseHitTest
|
||||
{
|
||||
get => _usePreciseHitTest;
|
||||
set => SetProperty(ref _usePreciseHitTest, value);
|
||||
}
|
||||
private bool _usePreciseHitTest;
|
||||
|
||||
/// <summary>
|
||||
/// 启用完整的命中测试并在日志中输出命中测试的插槽结果
|
||||
/// </summary>
|
||||
public bool LogHitSlots
|
||||
{
|
||||
get => _logHitSlots;
|
||||
set => SetProperty(ref _logHitSlots, value);
|
||||
}
|
||||
private bool _logHitSlots;
|
||||
|
||||
/// <summary>
|
||||
/// 启用桌面投影
|
||||
/// </summary>
|
||||
@@ -368,22 +354,9 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
if (_renderSelectedOnly)
|
||||
{
|
||||
bool hit = false;
|
||||
if (!_logHitSlots)
|
||||
{
|
||||
// 只在被选中的对象里判断是否有效命中
|
||||
hit = _models.Any(m => m.IsSelected && m.HitTest(src.X, src.Y, _usePreciseHitTest));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var sp in _models.Where(m => m.IsSelected))
|
||||
{
|
||||
var slotNames = sp.HitTestFull(src.X, src.Y, _usePreciseHitTest);
|
||||
if (slotNames.Length <= 0) continue;
|
||||
|
||||
hit = true;
|
||||
_logger.Debug("Model Hit ({0}): [{1}]", sp.Name, string.Join(", ", slotNames));
|
||||
}
|
||||
}
|
||||
// 只在被选中的对象里判断是否有效命中
|
||||
hit = _models.Any(m => m.IsSelected && m.HitTest(src.X, src.Y));
|
||||
|
||||
// 如果没点到被选中的模型, 则不允许拖动
|
||||
if (!hit) _draggingSrc = null;
|
||||
@@ -395,40 +368,19 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
// 没按 Ctrl 的情况下, 如果命中了已选中对象, 则就算普通命中
|
||||
bool hit = false;
|
||||
|
||||
if (!_logHitSlots)
|
||||
foreach (var sp in _models.Where(m => m.IsShown))
|
||||
{
|
||||
foreach (var sp in _models.Where(m => m.IsShown))
|
||||
if (!sp.HitTest(src.X, src.Y)) continue;
|
||||
|
||||
hit = true;
|
||||
|
||||
// 如果点到了没被选中的东西, 则清空原先选中的, 改为只选中这一次点的
|
||||
if (!sp.IsSelected)
|
||||
{
|
||||
if (!sp.HitTest(src.X, src.Y, _usePreciseHitTest)) continue;
|
||||
|
||||
hit = true;
|
||||
|
||||
// 如果点到了没被选中的东西, 则清空原先选中的, 改为只选中这一次点的
|
||||
if (!sp.IsSelected)
|
||||
{
|
||||
RequestSelectionChanging?.Invoke(this, new(NotifyCollectionChangedAction.Reset));
|
||||
RequestSelectionChanging?.Invoke(this, new(NotifyCollectionChangedAction.Add, sp));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var sp in _models.Where(m => m.IsShown))
|
||||
{
|
||||
var slotNames = sp.HitTestFull(src.X, src.Y, _usePreciseHitTest);
|
||||
if (slotNames.Length <= 0) continue;
|
||||
|
||||
// 如果点到了没被选中的东西, 则清空原先选中的, 改为只选中这一次点的
|
||||
// 仅判断顶层对象 (首次命中)
|
||||
if (!hit && !sp.IsSelected)
|
||||
{
|
||||
RequestSelectionChanging?.Invoke(this, new(NotifyCollectionChangedAction.Reset));
|
||||
RequestSelectionChanging?.Invoke(this, new(NotifyCollectionChangedAction.Add, sp));
|
||||
}
|
||||
hit = true;
|
||||
_logger.Debug("Model Hit ({0}): [{1}]", sp.Name, string.Join(", ", slotNames));
|
||||
RequestSelectionChanging?.Invoke(this, new(NotifyCollectionChangedAction.Reset));
|
||||
RequestSelectionChanging?.Invoke(this, new(NotifyCollectionChangedAction.Add, sp));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果点了空白的地方, 就清空选中列表
|
||||
@@ -437,7 +389,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
else
|
||||
{
|
||||
// 按下 Ctrl 的情况就执行多选, 并且点空白处也不会清空选中, 如果点击了本来就是选中的则取消选中
|
||||
if (_models.FirstOrDefault(m => m.IsShown && m.HitTest(src.X, src.Y, _usePreciseHitTest), null) is SpineObjectModel sp)
|
||||
if (_models.FirstOrDefault(m => m.IsShown && m.HitTest(src.X, src.Y), null) is SpineObjectModel sp)
|
||||
{
|
||||
if (sp.IsSelected)
|
||||
RequestSelectionChanging?.Invoke(this, new(NotifyCollectionChangedAction.Remove, sp));
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
return;
|
||||
if (!DialogService.ShowOpenFileDialog(out var atlasFileName, AppResource.Str_OpenAtlasFileTitle))
|
||||
return;
|
||||
AddSpineObject(skelFileName, atlasFileName);
|
||||
InsertSpineObject(skelFileName, atlasFileName);
|
||||
_logger.LogCurrentProcessMemoryUsage();
|
||||
}
|
||||
|
||||
@@ -218,8 +218,8 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message);
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,8 +279,8 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
catch (Exception ex)
|
||||
{
|
||||
error++;
|
||||
_logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message);
|
||||
_logger.Trace(ex.ToString());
|
||||
_logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,7 +479,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
}
|
||||
else if (validPaths.Count > 0)
|
||||
{
|
||||
AddSpineObject(validPaths[0]);
|
||||
InsertSpineObject(validPaths[0]);
|
||||
_logger.LogCurrentProcessMemoryUsage();
|
||||
}
|
||||
}
|
||||
@@ -506,7 +506,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
var skelPath = paths[i];
|
||||
reporter.ProgressText = $"[{i}/{totalCount}] {skelPath}";
|
||||
|
||||
if (AddSpineObject(skelPath))
|
||||
if (InsertSpineObject(skelPath))
|
||||
success++;
|
||||
else
|
||||
error++;
|
||||
@@ -529,7 +529,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
/// 安全地在列表头添加一个模型, 发生错误会输出日志
|
||||
/// </summary>
|
||||
/// <returns>是否添加成功</returns>
|
||||
private bool AddSpineObject(string skelPath, string? atlasPath = null)
|
||||
private bool InsertSpineObject(string skelPath, string? atlasPath = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -599,7 +599,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
}
|
||||
else if (models.Count > 0)
|
||||
{
|
||||
AddSpineObject(models[0]);
|
||||
InsertSpineObject(models[0]);
|
||||
_logger.LogCurrentProcessMemoryUsage();
|
||||
}
|
||||
}
|
||||
@@ -620,10 +620,10 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
{
|
||||
if (ct.IsCancellationRequested) break;
|
||||
|
||||
var cfg = models[i];
|
||||
var cfg = models[totalCount - 1 - i];
|
||||
reporter.ProgressText = $"[{i}/{totalCount}] {cfg}";
|
||||
|
||||
if (AddSpineObject(cfg))
|
||||
if (InsertSpineObject(cfg))
|
||||
success++;
|
||||
else
|
||||
error++;
|
||||
@@ -653,7 +653,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
/// 安全地在列表头添加一个模型, 发生错误会输出日志
|
||||
/// </summary>
|
||||
/// <returns>是否添加成功</returns>
|
||||
private bool AddSpineObject(SpineObjectWorkspaceConfigModel cfg)
|
||||
private bool InsertSpineObject(SpineObjectWorkspaceConfigModel cfg)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
||||
private readonly ObservableCollection<SlotViewModel> _slots = [];
|
||||
private readonly ObservableCollection<AnimationTrackViewModel> _animationTracks = [];
|
||||
|
||||
public ImmutableArray<ISkeleton.Physics> PhysicsOptions { get; } = Enum.GetValues<ISkeleton.Physics>().ToImmutableArray();
|
||||
public static ImmutableArray<ISkeleton.Physics> PhysicsOptions { get; } = Enum.GetValues<ISkeleton.Physics>().ToImmutableArray();
|
||||
|
||||
public SpineObjectModel[] SelectedObjects
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
d:DataContext="{d:DesignInstance Type=viewmodels:AboutDialogViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{DynamicResource Str_Abount}"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
Height="300"
|
||||
Width="500"
|
||||
ShowInTaskbar="False"
|
||||
@@ -26,25 +27,31 @@
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 程序版本 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ProgremVersion}"/>
|
||||
<Label Grid.Row="0" Grid.Column="1" Content="{Binding ProgramVersion}"/>
|
||||
<StackPanel Grid.IsSharedSizeScope="True">
|
||||
<!-- 程序版本 -->
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ProgremVersion}"/>
|
||||
<Label Grid.Column="1" Content="{Binding ProgramVersion}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 项目地址 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_ProjectUrl}"/>
|
||||
<Label Grid.Row="1" Grid.Column="1">
|
||||
<Hyperlink NavigateUri="{Binding ProjectUrl}" Command="{Binding Cmd_OpenProjectUrl}">
|
||||
<Run Text="{Binding ProjectUrl, Mode=OneWay}"/>
|
||||
</Hyperlink>
|
||||
</Label>
|
||||
<!-- 项目地址 -->
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ProjectUrl}"/>
|
||||
<Label Grid.Column="1">
|
||||
<Hyperlink NavigateUri="{Binding ProjectUrl}" Command="{Binding Cmd_OpenProjectUrl}">
|
||||
<Run Text="{Binding ProjectUrl, Mode=OneWay}"/>
|
||||
</Hyperlink>
|
||||
</Label>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -8,6 +10,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
@@ -22,6 +25,14 @@ namespace SpineViewer.Views
|
||||
public AboutDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += AboutDialog_SourceInitialized;
|
||||
}
|
||||
|
||||
private void AboutDialog_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
d:DataContext="{d:DesignInstance Type=viewmodels:DiagnosticsDialogViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{DynamicResource Str_Diagnostics}"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
Height="450"
|
||||
Width="800"
|
||||
ShowInTaskbar="False"
|
||||
@@ -21,7 +22,7 @@
|
||||
<Button Width="120" Content="{DynamicResource Str_CopyDiagnosticsInfo}" Command="{Binding Cmd_CopyToClipboard}"/>
|
||||
</Border>
|
||||
|
||||
<Border Grid.IsSharedSizeScope="True">
|
||||
<Border>
|
||||
<Border.Resources>
|
||||
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource LabelDefault}">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
@@ -31,8 +32,8 @@
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
</Style>
|
||||
</Border.Resources>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="30 10">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Grid.IsSharedSizeScope="True" Margin="30 10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
@@ -134,7 +135,6 @@
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding HandyControlVersion, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -8,6 +10,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
@@ -22,6 +25,14 @@ namespace SpineViewer.Views
|
||||
public DiagnosticsDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += DiagnosticsDialog_SourceInitialized;
|
||||
}
|
||||
|
||||
private void DiagnosticsDialog_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:local="clr-namespace:SpineViewer.Views.ExporterDialogs"
|
||||
xmlns:utils="clr-namespace:SpineViewer.Utils"
|
||||
xmlns:exporters="clr-namespace:SpineViewer.ViewModels.Exporters"
|
||||
d:DataContext="{d:DesignInstance Type=exporters:CustomFFmpegExporterViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{DynamicResource Str_CustomFFmpegExporterTitle}"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
Width="450"
|
||||
Height="800"
|
||||
ShowInTaskbar="False"
|
||||
@@ -39,173 +41,235 @@
|
||||
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ComboBoxBaseStyle}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButton}">
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButtonBaseStyle}">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource GroupBoxTab}">
|
||||
<Setter Property="BorderBrush" Value="LightGray"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="hc:TitleElement.Background" Value="Transparent"/>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource MyGroupBoxBaseStyle}">
|
||||
<Setter Property="Margin" Value="0 5 0 10"/>
|
||||
</Style>
|
||||
</Border.Resources>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
|
||||
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
|
||||
<StackPanel Grid.IsSharedSizeScope="True">
|
||||
<GroupBox Header="{DynamicResource Str_ExportBaseArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 水平分辨率 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ResolutionX}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionX, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ResolutionX}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionX, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 垂直分辨率 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_ResolutionY}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionY, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ResolutionY}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionY, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 是否导出单个 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 输出文件夹 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
<DockPanel Grid.Row="3" Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Click="ButtonSelectOutputDir_Click"/>
|
||||
<TextBox Text="{Binding OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
<DockPanel Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Command="{Binding Cmd_SelectOutputDir}"/>
|
||||
<TextBox Text="{Binding OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 背景颜色 -->
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
<DockPanel Grid.Row="4" Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Click="ButtonPickColor_Click">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding BackgroundColor}"/>
|
||||
</Button.Background>
|
||||
</Button>
|
||||
<TextBox x:Name="_colorTextBox" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
<DockPanel Grid.Column="1">
|
||||
<Border DockPanel.Dock="Right">
|
||||
<Popup x:Name="_colorPopup" Placement="Right">
|
||||
<hc:ColorPicker Confirmed="ColorPicker_Confirmed" Canceled="ColorPicker_Canceled"/>
|
||||
</Popup>
|
||||
</Border>
|
||||
<Border DockPanel.Dock="Right" Background="White" CornerRadius="{DynamicResource DefaultCornerRadius}">
|
||||
<Button Content="..."
|
||||
Foreground="{Binding BackgroundColor, Converter={StaticResource Bg2FgCvter}}"
|
||||
Click="ButtonPickColor_Click">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding BackgroundColor}"/>
|
||||
</Button.Background>
|
||||
</Button>
|
||||
</Border>
|
||||
<TextBox Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 四周边距 -->
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- 自动分辨率 -->
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource Str_AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<ToggleButton Grid.Row="6" Grid.Column="1" IsChecked="{Binding AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 最大分辨率 -->
|
||||
<Label Grid.Row="7" Grid.Column="0" Content="{DynamicResource Str_MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="1" Text="{Binding MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
</Grid>
|
||||
<Grid IsEnabled="{Binding AutoResolution}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="{DynamicResource Str_ExportVideoArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 导出时长 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 导出帧率 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_Fps}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Fps}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Fps}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Fps}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 导出速度 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 是否保留最后一帧 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
|
||||
<ToggleButton Grid.Row="3" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="{DynamicResource Str_ExportOtherArgs}">
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource LabelDefault}">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource TextBoxBaseStyle}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ComboBoxBaseStyle}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButton}">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="60"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 导出格式 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_FFmpegFormat}" ToolTip="{DynamicResource Str_FFmpegFormatTooltip}"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Format}" ToolTip="{DynamicResource Str_FFmpegFormatTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_FFmpegFormat}" ToolTip="{DynamicResource Str_FFmpegFormatTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Format}" ToolTip="{DynamicResource Str_FFmpegFormatTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 编码器 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_FFmpegCodec}" ToolTip="{DynamicResource Str_FFmpegCodecTooltip}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Codec}" ToolTip="{DynamicResource Str_FFmpegCodecTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_FFmpegCodec}" ToolTip="{DynamicResource Str_FFmpegCodecTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Codec}" ToolTip="{DynamicResource Str_FFmpegCodecTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 像素格式 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_FFmpegPixelFormat}" ToolTip="{DynamicResource Str_FFmpegPixelFormatTooltip}"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding PixelFormat}" ToolTip="{DynamicResource Str_FFmpegPixelFormatTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_FFmpegPixelFormat}" ToolTip="{DynamicResource Str_FFmpegPixelFormatTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding PixelFormat}" ToolTip="{DynamicResource Str_FFmpegPixelFormatTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 比特率 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_FFmpegBitrate}" ToolTip="{DynamicResource Str_FFmpegBitrateTooltip}"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Bitrate}" ToolTip="{DynamicResource Str_FFmpegBitrateTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_FFmpegBitrate}" ToolTip="{DynamicResource Str_FFmpegBitrateTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Bitrate}" ToolTip="{DynamicResource Str_FFmpegBitrateTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 滤镜 -->
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_FFmpegFilter}" ToolTip="{DynamicResource Str_FFmpegFilterTooltip}"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Filter}" ToolTip="{DynamicResource Str_FFmpegFilterTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_FFmpegFilter}" ToolTip="{DynamicResource Str_FFmpegFilterTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Filter}" ToolTip="{DynamicResource Str_FFmpegFilterTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 自定义参数 -->
|
||||
<Label Grid.Row="5" Grid.Column="0"
|
||||
VerticalAlignment="Top"
|
||||
Content="{DynamicResource Str_FFmpegCustomArgs}"
|
||||
ToolTip="{DynamicResource Str_FFmpegCustomArgsTooltip}"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalContentAlignment="Top"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
Text="{Binding CustomArgs}"
|
||||
ToolTip="{DynamicResource Str_FFmpegCustomArgsTooltip}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label VerticalAlignment="Top"
|
||||
Content="{DynamicResource Str_FFmpegCustomArgs}"
|
||||
ToolTip="{DynamicResource Str_FFmpegCustomArgsTooltip}"/>
|
||||
<TextBox Grid.Column="1"
|
||||
Height="75"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalContentAlignment="Top"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
Text="{Binding CustomArgs}"
|
||||
ToolTip="{DynamicResource Str_FFmpegCustomArgsTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.ViewModels.Exporters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -10,6 +12,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
@@ -24,6 +27,14 @@ namespace SpineViewer.Views.ExporterDialogs
|
||||
public CustomFFmpegExporterDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += CustomFFmpegExporterDialog_SourceInitialized;
|
||||
}
|
||||
|
||||
private void CustomFFmpegExporterDialog_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
|
||||
private void ButtonOK_Click(object sender, RoutedEventArgs e)
|
||||
@@ -42,18 +53,22 @@ namespace SpineViewer.Views.ExporterDialogs
|
||||
DialogResult = false;
|
||||
}
|
||||
|
||||
private void ButtonSelectOutputDir_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DialogService.ShowOpenFolderDialog(out var selectedPath))
|
||||
{
|
||||
var vm = (CustomFFmpegExporterViewModel)DataContext;
|
||||
vm.OutputDir = selectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonPickColor_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = !_colorPopup.IsOpen;
|
||||
}
|
||||
|
||||
private void ColorPicker_Confirmed(object sender, HandyControl.Data.FunctionEventArgs<Color> e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
var color = e.Info;
|
||||
var vm = (BaseExporterViewModel)DataContext;
|
||||
vm.BackgroundColor = color;
|
||||
}
|
||||
|
||||
private void ColorPicker_Canceled(object sender, EventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:local="clr-namespace:SpineViewer.Views.ExporterDialogs"
|
||||
xmlns:exporters="clr-namespace:SpineViewer.ViewModels.Exporters"
|
||||
d:DataContext="{d:DesignInstance Type=exporters:FFmpegVideoExporterViewModel}"
|
||||
xmlns:utils="clr-namespace:SpineViewer.Utils"
|
||||
xmlns:vmexp="clr-namespace:SpineViewer.ViewModels.Exporters"
|
||||
d:DataContext="{d:DesignInstance Type=vmexp:FFmpegVideoExporterViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{DynamicResource Str_FFmpegVideoExporterTitle}"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
Width="450"
|
||||
Height="750"
|
||||
ShowInTaskbar="False"
|
||||
@@ -39,150 +41,236 @@
|
||||
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ComboBoxBaseStyle}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButton}">
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButtonBaseStyle}">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource GroupBoxTab}">
|
||||
<Setter Property="BorderBrush" Value="LightGray"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="hc:TitleElement.Background" Value="Transparent"/>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource MyGroupBoxBaseStyle}">
|
||||
<Setter Property="Margin" Value="0 5 0 10"/>
|
||||
</Style>
|
||||
</Border.Resources>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
|
||||
<StackPanel Grid.IsSharedSizeScope="True">
|
||||
<GroupBox Header="{DynamicResource Str_ExportBaseArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 水平分辨率 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ResolutionX}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionX, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ResolutionX}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionX, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 垂直分辨率 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_ResolutionY}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionY, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ResolutionY}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionY, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 是否导出单个 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 输出文件夹 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
<DockPanel Grid.Row="3" Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Click="ButtonSelectOutputDir_Click"/>
|
||||
<TextBox Text="{Binding OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
<DockPanel Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Command="{Binding Cmd_SelectOutputDir}"/>
|
||||
<TextBox Text="{Binding OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 背景颜色 -->
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
<DockPanel Grid.Row="4" Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Click="ButtonPickColor_Click">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding BackgroundColor}"/>
|
||||
</Button.Background>
|
||||
</Button>
|
||||
<TextBox x:Name="_colorTextBox" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
<DockPanel Grid.Column="1">
|
||||
<Border DockPanel.Dock="Right">
|
||||
<Popup x:Name="_colorPopup" Placement="Right">
|
||||
<hc:ColorPicker Confirmed="ColorPicker_Confirmed" Canceled="ColorPicker_Canceled"/>
|
||||
</Popup>
|
||||
</Border>
|
||||
<Border DockPanel.Dock="Right" Background="White" CornerRadius="{DynamicResource DefaultCornerRadius}">
|
||||
<Button Content="..."
|
||||
Foreground="{Binding BackgroundColor, Converter={StaticResource Bg2FgCvter}}"
|
||||
Click="ButtonPickColor_Click">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding BackgroundColor}"/>
|
||||
</Button.Background>
|
||||
</Button>
|
||||
</Border>
|
||||
<TextBox Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 四周边距 -->
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- 自动分辨率 -->
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource Str_AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<ToggleButton Grid.Row="6" Grid.Column="1" IsChecked="{Binding AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 最大分辨率 -->
|
||||
<Label Grid.Row="7" Grid.Column="0" Content="{DynamicResource Str_MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="1" Text="{Binding MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
</Grid>
|
||||
<Grid IsEnabled="{Binding AutoResolution}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="{DynamicResource Str_ExportVideoArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 导出时长 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 导出帧率 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_Fps}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Fps}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Fps}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Fps}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 导出速度 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 是否保留最后一帧 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
|
||||
<ToggleButton Grid.Row="3" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="{DynamicResource Str_ExportOtherArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 视频格式 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_VideoFormat}"/>
|
||||
<ComboBox Grid.Row="0" Grid.Column="1" SelectedItem="{Binding Format}" ItemsSource="{Binding VideoFormatOptions}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_VideoFormat}"/>
|
||||
<ComboBox Grid.Column="1" SelectedItem="{Binding Format}" ItemsSource="{x:Static vmexp:FFmpegVideoExporterViewModel.VideoFormatOptions}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 动图是否循环 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_LoopPlay}" ToolTip="{DynamicResource Str_LoopPlayTooltip}"/>
|
||||
<ToggleButton Grid.Row="1" Grid.Column="1" IsChecked="{Binding Loop}" ToolTip="{DynamicResource Str_LoopPlayTooltip}"/>
|
||||
<Grid Visibility="{Binding EnableParamLoop, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_LoopPlay}" ToolTip="{DynamicResource Str_LoopPlayTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding Loop}" ToolTip="{DynamicResource Str_LoopPlayTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 质量参数 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_QualityParameter}" ToolTip="{DynamicResource Str_QualityParameterTooltip}"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Quality}" ToolTip="{DynamicResource Str_QualityParameterTooltip}"/>
|
||||
<Grid Visibility="{Binding EnableParamQuality, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_QualityParameter}" ToolTip="{DynamicResource Str_QualityParameterTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Quality}" ToolTip="{DynamicResource Str_QualityParameterTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 无损压缩 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_LosslessParam}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/>
|
||||
<ToggleButton Grid.Row="3" Grid.Column="1" IsChecked="{Binding Lossless}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/>
|
||||
<Grid Visibility="{Binding EnableParamLossless, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_LosslessParam}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding Lossless}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 预测器方法 -->
|
||||
<Grid Visibility="{Binding EnableParamApngPred, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ApngPred}" ToolTip="{DynamicResource Str_ApngPredTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding ApngPred}" ToolTip="{DynamicResource Str_ApngPredTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- CRF 参数 -->
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_CrfParameter}" ToolTip="{DynamicResource Str_CrfParameterTooltip}"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Crf}" ToolTip="{DynamicResource Str_CrfParameterTooltip}"/>
|
||||
<Grid Visibility="{Binding EnableParamCrf, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_CrfParameter}" ToolTip="{DynamicResource Str_CrfParameterTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Crf}" ToolTip="{DynamicResource Str_CrfParameterTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Profile 参数 -->
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_ProfileParameter}" ToolTip="{DynamicResource Str_ProfileParameterTooltip}"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Profile}" ToolTip="{DynamicResource Str_ProfileParameterTooltip}"/>
|
||||
</Grid>
|
||||
<Grid Visibility="{Binding EnableParamProfile, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ProfileParameter}" ToolTip="{DynamicResource Str_ProfileParameterTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Profile}" ToolTip="{DynamicResource Str_ProfileParameterTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.ViewModels.Exporters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -10,6 +12,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
@@ -24,6 +27,14 @@ namespace SpineViewer.Views.ExporterDialogs
|
||||
public FFmpegVideoExporterDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += FFmpegVideoExporterDialog_SourceInitialized;
|
||||
}
|
||||
|
||||
private void FFmpegVideoExporterDialog_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
|
||||
private void ButtonOK_Click(object sender, RoutedEventArgs e)
|
||||
@@ -42,18 +53,22 @@ namespace SpineViewer.Views.ExporterDialogs
|
||||
DialogResult = false;
|
||||
}
|
||||
|
||||
private void ButtonSelectOutputDir_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DialogService.ShowOpenFolderDialog(out var selectedPath))
|
||||
{
|
||||
var vm = (FFmpegVideoExporterViewModel)DataContext;
|
||||
vm.OutputDir = selectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonPickColor_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = !_colorPopup.IsOpen;
|
||||
}
|
||||
|
||||
private void ColorPicker_Confirmed(object sender, HandyControl.Data.FunctionEventArgs<Color> e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
var color = e.Info;
|
||||
var vm = (BaseExporterViewModel)DataContext;
|
||||
vm.BackgroundColor = color;
|
||||
}
|
||||
|
||||
private void ColorPicker_Canceled(object sender, EventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:local="clr-namespace:SpineViewer.Views"
|
||||
xmlns:utils="clr-namespace:SpineViewer.Utils"
|
||||
xmlns:vmexp="clr-namespace:SpineViewer.ViewModels.Exporters"
|
||||
d:DataContext="{d:DesignInstance Type=vmexp:FrameExporterViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{DynamicResource Str_FrameExporterTitle}"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
Width="450"
|
||||
Height="480"
|
||||
ShowInTaskbar="False"
|
||||
@@ -39,99 +41,142 @@
|
||||
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ComboBoxBaseStyle}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButton}">
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButtonBaseStyle}">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource GroupBoxTab}">
|
||||
<Setter Property="BorderBrush" Value="LightGray"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="hc:TitleElement.Background" Value="Transparent"/>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource MyGroupBoxBaseStyle}">
|
||||
<Setter Property="Margin" Value="0 5 0 10"/>
|
||||
</Style>
|
||||
</Border.Resources>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
|
||||
<StackPanel Grid.IsSharedSizeScope="True">
|
||||
<GroupBox Header="{DynamicResource Str_ExportBaseArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 水平分辨率 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ResolutionX}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionX, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ResolutionX}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionX, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 垂直分辨率 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_ResolutionY}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionY, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ResolutionY}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionY, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 是否导出单个 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 输出文件夹 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
<DockPanel Grid.Row="3" Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Click="ButtonSelectOutputDir_Click"/>
|
||||
<TextBox Text="{Binding OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
<DockPanel Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Command="{Binding Cmd_SelectOutputDir}"/>
|
||||
<TextBox Text="{Binding OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 背景颜色 -->
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
<DockPanel Grid.Row="4" Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Click="ButtonPickColor_Click">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding BackgroundColor}"/>
|
||||
</Button.Background>
|
||||
</Button>
|
||||
<TextBox x:Name="_colorTextBox" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
<DockPanel Grid.Column="1">
|
||||
<Border DockPanel.Dock="Right">
|
||||
<Popup x:Name="_colorPopup" Placement="Right">
|
||||
<hc:ColorPicker Confirmed="ColorPicker_Confirmed" Canceled="ColorPicker_Canceled"/>
|
||||
</Popup>
|
||||
</Border>
|
||||
<Border DockPanel.Dock="Right" Background="White" CornerRadius="{DynamicResource DefaultCornerRadius}">
|
||||
<Button Content="..."
|
||||
Foreground="{Binding BackgroundColor, Converter={StaticResource Bg2FgCvter}}"
|
||||
Click="ButtonPickColor_Click">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding BackgroundColor}"/>
|
||||
</Button.Background>
|
||||
</Button>
|
||||
</Border>
|
||||
<TextBox Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 四周边距 -->
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- 自动分辨率 -->
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource Str_AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<ToggleButton Grid.Row="6" Grid.Column="1" IsChecked="{Binding AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 最大分辨率 -->
|
||||
<Label Grid.Row="7" Grid.Column="0" Content="{DynamicResource Str_MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="1" Text="{Binding MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
</Grid>
|
||||
<Grid IsEnabled="{Binding AutoResolution}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="{DynamicResource Str_ExportOtherArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 图像格式 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ImageFormat}"/>
|
||||
<ComboBox Grid.Row="0" Grid.Column="1" SelectedItem="{Binding Format}" ItemsSource="{Binding FrameFormatOptions}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ImageFormat}"/>
|
||||
<ComboBox Grid.Column="1" SelectedItem="{Binding Format}" ItemsSource="{x:Static vmexp:FrameExporterViewModel.FrameFormatOptions}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 图像质量 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_ImageQuality}" ToolTip="{DynamicResource Str_ImageQualityTooltip}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Quality}" ToolTip="{DynamicResource Str_ImageQualityTooltip}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ImageQuality}" ToolTip="{DynamicResource Str_ImageQualityTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Quality}" ToolTip="{DynamicResource Str_ImageQualityTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.ViewModels.Exporters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -10,6 +12,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
@@ -24,6 +27,14 @@ namespace SpineViewer.Views.ExporterDialogs
|
||||
public FrameExporterDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += FrameExporterDialog_SourceInitialized;
|
||||
}
|
||||
|
||||
private void FrameExporterDialog_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
|
||||
private void ButtonOK_Click(object sender, RoutedEventArgs e)
|
||||
@@ -42,18 +53,22 @@ namespace SpineViewer.Views.ExporterDialogs
|
||||
DialogResult = false;
|
||||
}
|
||||
|
||||
private void ButtonSelectOutputDir_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DialogService.ShowOpenFolderDialog(out var selectedPath))
|
||||
{
|
||||
var vm = (FrameExporterViewModel)DataContext;
|
||||
vm.OutputDir = selectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonPickColor_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
_colorPopup.IsOpen = !_colorPopup.IsOpen;
|
||||
}
|
||||
|
||||
private void ColorPicker_Confirmed(object sender, HandyControl.Data.FunctionEventArgs<Color> e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
var color = e.Info;
|
||||
var vm = (BaseExporterViewModel)DataContext;
|
||||
vm.BackgroundColor = color;
|
||||
}
|
||||
|
||||
private void ColorPicker_Canceled(object sender, EventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:local="clr-namespace:SpineViewer.Views"
|
||||
xmlns:utils="clr-namespace:SpineViewer.Utils"
|
||||
xmlns:vmexp="clr-namespace:SpineViewer.ViewModels.Exporters"
|
||||
d:DataContext="{d:DesignInstance Type=vmexp:FrameSequenceExporterViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{DynamicResource Str_FrameSequenceExporterTitle}"
|
||||
Title="{DynamicResource Str_FrameSequenceExporterTitle}"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
Width="450"
|
||||
Height="550"
|
||||
ShowInTaskbar="False"
|
||||
@@ -39,109 +41,162 @@
|
||||
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ComboBoxBaseStyle}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButton}">
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButtonBaseStyle}">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource GroupBoxTab}">
|
||||
<Setter Property="BorderBrush" Value="LightGray"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="hc:TitleElement.Background" Value="Transparent"/>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource MyGroupBoxBaseStyle}">
|
||||
<Setter Property="Margin" Value="0 5 0 10"/>
|
||||
</Style>
|
||||
</Border.Resources>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
|
||||
<StackPanel Grid.IsSharedSizeScope="True">
|
||||
<GroupBox Header="{DynamicResource Str_ExportBaseArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 水平分辨率 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ResolutionX}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionX, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ResolutionX}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionX, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 垂直分辨率 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_ResolutionY}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionY, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ResolutionY}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{Binding ResolutionY, Mode=OneWay}" ToolTip="{DynamicResource Str_ResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 是否导出单个 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding ExportSingle}" ToolTip="{DynamicResource Str_ExportSingleTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 输出文件夹 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
<DockPanel Grid.Row="3" Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Click="ButtonSelectOutputDir_Click"/>
|
||||
<TextBox Text="{Binding OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
<DockPanel Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Command="{Binding Cmd_SelectOutputDir}"/>
|
||||
<TextBox Text="{Binding OutputDir}" ToolTip="{DynamicResource Str_OutputDirTooltip}"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 背景颜色 -->
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
<DockPanel Grid.Row="4" Grid.Column="1">
|
||||
<Button DockPanel.Dock="Right" Content="..." Click="ButtonPickColor_Click">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding BackgroundColor}"/>
|
||||
</Button.Background>
|
||||
</Button>
|
||||
<TextBox x:Name="_colorTextBox" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
<DockPanel Grid.Column="1">
|
||||
<Border DockPanel.Dock="Right">
|
||||
<Popup x:Name="_colorPopup" Placement="Right">
|
||||
<hc:ColorPicker Confirmed="ColorPicker_Confirmed" Canceled="ColorPicker_Canceled"/>
|
||||
</Popup>
|
||||
</Border>
|
||||
<Border DockPanel.Dock="Right" Background="White" CornerRadius="{DynamicResource DefaultCornerRadius}">
|
||||
<Button Content="..."
|
||||
Foreground="{Binding BackgroundColor, Converter={StaticResource Bg2FgCvter}}"
|
||||
Click="ButtonPickColor_Click">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{Binding BackgroundColor}"/>
|
||||
</Button.Background>
|
||||
</Button>
|
||||
</Border>
|
||||
<TextBox Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 四周边距 -->
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Margin}" ToolTip="{DynamicResource Str_MarginTooltip}"/>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- 自动分辨率 -->
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource Str_AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<ToggleButton Grid.Row="6" Grid.Column="1" IsChecked="{Binding AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding AutoResolution}" ToolTip="{DynamicResource Str_AutoResolutionTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 最大分辨率 -->
|
||||
<Label Grid.Row="7" Grid.Column="0" Content="{DynamicResource Str_MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="1" Text="{Binding MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
</Grid>
|
||||
<Grid IsEnabled="{Binding AutoResolution}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding MaxResolution}" ToolTip="{DynamicResource Str_MaxResolutionTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="{DynamicResource Str_ExportVideoArgs}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<!-- 导出时长 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Duration}" ToolTip="{DynamicResource Str_ExportDurationTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 导出帧率 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_Fps}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Fps}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Fps}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Fps}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 导出速度 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 是否保留最后一帧 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
|
||||
<ToggleButton Grid.Row="3" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.ViewModels.Exporters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -10,6 +12,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
@@ -24,6 +27,14 @@ namespace SpineViewer.Views.ExporterDialogs
|
||||
public FrameSequenceExporterDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += FrameSequenceExporterDialog_SourceInitialized;
|
||||
}
|
||||
|
||||
private void FrameSequenceExporterDialog_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
|
||||
private void ButtonOK_Click(object sender, RoutedEventArgs e)
|
||||
@@ -42,18 +53,22 @@ namespace SpineViewer.Views.ExporterDialogs
|
||||
DialogResult = false;
|
||||
}
|
||||
|
||||
private void ButtonSelectOutputDir_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DialogService.ShowOpenFolderDialog(out var selectedPath))
|
||||
{
|
||||
var vm = (FrameSequenceExporterViewModel)DataContext;
|
||||
vm.OutputDir = selectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonPickColor_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = !_colorPopup.IsOpen;
|
||||
}
|
||||
|
||||
private void ColorPicker_Confirmed(object sender, HandyControl.Data.FunctionEventArgs<Color> e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
var color = e.Info;
|
||||
var vm = (BaseExporterViewModel)DataContext;
|
||||
vm.BackgroundColor = color;
|
||||
}
|
||||
|
||||
private void ColorPicker_Canceled(object sender, EventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,12 +8,14 @@ using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.Utils;
|
||||
using SpineViewer.ViewModels.Exporters;
|
||||
using SpineViewer.ViewModels.MainWindow;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -64,11 +66,11 @@ public partial class MainWindow : Window
|
||||
// XXX: hc 的 NotifyIcon 的 Text 似乎没法双向绑定
|
||||
_notifyIcon.Text = _vm.Title;
|
||||
|
||||
SourceInitialized += MainWindow_SourceInitialized;
|
||||
Loaded += MainWindow_Loaded;
|
||||
ContentRendered += MainWindow_ContentRendered;
|
||||
Closing += MainWindow_Closing;
|
||||
Closed += MainWindow_Closed;
|
||||
|
||||
_vm.SpineObjectListViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging;
|
||||
_vm.SFMLRendererViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging;
|
||||
|
||||
@@ -95,7 +97,7 @@ public partial class MainWindow : Window
|
||||
rtbTarget.WordColoringRules.Add(new("[I]", "DimGray", "Empty"));
|
||||
rtbTarget.WordColoringRules.Add(new("[W]", "DarkOrange", "Empty"));
|
||||
rtbTarget.WordColoringRules.Add(new("[E]", "Red", "Empty"));
|
||||
rtbTarget.WordColoringRules.Add(new("[F]", "DarkRed", "Empty"));
|
||||
rtbTarget.WordColoringRules.Add(new("[F]", "White", "DarkRed"));
|
||||
|
||||
LogManager.Configuration.AddTarget(rtbTarget);
|
||||
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Fatal, rtbTarget);
|
||||
@@ -177,6 +179,13 @@ public partial class MainWindow : Window
|
||||
|
||||
#region MainWindow 事件处理
|
||||
|
||||
private void MainWindow_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
|
||||
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = _vm.SFMLRendererViewModel;
|
||||
@@ -253,6 +262,28 @@ public partial class MainWindow : Window
|
||||
|
||||
#endregion
|
||||
|
||||
#region ColorPicker 弹窗事件处理
|
||||
|
||||
private void ButtonPickColor_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = !_colorPopup.IsOpen;
|
||||
}
|
||||
|
||||
private void ColorPicker_Confirmed(object sender, HandyControl.Data.FunctionEventArgs<Color> e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
var color = e.Info;
|
||||
var vm = ((MainWindowViewModel)DataContext).SFMLRendererViewModel;
|
||||
vm.BackgroundColor = color;
|
||||
}
|
||||
|
||||
private void ColorPicker_Canceled(object sender, EventArgs e)
|
||||
{
|
||||
_colorPopup.IsOpen = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ViewModel PropertyChanged 事件处理
|
||||
|
||||
private void SFMLRendererViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
@@ -705,15 +736,17 @@ public partial class MainWindow : Window
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private void DebugMenuItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
#if DEBUG
|
||||
var a = _rootGrid.ColumnDefinitions[0].Width;
|
||||
var b = _rootGrid.ColumnDefinitions[1].Width;
|
||||
var c = _rootGrid.ColumnDefinitions[2].Width;
|
||||
Debug.WriteLine(a);
|
||||
Debug.WriteLine(_rootGrid.ColumnDefinitions[0].Width.IsStar);
|
||||
_logger.Debug("Debug");
|
||||
_logger.Info("Info");
|
||||
_logger.Warn("Warn");
|
||||
_logger.Error("Error");
|
||||
_logger.Fatal("Fatal");
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
d:DataContext="{d:DesignInstance Type=models:PreferenceModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{DynamicResource Str_Preference}"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
Height="650"
|
||||
Width="600"
|
||||
ShowInTaskbar="False"
|
||||
@@ -40,24 +41,22 @@
|
||||
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ComboBoxBaseStyle}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButton}">
|
||||
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MyToggleButtonBaseStyle}">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource GroupBoxTab}">
|
||||
<Setter Property="BorderBrush" Value="LightGray"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="hc:TitleElement.Background" Value="Transparent"/>
|
||||
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource MyGroupBoxBaseStyle}">
|
||||
<Setter Property="Margin" Value="0 5 0 10"/>
|
||||
</Style>
|
||||
</Border.Resources>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
|
||||
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
|
||||
<StackPanel Grid.IsSharedSizeScope="True">
|
||||
<GroupBox Header="{DynamicResource Str_TextureLoadPreference}">
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ForcePremul}" ToolTip="{DynamicResource Str_ForcePremulTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding ForcePremul}" ToolTip="{DynamicResource Str_ForcePremulTooltip}"/>
|
||||
@@ -66,7 +65,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ForceNearest}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding ForceNearest}"/>
|
||||
@@ -75,7 +74,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_ForceMipmap}" ToolTip="{DynamicResource Str_ForceMipmapTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding ForceMipmap}" ToolTip="{DynamicResource Str_ForceMipmapTooltip}"/>
|
||||
@@ -88,7 +87,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_IsShown}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding IsShown}"/>
|
||||
@@ -97,7 +96,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_UsePma}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding UsePma}"/>
|
||||
@@ -106,7 +105,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugTexture}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugTexture}"/>
|
||||
@@ -115,7 +114,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugBounds}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugBounds}"/>
|
||||
@@ -124,7 +123,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugBones}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugBones}"/>
|
||||
@@ -133,7 +132,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugRegions}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugRegions}"/>
|
||||
@@ -142,7 +141,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugMeshHulls}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugMeshHulls}"/>
|
||||
@@ -151,7 +150,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugMeshes}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugMeshes}"/>
|
||||
@@ -160,7 +159,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugClippings}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugClippings}"/>
|
||||
@@ -169,7 +168,7 @@
|
||||
<!-- <Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugBoundingBoxes}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugBoundingBoxes}"/>
|
||||
@@ -178,7 +177,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugPaths}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugPaths}"/>
|
||||
@@ -187,7 +186,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_DebugPoints}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugPoints}"/>
|
||||
@@ -201,18 +200,29 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Language}"/>
|
||||
<ComboBox Grid.Column="1"
|
||||
SelectedItem="{Binding AppLanguage}"
|
||||
ItemsSource="{x:Static vm:PreferenceViewModel.AppLanguageOptions}"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_Skin}"/>
|
||||
<ComboBox Grid.Column="1"
|
||||
SelectedItem="{Binding AppSkin}"
|
||||
ItemsSource="{x:Static vm:PreferenceViewModel.AppSkinOptions}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_RenderSelectedOnly}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding RenderSelectedOnly}"/>
|
||||
@@ -221,16 +231,18 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_UsePreciseHitTest}" ToolTip="{DynamicResource Str_UsePreciseHitTestTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding UsePreciseHitTest}" ToolTip="{DynamicResource Str_UsePreciseHitTestTooltip}"/>
|
||||
<Label Content="{DynamicResource Str_HitTestLevel}"/>
|
||||
<ComboBox Grid.Column="1"
|
||||
SelectedItem="{Binding HitTestLevel}"
|
||||
ItemsSource="{x:Static vm:PreferenceViewModel.HitTestLevelOptions}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_LogHitSlots}" ToolTip="{DynamicResource Str_LogHitSlotsTooltip}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding LogHitSlots}" ToolTip="{DynamicResource Str_LogHitSlotsTooltip}"/>
|
||||
@@ -239,7 +251,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_WallpaperView}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding WallpaperView}"/>
|
||||
@@ -248,7 +260,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_CloseToTray}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding CloseToTray}"/>
|
||||
@@ -257,7 +269,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_AutoRun}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding AutoRun}"/>
|
||||
@@ -266,7 +278,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_AutoRunWorkspaceConfigPath}"
|
||||
ToolTip="{DynamicResource Str_AutoRunWorkspaceConfigPathTooltip}"/>
|
||||
@@ -283,7 +295,7 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{DynamicResource Str_AssociateFileSuffix}"/>
|
||||
<ToggleButton Grid.Column="1" IsChecked="{Binding AssociateFileSuffix}"/>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.Services;
|
||||
using SpineViewer.ViewModels.Exporters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -10,6 +12,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
@@ -24,6 +27,14 @@ namespace SpineViewer.Views
|
||||
public PreferenceDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += PreferenceDialog_SourceInitialized;
|
||||
}
|
||||
|
||||
private void PreferenceDialog_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
|
||||
private void ButtonOK_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
d:DataContext="{d:DesignInstance Type=vm:ProgressDialogViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{Binding Title}"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
Width="550"
|
||||
Height="250"
|
||||
ResizeMode="NoResize"
|
||||
@@ -21,7 +22,11 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="{Binding ProgressText}" Padding="20" LineHeight="25" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{Binding ProgressText}"
|
||||
Foreground="{DynamicResource PrimaryTextBrush}"
|
||||
Padding="20"
|
||||
LineHeight="25"
|
||||
TextWrapping="Wrap"/>
|
||||
<ProgressBar Grid.Row="1"
|
||||
Value="{Binding Done}"
|
||||
Maximum="{Binding Total}"
|
||||
@@ -29,7 +34,7 @@
|
||||
VerticalAlignment="Center"
|
||||
Margin="20 5"/>
|
||||
<Button Grid.Row="2"
|
||||
Content="{StaticResource Str_Cancel}"
|
||||
Content="{DynamicResource Str_Cancel}"
|
||||
Command="{Binding Cmd_Cancel}"
|
||||
Width="100"
|
||||
Margin="15"/>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using SpineViewer.ViewModels;
|
||||
using SpineViewer.Natives;
|
||||
using SpineViewer.Resources;
|
||||
using SpineViewer.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -25,9 +27,17 @@ namespace SpineViewer.Views
|
||||
public ProgressDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += ProgressDialog_SourceInitialized;
|
||||
Loaded += ProgressWindow_Loaded;
|
||||
}
|
||||
|
||||
private void ProgressDialog_SourceInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
|
||||
Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
|
||||
}
|
||||
|
||||
private void ProgressWindow_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
|
||||
@@ -4,7 +4,6 @@ using SFML.Graphics;
|
||||
using SFML.System;
|
||||
using Spine;
|
||||
using Spine.Exporters;
|
||||
using Spine.Interfaces;
|
||||
|
||||
namespace SpineViewerCLI
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user