新增翻译模组保存功能

This commit is contained in:
Hakoyu 2023-11-18 22:31:06 +08:00
parent d1619d074f
commit a588e09080
15 changed files with 414 additions and 36 deletions

View File

@ -13,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using VPet.ModMaker.Models.ModModel;
using VPet.ModMaker.Views.ModEdit.I18nEdit;
using VPet_Simulator.Core;
using VPet_Simulator.Windows.Interface;
@ -578,11 +579,42 @@ public class ModInfoModel : I18nModel<I18nModInfoModel>
}
}
#endregion
/// <summary>
/// 关闭
/// </summary>
public void Close()
{
Image.Value.CloseStream();
foreach (var food in Foods)
food.Close();
foreach (var pet in Pets)
pet.Close();
Current = null;
}
public void SaveTranslationMod(string path, IEnumerable<string> cultures)
{
// 保存模型信息
SaveModInfo(path);
// 保存文化数据
var langPath = Path.Combine(path, "lang");
Directory.CreateDirectory(langPath);
foreach (var cultureName in cultures)
{
var culturePath = Path.Combine(langPath, cultureName);
Directory.CreateDirectory(culturePath);
var cultureFile = Path.Combine(culturePath, $"{cultureName}.lps");
File.Create(cultureFile).Close();
var lps = new LPS();
foreach (var data in I18nEditWindow.Current.ViewModel.AllI18nDatas)
lps.Add(
new Line(
data.Key,
data.Value.Datas[I18nHelper.Current.CultureNames.IndexOf(cultureName)].Value
)
);
File.WriteAllText(cultureFile, lps.ToString());
}
}
}

View File

@ -206,7 +206,13 @@ public class PetModel : I18nModel<I18nPetInfoModel>
IsSimplePetModel = isSimplePet;
}
public void Close() { }
public void Close()
{
foreach (var anime in Animes)
anime.Close();
foreach (var anime in FoodAnimes)
anime.Close();
}
#region Save
/// <summary>

View File

@ -47,7 +47,7 @@ public static class Utils
/// <summary>
/// 载入图片至内存流
/// </summary>
/// <param name="imagePath"></param>
/// <param name="imagePath">图片路径</param>
/// <returns></returns>
public static BitmapImage LoadImageToMemoryStream(string imagePath)
{
@ -65,4 +65,25 @@ public static class Utils
}
return bitmapImage;
}
/// <summary>
/// 载入图片至内存流
/// </summary>
/// <param name="imageStream">图片流</param>
/// <returns></returns>
public static BitmapImage LoadImageToMemoryStream(Stream imageStream)
{
BitmapImage bitmapImage = new();
bitmapImage.BeginInit();
try
{
bitmapImage.StreamSource = imageStream;
bitmapImage.DecodePixelWidth = DecodePixelWidth;
}
finally
{
bitmapImage.EndInit();
}
return bitmapImage;
}
}

View File

@ -0,0 +1,64 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
namespace VPet.ModMaker.Resources;
/// <summary>
/// 本地资源
/// </summary>
internal class NativeResources
{
/// <summary>
/// 资源基路径
/// </summary>
public const string ResourcePath = $"{nameof(VPet)}.{nameof(ModMaker)}.{nameof(Resources)}";
/// <summary>
/// 食物图片
/// </summary>
public const string FoodImage = $"{ResourcePath}.food.png";
#region Native
private static readonly Assembly _assembly = Assembly.GetExecutingAssembly();
/// <summary>
/// 获取资源流
/// </summary>
/// <param name="resourceName">资源名</param>
/// <returns>资源流</returns>
public static Stream GetStream(string resourceName) =>
_assembly.GetManifestResourceStream(resourceName)!;
/// <summary>
/// 尝试获取资源流
/// </summary>
/// <param name="resourceName">资源名</param>
/// <param name="resourceStream">资源流</param>
/// <returns>成功为 <see langword="true"/> 失败为 <see langword="false"/></returns>
public static bool TryGetStream(string resourceName, out Stream resourceStream)
{
resourceStream = null;
if (_assembly.GetManifestResourceStream(resourceName) is not Stream stream)
return false;
resourceStream = stream;
return true;
}
/// <summary>
/// 将流保存至文件
/// </summary>
/// <param name="resourceName">资源名</param>
/// <param name="path">文件路径</param>
/// <returns>成功为 <see langword="true"/> 失败为 <see langword="false"/></returns>
public static bool SaveTo(string resourceName, string path)
{
if (_assembly.GetManifestResourceStream(resourceName) is not Stream stream)
return false;
using var sr = new StreamReader(stream);
using var sw = new StreamWriter(path);
sr.BaseStream.CopyTo(sw.BaseStream);
return true;
}
#endregion
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -94,4 +94,43 @@
<Setter Property="pu:WindowXCaption.Background" Value="{DynamicResource DARKPrimary}" />
<Setter Property="pu:WindowXCaption.Foreground" Value="{DynamicResource DARKPrimaryText}" />
</Style>
<Style
x:Key="Menu_Style"
BasedOn="{StaticResource {x:Static pu:StyleKeys.MenuStyle}}"
TargetType="Menu">
<Setter Property="Height" Value="NaN" />
<Setter Property="Margin" Value="0" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Foreground" Value="{DynamicResource DARKPrimaryText}" />
<Setter Property="Background" Value="{DynamicResource DARKPrimary}" />
<Setter Property="pu:MenuHelper.CornerRadius" Value="4" />
<Setter Property="pu:DropDownHelper.CornerRadius" Value="4" />
<!-- -->
<Setter Property="pu:MenuHelper.TopLevelItemsBorderThickness" Value="0" />
<Setter Property="pu:MenuHelper.TopLevelItemsPadding" Value="5,0,5,0" />
<Setter Property="pu:MenuHelper.TopLevelItemsMargin" Value="0" />
<Setter Property="pu:MenuHelper.TopLevelItemsHorizontalContentAlignment" Value="Center" />
<Setter Property="pu:MenuHelper.TopLevelItemsBackground" Value="{DynamicResource DARKPrimary}" />
<Setter Property="pu:MenuHelper.TopLevelItemsForeground" Value="{DynamicResource DARKPrimaryText}" />
<!-- -->
<Setter Property="pu:MenuHelper.SubmenuItemsBorderThickness" Value="0" />
<Setter Property="pu:MenuHelper.SubmenuItemsBorderBrush" Value="{DynamicResource DARKPrimary}" />
<Setter Property="pu:MenuHelper.SubmenuItemsWidth" Value="NaN" />
<Setter Property="pu:MenuHelper.SubmenuItemsHeight" Value="NaN" />
<Setter Property="pu:MenuHelper.SubmenuItemsPadding" Value="5" />
<Setter Property="pu:MenuHelper.SubmenuItemsMargin" Value="0" />
<!--<Setter Property="pu:MenuHelper.SubmenuItemsCornerRadius" Value="4" />-->
<Setter Property="pu:MenuHelper.SubmenuItemsBackground" Value="{DynamicResource DARKPrimary}" />
<Setter Property="pu:MenuHelper.SubmenuItemsForeground" Value="{DynamicResource DARKPrimaryText}" />
<!--<Setter Property="Width" Value="NaN" />
<Setter Property="Height" Value="NaN" />
<Setter Property="Padding" Value="5" />
<Setter Property="Background" Value="{DynamicResource BackgroundColor}" />
<Setter Property="Foreground" Value="{DynamicResource ForegroundColor}" />
<Setter Property="FontSize" Value="{DynamicResource BodyFontSize}" />
<Setter Property="pu:MenuItemHelper.HoverBackground" Value="{DynamicResource HoverColor}" />
<Setter Property="pu:MenuItemHelper.ClickBackground" Value="{DynamicResource ClickColor}" />
<Setter Property="pu:MenuItemHelper.CheckedBackground" Value="{DynamicResource CheckedColor}" />-->
</Style>
</ResourceDictionary>

View File

@ -125,6 +125,7 @@
<Compile Include="Models\ModModel\PetModel.cs" />
<Compile Include="Models\ModModel\SelectTextModel.cs" />
<Compile Include="Models\ModModel\WorkModel.cs" />
<Compile Include="Resources\NativeResources.cs" />
<Compile Include="SimpleObservable\ObservableCommandT.cs" />
<Compile Include="ModMakerStyles.cs" />
<Compile Include="SimpleObservable\ObservableValueGroup.cs" />
@ -144,6 +145,7 @@
<Compile Include="ViewModels\ModEdit\MoveEdit\MovePageVM.cs" />
<Compile Include="ViewModels\ModEdit\PetEdit\PetEditWindowVM.cs" />
<Compile Include="ViewModels\ModEdit\PetEdit\PetPageVM.cs" />
<Compile Include="ViewModels\ModEdit\SaveTranslationModWindowVM.cs" />
<Compile Include="ViewModels\ModEdit\SelectTextEdit\SelectTextEditWindowVM.cs" />
<Compile Include="ViewModels\ModEdit\SelectTextEdit\SelectTextPageVM.cs" />
<Compile Include="ViewModels\ModEdit\WorkEdit\WorkEditWindowVM.cs" />
@ -218,6 +220,9 @@
<Compile Include="Views\ModEdit\PetEdit\PetPage.xaml.cs">
<DependentUpon>PetPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ModEdit\SaveTranslationModWindow.xaml.cs">
<DependentUpon>SaveTranslationModWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ModEdit\SelectTextEdit\SelectTextPage.xaml.cs">
<DependentUpon>SelectTextPage.xaml</DependentUpon>
</Compile>
@ -329,6 +334,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ModEdit\SaveTranslationModWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ModEdit\SelectTextEdit\SelectTextPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -366,6 +375,8 @@
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<EmbeddedResource Include="Resources\food.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -11,6 +11,7 @@ using System.Windows;
using System.Windows.Media.Imaging;
using VPet.ModMaker.Models;
using VPet.ModMaker.Models.ModModel;
using VPet.ModMaker.Resources;
using VPet_Simulator.Core;
namespace VPet.ModMaker.ViewModels.ModEdit.AnimeEdit;
@ -22,13 +23,12 @@ public class FoodAnimeEditWindowVM
/// </summary>
public PetModel CurrentPet { get; set; }
// TODO: 使用内部资源
/// <summary>
/// 默认食物图片
/// </summary>
public static BitmapImage DefaultFoodImage { get; } =
Utils.LoadImageToMemoryStream(
"C:\\Users\\HKW\\Desktop\\TestPicture\\0000_core\\image\\food.png"
);
Utils.LoadImageToMemoryStream(NativeResources.GetStream(NativeResources.FoodImage));
/// <summary>
/// 食物图片
@ -377,16 +377,6 @@ public class FoodAnimeEditWindowVM
CurrentFrontImageModel.Value.Close();
CurrentFrontImageModel.Value.Image.Value = newImage;
}
private void ShowFrontImagesPathInfo(FoodImagesPath imagesPath)
{
MessageBox.Show(
"此顶层动画源位于其它位置\n请去源位置修改此动画\n源位置 模式:{0} 索引:{1}".Translate(
imagesPath.Mode,
imagesPath.Index
)
);
}
#endregion
#region BackImageCommand

View File

@ -72,12 +72,17 @@ public class ModEditWindowVM
/// 编辑多语言内容
/// </summary>
public ObservableCommand EditI18nCommand { get; } = new();
/// <summary>
/// 保存为翻译模组
/// </summary>
public ObservableCommand SaveAsTranslationModCommand { get; } = new();
#endregion
public ModEditWindowVM(ModEditWindow window)
{
ModEditWindow = window;
new I18nEditWindow();
ChangeImageCommand.ExecuteEvent += ChangeImage;
AddCultureCommand.ExecuteEvent += AddCulture;
EditCultureCommand.ExecuteEvent += EditCulture;
@ -86,17 +91,21 @@ public class ModEditWindowVM
SaveCommand.ExecuteEvent += Save;
SaveToCommand.ExecuteEvent += SaveTo;
SaveAsTranslationModCommand.ExecuteEvent += SaveAsTranslationMod;
}
private void SaveAsTranslationMod()
{
if (ValidationData(ModInfo.Value) is false)
return;
var window = new SaveTranslationModWindow();
window.ShowDialog();
}
private void EditI18n()
{
if (I18nEditWindow.Instance is not null)
{
I18nEditWindow.Instance.Activate();
return;
}
var window = new I18nEditWindow(ModInfo.Value);
window.Show();
I18nEditWindow.Current.Visibility = Visibility.Visible;
I18nEditWindow.Current.Activate();
}
/// <summary>
@ -105,7 +114,7 @@ public class ModEditWindowVM
public void Close()
{
ModInfo.Value.Image.Value?.StreamSource?.Close();
I18nEditWindow.Instance?.Close();
I18nEditWindow.Current?.Close(true);
}
/// <summary>
@ -205,7 +214,7 @@ public class ModEditWindowVM
SaveFileDialog saveFileDialog =
new()
{
Title = "保存 模组信息文件,并在文件夹内保存模组数据".Translate(),
Title = "保存模组信息文件,并在文件夹内保存模组数据".Translate(),
Filter = $"LPS文件|*.lps;".Translate(),
FileName = "info.lps".Translate()
};

View File

@ -0,0 +1,83 @@
using HKW.HKWViewModels.SimpleObservable;
using LinePutScript.Localization.WPF;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using VPet.ModMaker.Models;
namespace VPet.ModMaker.ViewModels.ModEdit;
public class SaveTranslationModWindowVM
{
#region Value
public ObservableValue<bool?> CheckAll { get; } = new(true);
public ObservableCollection<CheckCultureModel> CheckCultures { get; } = [];
#endregion
#region Command
public ObservableCommand SaveCommand { get; } = new();
#endregion
public SaveTranslationModWindowVM()
{
foreach (var culture in I18nHelper.Current.CultureNames)
{
var model = new CheckCultureModel();
model.CultureName.Value = culture;
CheckCultures.Add(model);
CheckAll.AddNotifySender(model.IsChecked);
}
CheckAll.SenderPropertyChanged += CheckAll_SenderPropertyChanged;
SaveCommand.ExecuteEvent += Save;
}
private void CheckAll_SenderPropertyChanged(ObservableValue<bool?> source, INotifyPropertyChanged sender)
{
var count = 0;
foreach (var model in CheckCultures)
if (model.IsChecked.Value)
count += 1;
if (count == CheckCultures.Count)
source.Value = true;
else if (count == 0)
source.Value = false;
else
source.Value = null;
}
public void Save()
{
SaveFileDialog saveFileDialog = new()
{
Title = "保存模组信息文件,并在文件夹内保存模组数据".Translate(),
Filter = $"LPS文件|*.lps;".Translate(),
FileName = "info.lps".Translate()
};
if (saveFileDialog.ShowDialog() is not true)
return;
try
{
ModInfoModel.Current.SaveTranslationMod(Path.GetDirectoryName(saveFileDialog.FileName), CheckCultures.Where(m => m.IsChecked.Value).Select(m => m.CultureName.Value));
MessageBox.Show("保存成功".Translate());
}
catch (Exception ex)
{
MessageBox.Show("保存失败 错误信息:\n{0}".Translate(ex));
}
}
}
public class CheckCultureModel
{
public ObservableValue<bool> IsChecked { get; } = new(true);
public ObservableValue<string> CultureName { get; } = new();
}

View File

@ -202,7 +202,6 @@ public class ModMakerWindowVM
SaveHistories();
}
ModInfoModel.Current.Close();
ModInfoModel.Current = null;
I18nHelper.Current = new();
ModMakerWindow.Show();
};

View File

@ -20,28 +20,41 @@ public partial class I18nEditWindow : WindowX
{
public bool IsCancel { get; private set; } = true;
public static I18nEditWindow Instance { get; private set; }
public static I18nEditWindow Current { get; private set; }
public I18nEditWindowVM ViewModel => (I18nEditWindowVM)DataContext;
public I18nEditWindow(ModInfoModel model)
public I18nEditWindow()
{
InitializeComponent();
DataContext = new I18nEditWindowVM();
ViewModel.CultureChanged += ViewModel_CultureChanged;
ViewModel.InitializeI18nData(model);
Instance = this;
ViewModel.InitializeI18nData(ModInfoModel.Current);
Current = this;
// 只隐藏, 不关闭
Closing += (s, e) =>
{
Visibility = Visibility.Hidden;
if (_close is false)
e.Cancel = true;
};
Closed += (s, e) =>
{
ViewModel.Close();
try
{
DataContext = null;
Instance = null;
Current = null;
}
catch { }
};
}
private bool _close = false;
public void Close(bool close)
{
_close = close;
Close();
}
private void ViewModel_CultureChanged(object sender, string newCulture)
{
@ -60,7 +73,7 @@ public partial class I18nEditWindow : WindowX
/// <summary>
/// (culture, Column)
/// </summary>
private readonly Dictionary<string, DataGridTextColumn> _dataGridI18nColumns = new();
private readonly Dictionary<string, DataGridTextColumn> _dataGridI18nColumns = [];
/// <summary>
/// 添加文化列

View File

@ -305,20 +305,30 @@
</ListBox>
<StackPanel Grid.Row="2">
<Button
x:Name="Button_EditI18n"
Command="{Binding EditI18nCommand}"
Content="{ll:Str 编辑多语言内容}"
Style="{DynamicResource ThemedButtonStyle}" />
<Button
x:Name="Button_Save"
Command="{Binding SaveCommand}"
Content="{ll:Str 保存}"
Style="{DynamicResource ThemedButtonStyle}" />
<Button
x:Name="Button_SaveTo"
Command="{Binding SaveToCommand}"
Content="{ll:Str 保存至}"
Style="{DynamicResource ThemedButtonStyle}" />
<Button
Command="{Binding SaveAsTranslationModCommand}"
Content="{ll:Str 保存为翻译模组}"
Style="{DynamicResource ThemedButtonStyle}" />
<!--<Menu
x:Name="Button_SaveAs"
HorizontalContentAlignment="Stretch"
pu:MenuHelper.TopLevelItemsHorizontalContentAlignment="Stretch"
Style="{DynamicResource Menu_Style}">
<MenuItem Header="{ll:Str 保存为}">
<MenuItem Command="{Binding SaveAsCommand}" />
</MenuItem>
</Menu>-->
</StackPanel>
</Grid>
</Grid>

View File

@ -0,0 +1,61 @@
<pu:WindowX
x:Class="VPet.ModMaker.Views.ModEdit.SaveTranslationModWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ll="clr-namespace:LinePutScript.Localization.WPF;assembly=LinePutScript.Localization.WPF"
xmlns:local="clr-namespace:VPet.ModMaker.Views.ModEdit"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:vm="clr-namespace:VPet.ModMaker.ViewModels.ModEdit"
Title="{ll:Str 保存为翻译模组}"
Width="500"
Height="300"
d:DataContext="{d:DesignInstance Type=vm:SaveTranslationModWindowVM}"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label HorizontalContentAlignment="Center" Content="{ll:Str 选择文化}" />
<ListBox
Grid.Row="1"
d:ItemsSource="{d:SampleData ItemCount=5}"
ItemsSource="{Binding CheckCultures}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox
d:Content="zh-CN"
d:IsChecked="True"
Content="{Binding CultureName.Value}"
IsChecked="{Binding IsChecked.Value}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox
Margin="5,0,5,0"
Content="{ll:Str 全选}"
IsChecked="{Binding CheckAll.Value}"
IsThreeState="True" />
<Button
Grid.Column="1"
Margin="10"
Command="{Binding SaveCommand}"
Content="{ll:Str 保存}"
Style="{DynamicResource ThemedButtonStyle}" />
</Grid>
</Grid>
</pu:WindowX>

View File

@ -0,0 +1,40 @@
using Panuon.WPF.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using VPet.ModMaker.ViewModels.ModEdit;
namespace VPet.ModMaker.Views.ModEdit;
/// <summary>
/// SaveTranslationModWindow.xaml 的交互逻辑
/// </summary>
public partial class SaveTranslationModWindow : WindowX
{
public SaveTranslationModWindowVM ViewModel => (SaveTranslationModWindowVM)DataContext;
public SaveTranslationModWindow()
{
InitializeComponent();
DataContext = new SaveTranslationModWindowVM();
Closed += (s, e) =>
{
//ViewModel.Close();
try
{
DataContext = null;
}
catch { }
};
}
}