1
0

feat: add pref window view model

This commit is contained in:
2026-04-20 10:45:16 +08:00
parent 6cb984fa92
commit 394768d862
8 changed files with 208 additions and 65 deletions

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace BallanceTasEditor.Frontend.Converters {
internal class GenericEnumRadioButtonConverter<T> where T: struct {
public object Convert(object value, object parameter) {
var susValue = value as T?;
var susParam = parameter as T?;
if (susValue is null || susParam is null) {
return Binding.DoNothing;
} else {
return susValue.Value.Equals(susParam.Value);
}
}
public object ConvertBack(object value, object parameter) {
var susValue = value as bool?;
var susParam = parameter as T?;
if (susValue is null || susParam is null) {
return Binding.DoNothing;
} else {
return susValue.Value ? susParam.Value : Binding.DoNothing;
}
}
}
[ValueConversion(typeof(Shared.SequenceKind), typeof(bool))]
public class SequenceKindRadioButtonConverter : IValueConverter {
public static readonly SequenceKindRadioButtonConverter Instance = new();
private readonly GenericEnumRadioButtonConverter<Shared.SequenceKind> m_Inner = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return m_Inner.Convert(value, parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return m_Inner.ConvertBack(value, parameter);
}
}
[ValueConversion(typeof(Shared.EditorLayoutKind), typeof(bool))]
public class EditorLayoutKindRadioButtonConverter : IValueConverter {
public static readonly EditorLayoutKindRadioButtonConverter Instance = new();
private readonly GenericEnumRadioButtonConverter<Shared.EditorLayoutKind> m_Inner = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return m_Inner.Convert(value, parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return m_Inner.ConvertBack(value, parameter);
}
}
[ValueConversion(typeof(Shared.EditorPasteBehavior), typeof(bool))]
public class EditorPasteBehaviorRadioButtonConverter : IValueConverter {
public static readonly EditorPasteBehaviorRadioButtonConverter Instance = new();
private readonly GenericEnumRadioButtonConverter<Shared.EditorPasteBehavior> m_Inner = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return m_Inner.Convert(value, parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return m_Inner.ConvertBack(value, parameter);
}
}
}

View File

@@ -23,11 +23,11 @@ namespace BallanceTasEditor.Frontend.Models {
} }
[ObservableProperty] [ObservableProperty]
private Shared.TasSequenceKind sequenceKind; private Shared.SequenceKind sequenceKind;
[ObservableProperty] [ObservableProperty]
private Shared.EditorLayoutKind editorLayout; private Shared.EditorLayoutKind editorLayout;
[ObservableProperty] [ObservableProperty]
private Shared.EditorPasteMode pasteMode; private Shared.EditorPasteBehavior pasteBehavior;
[ObservableProperty] [ObservableProperty]
private int frameCount; private int frameCount;
[ObservableProperty] [ObservableProperty]
@@ -35,7 +35,7 @@ namespace BallanceTasEditor.Frontend.Models {
[MemberNotNull(nameof(SequenceKind))] [MemberNotNull(nameof(SequenceKind))]
[MemberNotNull(nameof(EditorLayout))] [MemberNotNull(nameof(EditorLayout))]
[MemberNotNull(nameof(PasteMode))] [MemberNotNull(nameof(PasteBehavior))]
[MemberNotNull(nameof(FrameCount))] [MemberNotNull(nameof(FrameCount))]
[MemberNotNull(nameof(GamePath))] [MemberNotNull(nameof(GamePath))]
private void FromSingleton() { private void FromSingleton() {
@@ -43,7 +43,7 @@ namespace BallanceTasEditor.Frontend.Models {
SequenceKind = singleton.SequenceKind; SequenceKind = singleton.SequenceKind;
EditorLayout = singleton.EditorLayout; EditorLayout = singleton.EditorLayout;
PasteMode = singleton.PasteMode; PasteBehavior = singleton.PasteBehavior;
FrameCount = singleton.FrameCount; FrameCount = singleton.FrameCount;
GamePath = singleton.GamePath; GamePath = singleton.GamePath;
} }
@@ -53,7 +53,7 @@ namespace BallanceTasEditor.Frontend.Models {
singleton.SequenceKind = SequenceKind; singleton.SequenceKind = SequenceKind;
singleton.EditorLayout = EditorLayout; singleton.EditorLayout = EditorLayout;
singleton.PasteMode = PasteMode; singleton.PasteBehavior = PasteBehavior;
singleton.FrameCount = FrameCount; singleton.FrameCount = FrameCount;
singleton.GamePath = GamePath; singleton.GamePath = GamePath;
} }

View File

@@ -30,28 +30,28 @@ namespace BallanceTasEditor.Frontend.Models {
get => FileBody is null; get => FileBody is null;
} }
public void NewFile(Shared.TasSequenceKind kind, int count, uint fps) { public void NewFile(Shared.SequenceKind kind, int count, uint fps) {
// Check status // Check status
if (IsFileLoaded) { if (IsFileLoaded) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
// Initialize sequence // Initialize sequence
var seq = Shared.TasSequenceKindHelper.CreateSequenceByKind(kind); var seq = Shared.SequenceKindHelper.CreateSequenceByKind(kind);
// Initialize items // Initialize items
Backend.TasStorage.Init(seq, count, fps); Backend.TasStorage.Init(seq, count, fps);
// Set members // Set members
FileBody = seq; FileBody = seq;
} }
public void LoadFile(Shared.TasSequenceKind kind, string path) { public void LoadFile(Shared.SequenceKind kind, string path) {
// Check status // Check status
if (IsFileLoaded) { if (IsFileLoaded) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
// Initialize sequence // Initialize sequence
var seq = Shared.TasSequenceKindHelper.CreateSequenceByKind(kind); var seq = Shared.SequenceKindHelper.CreateSequenceByKind(kind);
// Load into sequence // Load into sequence
Backend.TasStorage.Load(path, seq); Backend.TasStorage.Load(path, seq);
// Set members // Set members

View File

@@ -18,21 +18,21 @@ namespace BallanceTasEditor.Frontend.Shared {
Init(); Init();
} }
public TasSequenceKind SequenceKind { get; set; } public SequenceKind SequenceKind { get; set; }
public EditorLayoutKind EditorLayout { get; set; } public EditorLayoutKind EditorLayout { get; set; }
public EditorPasteMode PasteMode { get; set; } public EditorPasteBehavior PasteBehavior { get; set; }
public int FrameCount { get; set; } public int FrameCount { get; set; }
public string GamePath { get; set; } public string GamePath { get; set; }
[MemberNotNull(nameof(SequenceKind))] [MemberNotNull(nameof(SequenceKind))]
[MemberNotNull(nameof(EditorLayout))] [MemberNotNull(nameof(EditorLayout))]
[MemberNotNull(nameof(PasteMode))] [MemberNotNull(nameof(PasteBehavior))]
[MemberNotNull(nameof(FrameCount))] [MemberNotNull(nameof(FrameCount))]
[MemberNotNull(nameof(GamePath))] [MemberNotNull(nameof(GamePath))]
private void FromRaw(RawEditorConfiguration raw) { private void FromRaw(RawEditorConfiguration raw) {
SequenceKind = raw.SequenceKind; SequenceKind = raw.SequenceKind;
EditorLayout = raw.EditorLayout; EditorLayout = raw.EditorLayout;
PasteMode = raw.PasteMode; PasteBehavior = raw.PasteBehavior;
FrameCount = raw.FrameCount; FrameCount = raw.FrameCount;
GamePath = raw.GamePath; GamePath = raw.GamePath;
} }
@@ -41,7 +41,7 @@ namespace BallanceTasEditor.Frontend.Shared {
return new RawEditorConfiguration { return new RawEditorConfiguration {
SequenceKind = SequenceKind, SequenceKind = SequenceKind,
EditorLayout = EditorLayout, EditorLayout = EditorLayout,
PasteMode = PasteMode, PasteBehavior = PasteBehavior,
FrameCount = FrameCount, FrameCount = FrameCount,
GamePath = GamePath GamePath = GamePath
}; };
@@ -69,7 +69,7 @@ namespace BallanceTasEditor.Frontend.Shared {
[MemberNotNull(nameof(SequenceKind))] [MemberNotNull(nameof(SequenceKind))]
[MemberNotNull(nameof(EditorLayout))] [MemberNotNull(nameof(EditorLayout))]
[MemberNotNull(nameof(PasteMode))] [MemberNotNull(nameof(PasteBehavior))]
[MemberNotNull(nameof(FrameCount))] [MemberNotNull(nameof(FrameCount))]
[MemberNotNull(nameof(GamePath))] [MemberNotNull(nameof(GamePath))]
private void Init() { private void Init() {
@@ -103,15 +103,15 @@ namespace BallanceTasEditor.Frontend.Shared {
private class RawEditorConfiguration { private class RawEditorConfiguration {
[JsonProperty("sequence_kind", Required = Required.Always)] [JsonProperty("sequence_kind", Required = Required.Always)]
[JsonConverter(typeof(TasSequenceKindConverter))] [JsonConverter(typeof(TasSequenceKindConverter))]
public TasSequenceKind SequenceKind { get; set; } = TasSequenceKind.Array; public SequenceKind SequenceKind { get; set; } = SequenceKind.Array;
[JsonProperty("editor_layout", Required = Required.Always)] [JsonProperty("editor_layout", Required = Required.Always)]
[JsonConverter(typeof(EditorLayoutKindConverter))] [JsonConverter(typeof(EditorLayoutKindConverter))]
public EditorLayoutKind EditorLayout { get; set; } = EditorLayoutKind.Vertical; public EditorLayoutKind EditorLayout { get; set; } = EditorLayoutKind.Vertical;
[JsonProperty("paste_mode", Required = Required.Always)] [JsonProperty("paste_behavior", Required = Required.Always)]
[JsonConverter(typeof(EditorPasteModeConverter))] [JsonConverter(typeof(EditorPasteBehaviorConverter))]
public EditorPasteMode PasteMode { get; set; } = EditorPasteMode.Insert; public EditorPasteBehavior PasteBehavior { get; set; } = EditorPasteBehavior.Insert;
[JsonProperty("frame_count", Required = Required.Always)] [JsonProperty("frame_count", Required = Required.Always)]
public int FrameCount { get; set; } = 20; public int FrameCount { get; set; } = 20;
@@ -122,11 +122,11 @@ namespace BallanceTasEditor.Frontend.Shared {
#region Custom JSON Converter #region Custom JSON Converter
private class TasSequenceKindConverter : JsonConverter<TasSequenceKind> { private class TasSequenceKindConverter : JsonConverter<SequenceKind> {
public override TasSequenceKind ReadJson(JsonReader reader, Type objectType, TasSequenceKind existingValue, bool hasExistingValue, JsonSerializer serializer) { public override SequenceKind ReadJson(JsonReader reader, Type objectType, SequenceKind existingValue, bool hasExistingValue, JsonSerializer serializer) {
if (reader.TokenType == JsonToken.String) { if (reader.TokenType == JsonToken.String) {
var value = (reader.Value as string).Unwrap(); var value = (reader.Value as string).Unwrap();
if (TasSequenceKindHelper.TryParse(value, out var kind)) { if (SequenceKindHelper.TryParse(value, out var kind)) {
return kind; return kind;
} else { } else {
throw new JsonSerializationException($"given string can not be parsed as TasSequenceKind: {value}"); throw new JsonSerializationException($"given string can not be parsed as TasSequenceKind: {value}");
@@ -136,8 +136,8 @@ namespace BallanceTasEditor.Frontend.Shared {
} }
} }
public override void WriteJson(JsonWriter writer, TasSequenceKind value, JsonSerializer serializer) { public override void WriteJson(JsonWriter writer, SequenceKind value, JsonSerializer serializer) {
writer.WriteValue(TasSequenceKindHelper.ToString(value)); writer.WriteValue(SequenceKindHelper.ToString(value));
} }
} }
@@ -160,22 +160,22 @@ namespace BallanceTasEditor.Frontend.Shared {
} }
} }
private class EditorPasteModeConverter : JsonConverter<EditorPasteMode> { private class EditorPasteBehaviorConverter : JsonConverter<EditorPasteBehavior> {
public override EditorPasteMode ReadJson(JsonReader reader, Type objectType, EditorPasteMode existingValue, bool hasExistingValue, JsonSerializer serializer) { public override EditorPasteBehavior ReadJson(JsonReader reader, Type objectType, EditorPasteBehavior existingValue, bool hasExistingValue, JsonSerializer serializer) {
if (reader.TokenType == JsonToken.String) { if (reader.TokenType == JsonToken.String) {
var value = (reader.Value as string).Unwrap(); var value = (reader.Value as string).Unwrap();
if (EditorPasteModeHelper.TryParse(value, out var kind)) { if (EditorPasteBehaviorHelper.TryParse(value, out var kind)) {
return kind; return kind;
} else { } else {
throw new JsonSerializationException($"given string can not be parsed as EditorPasteMode: {value}"); throw new JsonSerializationException($"given string can not be parsed as EditorPasteBehavior: {value}");
} }
} else { } else {
throw new JsonSerializationException($"expect a integer but got {reader.TokenType}"); throw new JsonSerializationException($"expect a integer but got {reader.TokenType}");
} }
} }
public override void WriteJson(JsonWriter writer, EditorPasteMode value, JsonSerializer serializer) { public override void WriteJson(JsonWriter writer, EditorPasteBehavior value, JsonSerializer serializer) {
writer.WriteValue(EditorPasteModeHelper.ToString(value)); writer.WriteValue(EditorPasteBehaviorHelper.ToString(value));
} }
} }

View File

@@ -8,27 +8,27 @@ using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.Shared { namespace BallanceTasEditor.Frontend.Shared {
public enum TasSequenceKind { public enum SequenceKind {
Array, Array,
DoubleLinkedList DoubleLinkedList
} }
public static class TasSequenceKindHelper { public static class SequenceKindHelper {
public static Backend.ITasSequence CreateSequenceByKind(TasSequenceKind kind) { public static Backend.ITasSequence CreateSequenceByKind(SequenceKind kind) {
return kind switch { return kind switch {
TasSequenceKind.Array => new Backend.ListTasSequence(), SequenceKind.Array => new Backend.ListTasSequence(),
TasSequenceKind.DoubleLinkedList => new Backend.LegacyTasSequence(), SequenceKind.DoubleLinkedList => new Backend.LegacyTasSequence(),
_ => throw new UnreachableException(), _ => throw new UnreachableException(),
}; };
} }
public static bool TryParse(string s, out TasSequenceKind value) { public static bool TryParse(string s, out SequenceKind value) {
switch (s) { switch (s) {
case "array": case "array":
value = TasSequenceKind.Array; value = SequenceKind.Array;
return true; return true;
case "double_linked_list": case "double_linked_list":
value = TasSequenceKind.DoubleLinkedList; value = SequenceKind.DoubleLinkedList;
return true; return true;
default: default:
value = default; value = default;
@@ -36,10 +36,10 @@ namespace BallanceTasEditor.Frontend.Shared {
} }
} }
public static string ToString(TasSequenceKind kind) { public static string ToString(SequenceKind kind) {
return kind switch { return kind switch {
TasSequenceKind.Array => "array", SequenceKind.Array => "array",
TasSequenceKind.DoubleLinkedList => "double_linked_list", SequenceKind.DoubleLinkedList => "double_linked_list",
_ => throw new UnreachableException(), _ => throw new UnreachableException(),
}; };
} }
@@ -73,19 +73,19 @@ namespace BallanceTasEditor.Frontend.Shared {
} }
} }
public enum EditorPasteMode { public enum EditorPasteBehavior {
Insert, Insert,
Override Override
} }
public static class EditorPasteModeHelper { public static class EditorPasteBehaviorHelper {
public static bool TryParse(string s, out EditorPasteMode value) { public static bool TryParse(string s, out EditorPasteBehavior value) {
switch (s) { switch (s) {
case "insert": case "insert":
value = EditorPasteMode.Insert; value = EditorPasteBehavior.Insert;
return true; return true;
case "override": case "override":
value = EditorPasteMode.Override; value = EditorPasteBehavior.Override;
return true; return true;
default: default:
value = default; value = default;
@@ -93,10 +93,10 @@ namespace BallanceTasEditor.Frontend.Shared {
} }
} }
public static string ToString(EditorPasteMode mode) { public static string ToString(EditorPasteBehavior mode) {
return mode switch { return mode switch {
EditorPasteMode.Insert => "insert", EditorPasteBehavior.Insert => "insert",
EditorPasteMode.Override => "override", EditorPasteBehavior.Override => "override",
_ => throw new UnreachableException(), _ => throw new UnreachableException(),
}; };
} }

View File

@@ -61,7 +61,7 @@ namespace BallanceTasEditor.Frontend.ViewModels {
if (dialog is null) return; if (dialog is null) return;
// Create new file // Create new file
TasFile.NewFile(Shared.TasSequenceKind.Array, dialog.Count, dialog.Fps); TasFile.NewFile(Shared.SequenceKind.Array, dialog.Count, dialog.Fps);
// Set members // Set members
TasFilePath = null; TasFilePath = null;
// Send notification // Send notification
@@ -80,7 +80,7 @@ namespace BallanceTasEditor.Frontend.ViewModels {
// Load file // Load file
try { try {
TasFile.LoadFile(Shared.TasSequenceKind.Array, dialog.Path); TasFile.LoadFile(Shared.SequenceKind.Array, dialog.Path);
} catch (Exception e) { } catch (Exception e) {
m_DialogService.ShowOpenFileFailedDialog(e); m_DialogService.ShowOpenFileFailedDialog(e);
return; return;

View File

@@ -2,17 +2,47 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels { namespace BallanceTasEditor.Frontend.ViewModels {
public partial class PreferenceDialog : ObservableObject { public partial class PreferenceDialog : ObservableValidator {
public PreferenceDialog() { public PreferenceDialog() {
} }
[ObservableProperty]
private Shared.SequenceKind sequenceKind;
[ObservableProperty]
private Shared.EditorLayoutKind layoutKind;
[ObservableProperty]
private Shared.EditorPasteBehavior pasteBehavior;
[ObservableProperty]
[NotifyDataErrorInfo]
[CustomValidation(typeof(PreferenceDialog), nameof(ValidateCount))]
[NotifyCanExecuteChangedFor(nameof(OkCommand))]
private string frameCount;
[ObservableProperty]
private string gamePath;
#region Validators
private static readonly Validators.ValidatorAdapter<string, int, Validators.CountValidator> g_CountValidator =
new Validators.ValidatorAdapter<string, int, Validators.CountValidator>(new Validators.CountValidator());
public static ValidationResult? ValidateCount(string value, ValidationContext context) {
return g_CountValidator.Validate(value, context);
}
//public Shared.NewFileDialogResult GetUserInput() {
// return
//}
#endregion
#region Commands
[RelayCommand(CanExecute = nameof(CanOk))] [RelayCommand(CanExecute = nameof(CanOk))]
private void Ok() { private void Ok() {
@@ -20,8 +50,7 @@ namespace BallanceTasEditor.Frontend.ViewModels {
} }
private bool CanOk() { private bool CanOk() {
// TODO return !HasErrors;
return true;
} }
[RelayCommand] [RelayCommand]
@@ -29,12 +58,13 @@ namespace BallanceTasEditor.Frontend.ViewModels {
OnRequestCloseDialog(false); OnRequestCloseDialog(false);
} }
public event Shared.RequestCloseDialogEventHandler? RequestCloseDialog; public event Shared.RequestCloseDialogEventHandler? RequestCloseDialog;
private void OnRequestCloseDialog(bool result) { private void OnRequestCloseDialog(bool result) {
RequestCloseDialog?.Invoke(new Shared.RequestCloseDialogEventArgs { Result = result }); RequestCloseDialog?.Invoke(new Shared.RequestCloseDialogEventArgs { Result = result });
} }
#endregion
} }
} }

View File

@@ -5,10 +5,12 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views" xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
xmlns:vm="clr-namespace:BallanceTasEditor.Frontend.ViewModels" xmlns:vm="clr-namespace:BallanceTasEditor.Frontend.ViewModels"
xmlns:conveter="clr-namespace:BallanceTasEditor.Frontend.Converters"
xmlns:shared="clr-namespace:BallanceTasEditor.Frontend.Shared"
xmlns:widget="clr-namespace:BallanceTasEditor.Frontend.Widgets" xmlns:widget="clr-namespace:BallanceTasEditor.Frontend.Widgets"
d:DataContext="{d:DesignInstance vm:PreferenceDialog}" d:DataContext="{d:DesignInstance vm:PreferenceDialog}"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False" mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Editor Preference" Height="450" Width="400" Icon="/Frontend/Assets/Preference.ico"> Title="Editor Preference" Height="500" Width="400" Icon="/Frontend/Assets/Preference.ico">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@@ -17,7 +19,7 @@
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Visible"> <ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Visible">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<widget:IconGroupBox GroupBoxText="Editor Layout" <widget:IconGroupBox GroupBoxText="Sequence Kind"
GroupBoxIcon="/Frontend/Assets/EditorLayout.ico" GroupBoxIcon="/Frontend/Assets/EditorLayout.ico"
Margin="10" Padding="10"> Margin="10" Padding="10">
<Grid> <Grid>
@@ -25,26 +27,56 @@
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<RadioButton Content="Horizontal Layout" GroupName="EditorLayout" Grid.Column="0"/> <RadioButton Content="Array" GroupName="SequenceKind"
<RadioButton Content="Vertical Layout" GroupName="EditorLayout" Grid.Column="1"/> ToolTip="Good for browsing. Bad for frequently inserting and removing frames."
IsChecked="{Binding SequenceKind, Mode=TwoWay, Converter={x:Static conveter:SequenceKindRadioButtonConverter.Instance}, ConverterParameter={x:Static shared:SequenceKind.Array}}"
Grid.Column="0"/>
<RadioButton Content="Double Linked List" GroupName="SequenceKind"
ToolTip="Good for frequently inserting and removing frames. Bad for browsing."
IsChecked="{Binding SequenceKind, Mode=TwoWay, Converter={x:Static conveter:SequenceKindRadioButtonConverter.Instance}, ConverterParameter={x:Static shared:SequenceKind.DoubleLinkedList}}"
Grid.Column="1"/>
</Grid> </Grid>
</widget:IconGroupBox> </widget:IconGroupBox>
<widget:IconGroupBox GroupBoxText="Paste Behavior" <widget:IconGroupBox GroupBoxText="Editor Layout"
GroupBoxIcon="/Frontend/Assets/PasteFrame.ico" GroupBoxIcon="/Frontend/Assets/EditorLayout.ico"
Margin="10" Padding="10"> Margin="10" Padding="10"
IsEnabled="False">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<RadioButton Content="Insert Paste" GroupName="PasteBehavior" Grid.Column="0"/> <RadioButton Content="Horizontal Layout" GroupName="EditorLayout"
<RadioButton Content="Overwritten Paste" GroupName="PasteBehavior" Grid.Column="1"/> IsChecked="{Binding LayoutKind, Mode=TwoWay, Converter={x:Static conveter:EditorLayoutKindRadioButtonConverter.Instance}, ConverterParameter={x:Static shared:EditorLayoutKind.Horizontal}}"
Grid.Column="0"/>
<RadioButton Content="Vertical Layout" GroupName="EditorLayout"
IsChecked="{Binding LayoutKind, Mode=TwoWay, Converter={x:Static conveter:EditorLayoutKindRadioButtonConverter.Instance}, ConverterParameter={x:Static shared:EditorLayoutKind.Vertical}}"
Grid.Column="1"/>
</Grid>
</widget:IconGroupBox>
<widget:IconGroupBox GroupBoxText="Paste Behavior"
GroupBoxIcon="/Frontend/Assets/PasteFrame.ico"
Margin="10" Padding="10"
IsEnabled="False">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<RadioButton Content="Insert Paste" GroupName="PasteBehavior"
IsChecked="{Binding PasteBehavior, Mode=TwoWay, Converter={x:Static conveter:EditorPasteBehaviorRadioButtonConverter.Instance}, ConverterParameter={x:Static shared:EditorPasteBehavior.Insert}}"
Grid.Column="0"/>
<RadioButton Content="Overwritten Paste" GroupName="PasteBehavior"
IsChecked="{Binding PasteBehavior, Mode=TwoWay, Converter={x:Static conveter:EditorPasteBehaviorRadioButtonConverter.Instance}, ConverterParameter={x:Static shared:EditorPasteBehavior.Override}}"
Grid.Column="1"/>
</Grid> </Grid>
</widget:IconGroupBox> </widget:IconGroupBox>
<widget:IconGroupBox GroupBoxText="Frame Count" <widget:IconGroupBox GroupBoxText="Frame Count"
GroupBoxIcon="/Frontend/Assets/Count.ico" GroupBoxIcon="/Frontend/Assets/Count.ico"
Margin="10" Padding="10"> Margin="10" Padding="10">
<TextBox Padding="3"/> <TextBox Padding="3"
Text="{Binding FrameCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
Style="{StaticResource TextBoxWithErrorNotifyStyle}"/>
</widget:IconGroupBox> </widget:IconGroupBox>
<widget:IconGroupBox GroupBoxText="Game Path" <widget:IconGroupBox GroupBoxText="Game Path"
GroupBoxIcon="/Frontend/Assets/SaveFileThenRunGame.ico" GroupBoxIcon="/Frontend/Assets/SaveFileThenRunGame.ico"