diff --git a/CHANGELOG.md b/CHANGELOG.md
index b367269..3d0bdd2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# CHANGELOG
+## v0.16.11
+
+- 增加 shift 切换缩放倍数
+- 改善后台性能
+- 修复字体显示颜色问题
+- 调整浏览目录参数保存至用户状态
+- 调整浏览面板至最后
+
## v0.16.10
- 增加 Linux 平台 CLI 工具构建
diff --git a/README.en.md b/README.en.md
index a036ae2..b9951c0 100644
--- a/README.en.md
+++ b/README.en.md
@@ -89,7 +89,7 @@ Mouse interactions in the preview panel:
- **Left click**: select and drag models. Hold `Ctrl` for multi-selection (synchronized with the model list).
- **Right click**: drag the entire canvas.
-- **Mouse wheel**: zoom in/out. Hold `Ctrl` to scale selected models together.
+- **Mouse wheel**: zoom in/out. Hold `Ctrl` to scale selected models together, use `Shift` to switch zoom factor.
- **Render selected only**: preview only the selected models, selection can only be changed via the left panel.
Playback controls below the preview allow time adjustment, acting as a simple player.
diff --git a/README.md b/README.md
index 229815b..801333e 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,7 @@ https://github.com/user-attachments/assets/37b6b730-088a-4352-827a-c338127a16f0
- 左键可以选择和拖拽模型, 按下 `Ctrl` 键可以实现多选, 与左侧列表选择是联动的.
- 右键对整体画面进行拖动.
-- 滚轮进行画面缩放, 按住 `Ctrl` 可以对选中的模型进行批量缩放.
+- 滚轮进行画面缩放, 按住 `Ctrl` 可以对选中的模型进行批量缩放, `Shift` 可以切换缩放倍数.
- 仅渲染选中模式, 在该模式下, 预览画面仅包含被选中的模型, 并且只能通过左侧列表改变选中状态.
预览画面下方按钮支持对画面时间进行调整, 可以当作一个简易的播放器.
diff --git a/Spine/Spine.csproj b/Spine/Spine.csproj
index d608a48..87ebe29 100644
--- a/Spine/Spine.csproj
+++ b/Spine/Spine.csproj
@@ -8,7 +8,7 @@
net8.0
$(SolutionDir)out
false
- 0.16.10
+ 0.16.11
diff --git a/SpineViewer/App.xaml.cs b/SpineViewer/App.xaml.cs
index 32b1040..9baa531 100644
--- a/SpineViewer/App.xaml.cs
+++ b/SpineViewer/App.xaml.cs
@@ -23,9 +23,11 @@ namespace SpineViewer
public partial class App : Application
{
#if DEBUG
+ public const bool IsDebug = true;
public const string AppName = "SpineViewer_D";
public const string ProgId = "SpineViewer_D.skel";
#else
+ public const bool IsDebug = false;
public const string AppName = "SpineViewer";
public const string ProgId = "SpineViewer.skel";
#endif
diff --git a/SpineViewer/Models/UserStateModel.cs b/SpineViewer/Models/UserStateModel.cs
index 25eedaa..8a0d73f 100644
--- a/SpineViewer/Models/UserStateModel.cs
+++ b/SpineViewer/Models/UserStateModel.cs
@@ -33,6 +33,12 @@ namespace SpineViewer.Models
#endregion
+ #region 浏览页面状态
+
+ public string? ExploringDirectory { get; set; }
+
+ #endregion
+
#region 预览画面状态
public uint ResolutionX { get; set; } = 1500;
diff --git a/SpineViewer/Models/WorkspaceModel.cs b/SpineViewer/Models/WorkspaceModel.cs
index 298514a..a608cbb 100644
--- a/SpineViewer/Models/WorkspaceModel.cs
+++ b/SpineViewer/Models/WorkspaceModel.cs
@@ -12,7 +12,6 @@ namespace SpineViewer.Models
{
public class WorkspaceModel
{
- public string? ExploringDirectory { get; set; }
public RendererWorkspaceConfigModel RendererConfig { get; set; } = new();
public List LoadedSpineObjects { get; set; } = [];
}
diff --git a/SpineViewer/SpineViewer.csproj b/SpineViewer/SpineViewer.csproj
index dbf1c3b..03b940e 100644
--- a/SpineViewer/SpineViewer.csproj
+++ b/SpineViewer/SpineViewer.csproj
@@ -8,7 +8,7 @@
net8.0-windows
$(SolutionDir)out
false
- 0.16.10
+ 0.16.11
WinExe
true
diff --git a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs
index c86ee78..a1198c0 100644
--- a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs
+++ b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs
@@ -27,7 +27,24 @@ namespace SpineViewer.ViewModels.MainWindow
_preferenceViewModel = new(this);
}
- public string Title => $"SpineViewer - v{App.Version}";
+ public bool IsDebug => App.IsDebug;
+
+ public string Title => $"{App.AppName} - v{App.Version}";
+
+ public Visibility Visibility
+ {
+ get => _visibility;
+ set
+ {
+ if (SetProperty(ref _visibility, value))
+ {
+ OnPropertyChanged(nameof(IsVisible));
+ }
+ }
+ }
+ private Visibility _visibility = Visibility.Visible;
+
+ public bool IsVisible => _visibility == Visibility.Visible;
///
/// 指示是否通过托盘图标进行退出
@@ -164,14 +181,12 @@ namespace SpineViewer.ViewModels.MainWindow
{
return new()
{
- ExploringDirectory = _explorerListViewModel.CurrentDirectory,
RendererConfig = _sfmlRendererViewModel.WorkspaceConfig,
LoadedSpineObjects = _spineObjectListViewModel.LoadedSpineObjects
};
}
set
{
- _explorerListViewModel.CurrentDirectory = value.ExploringDirectory;
_sfmlRendererViewModel.WorkspaceConfig = value.RendererConfig;
_spineObjectListViewModel.LoadedSpineObjects = value.LoadedSpineObjects;
}
diff --git a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs
index 7a4041a..e70f138 100644
--- a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs
+++ b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs
@@ -153,7 +153,7 @@ namespace SpineViewer.ViewModels.MainWindow
public uint MaxFps
{
get => _renderer.MaxFps;
- set => SetProperty(_renderer.MaxFps, value, v => _renderer.MaxFps = value);
+ set => SetProperty(_renderer.MaxFps, value, v => _renderer.MaxFps = _wallpaperRenderer.MaxFps = value);
}
public float Speed
@@ -320,7 +320,8 @@ namespace SpineViewer.ViewModels.MainWindow
public void CanvasMouseWheelScrolled(object? s, SFML.Window.MouseWheelScrollEventArgs e)
{
- var factor = e.Delta > 0 ? 1.1f : 0.9f;
+ float delta = ((Keyboard.Modifiers & ModifierKeys.Shift) == 0) ? 0.1f : 0.01f;
+ var factor = e.Delta > 0 ? (1f + delta) : (1f - delta);
if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
{
Zoom = Math.Clamp(Zoom * factor, 0.001f, 1000f); // 滚轮缩放限制一下缩放范围
@@ -484,21 +485,17 @@ namespace SpineViewer.ViewModels.MainWindow
_forwardDelta = 0;
}
- using var v = _renderer.GetView();
- _renderer.Clear(_backgroundColor);
+ using var view = _renderer.GetView();
+ _wallpaperRenderer.SetView(view);
- if (_wallpaperView)
- {
- _wallpaperRenderer.SetView(v);
- _wallpaperRenderer.Clear(_backgroundColor);
- }
+ if (_vmMain.IsVisible) _renderer.Clear(_backgroundColor);
+ if (_wallpaperView) _wallpaperRenderer.Clear(_backgroundColor);
// 渲染背景
lock (_bgLock)
{
if (_backgroundImageSprite is not null)
{
- using var view = _renderer.GetView();
var bg = _backgroundImageSprite;
var viewSize = view.Size;
var bgSize = bg.Texture.Size;
@@ -521,16 +518,13 @@ namespace SpineViewer.ViewModels.MainWindow
bg.Scale = new(signX * scaleX, signY * scaleY);
bg.Position = view.Center;
bg.Rotation = view.Rotation;
- _renderer.Draw(bg);
- if (_wallpaperView)
- {
- _wallpaperRenderer.Draw(bg);
- }
+ if (_vmMain.IsVisible) _renderer.Draw(bg);
+ if (_wallpaperView) _wallpaperRenderer.Draw(bg);
}
}
- if (_showAxis)
+ if (_showAxis && _vmMain.IsVisible)
{
// 画一个很长的坐标轴, 用 1e9 比较合适
_axisVertices[0] = new(new(-1e9f, 0), _axisColor);
@@ -551,35 +545,30 @@ namespace SpineViewer.ViewModels.MainWindow
sp.Update(0); // 避免物理效果出现问题
sp.Update(delta * _speed);
- // 为选中对象绘制一个半透明背景
- if (sp.IsSelected)
+ if (_vmMain.IsVisible)
{
- var rc = sp.GetCurrentBounds().ToFloatRect();
- _selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor);
- _selectedBackgroundVertices[1] = new(new(rc.Left + rc.Width, rc.Top), _selectedBackgroundColor);
- _selectedBackgroundVertices[2] = new(new(rc.Left + rc.Width, rc.Top + rc.Height), _selectedBackgroundColor);
- _selectedBackgroundVertices[3] = new(new(rc.Left, rc.Top + rc.Height), _selectedBackgroundColor);
- _renderer.Draw(_selectedBackgroundVertices);
- }
+ // 为选中对象绘制一个半透明背景
+ if (sp.IsSelected)
+ {
+ var rc = sp.GetCurrentBounds().ToFloatRect();
+ _selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor);
+ _selectedBackgroundVertices[1] = new(new(rc.Left + rc.Width, rc.Top), _selectedBackgroundColor);
+ _selectedBackgroundVertices[2] = new(new(rc.Left + rc.Width, rc.Top + rc.Height), _selectedBackgroundColor);
+ _selectedBackgroundVertices[3] = new(new(rc.Left, rc.Top + rc.Height), _selectedBackgroundColor);
+ _renderer.Draw(_selectedBackgroundVertices);
+ }
- // 仅在预览画面临时启用调试模式
- sp.EnableDebug = true;
- _renderer.Draw(sp);
- sp.EnableDebug = false;
-
- if (_wallpaperView)
- {
- _wallpaperRenderer.Draw(sp);
+ // 仅在预览画面临时启用调试模式
+ sp.EnableDebug = true;
+ _renderer.Draw(sp);
+ sp.EnableDebug = false;
}
+ if (_wallpaperView) _wallpaperRenderer.Draw(sp);
}
}
- _renderer.Display();
-
- if (_wallpaperView)
- {
- _wallpaperRenderer.Display();
- }
+ if (_vmMain.IsVisible) _renderer.Display();
+ if (_wallpaperView) _wallpaperRenderer.Display();
}
}
catch (Exception ex)
diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml
index 98cb0c3..06f20bf 100644
--- a/SpineViewer/Views/MainWindow.xaml
+++ b/SpineViewer/Views/MainWindow.xaml
@@ -15,6 +15,7 @@
Height="720"
Background="{DynamicResource RegionBrush}"
WindowStartupLocation="CenterScreen"
+ Visibility="{Binding Visibility, Mode=OneWayToSource}"
PreviewKeyDown="MainWindow_PreviewKeyDown"
LocationChanged="MainWindow_LocationChanged"
SizeChanged="MainWindow_SizeChanged">
@@ -31,6 +32,7 @@
MouseDoubleClick="_notifyIcon_MouseDoubleClick">
+
@@ -63,7 +65,7 @@
-
+
@@ -682,125 +684,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -991,6 +874,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/Views/MainWindow.xaml.cs b/SpineViewer/Views/MainWindow.xaml.cs
index ce801e9..0fadd96 100644
--- a/SpineViewer/Views/MainWindow.xaml.cs
+++ b/SpineViewer/Views/MainWindow.xaml.cs
@@ -189,6 +189,7 @@ public partial class MainWindow : Window
_userStateWatchers.Add(PropertyWatcher.Watch(_rightPanelGrid.RowDefinitions[0], RowDefinition.HeightProperty, DelayedSaveUserState));
_userStateWatchers.Add(PropertyWatcher.Watch(_rightPanelGrid.RowDefinitions[2], RowDefinition.HeightProperty, DelayedSaveUserState));
+ _vm.ExplorerListViewModel.PropertyChanged += ExplorerListUserStateChanged;
_vm.SFMLRendererViewModel.PropertyChanged += SFMLRendererUserStateChanged;
}
@@ -234,6 +235,7 @@ public partial class MainWindow : Window
// 撤除所有状态监听器
_vm.SFMLRendererViewModel.PropertyChanged -= SFMLRendererUserStateChanged;
+ _vm.ExplorerListViewModel.PropertyChanged -= ExplorerListUserStateChanged;
foreach (var w in _userStateWatchers) w.Dispose();
_userStateWatchers.Clear();
@@ -284,6 +286,8 @@ public partial class MainWindow : Window
_rightPanelGrid.RowDefinitions[0].Height = new(m.RightPanelGridRow0Height, GridUnitType.Star);
_rightPanelGrid.RowDefinitions[2].Height = new(m.RightPanelGridRow2Height, GridUnitType.Star);
+ _vm.ExplorerListViewModel.CurrentDirectory = m.ExploringDirectory;
+
_vm.SFMLRendererViewModel.SetResolution(m.ResolutionX, m.ResolutionY);
_vm.SFMLRendererViewModel.MaxFps = m.MaxFps;
_vm.SFMLRendererViewModel.Speed = m.Speed;
@@ -317,6 +321,8 @@ public partial class MainWindow : Window
RightPanelGridRow0Height = _rightPanelGrid.RowDefinitions[0].Height.Value,
RightPanelGridRow2Height = _rightPanelGrid.RowDefinitions[2].Height.Value,
+ ExploringDirectory = _vm.ExplorerListViewModel.CurrentDirectory,
+
ResolutionX = _vm.SFMLRendererViewModel.ResolutionX,
ResolutionY = _vm.SFMLRendererViewModel.ResolutionY,
MaxFps = _vm.SFMLRendererViewModel.MaxFps,
@@ -356,6 +362,18 @@ public partial class MainWindow : Window
_saveUserStateTimer.Start();
}
+ private void ExplorerListUserStateChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(ExplorerListViewModel.CurrentDirectory):
+ DelayedSaveUserState();
+ break;
+ default:
+ break;
+ }
+ }
+
private void SFMLRendererUserStateChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
diff --git a/SpineViewerCLI/SpineViewerCLI.csproj b/SpineViewerCLI/SpineViewerCLI.csproj
index 981e168..680952a 100644
--- a/SpineViewerCLI/SpineViewerCLI.csproj
+++ b/SpineViewerCLI/SpineViewerCLI.csproj
@@ -8,7 +8,7 @@
net8.0
$(SolutionDir)out
false
- 0.16.10
+ 0.16.11
Exe