GUI: Add plugin option validation + many bug fixes to dialog
This commit is contained in:
@@ -1,143 +1,52 @@
|
|||||||
<Window x:Class="Il2CppInspector.GUI.PluginConfigurationDialog"
|
<Window x:Class="Il2CppInspectorGUI.PluginConfigurationDialog"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:Il2CppInspector.GUI"
|
xmlns:local="clr-namespace:Il2CppInspectorGUI"
|
||||||
xmlns:pluginapi="clr-namespace:Il2CppInspector.PluginAPI.V100;assembly=Il2CppInspector.Common"
|
xmlns:local2="clr-namespace:Il2CppInspector.GUI"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="{Binding Path=Plugin.Name, StringFormat=Configuration for {0}}" Height="400" Width="800"
|
Title="{Binding Path=Plugin.Name, StringFormat=Configuration for {0}}" Height="400" Width="800"
|
||||||
ResizeMode="NoResize"
|
ResizeMode="NoResize"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
SizeToContent="Height" MaxHeight="800">
|
SizeToContent="Height" MaxHeight="800"
|
||||||
|
Closing="Window_Closing">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<local:OptionTemplateSelector x:Key="OptionTemplateSelector"/>
|
<local:OptionTemplateSelector x:Key="OptionTemplateSelector"/>
|
||||||
<local:HexStringValueConverter x:Key="HexStringValueConverter" />
|
<local2:HexStringValueConverter x:Key="HexStringValueConverter" />
|
||||||
<local:EqualityConverter x:Key="EqualityVisibilityConverter" TrueValue="{x:Static Visibility.Visible}" FalseValue="{x:Static Visibility.Collapsed}" />
|
<local2:EqualityConverter x:Key="EqualityVisibilityConverter" TrueValue="{x:Static Visibility.Visible}" FalseValue="{x:Static Visibility.Collapsed}" />
|
||||||
<BooleanToVisibilityConverter x:Key="VisibleIfTrueConverter" />
|
<BooleanToVisibilityConverter x:Key="VisibleIfTrueConverter" />
|
||||||
|
|
||||||
<!-- Option layouts -->
|
<!-- Validation error style -->
|
||||||
<DataTemplate x:Key="TextTemplate">
|
<Style x:Key="ValidationStyle" TargetType="TextBlock">
|
||||||
<DockPanel>
|
<Setter Property="Text" Value="{Binding ElementName=valueControl, Path=(Validation.Errors)[0].ErrorContent}"/>
|
||||||
<TextBlock DockPanel.Dock="Left" Width="350" VerticalAlignment="Center">
|
<Setter Property="Foreground" Value="Red" />
|
||||||
<TextBlock Text="{Binding Path=Description}" TextWrapping="Wrap"></TextBlock>
|
</Style>
|
||||||
<TextBlock Visibility="{Binding Required, Converter={StaticResource VisibleIfTrueConverter}}" Text="*" Foreground="Red"/>
|
|
||||||
</TextBlock>
|
|
||||||
<TextBox DockPanel.Dock="Right" VerticalAlignment="Center" Padding="2" Margin="0,4,4,4" Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
|
||||||
</DockPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
|
|
||||||
<DataTemplate x:Key="FilePathTemplate">
|
<DataTemplate x:Key="ValidationErrorTemplate">
|
||||||
<DockPanel>
|
|
||||||
<Button Name="btnFilePathSelector" DockPanel.Dock="Right" Width="70" Margin="4" Click="btnFilePathSelector_Click">Browse</Button>
|
|
||||||
<TextBlock DockPanel.Dock="Left" Width="3500" VerticalAlignment="Center">
|
|
||||||
<TextBlock Text="{Binding Description}" TextWrapping="Wrap"></TextBlock>
|
|
||||||
<TextBlock Visibility="{Binding Required, Converter={StaticResource VisibleIfTrueConverter}}" Text="*" Foreground="Red"/>
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock Name="txtFilePathSelector" DockPanel.Dock="Right" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="3" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Text="{Binding Value}"/>
|
|
||||||
</DockPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
|
|
||||||
<DataTemplate x:Key="NumberTemplate">
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<!-- Decimal number -->
|
<StackPanel.Style>
|
||||||
<DockPanel>
|
<Style TargetType="{x:Type StackPanel}">
|
||||||
<DockPanel.Visibility>
|
<Style.Triggers>
|
||||||
<MultiBinding Converter="{StaticResource EqualityVisibilityConverter}">
|
<DataTrigger Binding="{Binding ElementName=errorText, Path=Text}" Value="">
|
||||||
<Binding Path="Style" />
|
<Setter Property="Visibility" Value="Collapsed"/>
|
||||||
<Binding Source="{x:Static pluginapi:PluginOptionNumberStyle.Decimal}" Mode="OneWay" />
|
</DataTrigger>
|
||||||
</MultiBinding>
|
</Style.Triggers>
|
||||||
</DockPanel.Visibility>
|
</Style>
|
||||||
|
</StackPanel.Style>
|
||||||
<TextBlock DockPanel.Dock="Left" Width="350" VerticalAlignment="Center">
|
<TextBlock Name="errorText" Style="{StaticResource ValidationStyle}" />
|
||||||
<TextBlock Text="{Binding Description}" TextWrapping="Wrap"></TextBlock>
|
|
||||||
<TextBlock Visibility="{Binding Required, Converter={StaticResource VisibleIfTrueConverter}}" Text="*" Foreground="Red"/>
|
|
||||||
</TextBlock>
|
|
||||||
<TextBox Name="txtDecimalString" DockPanel.Dock="Right" VerticalAlignment="Center" Padding="2" Margin="0,4,4,4" Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<!-- Hex number -->
|
|
||||||
<DockPanel>
|
|
||||||
<DockPanel.Visibility>
|
|
||||||
<MultiBinding Converter="{StaticResource EqualityVisibilityConverter}">
|
|
||||||
<Binding Path="Style" />
|
|
||||||
<Binding Source="{x:Static pluginapi:PluginOptionNumberStyle.Hex}" Mode="OneWay" />
|
|
||||||
</MultiBinding>
|
|
||||||
</DockPanel.Visibility>
|
|
||||||
|
|
||||||
<TextBlock DockPanel.Dock="Left" Width="350" VerticalAlignment="Center">
|
|
||||||
<TextBlock Text="{Binding Description}" TextWrapping="Wrap"></TextBlock>
|
|
||||||
<TextBlock Visibility="{Binding Required, Converter={StaticResource VisibleIfTrueConverter}}" Text="*" Foreground="Red"/>
|
|
||||||
</TextBlock>
|
|
||||||
<DockPanel HorizontalAlignment="Stretch">
|
|
||||||
<Label Margin="0,3,3,5">0x</Label>
|
|
||||||
<TextBox Name="txtHexString" Padding="2" Margin="0,6,4,6" Text="{Binding Value, Converter={StaticResource HexStringValueConverter}, UpdateSourceTrigger=PropertyChanged}" PreviewTextInput="txtHexString_PreviewTextInput"/>
|
|
||||||
</DockPanel>
|
|
||||||
</DockPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
|
|
||||||
<DataTemplate x:Key="BooleanTemplate">
|
|
||||||
<DockPanel Margin="350,0,0,0">
|
|
||||||
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Top" Margin="0,10,2,6"
|
|
||||||
Visibility="{Binding Required, Converter={StaticResource VisibleIfTrueConverter}}" Text="*" Foreground="Red"/>
|
|
||||||
<CheckBox DockPanel.Dock="Right" VerticalAlignment="Center" Margin="0,10,2,6" IsChecked="{Binding Value}">
|
|
||||||
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding Description}" />
|
|
||||||
</CheckBox>
|
|
||||||
</DockPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
|
|
||||||
<DataTemplate x:Key="ChoiceTemplate">
|
|
||||||
<StackPanel>
|
|
||||||
<!-- Drop-down box version -->
|
|
||||||
<DockPanel>
|
|
||||||
<DockPanel.Visibility>
|
|
||||||
<MultiBinding Converter="{StaticResource EqualityVisibilityConverter}">
|
|
||||||
<Binding Path="Style" />
|
|
||||||
<Binding Source="{x:Static pluginapi:PluginOptionChoiceStyle.Dropdown}" Mode="OneWay" />
|
|
||||||
</MultiBinding>
|
|
||||||
</DockPanel.Visibility>
|
|
||||||
<TextBlock DockPanel.Dock="Left" Width="350" VerticalAlignment="Center">
|
|
||||||
<TextBlock Text="{Binding Description}" TextWrapping="Wrap"></TextBlock>
|
|
||||||
<TextBlock Visibility="{Binding Required, Converter={StaticResource VisibleIfTrueConverter}}" Text="*" Foreground="Red"/>
|
|
||||||
</TextBlock>
|
|
||||||
<ComboBox DockPanel.Dock="Right" Margin="4,8,4,8" ItemsSource="{Binding Choices}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding Value}"></ComboBox>
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<!-- Radio buttons version -->
|
|
||||||
<DockPanel>
|
|
||||||
<DockPanel.Visibility>
|
|
||||||
<MultiBinding Converter="{StaticResource EqualityVisibilityConverter}">
|
|
||||||
<Binding Path="Style" />
|
|
||||||
<Binding Source="{x:Static pluginapi:PluginOptionChoiceStyle.List}" Mode="OneWay" />
|
|
||||||
</MultiBinding>
|
|
||||||
</DockPanel.Visibility>
|
|
||||||
<GroupBox Header="{Binding Description}" Margin="5" Padding="5">
|
|
||||||
<ListBox ItemsSource="{Binding Choices}" SelectedItem="{Binding Value}" BorderBrush="Transparent">
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<RadioButton GroupName="{Binding Header, RelativeSource={RelativeSource AncestorType=GroupBox}}"
|
|
||||||
Content="{Binding}"
|
|
||||||
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
|
|
||||||
Focusable="False"
|
|
||||||
IsHitTestVisible="False"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListBox.ItemTemplate>
|
|
||||||
</ListBox>
|
|
||||||
</GroupBox>
|
|
||||||
</DockPanel>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<!-- Configure ListBox to display configuration controls nicely -->
|
<!-- Configure ListBox to display configuration controls nicely -->
|
||||||
<Style TargetType="{x:Type ListBoxItem}">
|
<Style x:Key="OptionItemStyle" TargetType="{x:Type ListBoxItem}">
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
|
||||||
<!-- Force ListBox to width of window -->
|
<!-- Force ListBox to width of window -->
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||||
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
|
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
|
||||||
<Setter Property="Padding" Value="2,0,0,0"/>
|
<Setter Property="Padding" Value="2,6,0,6"/>
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="{x:Type ListBoxItem}">
|
<ControlTemplate TargetType="{x:Type ListBoxItem}">
|
||||||
@@ -153,8 +62,115 @@
|
|||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<!-- Option label template -->
|
||||||
|
<DataTemplate x:Key="OptionLabelTemplate">
|
||||||
|
<TextBlock DockPanel.Dock="Left" Width="350" VerticalAlignment="Top">
|
||||||
|
<TextBlock Text="{Binding Path=Description}" TextWrapping="Wrap" Margin="0,4,0,0"></TextBlock>
|
||||||
|
<TextBlock Visibility="{Binding Required, Converter={StaticResource VisibleIfTrueConverter}}" Text="*" Foreground="Red"/>
|
||||||
|
</TextBlock>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Option layouts -->
|
||||||
|
|
||||||
|
<!-- Free text -->
|
||||||
|
<DataTemplate x:Key="TextTemplate">
|
||||||
|
<DockPanel>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource OptionLabelTemplate}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Margin="4,0,4,0">
|
||||||
|
<TextBox Name="valueControl" VerticalAlignment="Top" Padding="2" Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}" />
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource ValidationErrorTemplate}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- File path -->
|
||||||
|
<DataTemplate x:Key="FilePathTemplate">
|
||||||
|
<DockPanel>
|
||||||
|
<Button Name="btnFilePathSelector" DockPanel.Dock="Right" Width="70" Height="25" VerticalAlignment="Top" Margin="4,0,4,0" Click="btnFilePathSelector_Click">Browse</Button>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource OptionLabelTemplate}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Margin="4,0,4,0">
|
||||||
|
<TextBox Name="valueControl" VerticalAlignment="Top" HorizontalAlignment="Stretch" TextAlignment="Right" Padding="2" Margin="0,0,4,0" IsReadOnly="True" BorderBrush="Transparent" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}" />
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource ValidationErrorTemplate}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Decimal number -->
|
||||||
|
<DataTemplate x:Key="NumberDecimalTemplate">
|
||||||
|
<DockPanel>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource OptionLabelTemplate}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Margin="4,0,4,0">
|
||||||
|
<TextBox Name="valueControl" VerticalAlignment="Center" Padding="2" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource ValidationErrorTemplate}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Hex number -->
|
||||||
|
<DataTemplate x:Key="NumberHexTemplate">
|
||||||
|
<DockPanel>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource OptionLabelTemplate}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Margin="4,0,4,0">
|
||||||
|
<DockPanel HorizontalAlignment="Stretch">
|
||||||
|
<Label Padding="0" Margin="0,2,4,0">0x</Label>
|
||||||
|
<TextBox Name="valueControl" DockPanel.Dock="Right" Padding="2" Text="{Binding Value, Converter={StaticResource HexStringValueConverter}, UpdateSourceTrigger=PropertyChanged}" PreviewTextInput="txtHexString_PreviewTextInput"/>
|
||||||
|
</DockPanel>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource ValidationErrorTemplate}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Boolean tickbox (no validation required) -->
|
||||||
|
<DataTemplate x:Key="BooleanTemplate">
|
||||||
|
<DockPanel Margin="350,0,0,0">
|
||||||
|
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Top" Margin="0,4,2,4"
|
||||||
|
Visibility="{Binding Required, Converter={StaticResource VisibleIfTrueConverter}}" Text="*" Foreground="Red"/>
|
||||||
|
<CheckBox Name="valueControl" DockPanel.Dock="Right" VerticalAlignment="Center" Margin="0,4,2,4" IsChecked="{Binding Value}">
|
||||||
|
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding Description}" />
|
||||||
|
</CheckBox>
|
||||||
|
</DockPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Drop-down choices -->
|
||||||
|
<DataTemplate x:Key="ChoiceDropdownTemplate">
|
||||||
|
<DockPanel>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource OptionLabelTemplate}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Margin="4,0,4,0">
|
||||||
|
<ComboBox Name="valueControl" ItemsSource="{Binding Choices}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding Value}"/>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource ValidationErrorTemplate}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Radio button choices -->
|
||||||
|
<DataTemplate x:Key="ChoiceListTemplate">
|
||||||
|
<StackPanel>
|
||||||
|
<GroupBox Header="{Binding Description}" Margin="5" Padding="5">
|
||||||
|
<ListBox Name="valueControl" ItemsSource="{Binding Choices}" SelectedValuePath="Key" SelectedValue="{Binding Value}" BorderBrush="Transparent">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<RadioButton GroupName="{Binding Header, RelativeSource={RelativeSource AncestorType=GroupBox}}"
|
||||||
|
Content="{Binding Value}"
|
||||||
|
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
|
||||||
|
Focusable="False"
|
||||||
|
IsHitTestVisible="False"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
|
||||||
|
<ListBox.ItemContainerStyle>
|
||||||
|
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource OptionItemStyle}">
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
</Style>
|
||||||
|
</ListBox.ItemContainerStyle>
|
||||||
|
</ListBox>
|
||||||
|
</GroupBox>
|
||||||
|
<ContentPresenter ContentTemplate="{StaticResource ValidationErrorTemplate}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
</Window.Resources>
|
</Window.Resources>
|
||||||
|
|
||||||
|
<!-- Main dialog window -->
|
||||||
<Grid Margin="10">
|
<Grid Margin="10">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="50" />
|
<RowDefinition Height="50" />
|
||||||
@@ -167,13 +183,15 @@
|
|||||||
Content="{Binding Plugin.Name}" ContentStringFormat="Configuration for {0}" Padding="8,0,0,0"/>
|
Content="{Binding Plugin.Name}" ContentStringFormat="Configuration for {0}" Padding="8,0,0,0"/>
|
||||||
|
|
||||||
<!-- Options editor -->
|
<!-- Options editor -->
|
||||||
<ListBox Grid.Row="1" Name="lstOptions" ItemsSource="{Binding Plugin.Options}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Padding="8" BorderBrush="Transparent">
|
<ListBox Grid.Row="1" Name="lstOptions" ItemsSource="{Binding Plugin.Options}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Padding="8" BorderBrush="Transparent" ItemContainerStyle="{StaticResource OptionItemStyle}">
|
||||||
<ListBox.ItemTemplateSelector>
|
<ListBox.ItemTemplateSelector>
|
||||||
<local:OptionTemplateSelector TextTemplate="{StaticResource TextTemplate}"
|
<local:OptionTemplateSelector TextTemplate="{StaticResource TextTemplate}"
|
||||||
FilePathTemplate="{StaticResource FilePathTemplate}"
|
FilePathTemplate="{StaticResource FilePathTemplate}"
|
||||||
NumberTemplate="{StaticResource NumberTemplate}"
|
NumberDecimalTemplate="{StaticResource NumberDecimalTemplate}"
|
||||||
|
NumberHexTemplate="{StaticResource NumberHexTemplate}"
|
||||||
BooleanTemplate="{StaticResource BooleanTemplate}"
|
BooleanTemplate="{StaticResource BooleanTemplate}"
|
||||||
ChoiceTemplate="{StaticResource ChoiceTemplate}">
|
ChoiceDropdownTemplate="{StaticResource ChoiceDropdownTemplate}"
|
||||||
|
ChoiceListTemplate="{StaticResource ChoiceListTemplate}">
|
||||||
</local:OptionTemplateSelector>
|
</local:OptionTemplateSelector>
|
||||||
</ListBox.ItemTemplateSelector>
|
</ListBox.ItemTemplateSelector>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using System.Collections.Generic;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
using System.Windows.Data;
|
using System.Windows.Data;
|
||||||
using System.Windows.Documents;
|
using System.Windows.Documents;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
@@ -16,30 +17,33 @@ using System.Windows.Media;
|
|||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Il2CppInspectorGUI;
|
|
||||||
using System.Windows.Forms.Design;
|
using System.Windows.Forms.Design;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Il2CppInspector.PluginAPI.V100;
|
using Il2CppInspector.PluginAPI.V100;
|
||||||
using static Il2CppInspector.PluginManager;
|
using Il2CppInspector.Reflection;
|
||||||
|
|
||||||
namespace Il2CppInspector.GUI
|
namespace Il2CppInspectorGUI
|
||||||
{
|
{
|
||||||
// Class which selects the correct control to display for each plugin option
|
// Class which selects the correct control to display for each plugin option
|
||||||
public class OptionTemplateSelector : DataTemplateSelector
|
public class OptionTemplateSelector : DataTemplateSelector
|
||||||
{
|
{
|
||||||
public DataTemplate TextTemplate { get; set; }
|
public DataTemplate TextTemplate { get; set; }
|
||||||
public DataTemplate FilePathTemplate { get; set; }
|
public DataTemplate FilePathTemplate { get; set; }
|
||||||
public DataTemplate NumberTemplate { get; set; }
|
public DataTemplate NumberDecimalTemplate { get; set; }
|
||||||
|
public DataTemplate NumberHexTemplate { get; set; }
|
||||||
public DataTemplate BooleanTemplate { get; set; }
|
public DataTemplate BooleanTemplate { get; set; }
|
||||||
public DataTemplate ChoiceTemplate { get; set; }
|
public DataTemplate ChoiceDropdownTemplate { get; set; }
|
||||||
|
public DataTemplate ChoiceListTemplate { get; set; }
|
||||||
|
|
||||||
// Use some fancy reflection to get the right template property
|
// Use some fancy reflection to get the right template property
|
||||||
|
// If the plugin option is PluginOptionFooBar and its style enum property is Baz, the template will be FooBarBazTemplate
|
||||||
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
|
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
|
||||||
var option = (IPluginOption) item;
|
var option = (IPluginOption) item;
|
||||||
return (DataTemplate) GetType().GetProperty(option.GetType().Name.Split("`")[0]["PluginOption".Length..] + "Template").GetValue(this);
|
var style = item.GetType().GetProperty("Style")?.GetValue(item).ToString() ?? string.Empty;
|
||||||
|
return (DataTemplate) GetType().GetProperty(option.GetType().Name.Split("`")[0]["PluginOption".Length..] + style + "Template").GetValue(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,14 +55,76 @@ namespace Il2CppInspector.GUI
|
|||||||
// Item to configure
|
// Item to configure
|
||||||
public IPlugin Plugin { get; }
|
public IPlugin Plugin { get; }
|
||||||
|
|
||||||
|
// This helps us find XAML elements withing a DataTemplate
|
||||||
|
// Adapted from https://docs.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-find-datatemplate-generated-elements?view=netframeworkdesktop-4.8
|
||||||
|
private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject {
|
||||||
|
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) {
|
||||||
|
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
|
||||||
|
if (child != null && child is childItem) {
|
||||||
|
return (childItem) child;
|
||||||
|
} else {
|
||||||
|
childItem childOfChild = FindVisualChild<childItem>(child);
|
||||||
|
if (childOfChild != null)
|
||||||
|
return childOfChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from https://stackoverflow.com/a/565560
|
||||||
|
// The dependency object is valid if it has no errors and all
|
||||||
|
// of its children (that are dependency objects) are error-free.
|
||||||
|
private bool IsValid(DependencyObject obj)
|
||||||
|
=> !Validation.GetHasError(obj) && Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(obj)).All(n => IsValid(VisualTreeHelper.GetChild(obj, n)));
|
||||||
|
|
||||||
|
// Adapted from https://stackoverflow.com/questions/22510428/get-listboxitem-from-listbox
|
||||||
|
// In order to force validation with ExceptionValidationRule when the window opens,
|
||||||
|
// we need to wait until all of the ListBoxItems are populated, then find the element with the value
|
||||||
|
// then force WPF to try to update the source property to see if it raises an exception
|
||||||
|
// and cause the DataTriggers to execute.
|
||||||
|
// This relies on a 'valueControl' named element existing.
|
||||||
|
void OptionsListBoxStatusChanged(object sender, EventArgs e) {
|
||||||
|
// Wait for items to be generated
|
||||||
|
if (lstOptions.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Remove event
|
||||||
|
lstOptions.ItemContainerGenerator.StatusChanged -= OptionsListBoxStatusChanged;
|
||||||
|
|
||||||
|
// your items are now generated
|
||||||
|
|
||||||
|
// Adapted from https://stackoverflow.com/a/18008545
|
||||||
|
foreach (var item in lstOptions.Items) {
|
||||||
|
var listBoxItem = lstOptions.ItemContainerGenerator.ContainerFromItem(item);
|
||||||
|
var presenter = FindVisualChild<ContentPresenter>(listBoxItem);
|
||||||
|
|
||||||
|
var dataTemplate = presenter.ContentTemplateSelector.SelectTemplate(item, listBoxItem);
|
||||||
|
var boundControl = dataTemplate.FindName("valueControl", presenter);
|
||||||
|
|
||||||
|
// Adapted from https://stackoverflow.com/questions/794370/update-all-bindings-in-usercontrol-at-once
|
||||||
|
((FrameworkElement) boundControl).GetBindingExpression(boundControl switch {
|
||||||
|
TextBox t => TextBox.TextProperty,
|
||||||
|
CheckBox c => CheckBox.IsCheckedProperty,
|
||||||
|
ListBox l => ListBox.SelectedValueProperty,
|
||||||
|
ComboBox m => ComboBox.SelectedValueProperty,
|
||||||
|
TextBlock b => TextBlock.TextProperty,
|
||||||
|
_ => throw new InvalidOperationException("Unknown value control type")
|
||||||
|
}).UpdateSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize configuration dialog window
|
||||||
public PluginConfigurationDialog(IPlugin plugin) {
|
public PluginConfigurationDialog(IPlugin plugin) {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
Plugin = plugin;
|
Plugin = plugin;
|
||||||
|
|
||||||
|
// Validate options once they have loaded
|
||||||
|
lstOptions.ItemContainerGenerator.StatusChanged += OptionsListBoxStatusChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void okButton_Click(object sender, RoutedEventArgs e) {
|
private void okButton_Click(object sender, RoutedEventArgs e) {
|
||||||
// Closes dialog box automatically
|
// Close dialog box but call OnClosing first to validate all the options
|
||||||
DialogResult = true;
|
DialogResult = true;
|
||||||
|
|
||||||
// TODO: Plugin hook OptionsChanged (and make sure it works when clicking close icon as well)
|
// TODO: Plugin hook OptionsChanged (and make sure it works when clicking close icon as well)
|
||||||
@@ -73,17 +139,21 @@ namespace Il2CppInspector.GUI
|
|||||||
Title = option.Description,
|
Title = option.Description,
|
||||||
Filter = "All files (*.*)|*.*",
|
Filter = "All files (*.*)|*.*",
|
||||||
FileName = option.Value,
|
FileName = option.Value,
|
||||||
CheckFileExists = true
|
CheckFileExists = false,
|
||||||
|
CheckPathExists = false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (openFileDialog.ShowDialog() == true) {
|
if (openFileDialog.ShowDialog() == true) {
|
||||||
option.Value = openFileDialog.FileName;
|
|
||||||
|
|
||||||
// This spaghetti saves us from implementing INotifyPropertyChanged on Plugin.Options
|
// This spaghetti saves us from implementing INotifyPropertyChanged on Plugin.Options
|
||||||
// (we don't want to expose WPF stuff in our SDK)
|
// (we don't want to expose WPF stuff in our SDK)
|
||||||
// Will break if we change the format of the FilePathDataTemplate too much
|
// Will break if we change the format of the FilePathDataTemplate too much
|
||||||
var tb = ((DockPanel) ((Button) sender).Parent).Children.OfType<TextBlock>().First(n => n.Name == "txtFilePathSelector");
|
var tb = ((DockPanel) ((Button) sender).Parent).Children.OfType<StackPanel>().First().Children.OfType<TextBox>().First(n => n.Name == "valueControl");
|
||||||
tb.Text = option.Value;
|
try {
|
||||||
|
tb.Clear();
|
||||||
|
tb.AppendText(openFileDialog.FileName);
|
||||||
|
tb.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,5 +161,13 @@ namespace Il2CppInspector.GUI
|
|||||||
private void txtHexString_PreviewTextInput(object sender, TextCompositionEventArgs e) {
|
private void txtHexString_PreviewTextInput(object sender, TextCompositionEventArgs e) {
|
||||||
e.Handled = !Regex.IsMatch(e.Text, @"[A-Fa-f0-9]");
|
e.Handled = !Regex.IsMatch(e.Text, @"[A-Fa-f0-9]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check options validity before allowing the dialog to close either by clicking OK or the close icon
|
||||||
|
private void Window_Closing(object sender, CancelEventArgs e) {
|
||||||
|
if (!IsValid(lstOptions)) {
|
||||||
|
MessageBox.Show("One or more options are invalid.", "Il2CppInspector Plugin Configuration");
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Il2CppInspector.GUI
|
|||||||
private void btnConfig_Click(object sender, RoutedEventArgs e) {
|
private void btnConfig_Click(object sender, RoutedEventArgs e) {
|
||||||
var plugin = (ManagedPlugin) ((Button) sender).DataContext;
|
var plugin = (ManagedPlugin) ((Button) sender).DataContext;
|
||||||
|
|
||||||
var configDlg = new PluginConfigurationDialog(plugin.Plugin);
|
var configDlg = new Il2CppInspectorGUI.PluginConfigurationDialog(plugin.Plugin);
|
||||||
configDlg.Owner = this;
|
configDlg.Owner = this;
|
||||||
configDlg.ShowDialog();
|
configDlg.ShowDialog();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user