Merge pull request #118 from ww-rm/dev/wpf

v0.16.4
This commit is contained in:
ww-rm
2025-10-03 23:50:05 +08:00
committed by GitHub
45 changed files with 1777 additions and 850 deletions

View File

@@ -1,5 +1,12 @@
# CHANGELOG
## v0.16.4
- 增加 apng 导出格式
- 增加颜色拾取器面板
- 增加程序皮肤(主题颜色)首选项
- 优化部分使用体验
## v0.16.3
- 修复加载工作区时的顺序错误

View File

@@ -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 的像素格式

View File

@@ -7,7 +7,7 @@
<TargetFramework>net8.0-windows</TargetFramework>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.16.3</Version>
<Version>0.16.4</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -1,4 +1,5 @@
using NLog;
using Spine.Interfaces;
using Spine.Interfaces.Attachments;
using System;
using System.Collections.Generic;
@@ -6,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Spine.Interfaces
namespace Spine
{
/// <summary>
/// 命中测试等级枚举值
@@ -136,7 +137,7 @@ namespace Spine.Interfaces
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 if (HitTestLevel == HitTestLevel.Meshes || HitTestLevel == HitTestLevel.Pixels)
{
@@ -179,7 +180,7 @@ 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;
@@ -229,7 +230,7 @@ namespace Spine.Interfaces
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);
return x >= bx && x <= bx + bw && y >= by && y <= by + bh;
}
bool hit = false;

View File

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

View File

@@ -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
{
@@ -231,8 +233,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 +261,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 +345,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 +383,11 @@ namespace SpineViewer
EN,
JA
}
public enum AppSkin
{
Light,
Dark,
Violet,
}
}

View File

@@ -1,5 +1,4 @@
using Spine;
using Spine.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Spine.Interfaces;
using Spine;
using SpineViewer.Services;
using System;
using System.Collections.Generic;
@@ -86,6 +86,9 @@ namespace SpineViewer.Models
[ObservableProperty]
private AppLanguage _appLanguage;
[ObservableProperty]
private AppSkin _appSkin;
[ObservableProperty]
private bool _renderSelectedOnly;

View 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));
}
}
}

View File

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

View File

@@ -0,0 +1,2 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Source="pack://application:,,,/HandyControl;component/Themes/SkinDark.xaml"/>

View File

@@ -0,0 +1,2 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>

View File

@@ -0,0 +1,2 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Source="pack://application:,,,/HandyControl;component/Themes/SkinViolet.xaml"/>

View File

@@ -197,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]&#x0A;Whether the animation loops</s:String>
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp/Apng]&#x0A;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]&#x0A;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]&#x0A;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]&#x0A;Pred parameter, value range 0-5, corresponding to different encoding strategies: none, sub, up, avg, paeth, and mixed.&#x0A;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]&#x0A;CRF parameter, range 0-63, lower value means higher quality</s:String>
<s:String x:Key="Str_ProfileParameter">Profile Parameter</s:String>
@@ -242,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>

View File

@@ -197,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]&#x0A;アニメーションをループ再生するかどうか</s:String>
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp/Apng]&#x0A;アニメーションをループ再生するかどうか</s:String>
<s:String x:Key="Str_QualityParameter">品質パラメータ</s:String>
<s:String x:Key="Str_QualityParameterTooltip" xml:space="preserve">[Webp]&#x0A;品質パラメータ、範囲は0-100。値が高いほど品質が良い</s:String>
<s:String x:Key="Str_LosslessParam">無損失圧縮</s:String>
<s:String x:Key="Str_LosslessParamTooltip" xml:space="preserve">[Webp]&#x0A;無損失圧縮、品質パラメータは無視されます</s:String>
<s:String x:Key="Str_ApngPred">予測器方式</s:String>
<s:String x:Key="Str_ApngPredTooltip" xml:space="preserve">[Apng]&#x0A;Pred パラメータ。値の範囲は 05 で、それぞれ none、sub、up、avg、paeth、mixed の異なるエンコード方式に対応します。&#x0A;エンコード時間とファイルサイズに影響します。</s:String>
<s:String x:Key="Str_CrfParameter">CRF パラメータ</s:String>
<s:String x:Key="Str_CrfParameterTooltip" xml:space="preserve">[Mp4/Webm/Mkv]&#x0A;CRF パラメータ、範囲0-63。値が小さいほど品質が高い</s:String>
<s:String x:Key="Str_ProfileParameter">プロファイルパラメータ</s:String>
@@ -242,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>

View File

@@ -197,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]&#x0A;动图是否循环播放</s:String>
<s:String x:Key="Str_LoopPlayTooltip" xml:space="preserve">[Gif/Webp/Apng]&#x0A;动图是否循环播放</s:String>
<s:String x:Key="Str_QualityParameter">质量参数</s:String>
<s:String x:Key="Str_QualityParameterTooltip" xml:space="preserve">[Webp]&#x0A;质量参数,取值范围 0-100越高质量越好</s:String>
<s:String x:Key="Str_LosslessParam">无损压缩</s:String>
<s:String x:Key="Str_LosslessParamTooltip" xml:space="preserve">[Webp]&#x0A;无损压缩,会忽略质量参数</s:String>
<s:String x:Key="Str_ApngPred">预测器方法</s:String>
<s:String x:Key="Str_ApngPredTooltip" xml:space="preserve">[Apng]&#x0A;Pred 参数,取值范围 0-5分别对应 none、sub、up、avg、paeth、mixed 几种不同的编码策略,&#x0A;影响编码时间和文件大小</s:String>
<s:String x:Key="Str_CrfParameter">CRF 参数</s:String>
<s:String x:Key="Str_CrfParameterTooltip" xml:space="preserve">[Mp4/Webm/Mkv]&#x0A;CRF 参数,取值范围 0-63越小质量越高</s:String>
<s:String x:Key="Str_ProfileParameter">Profile 参数</s:String>
@@ -242,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>

View File

@@ -0,0 +1,83 @@
<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"/>
</Style>
<Style x:Key="MyListBoxItemBaseStyle" TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource ListBoxItemBaseStyle}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</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 ListBoxItem}" BasedOn="{StaticResource MyListBoxItemBaseStyle}"/>
<Style TargetType="{x:Type ListView}" BasedOn="{StaticResource MyListViewBaseStyle}"/>
<Style TargetType="{x:Type GroupBox}" BasedOn="{StaticResource MyGroupBoxBaseStyle}"/>
</ResourceDictionary>

View File

@@ -7,7 +7,7 @@
<TargetFramework>net8.0-windows</TargetFramework>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.16.3</Version>
<Version>0.16.4</Version>
<OutputType>WinExe</OutputType>
<UseWPF>true</UseWPF>
</PropertyGroup>

View 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();
}
}
}

View File

@@ -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;
}

View File

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

View File

@@ -20,24 +20,63 @@ namespace SpineViewer.ViewModels.Exporters
{
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,
};

View File

@@ -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
{

View File

@@ -2,8 +2,8 @@
using CommunityToolkit.Mvvm.Input;
using Microsoft.Win32;
using NLog;
using Spine;
using Spine.Implementations;
using Spine.Interfaces;
using SpineViewer.Models;
using SpineViewer.Natives;
using SpineViewer.Services;
@@ -109,6 +109,7 @@ namespace SpineViewer.ViewModels.MainWindow
DebugClippings = DebugClippings,
AppLanguage = AppLanguage,
AppSkin = AppSkin,
RenderSelectedOnly = RenderSelectedOnly,
HitTestLevel = HitTestLevel,
LogHitSlots = LogHitSlots,
@@ -140,6 +141,7 @@ namespace SpineViewer.ViewModels.MainWindow
DebugClippings = value.DebugClippings;
AppLanguage = value.AppLanguage;
AppSkin = value.AppSkin;
RenderSelectedOnly = value.RenderSelectedOnly;
HitTestLevel = value.HitTestLevel;
LogHitSlots = value.LogHitSlots;
@@ -253,6 +255,8 @@ 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
@@ -261,6 +265,12 @@ namespace SpineViewer.ViewModels.MainWindow
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;

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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>
<StackPanel Grid.IsSharedSizeScope="True">
<!-- 程序版本 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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}"/>
<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">
<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>

View File

@@ -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);
}
}
}

View File

@@ -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"/>
@@ -32,7 +33,7 @@
</Style>
</Border.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="30 10">
<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>

View File

@@ -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);
}
}
}

View File

@@ -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,166 +41,227 @@
<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>
<StackPanel>
<!-- 水平分辨率 -->
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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>
<!-- 水平分辨率 -->
<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}"/>
<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"/>
<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">
<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>
<TextBox x:Name="_colorTextBox" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
</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 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}">
<StackPanel>
<!-- 导出时长 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 导出时长 -->
<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}"/>
<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.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}">
<StackPanel>
<!-- 导出格式 -->
<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="Auto" SharedSizeGroup="LabelCol"/>
<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>
<!-- 导出格式 -->
<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}"/>
<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"
<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.Row="5" Grid.Column="1"
<TextBox Grid.Column="1"
Height="75"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Top"
TextWrapping="Wrap"
@@ -206,6 +269,7 @@
Text="{Binding CustomArgs}"
ToolTip="{DynamicResource Str_FFmpegCustomArgsTooltip}"/>
</Grid>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>

View File

@@ -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;
}
}
}

View File

@@ -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: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>
<StackPanel>
<!-- 水平分辨率 -->
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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>
<!-- 水平分辨率 -->
<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}"/>
<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"/>
<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">
<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>
<TextBox x:Name="_colorTextBox" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
</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 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}">
<StackPanel>
<!-- 导出时长 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 导出时长 -->
<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}"/>
<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.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}">
<StackPanel>
<!-- 视频格式 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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>
<!-- 视频格式 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_VideoFormat}"/>
<ComboBox Grid.Row="0" Grid.Column="1" SelectedItem="{Binding Format}" ItemsSource="{x:Static vmexp:FFmpegVideoExporterViewModel.VideoFormatOptions}"/>
<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 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>

View File

@@ -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;
}
}
}

View File

@@ -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>
<StackPanel>
<!-- 水平分辨率 -->
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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>
<!-- 水平分辨率 -->
<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}"/>
<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"/>
<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">
<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>
<TextBox x:Name="_colorTextBox" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
</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 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}">
<StackPanel>
<!-- 图像格式 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 图像格式 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ImageFormat}"/>
<ComboBox Grid.Row="0" Grid.Column="1" SelectedItem="{Binding Format}" ItemsSource="{x:Static vmexp:FrameExporterViewModel.FrameFormatOptions}"/>
<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.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>

View File

@@ -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;
}
}
}

View File

@@ -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}"
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>
<StackPanel>
<!-- 水平分辨率 -->
<Grid IsEnabled="{Binding AutoResolution, Converter={StaticResource Boolean2BooleanReConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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>
<!-- 水平分辨率 -->
<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}"/>
<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"/>
<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">
<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>
<TextBox x:Name="_colorTextBox" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
</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 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}">
<StackPanel>
<!-- 导出时长 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 导出时长 -->
<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}"/>
<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.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>

View File

@@ -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;
}
}
}

View File

@@ -6,11 +6,11 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:vm="clr-namespace:SpineViewer.ViewModels.MainWindow"
xmlns:utils="clr-namespace:SpineViewer.Utils"
xmlns:SFMLRenderer="clr-namespace:SFMLRenderer;assembly=SFMLRenderer"
mc:Ignorable="d"
x:Name="_mainWindow"
Title="{Binding Title}"
Background="{DynamicResource RegionBrush}"
Width="1500"
Height="800"
WindowStartupLocation="CenterScreen"
@@ -19,31 +19,25 @@
LocationChanged="MainWindow_LocationChanged"
SizeChanged="MainWindow_SizeChanged">
<Window.Resources>
<Style TargetType="{x:Type GridSplitter}">
<Setter Property="Background" Value="Gray"/>
<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>
<utils:StringFormatMultiValueConverter x:Key="StrFmtCvter"/>
</Window.Resources>
<Window.TaskbarItemInfo>
<TaskbarItemInfo ProgressState="{Binding ProgressState}" ProgressValue="{Binding ProgressValue}"/>
</Window.TaskbarItemInfo>
<Grid>
<!-- 非可视元素通知栏图标 -->
<hc:NotifyIcon x:Name="_notifyIcon"
Icon="/Resources/Images/spineviewer.ico"
Click="_notifyIcon_Click"
MouseDoubleClick="_notifyIcon_MouseDoubleClick">
<hc:NotifyIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_WallpaperView}" Command="{Binding Cmd_SwitchWallpaperView}" IsChecked="{Binding PreferenceViewModel.WallpaperView}"/>
<Separator/>
<MenuItem Header="{DynamicResource Str_Exit}" Command="{Binding Cmd_ExitFromTray}"/>
</ContextMenu>
</hc:NotifyIcon.ContextMenu>
</hc:NotifyIcon>
<!-- 非全屏布局框架 -->
<Grid x:Name="_normalLayout" Visibility="Visible">
<Grid.RowDefinitions>
@@ -123,10 +117,6 @@
MouseLeftButtonUp="SpinesListView_MouseLeftButtonUp"
DragEnter="SpinesListView_DragEnter"
Drop="SpinesListView_Drop">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource ListViewItemBaseStyle.Small}"/>
</ListView.ItemContainerStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding Cmd_ListViewSelectionChanged}"
@@ -238,151 +228,205 @@
<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>
</TabControl.Resources>
<!-- 基本属性页 -->
<TabItem Header="{DynamicResource Str_BaseInfo}">
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
<StackPanel Grid.IsSharedSizeScope="True">
<!-- 名称 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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>
<!-- 名称 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_Name}"/>
<TextBox Grid.Row="0" Grid.Column="1"
<Label Content="{DynamicResource Str_Name}"/>
<TextBox Grid.Column="1"
Text="{Binding Name, Mode=OneWay}"
IsReadOnly="True"
ToolTip="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>
</Grid>
<!-- 运行时版本 -->
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_Version}"/>
<TextBox Grid.Row="1" Grid.Column="1"
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_Version}"/>
<TextBox Grid.Column="1"
Text="{Binding Version, Mode=OneWay}"
IsReadOnly="True"
ToolTip="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>
</Grid>
<!-- 文件版本 -->
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_FileVersion}"/>
<TextBox Grid.Row="2" Grid.Column="1"
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_FileVersion}"/>
<TextBox Grid.Column="1"
Text="{Binding FileVersion, Mode=OneWay}"
IsReadOnly="True"
ToolTip="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>
</Grid>
<!-- 资源目录 -->
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_AssetsDir}"/>
<TextBox Grid.Row="3" Grid.Column="1"
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_AssetsDir}"/>
<TextBox Grid.Column="1"
Text="{Binding AssetsDir, Mode=OneWay}"
IsReadOnly="True"
ToolTip="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>
</Grid>
<!-- skel 文件路径 -->
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_SkelPath}"/>
<TextBox Grid.Row="4" Grid.Column="1"
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_SkelPath}"/>
<TextBox Grid.Column="1"
Text="{Binding SkelPath, Mode=OneWay}"
IsReadOnly="True"
ToolTip="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>
</Grid>
<!-- atlas 文件路径 -->
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_AtlasPath}"/>
<TextBox Grid.Row="5" Grid.Column="1"
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_AtlasPath}"/>
<TextBox Grid.Column="1"
Text="{Binding AtlasPath, Mode=OneWay}"
IsReadOnly="True"
ToolTip="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>
</Grid>
</StackPanel>
</ScrollViewer>
</TabItem>
<!-- 渲染属性页 -->
<TabItem Header="{DynamicResource Str_Render}">
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
<StackPanel Grid.IsSharedSizeScope="True">
<!-- 显示 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 显示 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_IsShown}"/>
<ToggleButton Grid.Row="0" Grid.Column="1" IsChecked="{Binding IsShown}"/>
<Label Content="{DynamicResource Str_IsShown}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding IsShown}"/>
</Grid>
<!-- 预乘通道 -->
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_UsePma}"/>
<ToggleButton Grid.Row="1" Grid.Column="1" IsChecked="{Binding UsePma}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_UsePma}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding UsePma}"/>
</Grid>
<!-- 物理 -->
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_Physics}"/>
<ComboBox Grid.Row="2" Grid.Column="1" SelectedValue="{Binding Physics}" ItemsSource="{x:Static vm:SpineObjectTabViewModel.PhysicsOptions}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_Physics}"/>
<ComboBox Grid.Column="1"
SelectedValue="{Binding Physics}"
ItemsSource="{x:Static vm:SpineObjectTabViewModel.PhysicsOptions}"/>
</Grid>
<!-- 时间因子 -->
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_TimeScale}" ToolTip="{DynamicResource Str_TimeScaleTootltip}"/>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding TimeScale}" ToolTip="{DynamicResource Str_TimeScaleTootltip}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_TimeScale}" ToolTip="{DynamicResource Str_TimeScaleTootltip}"/>
<TextBox Grid.Column="1" Text="{Binding TimeScale}" ToolTip="{DynamicResource Str_TimeScaleTootltip}"/>
</Grid>
</StackPanel>
</ScrollViewer>
</TabItem>
<!-- 变换属性页 -->
<TabItem Header="{DynamicResource Str_Transform}">
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
<StackPanel Grid.IsSharedSizeScope="True">
<!-- 缩放 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 缩放 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_Scale}"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Scale}"/>
<Label Content="{DynamicResource Str_Scale}"/>
<TextBox Grid.Column="1" Text="{Binding Scale}"/>
</Grid>
<!-- 水平翻转 -->
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_FlipX}"/>
<ToggleButton Grid.Row="1" Grid.Column="1" IsChecked="{Binding FlipX}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_FlipX}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding FlipX}"/>
</Grid>
<!-- 垂直翻转 -->
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_FlipY}"/>
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding FlipY}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_FlipY}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding FlipY}"/>
</Grid>
<!-- 横坐标 -->
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_X}"/>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding X}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_X}"/>
<TextBox Grid.Column="1" Text="{Binding X}"/>
</Grid>
<!-- 纵坐标 -->
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_Y}"/>
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Y}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_Y}"/>
<TextBox Grid.Column="1" Text="{Binding Y}"/>
</Grid>
</StackPanel>
</ScrollViewer>
</TabItem>
<!-- 皮肤属性页 -->
<TabItem Header="{DynamicResource Str_Skin}">
<ListBox ItemsSource="{Binding Skins}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource ListBoxItemBaseStyle}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_EnableSkins}"
@@ -396,14 +440,16 @@
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Name}" Background="#bfffffff"/>
<Label Grid.Column="0" Content="{Binding Name}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding Status}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
@@ -412,14 +458,6 @@
<!-- 插槽属性页 -->
<TabItem Header="{DynamicResource Str_Slot}">
<ListBox ItemsSource="{Binding Slots}" Grid.IsSharedSizeScope="True">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource ListBoxItemBaseStyle}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_EnableSlots}"
@@ -433,16 +471,18 @@
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Col0"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="ColSlotName"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding SlotName}" HorizontalAlignment="Left" Background="#bfffffff"/>
<Label Grid.Column="0" Content="{Binding SlotName}" HorizontalAlignment="Left"/>
<ComboBox Grid.Column="1" SelectedValue="{Binding AttachmentName}" ItemsSource="{Binding AttachmentNames}"/>
<ToggleButton Grid.Column="2" IsChecked="{Binding Visible}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
@@ -451,14 +491,6 @@
<!-- 动画属性页 -->
<TabItem Header="{DynamicResource Str_Animation}">
<ListBox ItemsSource="{Binding AnimationTracks}" Grid.IsSharedSizeScope="True">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource ListBoxItemBaseStyle}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_AppendTrack}"
@@ -474,6 +506,7 @@
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="ColTrackIdx"/>
@@ -481,7 +514,7 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding TrackIndex}" HorizontalContentAlignment="Left" VerticalAlignment="Top" Background="#bfffffff"/>
<Label Grid.Column="0" Content="{Binding TrackIndex}" HorizontalContentAlignment="Left" VerticalAlignment="Top"/>
<Label Grid.Column="1" Content="{Binding AnimationDuration}" VerticalAlignment="Top" ContentStringFormat="{}{0:F3} s"/>
<Expander Grid.Column="2" HorizontalContentAlignment="Stretch">
@@ -509,6 +542,7 @@
</Grid>
</Expander>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
@@ -516,65 +550,110 @@
<!-- 调试属性页 -->
<TabItem Header="{DynamicResource Str_Debug}">
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
<StackPanel Grid.IsSharedSizeScope="True">
<!-- 调试纹理 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 调试纹理 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_DebugTexture}"/>
<ToggleButton Grid.Row="0" Grid.Column="1" IsChecked="{Binding DebugTexture}"/>
<Label Content="{DynamicResource Str_DebugTexture}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugTexture}"/>
</Grid>
<!-- 调试包围盒 -->
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_DebugBounds}"/>
<ToggleButton Grid.Row="1" Grid.Column="1" IsChecked="{Binding DebugBounds}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugBounds}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugBounds}"/>
</Grid>
<!-- 调试骨骼 -->
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_DebugBones}"/>
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding DebugBones}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugBones}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugBones}"/>
</Grid>
<!-- 调试区域 -->
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_DebugRegions}"/>
<ToggleButton Grid.Row="3" Grid.Column="1" IsChecked="{Binding DebugRegions}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugRegions}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugRegions}"/>
</Grid>
<!-- 调试网格外壳 -->
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_DebugMeshHulls}"/>
<ToggleButton Grid.Row="4" Grid.Column="1" IsChecked="{Binding DebugMeshHulls}"/>
<!-- 调试网格 -->
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_DebugMeshes}"/>
<ToggleButton Grid.Row="5" Grid.Column="1" IsChecked="{Binding DebugMeshes}"/>
<!-- 调试裁剪 -->
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource Str_DebugClippings}"/>
<ToggleButton Grid.Row="6" Grid.Column="1" IsChecked="{Binding DebugClippings}"/>
<!-- 调试碰撞盒 -->
<!--<Label Grid.Row="7" Grid.Column="0" Content="{DynamicResource Str_DebugBoundingBoxes}"/>
<ToggleButton Grid.Row="7" Grid.Column="1" IsChecked="{Binding DebugBoundingBoxes}"/>-->
<!-- 调试路径 -->
<!--<Label Grid.Row="8" Grid.Column="0" Content="{DynamicResource Str_DebugPaths}"/>
<ToggleButton Grid.Row="8" Grid.Column="1" IsChecked="{Binding DebugPaths}"/>-->
<!-- 调试点 -->
<!--<Label Grid.Row="9" Grid.Column="0" Content="{DynamicResource Str_DebugPoints}"/>
<ToggleButton Grid.Row="9" Grid.Column="1" IsChecked="{Binding DebugPoints}"/>-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugMeshHulls}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugMeshHulls}"/>
</Grid>
<!-- 调试网格 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugMeshes}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugMeshes}"/>
</Grid>
<!-- 调试裁剪 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugClippings}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugClippings}"/>
</Grid>
<!-- 调试碰撞盒 -->
<!--<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugBoundingBoxes}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugBoundingBoxes}"/>
</Grid>-->
<!-- 调试路径 -->
<!--<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugPaths}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugPaths}"/>
</Grid>-->
<!-- 调试点 -->
<!--<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_DebugPoints}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding DebugPoints}"/>
</Grid>-->
</StackPanel>
</ScrollViewer>
</TabItem>
</TabControl>
</Grid>
@@ -596,7 +675,7 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<hc:TextBox hc:InfoElement.Placeholder="{StaticResource Str_Filter}"
<hc:TextBox hc:InfoElement.Placeholder="{DynamicResource Str_Filter}"
Text="{Binding FilterString, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="1"
hc:IconElement.Geometry="{StaticResource Geo_Folder}"
@@ -640,7 +719,7 @@
<MenuItem Header="{DynamicResource Str_GeneratePreviewForSelected}"
Command="{Binding Cmd_GeneratePreviews}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
<MenuItem Header="{StaticResource Str_DeletePreviewsForSelected}"
<MenuItem Header="{DynamicResource Str_DeletePreviewsForSelected}"
Command="{Binding Cmd_DeletePreviews}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
@@ -684,7 +763,7 @@
IsReadOnly="True"/>
<!-- 预览图 -->
<Border Grid.Row="2" Grid.ColumnSpan="2" Background="#a0a0a0">
<Border Grid.Row="2" Grid.ColumnSpan="2" Background="{DynamicResource DarkDefaultBrush}">
<Image Source="{Binding PreviewImage, Mode=OneWay}" Stretch="Uniform"/>
</Border>
</Grid>
@@ -704,94 +783,174 @@
<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>
</TabItem.Resources>
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
<StackPanel Grid.IsSharedSizeScope="True">
<!-- 水平分辨率 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 水平分辨率 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_ResolutionX}"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding ResolutionX}"/>
<Label Content="{DynamicResource Str_ResolutionX}"/>
<TextBox Grid.Column="1" Text="{Binding ResolutionX}"/>
</Grid>
<!-- 垂直分辨率 -->
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_ResolutionY}"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding ResolutionY}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_ResolutionY}"/>
<TextBox Grid.Column="1" Text="{Binding ResolutionY}"/>
</Grid>
<!-- 中心点横坐标 -->
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Str_CenterX}"/>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding CenterX}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_CenterX}"/>
<TextBox Grid.Column="1" Text="{Binding CenterX}"/>
</Grid>
<!-- 中心点纵坐标 -->
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource Str_CenterY}"/>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding CenterY}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_CenterY}"/>
<TextBox Grid.Column="1" Text="{Binding CenterY}"/>
</Grid>
<!-- 缩放 -->
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource Str_Zoom}"/>
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Zoom}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_Zoom}"/>
<TextBox Grid.Column="1" Text="{Binding Zoom}"/>
</Grid>
<!-- 旋转角度 -->
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource Str_Rotation}"/>
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Rotation}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_Rotation}"/>
<TextBox Grid.Column="1" Text="{Binding Rotation}"/>
</Grid>
<!-- 水平翻转 -->
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource Str_FlipX}"/>
<ToggleButton Grid.Row="6" Grid.Column="1" IsChecked="{Binding FlipX}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_FlipX}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding FlipX}"/>
</Grid>
<!-- 垂直翻转 -->
<Label Grid.Row="7" Grid.Column="0" Content="{DynamicResource Str_FlipY}"/>
<ToggleButton Grid.Row="7" Grid.Column="1" IsChecked="{Binding FlipY}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_FlipY}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding FlipY}"/>
</Grid>
<Separator Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 5"/>
<Separator Margin="0 5"/>
<!-- 最大帧率 -->
<Label Grid.Row="9" Grid.Column="0" Content="{DynamicResource Str_MaxFps}" ToolTip="{DynamicResource Str_MaxFpsTooltip}"/>
<TextBox Grid.Row="9" Grid.Column="1" Text="{Binding MaxFps}" ToolTip="{DynamicResource Str_MaxFpsTooltip}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_MaxFps}" ToolTip="{DynamicResource Str_MaxFpsTooltip}"/>
<TextBox Grid.Column="1" Text="{Binding MaxFps}" ToolTip="{DynamicResource Str_MaxFpsTooltip}"/>
</Grid>
<!-- 播放速度 -->
<Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_PlaySpeed}"/>
<TextBox Grid.Row="10" Grid.Column="1" Text="{Binding Speed}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_PlaySpeed}"/>
<TextBox Grid.Column="1" Text="{Binding Speed}"/>
</Grid>
<!-- 显示坐标轴 -->
<Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource Str_ShowAxis}"/>
<ToggleButton Grid.Row="11" Grid.Column="1" IsChecked="{Binding ShowAxis}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_ShowAxis}"/>
<ToggleButton Grid.Column="1" IsChecked="{Binding ShowAxis}"/>
</Grid>
<!-- 背景颜色 -->
<Label Grid.Row="12" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
<TextBox Grid.Row="12" Grid.Column="1" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
<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="13" Grid.Column="0" Content="{DynamicResource Str_BackgroundImagePath}"/>
<DockPanel Grid.Row="13" Grid.Column="1" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_BackgroundImagePath}"/>
<DockPanel Grid.Column="1">
<Button DockPanel.Dock="Right" Content="..." Command="{Binding Cmd_SelectBackgroundImage}"/>
<TextBox Text="{Binding BackgroundImagePath}"/>
</DockPanel>
</Grid>
<!-- 背景图案模式 -->
<Label Grid.Row="14" Grid.Column="0" Content="{DynamicResource Str_BackgroundImageMode}"/>
<ComboBox Grid.Row="14" Grid.Column="1" SelectedValue="{Binding BackgroundImageMode}" ItemsSource="{x:Static vm:SFMLRendererViewModel.StretchOptions}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Str_BackgroundImageMode}"/>
<ComboBox Grid.Column="1" SelectedValue="{Binding BackgroundImageMode}" ItemsSource="{x:Static vm:SFMLRendererViewModel.StretchOptions}"/>
</Grid>
</StackPanel>
</ScrollViewer>
</TabItem>
</TabControl>
</Border>
@@ -813,7 +972,7 @@
</Grid.RowDefinitions>
<!-- 预览画面容器 -->
<Border Grid.Row="0" x:Name="_renderPanelContainer">
<Border Grid.Row="0" x:Name="_renderPanelContainer" Background="{DynamicResource SecondaryRegionBrush}">
<SFMLRenderer:SFMLRenderPanel x:Name="_renderPanel"/>
</Border>
@@ -825,7 +984,7 @@
DataContext="{Binding SFMLRendererViewModel}"
Grid.Row="1"
Grid.IsSharedSizeScope="True"
Background="{StaticResource DefaultBrush}">
Background="{DynamicResource RegionBrush}">
<WrapPanel HorizontalAlignment="Center">
<WrapPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource ButtonDefault}">
@@ -851,12 +1010,7 @@
<!-- 日志框容器 -->
<Border Grid.Row="2" Name="_loggerBoxContainer">
<RichTextBox x:Name="_loggerRichTextBox"
Grid.Row="2"
IsReadOnly="True"
FontFamily="Consolas"
Block.LineHeight="3"
VerticalScrollBarVisibility="Visible"/>
<RichTextBox x:Name="_loggerRichTextBox" Grid.Row="2" Style="{StaticResource MyLogRichTextBoxStyle}"/>
</Border>
</Grid>
</Border>
@@ -932,21 +1086,6 @@
Opened="BottomPopup_Opened"
MouseLeave="PopupContainer_MouseLeave"/>
</Grid>
<!-- 非可视元素通知栏图标 -->
<hc:NotifyIcon x:Name="_notifyIcon"
Icon="/Resources/Images/spineviewer.ico"
Click="_notifyIcon_Click"
MouseDoubleClick="_notifyIcon_MouseDoubleClick">
<hc:NotifyIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_WallpaperView}" Command="{Binding Cmd_SwitchWallpaperView}" IsChecked="{Binding PreferenceViewModel.WallpaperView}"/>
<Separator/>
<MenuItem Header="{DynamicResource Str_Exit}" Command="{Binding Cmd_ExitFromTray}"/>
</ContextMenu>
</hc:NotifyIcon.ContextMenu>
</hc:NotifyIcon>
</Grid>
</Window>

View File

@@ -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)
@@ -706,14 +737,16 @@ 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
}
}

View File

@@ -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,18 +41,16 @@
<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">
<StackPanel Grid.IsSharedSizeScope="True" Margin="0 0 0 50">
<ScrollViewer Style="{StaticResource MyVerticalScrollViewerBaseStyle}">
<StackPanel Grid.IsSharedSizeScope="True">
<GroupBox Header="{DynamicResource Str_TextureLoadPreference}">
<StackPanel>
<Grid>
@@ -209,6 +208,17 @@
ItemsSource="{x:Static vm:PreferenceViewModel.AppLanguageOptions}"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelCol"/>
<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"/>

View File

@@ -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)

View File

@@ -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"
@@ -29,7 +30,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"/>

View File

@@ -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;

View File

@@ -4,7 +4,6 @@ using SFML.Graphics;
using SFML.System;
using Spine;
using Spine.Exporters;
using Spine.Interfaces;
namespace SpineViewerCLI
{