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

v0.16.11
This commit is contained in:
ww-rm
2025-11-02 21:38:33 +08:00
committed by GitHub
13 changed files with 208 additions and 168 deletions

View File

@@ -1,5 +1,13 @@
# CHANGELOG # CHANGELOG
## v0.16.11
- 增加 shift 切换缩放倍数
- 改善后台性能
- 修复字体显示颜色问题
- 调整浏览目录参数保存至用户状态
- 调整浏览面板至最后
## v0.16.10 ## v0.16.10
- 增加 Linux 平台 CLI 工具构建 - 增加 Linux 平台 CLI 工具构建

View File

@@ -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). - **Left click**: select and drag models. Hold `Ctrl` for multi-selection (synchronized with the model list).
- **Right click**: drag the entire canvas. - **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. - **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. Playback controls below the preview allow time adjustment, acting as a simple player.

View File

@@ -77,7 +77,7 @@ https://github.com/user-attachments/assets/37b6b730-088a-4352-827a-c338127a16f0
- 左键可以选择和拖拽模型, 按下 `Ctrl` 键可以实现多选, 与左侧列表选择是联动的. - 左键可以选择和拖拽模型, 按下 `Ctrl` 键可以实现多选, 与左侧列表选择是联动的.
- 右键对整体画面进行拖动. - 右键对整体画面进行拖动.
- 滚轮进行画面缩放, 按住 `Ctrl` 可以对选中的模型进行批量缩放. - 滚轮进行画面缩放, 按住 `Ctrl` 可以对选中的模型进行批量缩放, `Shift` 可以切换缩放倍数.
- 仅渲染选中模式, 在该模式下, 预览画面仅包含被选中的模型, 并且只能通过左侧列表改变选中状态. - 仅渲染选中模式, 在该模式下, 预览画面仅包含被选中的模型, 并且只能通过左侧列表改变选中状态.
预览画面下方按钮支持对画面时间进行调整, 可以当作一个简易的播放器. 预览画面下方按钮支持对画面时间进行调整, 可以当作一个简易的播放器.

View File

@@ -8,7 +8,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath> <BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.16.10</Version> <Version>0.16.11</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -23,9 +23,11 @@ namespace SpineViewer
public partial class App : Application public partial class App : Application
{ {
#if DEBUG #if DEBUG
public const bool IsDebug = true;
public const string AppName = "SpineViewer_D"; public const string AppName = "SpineViewer_D";
public const string ProgId = "SpineViewer_D.skel"; public const string ProgId = "SpineViewer_D.skel";
#else #else
public const bool IsDebug = false;
public const string AppName = "SpineViewer"; public const string AppName = "SpineViewer";
public const string ProgId = "SpineViewer.skel"; public const string ProgId = "SpineViewer.skel";
#endif #endif

View File

@@ -33,6 +33,12 @@ namespace SpineViewer.Models
#endregion #endregion
#region
public string? ExploringDirectory { get; set; }
#endregion
#region #region
public uint ResolutionX { get; set; } = 1500; public uint ResolutionX { get; set; } = 1500;

View File

@@ -12,7 +12,6 @@ namespace SpineViewer.Models
{ {
public class WorkspaceModel public class WorkspaceModel
{ {
public string? ExploringDirectory { get; set; }
public RendererWorkspaceConfigModel RendererConfig { get; set; } = new(); public RendererWorkspaceConfigModel RendererConfig { get; set; } = new();
public List<SpineObjectWorkspaceConfigModel> LoadedSpineObjects { get; set; } = []; public List<SpineObjectWorkspaceConfigModel> LoadedSpineObjects { get; set; } = [];
} }

View File

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

View File

@@ -27,7 +27,24 @@ namespace SpineViewer.ViewModels.MainWindow
_preferenceViewModel = new(this); _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;
/// <summary> /// <summary>
/// 指示是否通过托盘图标进行退出 /// 指示是否通过托盘图标进行退出
@@ -164,14 +181,12 @@ namespace SpineViewer.ViewModels.MainWindow
{ {
return new() return new()
{ {
ExploringDirectory = _explorerListViewModel.CurrentDirectory,
RendererConfig = _sfmlRendererViewModel.WorkspaceConfig, RendererConfig = _sfmlRendererViewModel.WorkspaceConfig,
LoadedSpineObjects = _spineObjectListViewModel.LoadedSpineObjects LoadedSpineObjects = _spineObjectListViewModel.LoadedSpineObjects
}; };
} }
set set
{ {
_explorerListViewModel.CurrentDirectory = value.ExploringDirectory;
_sfmlRendererViewModel.WorkspaceConfig = value.RendererConfig; _sfmlRendererViewModel.WorkspaceConfig = value.RendererConfig;
_spineObjectListViewModel.LoadedSpineObjects = value.LoadedSpineObjects; _spineObjectListViewModel.LoadedSpineObjects = value.LoadedSpineObjects;
} }

View File

@@ -153,7 +153,7 @@ namespace SpineViewer.ViewModels.MainWindow
public uint MaxFps public uint MaxFps
{ {
get => _renderer.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 public float Speed
@@ -320,7 +320,8 @@ namespace SpineViewer.ViewModels.MainWindow
public void CanvasMouseWheelScrolled(object? s, SFML.Window.MouseWheelScrollEventArgs e) 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) if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
{ {
Zoom = Math.Clamp(Zoom * factor, 0.001f, 1000f); // 滚轮缩放限制一下缩放范围 Zoom = Math.Clamp(Zoom * factor, 0.001f, 1000f); // 滚轮缩放限制一下缩放范围
@@ -484,21 +485,17 @@ namespace SpineViewer.ViewModels.MainWindow
_forwardDelta = 0; _forwardDelta = 0;
} }
using var v = _renderer.GetView(); using var view = _renderer.GetView();
_renderer.Clear(_backgroundColor); _wallpaperRenderer.SetView(view);
if (_wallpaperView) if (_vmMain.IsVisible) _renderer.Clear(_backgroundColor);
{ if (_wallpaperView) _wallpaperRenderer.Clear(_backgroundColor);
_wallpaperRenderer.SetView(v);
_wallpaperRenderer.Clear(_backgroundColor);
}
// 渲染背景 // 渲染背景
lock (_bgLock) lock (_bgLock)
{ {
if (_backgroundImageSprite is not null) if (_backgroundImageSprite is not null)
{ {
using var view = _renderer.GetView();
var bg = _backgroundImageSprite; var bg = _backgroundImageSprite;
var viewSize = view.Size; var viewSize = view.Size;
var bgSize = bg.Texture.Size; var bgSize = bg.Texture.Size;
@@ -521,16 +518,13 @@ namespace SpineViewer.ViewModels.MainWindow
bg.Scale = new(signX * scaleX, signY * scaleY); bg.Scale = new(signX * scaleX, signY * scaleY);
bg.Position = view.Center; bg.Position = view.Center;
bg.Rotation = view.Rotation; bg.Rotation = view.Rotation;
_renderer.Draw(bg);
if (_wallpaperView) if (_vmMain.IsVisible) _renderer.Draw(bg);
{ if (_wallpaperView) _wallpaperRenderer.Draw(bg);
_wallpaperRenderer.Draw(bg);
}
} }
} }
if (_showAxis) if (_showAxis && _vmMain.IsVisible)
{ {
// 画一个很长的坐标轴, 用 1e9 比较合适 // 画一个很长的坐标轴, 用 1e9 比较合适
_axisVertices[0] = new(new(-1e9f, 0), _axisColor); _axisVertices[0] = new(new(-1e9f, 0), _axisColor);
@@ -551,35 +545,30 @@ namespace SpineViewer.ViewModels.MainWindow
sp.Update(0); // 避免物理效果出现问题 sp.Update(0); // 避免物理效果出现问题
sp.Update(delta * _speed); sp.Update(delta * _speed);
// 为选中对象绘制一个半透明背景 if (_vmMain.IsVisible)
if (sp.IsSelected)
{ {
var rc = sp.GetCurrentBounds().ToFloatRect(); // 为选中对象绘制一个半透明背景
_selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor); if (sp.IsSelected)
_selectedBackgroundVertices[1] = new(new(rc.Left + rc.Width, rc.Top), _selectedBackgroundColor); {
_selectedBackgroundVertices[2] = new(new(rc.Left + rc.Width, rc.Top + rc.Height), _selectedBackgroundColor); var rc = sp.GetCurrentBounds().ToFloatRect();
_selectedBackgroundVertices[3] = new(new(rc.Left, rc.Top + rc.Height), _selectedBackgroundColor); _selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor);
_renderer.Draw(_selectedBackgroundVertices); _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; sp.EnableDebug = true;
_renderer.Draw(sp); _renderer.Draw(sp);
sp.EnableDebug = false; sp.EnableDebug = false;
if (_wallpaperView)
{
_wallpaperRenderer.Draw(sp);
} }
if (_wallpaperView) _wallpaperRenderer.Draw(sp);
} }
} }
_renderer.Display(); if (_vmMain.IsVisible) _renderer.Display();
if (_wallpaperView) _wallpaperRenderer.Display();
if (_wallpaperView)
{
_wallpaperRenderer.Display();
}
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -15,6 +15,7 @@
Height="720" Height="720"
Background="{DynamicResource RegionBrush}" Background="{DynamicResource RegionBrush}"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
Visibility="{Binding Visibility, Mode=OneWayToSource}"
PreviewKeyDown="MainWindow_PreviewKeyDown" PreviewKeyDown="MainWindow_PreviewKeyDown"
LocationChanged="MainWindow_LocationChanged" LocationChanged="MainWindow_LocationChanged"
SizeChanged="MainWindow_SizeChanged"> SizeChanged="MainWindow_SizeChanged">
@@ -31,6 +32,7 @@
MouseDoubleClick="_notifyIcon_MouseDoubleClick"> MouseDoubleClick="_notifyIcon_MouseDoubleClick">
<hc:NotifyIcon.ContextMenu> <hc:NotifyIcon.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="{DynamicResource Str_Debug}" Click="DebugMenuItem_Click" Visibility="{Binding IsDebug, Mode=OneWay, Converter={StaticResource Boolean2VisibilityConverter}}"/>
<MenuItem Header="{DynamicResource Str_WallpaperView}" Command="{Binding Cmd_SwitchWallpaperView}" IsChecked="{Binding PreferenceViewModel.WallpaperView}"/> <MenuItem Header="{DynamicResource Str_WallpaperView}" Command="{Binding Cmd_SwitchWallpaperView}" IsChecked="{Binding PreferenceViewModel.WallpaperView}"/>
<Separator/> <Separator/>
<MenuItem Header="{DynamicResource Str_Exit}" Command="{Binding Cmd_ExitFromTray}"/> <MenuItem Header="{DynamicResource Str_Exit}" Command="{Binding Cmd_ExitFromTray}"/>
@@ -63,7 +65,7 @@
<MenuItem Header="{DynamicResource Str_Diagnostics}" Command="{Binding Cmd_ShowDiagnosticsDialog}"/> <MenuItem Header="{DynamicResource Str_Diagnostics}" Command="{Binding Cmd_ShowDiagnosticsDialog}"/>
<Separator/> <Separator/>
<MenuItem Header="{DynamicResource Str_Abount}" Command="{Binding Cmd_ShowAboutDialog}"/> <MenuItem Header="{DynamicResource Str_Abount}" Command="{Binding Cmd_ShowAboutDialog}"/>
<MenuItem Header="{DynamicResource Str_Debug}" Click="DebugMenuItem_Click"/> <MenuItem Header="{DynamicResource Str_Debug}" Click="DebugMenuItem_Click" Visibility="{Binding IsDebug, Mode=OneWay, Converter={StaticResource Boolean2VisibilityConverter}}"/>
</MenuItem> </MenuItem>
<!--<MenuItem Header="{DynamicResource Str_Experiment}"/>--> <!--<MenuItem Header="{DynamicResource Str_Experiment}"/>-->
</Menu> </Menu>
@@ -682,125 +684,6 @@
</Grid> </Grid>
</TabItem> </TabItem>
<!-- 浏览页 -->
<TabItem DataContext="{Binding ExplorerListViewModel}">
<TabItem.Header>
<Border Style="{StaticResource MyTabItemHeaderContainerStyle}"
MouseLeftButtonDown="MainTabControlHeader_MouseLeftButtonDown">
<Viewbox Width="24" Height="24" ToolTip="{DynamicResource Str_Explorer}">
<Path Data="{StaticResource Geo_Image}" Style="{StaticResource MyTabItemHeaderPathStyle}"/>
</Viewbox>
</Border>
</TabItem.Header>
<Grid x:Name="_explorerGrid">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<hc:TextBox hc:InfoElement.Placeholder="{DynamicResource Str_Filter}"
Text="{Binding FilterString, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="1"
hc:IconElement.Geometry="{StaticResource Geo_Folder}"
Command="{Binding Cmd_ChangeCurrentDirectory}"
ToolTip="{DynamicResource Str_ChangeCurrentDirectoryTooltip}"/>
<Button Grid.Column="2"
hc:IconElement.Geometry="{StaticResource Geo_ArrowRotateRight}"
Command="{Binding Cmd_RefreshItems}"
ToolTip="{DynamicResource Str_RefreshItemsTooltip}"/>
</Grid>
<StatusBar DockPanel.Dock="Bottom">
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource StrFmtCvter}" ConverterParameter="Str_ListViewStatusBar">
<Binding Path="Items.Count" ElementName="_spineFilesListBox"/>
<Binding Path="SelectedItems.Count" ElementName="_spineFilesListBox"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StatusBar>
<ListBox x:Name="_spineFilesListBox"
VirtualizingPanel.IsVirtualizing="True"
ItemsSource="{Binding ShownItems}"
DisplayMemberPath="FileName"
MouseLeftButtonDown="SpineFilesListBox_MouseLeftButtonDown">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding Cmd_SelectionChanged}"
CommandParameter="{Binding SelectedItems, ElementName=_spineFilesListBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_AddSelectedItems}"
Command="{Binding Cmd_AddSelectedItems}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
<Separator/>
<MenuItem Header="{DynamicResource Str_GeneratePreviewForSelected}"
Command="{Binding Cmd_GeneratePreviews}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
<MenuItem Header="{DynamicResource Str_DeletePreviewsForSelected}"
Command="{Binding Cmd_DeletePreviews}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</DockPanel>
<GridSplitter Grid.Row="1" ResizeDirection="Rows"/>
<Grid Grid.Row="2" DataContext="{Binding SelectedItem}">
<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>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 文件目录 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_FileDirectory}"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding FileDirectory, Mode=OneWay}"
IsReadOnly="True"
ToolTip="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>
<!-- 文件名 -->
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_FileName}"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding FileName, Mode=OneWay}"
IsReadOnly="True"/>
<!-- 预览图 -->
<Border Grid.Row="2" Grid.ColumnSpan="2" Background="{DynamicResource DarkDefaultBrush}">
<Image Source="{Binding PreviewImage, Mode=OneWay}" Stretch="Uniform"/>
</Border>
</Grid>
</Grid>
</TabItem>
<!-- 画面参数页 --> <!-- 画面参数页 -->
<TabItem DataContext="{Binding SFMLRendererViewModel}"> <TabItem DataContext="{Binding SFMLRendererViewModel}">
<TabItem.Header> <TabItem.Header>
@@ -991,6 +874,126 @@
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>
<!-- 浏览页 -->
<TabItem DataContext="{Binding ExplorerListViewModel}">
<TabItem.Header>
<Border Style="{StaticResource MyTabItemHeaderContainerStyle}"
MouseLeftButtonDown="MainTabControlHeader_MouseLeftButtonDown">
<Viewbox Width="24" Height="24" ToolTip="{DynamicResource Str_Explorer}">
<Path Data="{StaticResource Geo_Image}" Style="{StaticResource MyTabItemHeaderPathStyle}"/>
</Viewbox>
</Border>
</TabItem.Header>
<Grid x:Name="_explorerGrid">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<hc:TextBox hc:InfoElement.Placeholder="{DynamicResource Str_Filter}"
Text="{Binding FilterString, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="1"
hc:IconElement.Geometry="{StaticResource Geo_Folder}"
Command="{Binding Cmd_ChangeCurrentDirectory}"
ToolTip="{DynamicResource Str_ChangeCurrentDirectoryTooltip}"/>
<Button Grid.Column="2"
hc:IconElement.Geometry="{StaticResource Geo_ArrowRotateRight}"
Command="{Binding Cmd_RefreshItems}"
ToolTip="{DynamicResource Str_RefreshItemsTooltip}"/>
</Grid>
<StatusBar DockPanel.Dock="Bottom">
<TextBlock Foreground="{DynamicResource PrimaryTextBrush}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource StrFmtCvter}" ConverterParameter="Str_ListViewStatusBar">
<Binding Path="Items.Count" ElementName="_spineFilesListBox"/>
<Binding Path="SelectedItems.Count" ElementName="_spineFilesListBox"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StatusBar>
<ListBox x:Name="_spineFilesListBox"
VirtualizingPanel.IsVirtualizing="True"
ItemsSource="{Binding ShownItems}"
DisplayMemberPath="FileName"
MouseLeftButtonDown="SpineFilesListBox_MouseLeftButtonDown">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding Cmd_SelectionChanged}"
CommandParameter="{Binding SelectedItems, ElementName=_spineFilesListBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_AddSelectedItems}"
Command="{Binding Cmd_AddSelectedItems}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
<Separator/>
<MenuItem Header="{DynamicResource Str_GeneratePreviewForSelected}"
Command="{Binding Cmd_GeneratePreviews}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
<MenuItem Header="{DynamicResource Str_DeletePreviewsForSelected}"
Command="{Binding Cmd_DeletePreviews}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</DockPanel>
<GridSplitter Grid.Row="1" ResizeDirection="Rows"/>
<Grid Grid.Row="2" DataContext="{Binding SelectedItem}">
<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>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 文件目录 -->
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Str_FileDirectory}"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding FileDirectory, Mode=OneWay}"
IsReadOnly="True"
ToolTip="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>
<!-- 文件名 -->
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Str_FileName}"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding FileName, Mode=OneWay}"
IsReadOnly="True"/>
<!-- 预览图 -->
<Border Grid.Row="2" Grid.ColumnSpan="2" Background="{DynamicResource DarkDefaultBrush}">
<Image Source="{Binding PreviewImage, Mode=OneWay}" Stretch="Uniform"/>
</Border>
</Grid>
</Grid>
</TabItem>
</TabControl> </TabControl>
</Border> </Border>

View File

@@ -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[0], RowDefinition.HeightProperty, DelayedSaveUserState));
_userStateWatchers.Add(PropertyWatcher.Watch(_rightPanelGrid.RowDefinitions[2], RowDefinition.HeightProperty, DelayedSaveUserState)); _userStateWatchers.Add(PropertyWatcher.Watch(_rightPanelGrid.RowDefinitions[2], RowDefinition.HeightProperty, DelayedSaveUserState));
_vm.ExplorerListViewModel.PropertyChanged += ExplorerListUserStateChanged;
_vm.SFMLRendererViewModel.PropertyChanged += SFMLRendererUserStateChanged; _vm.SFMLRendererViewModel.PropertyChanged += SFMLRendererUserStateChanged;
} }
@@ -234,6 +235,7 @@ public partial class MainWindow : Window
// 撤除所有状态监听器 // 撤除所有状态监听器
_vm.SFMLRendererViewModel.PropertyChanged -= SFMLRendererUserStateChanged; _vm.SFMLRendererViewModel.PropertyChanged -= SFMLRendererUserStateChanged;
_vm.ExplorerListViewModel.PropertyChanged -= ExplorerListUserStateChanged;
foreach (var w in _userStateWatchers) w.Dispose(); foreach (var w in _userStateWatchers) w.Dispose();
_userStateWatchers.Clear(); _userStateWatchers.Clear();
@@ -284,6 +286,8 @@ public partial class MainWindow : Window
_rightPanelGrid.RowDefinitions[0].Height = new(m.RightPanelGridRow0Height, GridUnitType.Star); _rightPanelGrid.RowDefinitions[0].Height = new(m.RightPanelGridRow0Height, GridUnitType.Star);
_rightPanelGrid.RowDefinitions[2].Height = new(m.RightPanelGridRow2Height, 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.SetResolution(m.ResolutionX, m.ResolutionY);
_vm.SFMLRendererViewModel.MaxFps = m.MaxFps; _vm.SFMLRendererViewModel.MaxFps = m.MaxFps;
_vm.SFMLRendererViewModel.Speed = m.Speed; _vm.SFMLRendererViewModel.Speed = m.Speed;
@@ -317,6 +321,8 @@ public partial class MainWindow : Window
RightPanelGridRow0Height = _rightPanelGrid.RowDefinitions[0].Height.Value, RightPanelGridRow0Height = _rightPanelGrid.RowDefinitions[0].Height.Value,
RightPanelGridRow2Height = _rightPanelGrid.RowDefinitions[2].Height.Value, RightPanelGridRow2Height = _rightPanelGrid.RowDefinitions[2].Height.Value,
ExploringDirectory = _vm.ExplorerListViewModel.CurrentDirectory,
ResolutionX = _vm.SFMLRendererViewModel.ResolutionX, ResolutionX = _vm.SFMLRendererViewModel.ResolutionX,
ResolutionY = _vm.SFMLRendererViewModel.ResolutionY, ResolutionY = _vm.SFMLRendererViewModel.ResolutionY,
MaxFps = _vm.SFMLRendererViewModel.MaxFps, MaxFps = _vm.SFMLRendererViewModel.MaxFps,
@@ -356,6 +362,18 @@ public partial class MainWindow : Window
_saveUserStateTimer.Start(); _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) private void SFMLRendererUserStateChanged(object? sender, PropertyChangedEventArgs e)
{ {
switch (e.PropertyName) switch (e.PropertyName)

View File

@@ -8,7 +8,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath> <BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.16.10</Version> <Version>0.16.11</Version>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
</PropertyGroup> </PropertyGroup>