mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 12:47:58 +03:00
config_tool: add support for searching
This adds a search box to the config tool to allow filtering properties by keywords. Resolves #1889.
This commit is contained in:
parent
36eec3d72b
commit
1cbded12dc
15 changed files with 394 additions and 50 deletions
|
@ -7,6 +7,7 @@
|
|||
- added support for key/puzzle/pickup descriptions, allowing players to examine said items in the inventory (#1821)
|
||||
- added an option to fix inventory item usage duplication (#1586)
|
||||
- added optional automatic key/puzzle inventory item pre-selection (#1884)
|
||||
- added a search feature to the config tool (#1889)
|
||||
- changed OpenGL backend to use version 3.3, with fallback to 2.1 if initialization fails (#1738)
|
||||
- changed text backend to accept named sequences. Currently supported sequences (limited by the sprites available in OG):
|
||||
- `\{umlaut}`
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
- added support for custom levels to enforce values for any config setting (#1846)
|
||||
- added an option to fix inventory item usage duplication (#1586)
|
||||
- added optional automatic key/puzzle inventory item pre-selection (#1884)
|
||||
- added a search feature to the config tool (#1889)
|
||||
- fixed depth problems when drawing certain rooms (#1853, regression from 0.6)
|
||||
- fixed Lara getting stuck in her hit animation if she is hit while mounting the boat or skidoo (#1606)
|
||||
- fixed being unable to go from surface swimming to underwater swimming without first stopping (#1863, regression from 0.6)
|
||||
|
|
|
@ -58,55 +58,130 @@
|
|||
<utils:RelayKeyBinding
|
||||
CommandBinding="{Binding GitHubCommand}"
|
||||
Key="F11" />
|
||||
<utils:RelayKeyBinding
|
||||
CommandBinding="{Binding BeginSearchCommand}"
|
||||
Modifiers="Ctrl"
|
||||
Key="F" />
|
||||
</Window.InputBindings>
|
||||
|
||||
<DockPanel>
|
||||
<Menu
|
||||
DockPanel.Dock="Top"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrush}}">
|
||||
<MenuItem Header="{Binding ViewText[menu_file]}">
|
||||
<MenuItem
|
||||
Command="{Binding OpenCommand}"
|
||||
Header="{Binding ViewText[command_open]}"
|
||||
InputGestureText="Ctrl+O"/>
|
||||
<MenuItem
|
||||
Command="{Binding ReloadCommand}"
|
||||
Header="{Binding ViewText[command_reload]}"
|
||||
InputGestureText="F5"/>
|
||||
<Separator/>
|
||||
<MenuItem
|
||||
Command="{Binding SaveCommand}"
|
||||
Header="{Binding ViewText[command_save]}"
|
||||
InputGestureText="Ctrl+S"/>
|
||||
<MenuItem
|
||||
Command="{Binding SaveAsCommand}"
|
||||
Header="{Binding ViewText[command_save_as]}"
|
||||
InputGestureText="Ctrl+Alt+S"/>
|
||||
<Separator/>
|
||||
<MenuItem
|
||||
Command="{Binding ExitCommand}"
|
||||
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
Header="{Binding ViewText[command_exit]}"
|
||||
InputGestureText="Alt+F4"/>
|
||||
</MenuItem>
|
||||
<DockPanel DockPanel.Dock="Top">
|
||||
<Menu
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrush}}">
|
||||
<MenuItem Header="{Binding ViewText[menu_file]}">
|
||||
<MenuItem
|
||||
Command="{Binding OpenCommand}"
|
||||
Header="{Binding ViewText[command_open]}"
|
||||
InputGestureText="Ctrl+O"/>
|
||||
<MenuItem
|
||||
Command="{Binding ReloadCommand}"
|
||||
Header="{Binding ViewText[command_reload]}"
|
||||
InputGestureText="F5"/>
|
||||
<Separator/>
|
||||
<MenuItem
|
||||
Command="{Binding SaveCommand}"
|
||||
Header="{Binding ViewText[command_save]}"
|
||||
InputGestureText="Ctrl+S"/>
|
||||
<MenuItem
|
||||
Command="{Binding SaveAsCommand}"
|
||||
Header="{Binding ViewText[command_save_as]}"
|
||||
InputGestureText="Ctrl+Alt+S"/>
|
||||
<Separator/>
|
||||
<MenuItem
|
||||
Command="{Binding ExitCommand}"
|
||||
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
Header="{Binding ViewText[command_exit]}"
|
||||
InputGestureText="Alt+F4"/>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem Header="{Binding ViewText[menu_tools]}">
|
||||
<MenuItem
|
||||
Command="{Binding RestoreDefaultsCommand}"
|
||||
Header="{Binding ViewText[command_restore]}"/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="{Binding ViewText[menu_tools]}">
|
||||
<MenuItem
|
||||
Command="{Binding RestoreDefaultsCommand}"
|
||||
Header="{Binding ViewText[command_restore]}"/>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem Header="{Binding ViewText[menu_help]}">
|
||||
<MenuItem
|
||||
Command="{Binding GitHubCommand}"
|
||||
Header="{Binding ViewText[command_github]}"
|
||||
InputGestureText="F11"/>
|
||||
<Separator/>
|
||||
<MenuItem
|
||||
Command="{Binding AboutCommand}"
|
||||
Header="{Binding ViewText[command_about]}"/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<MenuItem Header="{Binding ViewText[menu_help]}">
|
||||
<MenuItem
|
||||
Command="{Binding GitHubCommand}"
|
||||
Header="{Binding ViewText[command_github]}"
|
||||
InputGestureText="F11"/>
|
||||
<Separator/>
|
||||
<MenuItem
|
||||
Command="{Binding AboutCommand}"
|
||||
Header="{Binding ViewText[command_about]}"/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<Grid
|
||||
DockPanel.Dock="Right"
|
||||
Margin="0,7,7,0"
|
||||
IsEnabled="{Binding IsEditorActive}">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
BorderBrush="#666"
|
||||
BorderThickness="1"
|
||||
Background="{Binding SearchFailStatus, Converter={utils:ConditionalMarkupConverter TrueValue='#FFC7CE', FalseValue='White'}}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="24"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Margin="0,1"
|
||||
x:Name="SearchTermTextBox"
|
||||
Text="{Binding Path=SearchText, UpdateSourceTrigger=PropertyChanged}">
|
||||
<TextBox.Style>
|
||||
<Style TargetType="{x:Type TextBox}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.BeginSearch}" Value="True">
|
||||
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=SearchTermTextBox}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBox.Style>
|
||||
</TextBox>
|
||||
|
||||
<TextBlock
|
||||
IsHitTestVisible="False"
|
||||
Text="{Binding ViewText[label_search]}"
|
||||
Padding="4,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Foreground="#666">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchTermTextBox}" Value="">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Command="{Binding CloseSearchCommand}"
|
||||
Style="{StaticResource SmallButtonStyle}"
|
||||
IsEnabled="{Binding IsSearchTextDefined}"
|
||||
Content="../Resources/close.png"
|
||||
ToolTip="{Binding ViewText[command_close_search]}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
||||
<StatusBar
|
||||
DockPanel.Dock="Bottom">
|
||||
|
@ -163,7 +238,8 @@
|
|||
IsEnabled="{Binding IsEditorActive}"
|
||||
ItemsSource="{Binding Categories}"
|
||||
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"
|
||||
SelectionChanged="TabControl_SelectionChanged">
|
||||
SelectionChanged="TabControl_SelectionChanged"
|
||||
Visibility="{Binding IsSearchActive, Converter={StaticResource InverseBoolToCollapsedConverter}}">
|
||||
<TabControl.Resources>
|
||||
<ResourceDictionary Source="/TRX_ConfigToolLib;component/Resources/styles.xaml" />
|
||||
</TabControl.Resources>
|
||||
|
@ -181,6 +257,34 @@
|
|||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
|
||||
<TabControl
|
||||
Grid.Row="1"
|
||||
Margin="0,0,0,7"
|
||||
Padding="0"
|
||||
IsEnabled="{Binding IsEditorActive}"
|
||||
ItemsSource="{Binding SearchResults}"
|
||||
SelectedItem="{Binding SearchCategory, Mode=OneWay}"
|
||||
Visibility="{Binding IsSearchActive, Converter={StaticResource BoolToCollapsedConverter}}">
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ViewText[label_search_results]}"/>
|
||||
<Button
|
||||
Content="../Resources/close.png"
|
||||
ToolTip="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ViewText[command_close_search]}"
|
||||
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.CloseSearchCommand}"
|
||||
Style="{StaticResource TabButtonStyle}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</TabControl.ItemTemplate>
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<controls:CategoryControl />
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
|
||||
<Grid
|
||||
Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
|
|
|
@ -11,6 +11,7 @@ public class CategoryViewModel
|
|||
public CategoryViewModel(Category category)
|
||||
{
|
||||
_category = category;
|
||||
ItemsSource = new(category.Properties);
|
||||
}
|
||||
|
||||
public string Title
|
||||
|
@ -23,10 +24,7 @@ public class CategoryViewModel
|
|||
get => AssemblyUtils.GetEmbeddedResourcePath(_category.Image ?? _defaultImage);
|
||||
}
|
||||
|
||||
public IEnumerable<BaseProperty> ItemsSource
|
||||
{
|
||||
get => _category.Properties;
|
||||
}
|
||||
public FastObservableCollection<BaseProperty> ItemsSource { get; private set; }
|
||||
|
||||
public double ViewPosition { get; set; }
|
||||
}
|
||||
|
|
|
@ -10,9 +10,12 @@ namespace TRX_ConfigToolLib.Models;
|
|||
|
||||
public class MainWindowViewModel : BaseLanguageViewModel
|
||||
{
|
||||
private const int _minSearchLength = 3;
|
||||
|
||||
private readonly Configuration _configuration;
|
||||
|
||||
public IEnumerable<CategoryViewModel> Categories { get; private set; }
|
||||
public IEnumerable<CategoryViewModel> SearchResults { get; private set; }
|
||||
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
|
@ -28,6 +31,17 @@ public class MainWindowViewModel : BaseLanguageViewModel
|
|||
|
||||
Categories = categories;
|
||||
SelectedCategory = Categories.FirstOrDefault();
|
||||
|
||||
// Search results are contained within a special category whose properties are built
|
||||
// on-the-fly. Pick a random picture for it each time the application is used.
|
||||
Random random = new();
|
||||
SearchCategory = new(new()
|
||||
{
|
||||
Properties = new(),
|
||||
Image = _configuration.Categories[random.Next(_configuration.Categories.Count)].Image,
|
||||
});
|
||||
|
||||
SearchResults = new List<CategoryViewModel> { SearchCategory };
|
||||
}
|
||||
|
||||
private void EditorPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
|
@ -53,6 +67,8 @@ public class MainWindowViewModel : BaseLanguageViewModel
|
|||
}
|
||||
}
|
||||
|
||||
public CategoryViewModel SearchCategory { get; private set; }
|
||||
|
||||
private bool _isEditorDirty;
|
||||
public bool IsEditorDirty
|
||||
{
|
||||
|
@ -230,6 +246,78 @@ public class MainWindowViewModel : BaseLanguageViewModel
|
|||
return IsEditorActive;
|
||||
}
|
||||
|
||||
private bool _beginSearch;
|
||||
public bool BeginSearch
|
||||
{
|
||||
get => _beginSearch;
|
||||
set
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_beginSearch = true;
|
||||
NotifyPropertyChanged();
|
||||
_beginSearch = false;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private string _searchText = string.Empty;
|
||||
public string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set
|
||||
{
|
||||
if (value == _searchText)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_searchText = value;
|
||||
RunPropertySearch();
|
||||
NotifyPropertyChanged();
|
||||
NotifyPropertyChanged(nameof(IsSearchActive));
|
||||
NotifyPropertyChanged(nameof(IsSearchTextDefined));
|
||||
NotifyPropertyChanged(nameof(SearchFailStatus));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSearchActive => SearchCategory.ItemsSource.Any();
|
||||
public bool IsSearchTextDefined => _searchText.Length > 0;
|
||||
public bool SearchFailStatus => _searchText.Trim().Length >= _minSearchLength && !IsSearchActive;
|
||||
|
||||
private void RunPropertySearch()
|
||||
{
|
||||
string text = _searchText.Trim().ToLower();
|
||||
if (text.Length < _minSearchLength)
|
||||
{
|
||||
SearchCategory.ItemsSource.RemoveAll();
|
||||
return;
|
||||
}
|
||||
|
||||
text = TextUtilities.Normalise(text);
|
||||
List<string> keywords = new(text.Split(null));
|
||||
IEnumerable<BaseProperty> matchedProperties = Categories
|
||||
.SelectMany(c => c.ItemsSource)
|
||||
.Where(p => keywords.Any(p.NormalisedText.Contains));
|
||||
|
||||
SearchCategory.ItemsSource.ReplaceCollection(matchedProperties);
|
||||
}
|
||||
|
||||
private RelayCommand _beginSearchCommand;
|
||||
public ICommand BeginSearchCommand
|
||||
{
|
||||
get => _beginSearchCommand ??= new RelayCommand(() => BeginSearch = true);
|
||||
}
|
||||
|
||||
private RelayCommand _closeSearchCommand;
|
||||
public ICommand CloseSearchCommand
|
||||
{
|
||||
get => _closeSearchCommand ??= new RelayCommand(() => SearchText = string.Empty);
|
||||
}
|
||||
|
||||
private RelayCommand _launchGameCommand;
|
||||
public ICommand LaunchGameCommand
|
||||
{
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace TRX_ConfigToolLib.Models;
|
||||
using TRX_ConfigToolLib.Utils;
|
||||
|
||||
namespace TRX_ConfigToolLib.Models;
|
||||
|
||||
public abstract class BaseProperty : BaseNotifyPropertyChanged
|
||||
{
|
||||
|
@ -16,6 +18,8 @@ public abstract class BaseProperty : BaseNotifyPropertyChanged
|
|||
get => Language.Instance.Properties[Field].Description;
|
||||
}
|
||||
|
||||
public string NormalisedText { get; private set; }
|
||||
|
||||
public abstract object ExportValue();
|
||||
public abstract void LoadValue(string value);
|
||||
public abstract void SetToDefault();
|
||||
|
@ -41,5 +45,8 @@ public abstract class BaseProperty : BaseNotifyPropertyChanged
|
|||
public virtual void Initialise(Specification specification)
|
||||
{
|
||||
SetToDefault();
|
||||
|
||||
string text = (Title + " " + Description).ToLower();
|
||||
NormalisedText = TextUtilities.Normalise(text);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
"menu_help": "_Help",
|
||||
"command_github": "_GitHub",
|
||||
"command_about": "_About",
|
||||
"label_search": "Search...",
|
||||
"label_search_results": "Search results",
|
||||
"command_close_search": "Close search",
|
||||
"label_locked_properties": "This configuration set contains some read-only values defined by the game author.",
|
||||
"label_enforced": "Value enforced to:",
|
||||
"checkbox_enabled": "Enabled",
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
"menu_help": "_Ayuda",
|
||||
"command_github": "_GitHub",
|
||||
"command_about": "_Acerca de",
|
||||
"label_search": "Buscar...",
|
||||
"label_search_results": "Resultados de búsqueda",
|
||||
"command_close_search": "Cerrar la búsqueda",
|
||||
"label_locked_properties": "Este conjunto de configuración contiene algunos valores de solo lectura definidos por el autor del juego.",
|
||||
"label_enforced": "Valor aplicado a:",
|
||||
"checkbox_enabled": "Habilitado",
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
"menu_help": "_Aide",
|
||||
"command_github": "_GitHub",
|
||||
"command_about": "_A propos",
|
||||
"label_search": "Rechercher...",
|
||||
"label_search_results": "Résultats de recherche",
|
||||
"command_close_search": "Fermer la recherche",
|
||||
"label_locked_properties": "Cet ensemble de configuration contient certaines valeurs en lecture seule définies par l'auteur du jeu.",
|
||||
"label_enforced": "Valeur imposée à:",
|
||||
"checkbox_enabled": "Activé",
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
"menu_help": "_Aiuto",
|
||||
"command_github": "_GitHub",
|
||||
"command_about": "_Informazioni su",
|
||||
"label_search": "Ricerca...",
|
||||
"label_search_results": "Risultati della ricerca",
|
||||
"command_close_search": "Chiudi la ricerca",
|
||||
"label_locked_properties": "Questo set di configurazione contiene alcuni valori di sola lettura definiti dall'autore del gioco.",
|
||||
"label_enforced": "Valore applicato a:",
|
||||
"checkbox_enabled": "Abilitato",
|
||||
|
|
BIN
tools/config/TRX_ConfigToolLib/Resources/close.png
Normal file
BIN
tools/config/TRX_ConfigToolLib/Resources/close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -225,4 +225,70 @@
|
|||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
TargetType="{x:Type Button}"
|
||||
x:Key="SmallButtonStyle">
|
||||
<Setter
|
||||
Property="Background"
|
||||
Value="#333"/>
|
||||
<Setter
|
||||
Property="Foreground"
|
||||
Value="#FFF"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate
|
||||
TargetType="{x:Type Button}">
|
||||
<Border
|
||||
Background="{TemplateBinding Background}"
|
||||
Padding="0,2"
|
||||
BorderThickness="0"
|
||||
BorderBrush="#ABADB3">
|
||||
<Image
|
||||
Source="{Binding Content, RelativeSource={RelativeSource AncestorType=Button}}"
|
||||
Height="6"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter
|
||||
Property="Background"
|
||||
Value="#121212"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter
|
||||
Property="Background"
|
||||
Value="#858585"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
TargetType="{x:Type Button}"
|
||||
BasedOn="{StaticResource SmallButtonStyle}"
|
||||
x:Key="TabButtonStyle">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate
|
||||
TargetType="{x:Type Button}">
|
||||
<Border
|
||||
Background="{TemplateBinding Background}"
|
||||
Padding="6,2"
|
||||
Margin="4,0,0,0"
|
||||
BorderThickness="1"
|
||||
BorderBrush="#ABADB3">
|
||||
<Image
|
||||
Source="../Resources/close.png"
|
||||
Height="6"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<ItemGroup>
|
||||
<None Remove="Resources\arrow-down.png" />
|
||||
<None Remove="Resources\arrow-up.png" />
|
||||
<None Remove="Resources\close.png" />
|
||||
<None Remove="Resources\const.json" />
|
||||
<None Remove="Resources\Lang\en.json" />
|
||||
<None Remove="Resources\Lang\es.json" />
|
||||
|
@ -28,6 +29,7 @@
|
|||
<ItemGroup>
|
||||
<Resource Include="Resources\arrow-down.png" />
|
||||
<Resource Include="Resources\arrow-up.png" />
|
||||
<Resource Include="Resources\close.png" />
|
||||
<Resource Include="Resources\styles.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Resource>
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace TRX_ConfigToolLib.Utils;
|
||||
|
||||
public class FastObservableCollection<T> : ObservableCollection<T>
|
||||
{
|
||||
public FastObservableCollection()
|
||||
: base() { }
|
||||
|
||||
public FastObservableCollection(IEnumerable<T> collection)
|
||||
: base(collection) { }
|
||||
|
||||
public virtual void RemoveAll()
|
||||
{
|
||||
if (Items.Any())
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ReplaceCollection(IEnumerable<T> collection)
|
||||
{
|
||||
if (collection == null || collection.SequenceEqual(Items))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!collection.Any())
|
||||
{
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Items.Clear();
|
||||
|
||||
int oldCount = Count;
|
||||
foreach (T item in collection)
|
||||
{
|
||||
Items.Add(item);
|
||||
}
|
||||
|
||||
if (Count != oldCount)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
|
||||
}
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
14
tools/config/TRX_ConfigToolLib/Utils/TextUtils.cs
Normal file
14
tools/config/TRX_ConfigToolLib/Utils/TextUtils.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace TRX_ConfigToolLib.Utils;
|
||||
|
||||
public static class TextUtilities
|
||||
{
|
||||
public static string Normalise(string s)
|
||||
{
|
||||
return new string(s.Normalize(NormalizationForm.FormD)
|
||||
.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
|
||||
.ToArray());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue