This commit is contained in:
Hakoyu 2023-10-16 23:53:07 +08:00
parent 434c43a74c
commit 9f361f1bab
11 changed files with 280 additions and 33 deletions

View File

@ -9,4 +9,6 @@
<c:BrushToMediaColorConverter x:Key="BrushToMediaColorConverter" />
<c:MaxConverter x:Key="MaxConverter" />
<c:FalseToHiddenConverter x:Key="FalseToHiddenConverter" />
<c:EqualsConverter x:Key="EqualsConverter" />
<c:NotEqualsConverter x:Key="NotEqualsConverter" />
</ResourceDictionary>

View File

@ -0,0 +1,29 @@
using System;
using System.Windows.Data;
namespace VPet.ModMaker.Converters;
public class EqualsConverter : IMultiValueConverter
{
public object Convert(
object[] values,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture
)
{
if (values.Length != 2)
throw new NotImplementedException("Values length must be 2");
return values[0].Equals(values[1]);
}
public object[] ConvertBack(
object value,
Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture
)
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace VPet.ModMaker.Converters;
public class FalseToCollapsedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool.TryParse(value.ToString(), out var result) && result)
? Visibility.Visible
: Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is Visibility visibility && visibility == Visibility.Collapsed;
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Windows.Data;
namespace VPet.ModMaker.Converters;
public class NotEqualsConverter : IMultiValueConverter
{
public object Convert(
object[] values,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture
)
{
if (values.Length != 2)
throw new NotImplementedException("Values length must be 2");
return !values[0].Equals(values[1]);
}
public object[] ConvertBack(
object value,
Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture
)
{
throw new NotImplementedException();
}
}

View File

@ -18,7 +18,7 @@ public class FoodLocationModel
public ObservableValue<int> Duration { get; } = new(100);
/// <summary>
/// 定位
/// 范围
/// </summary>
public ObservableRect<double> Rect { get; } = new();

View File

@ -93,8 +93,11 @@
</Compile>
<Compile Include="Converters\BrushToMediaColorConverter.cs" />
<Compile Include="Converters\CalculatorConverter.cs" />
<Compile Include="Converters\EqualsConverter.cs" />
<Compile Include="Converters\FalseToCollapsedConverter.cs" />
<Compile Include="Converters\FalseToHiddenConverter.cs" />
<Compile Include="Converters\MediaColorToBrushConverter.cs" />
<Compile Include="Converters\NotEqualsConverter.cs" />
<Compile Include="Converters\StringFormatConverter.cs" />
<Compile Include="Converters\RatioMarginConverter.cs" />
<Compile Include="Converters\MaxConverter.cs" />

View File

@ -3,6 +3,7 @@ using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -19,13 +20,23 @@ public class AnimePageVM
/// <summary>
/// 显示的动画
/// </summary>
public ObservableValue<ObservableCollection<AnimeTypeModel>> ShowAnimes { get; } = new();
public ObservableValue<ObservableCollection<object>> ShowAnimes { get; } = new();
/// <summary>
/// 所有动画
/// </summary>
public ObservableCollection<object> AllAnimes { get; } = new();
/// <summary>
/// 动画
/// </summary>
public ObservableCollection<AnimeTypeModel> Animes => CurrentPet.Value.Animes;
/// <summary>
/// 食物动画
/// </summary>
public ObservableCollection<FoodAnimeTypeModel> FoodAnimes => CurrentPet.Value.FoodAnimes;
/// <summary>
/// 宠物列表
/// </summary>
@ -59,7 +70,7 @@ public class AnimePageVM
#endregion
public AnimePageVM()
{
ShowAnimes.Value = Animes;
ShowAnimes.Value = AllAnimes;
CurrentPet.ValueChanged += CurrentPet_ValueChanged;
Search.ValueChanged += Search_ValueChanged;
@ -68,21 +79,57 @@ public class AnimePageVM
RemoveCommand.ExecuteEvent += Remove;
}
private void InitializeAllAnimes()
{
AllAnimes.Clear();
foreach (var item in Animes)
AllAnimes.Add(item);
foreach (var item in FoodAnimes)
AllAnimes.Add(item);
Animes.CollectionChanged += Animes_CollectionChanged;
FoodAnimes.CollectionChanged += Animes_CollectionChanged;
}
private void Animes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action is NotifyCollectionChangedAction.Add)
AllAnimes.Add(e.NewItems[0]);
else if (e.Action is NotifyCollectionChangedAction.Remove)
AllAnimes.Remove(e.OldItems[0]);
else if (e.Action is NotifyCollectionChangedAction.Replace)
AllAnimes[AllAnimes.IndexOf(e.OldItems[0])] = e.NewItems[0];
}
private void CurrentPet_ValueChanged(PetModel oldValue, PetModel newValue)
{
ShowAnimes.Value = newValue.Animes;
InitializeAllAnimes();
ShowAnimes.Value = AllAnimes;
}
private void Search_ValueChanged(string oldValue, string newValue)
{
if (string.IsNullOrWhiteSpace(newValue))
{
ShowAnimes.Value = Animes;
ShowAnimes.Value = AllAnimes;
}
else
{
ShowAnimes.Value = new(
Animes.Where(m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase))
AllAnimes.Where(m =>
{
if (m is AnimeTypeModel animeTypeModel)
return animeTypeModel.Id.Value.Contains(
newValue,
StringComparison.OrdinalIgnoreCase
);
else if (m is FoodAnimeTypeModel foodAnimeTypeModel)
return foodAnimeTypeModel.Id.Value.Contains(
newValue,
StringComparison.OrdinalIgnoreCase
);
else
throw new Exception("???");
})
);
}
}
@ -98,6 +145,7 @@ public class AnimePageVM
var graphType = selectGraphTypeWindow.GraphType.Value;
if (selectGraphTypeWindow.IsCancel)
return;
// TODO: FoodAnime
var window = new AnimeEditWindow();
var vm = window.ViewModel;
vm.CurrentPet = CurrentPet.Value;
@ -115,6 +163,7 @@ public class AnimePageVM
/// <param name="model">动画类型模型</param>
public void Edit(AnimeTypeModel model)
{
// TODO: FoodAnime
var window = new AnimeEditWindow();
var vm = window.ViewModel;
vm.CurrentPet = CurrentPet.Value;
@ -142,14 +191,14 @@ public class AnimePageVM
{
if (MessageBox.Show("确定删除吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.No)
return;
if (ShowAnimes.Value.Count == Animes.Count)
if (ShowAnimes.Value.Count == AllAnimes.Count)
{
Animes.Remove(model);
AllAnimes.Remove(model);
}
else
{
ShowAnimes.Value.Remove(model);
Animes.Remove(model);
AllAnimes.Remove(model);
}
}
}

View File

@ -24,6 +24,11 @@ public class FoodAnimeEditWindowVM
/// </summary>
public PetModel CurrentPet { get; set; }
/// <summary>
/// 默认食物图片
/// </summary>
public static BitmapImage DefaultFoodImage { get; } = Utils.LoadImageToMemoryStream("");
/// <summary>
/// 食物图片
/// </summary>
@ -114,6 +119,16 @@ public class FoodAnimeEditWindowVM
/// 删除图片命令
/// </summary>
public ObservableCommand<AnimeModel> RemoveImageCommand { get; } = new();
/// <summary>
/// 改变食物图片
/// </summary>
public ObservableCommand ReplaceFoodImageCommand { get; } = new();
/// <summary>
/// 重置食物图片
/// </summary>
public ObservableCommand ResetFoodImageCommand { get; } = new();
#endregion
/// <summary>
@ -150,10 +165,31 @@ public class FoodAnimeEditWindowVM
ClearImageCommand.ExecuteEvent += ClearImageCommand_ExecuteEvent;
RemoveAnimeCommand.ExecuteEvent += RemoveAnimeCommand_ExecuteEvent;
RemoveImageCommand.ExecuteEvent += RemoveImageCommand_ExecuteEvent;
ReplaceFoodImageCommand.ExecuteEvent += ReplaceFoodImageCommand_ExecuteEvent;
ResetFoodImageCommand.ExecuteEvent += ResetFoodImageCommand_ExecuteEvent;
Anime.ValueChanged += Anime_ValueChanged;
}
private void ResetFoodImageCommand_ExecuteEvent()
{
if (FoodImage.Value != DefaultFoodImage)
FoodImage.Value.CloseStream();
FoodImage.Value = DefaultFoodImage;
}
private void ReplaceFoodImageCommand_ExecuteEvent()
{
OpenFileDialog openFileDialog =
new() { Title = "选择食物图片".Translate(), Filter = $"图片|*.png".Translate() };
if (openFileDialog.ShowDialog() is true)
{
if (FoodImage.Value != DefaultFoodImage)
FoodImage.Value.CloseStream();
FoodImage.Value = Utils.LoadImageToMemoryStream(openFileDialog.FileName);
}
}
#region LoadAnime
private void Anime_ValueChanged(FoodAnimeTypeModel oldValue, FoodAnimeTypeModel newValue)
{

View File

@ -21,7 +21,7 @@
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
<DataTemplate x:Key="Expander_AnimeItem" DataType="ListBoxItem">
<DataTemplate x:Key="Expander_AnimeItem" DataType="Expander">
<Expander
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"

View File

@ -21,7 +21,7 @@
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
<DataTemplate x:Key="Expander_AnimeItem" DataType="ListBoxItem">
<DataTemplate x:Key="Expander_AnimeItem" DataType="Expander">
<Expander
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
@ -30,14 +30,17 @@
<Expander.ContextMenu>
<ContextMenu d:DataContext="">
<MenuItem
d:Header="添加图片"
Command="{Binding PlacementTarget.Tag.AddImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="{ll:Str 添加图片}" />
<MenuItem
d:Header="添加图片"
Command="{Binding PlacementTarget.Tag.ClearImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="{ll:Str 清空图片}" />
<MenuItem
d:Header="添加图片"
Command="{Binding PlacementTarget.Tag.RemoveAnimeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="{ll:Str 删除此项}" />
@ -57,16 +60,8 @@
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBox
Grid.Column="1"
pu:TextBoxHelper.Watermark="{ll:Str 动画Id}"
Text="{Binding Id.Value, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox
Grid.Column="2"
Margin="10,0,0,0"
ItemsSource="{Binding DataContext.Anime.Value.AnimatTypes, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
SelectedItem="{Binding AnimeType.Value}"
Visibility="{Binding DataContext.HasMultiType.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}, Converter={StaticResource FalseToHiddenConverter}}" />
<TextBox Grid.Column="1" Text="{Binding Id.Value, UpdateSourceTrigger=PropertyChanged}" />
<!-- pu:TextBoxHelper.Watermark="{ll:Str 动画Id(非必要)}" -->
</Grid>
</Expander.Header>
<Grid>
@ -81,16 +76,16 @@
</Grid.RowDefinitions>
<GroupBox>
<GroupBox.Header>
<Label Content="{ll:Str 顶层图片}" />
<Label d:Content="顶层图片" Content="{ll:Str 顶层图片}" />
</GroupBox.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
AllowDrop="True"
Drop="ListBox_Drop"
ItemsSource="{Binding Images, IsAsync=True}"
ItemsSource="{Binding FrontImages, IsAsync=True}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseWheel="ListBox_PreviewMouseWheel"
SelectedItem="{Binding DataContext.CurrentImageModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
SelectedItem="{Binding DataContext.CurrentFrontImageModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
@ -103,6 +98,7 @@
<Grid.ContextMenu>
<ContextMenu>
<MenuItem
d:Header="删除图片"
Command="{Binding PlacementTarget.Tag.RemoveImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="{ll:Str 删除图片}" />
@ -130,7 +126,7 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="{ll:Str 持续时间(ms)}" />
<Label d:Content="持续时间(ms)" Content="{ll:Str 持续时间(ms)}" />
<pu:NumberInput Grid.Column="1" Value="{Binding DataContext.Duration.Value, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}" />
</Grid>
</Grid>
@ -140,16 +136,16 @@
</GroupBox>
<GroupBox Grid.Row="1">
<GroupBox.Header>
<Label Content="{ll:Str 底层图片}" />
<Label d:Content="底层图片" Content="{ll:Str 底层图片}" />
</GroupBox.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
AllowDrop="True"
Drop="ListBox_Drop"
ItemsSource="{Binding Images, IsAsync=True}"
ItemsSource="{Binding BackImages, IsAsync=True}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseWheel="ListBox_PreviewMouseWheel"
SelectedItem="{Binding DataContext.CurrentImageModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
SelectedItem="{Binding DataContext.CurrentBackImageModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
@ -162,6 +158,7 @@
<Grid.ContextMenu>
<ContextMenu>
<MenuItem
d:Header="删除图片"
Command="{Binding PlacementTarget.Tag.RemoveImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="{ll:Str 删除图片}" />
@ -189,7 +186,7 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="{ll:Str 持续时间(ms)}" />
<Label d:Content="持续时间(ms)" Content="{ll:Str 持续时间(ms)}" />
<pu:NumberInput Grid.Column="1" Value="{Binding DataContext.Duration.Value, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}" />
</Grid>
</Grid>
@ -199,7 +196,81 @@
</GroupBox>
</Grid>
<GroupBox Grid.Column="1">
<ListBox />
<GroupBox.Header>
<Label d:Content="食物位置" Content="{ll:Str 食物位置}" />
</GroupBox.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
ItemsSource="{Binding FoodLocations, IsAsync=True}"
PreviewMouseWheel="ListBox_PreviewMouseWheel"
SelectedItem="{Binding DataContext.CurrentFoodLocationModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</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 d:Content="持续时间 (ms)" Content="{ll:Str 持续时间 (ms)}" />
<pu:NumberInput Grid.Column="1" Value="{Binding Duration.Value}" />
<Label
Grid.Row="1"
d:Content="旋转角度"
Content="{ll:Str 旋转角度}" />
<pu:NumberInput
Grid.Row="1"
Grid.Column="1"
Value="{Binding Rotate.Value}" />
<Label
Grid.Row="2"
d:Content="透明度"
Content="{ll:Str 透明度}" />
<pu:NumberInput
Grid.Row="2"
Grid.Column="1"
Value="{Binding Opacity.Value}" />
<Label
Grid.Row="3"
d:Content="长度"
Content="{ll:Str 长度}" />
<pu:NumberInput
Grid.Row="3"
Grid.Column="1"
Value="{Binding Rect.Width.Value}" />
<Grid Grid.Row="4" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="X:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Rect.X.Value}" />
</Grid>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="Y:" />
<pu:NumberInput Grid.Column="1" Value="{Binding Rect.Y.Value}" />
</Grid>
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</Grid>
</Expander>
@ -219,8 +290,15 @@
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="{ll:Str 替换测试食物图片}" />
<MenuItem Header="{ll:Str 重置测试食物图片}" />
<MenuItem Command="{Binding ReplaceFoodImageCommand}" Header="{ll:Str 替换测试食物图片}" />
<MenuItem Command="{Binding ResetFoodImageCommand}" Header="{ll:Str 重置测试食物图片}">
<MenuItem.IsEnabled>
<MultiBinding Converter="{StaticResource NotEqualsConverter}">
<Binding Path="FoodImage.Value" />
<Binding Path="DefaultFoodImage" />
</MultiBinding>
</MenuItem.IsEnabled>
</MenuItem>
</ContextMenu>
</Grid.ContextMenu>
<Image

View File

@ -25,7 +25,7 @@ public partial class FoodAnimeEditWindow : Window
{
public FoodAnimeEditWindow()
{
DataContext = new AnimeEditWindowVM();
DataContext = new FoodAnimeEditWindowVM();
InitializeComponent();
Closed += (s, e) =>
{