From 2cbe25e0f82405ff4bc17e104e1aef49b6ce6b1d Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 18 Nov 2025 16:21:34 +0100 Subject: [PATCH] ui(manager): improve search bindings --- .../Features/SharedFunctions.cs | 2 +- .../Tools/CheckAllActionsHealthyFeature.cs | 4 +- .../Features/Tools/ClearDirectLinksFeature.cs | 4 +- .../Tools/UpdateDirectLinksFeature.cs | 4 +- .../ModpackUpdater.Apps.Manager.csproj | 1 + .../Ui/MainWindow.axaml | 26 ++++++------- .../Ui/MainWindow.axaml.cs | 7 ++-- .../Ui/Models/DynamicDataView.cs | 38 +++++++++++++++++++ .../Models/MainWindow/MainWindowViewModel.cs | 19 +++++----- .../UpdatesCollectorViewModel.cs | 12 +++++- .../Ui/UpdatesCollectorView.axaml | 8 ++-- .../Ui/UpdatesCollectorView.axaml.cs | 16 ++------ 12 files changed, 89 insertions(+), 52 deletions(-) create mode 100644 ModpackUpdater.Apps.Manager/Ui/Models/DynamicDataView.cs diff --git a/ModpackUpdater.Apps.Manager/Features/SharedFunctions.cs b/ModpackUpdater.Apps.Manager/Features/SharedFunctions.cs index b0352ce..ecce6fd 100644 --- a/ModpackUpdater.Apps.Manager/Features/SharedFunctions.cs +++ b/ModpackUpdater.Apps.Manager/Features/SharedFunctions.cs @@ -70,7 +70,7 @@ internal static class SharedFunctions foreach (var update in updates) { var sourceTag = update.AvailableVersions[update.NewVersion].Tag; - if (api.Model.CurrentGridRows?.FirstOrDefault(n => n.Action == update.Origin) is { } row) + if (api.Model.CurrentGridRows.List.Items.FirstOrDefault(n => n.Action == update.Origin) is { } row) row.SourceTag = sourceTag; else update.Origin.SourceTag = sourceTag; diff --git a/ModpackUpdater.Apps.Manager/Features/Tools/CheckAllActionsHealthyFeature.cs b/ModpackUpdater.Apps.Manager/Features/Tools/CheckAllActionsHealthyFeature.cs index a3f1b01..7a68aad 100644 --- a/ModpackUpdater.Apps.Manager/Features/Tools/CheckAllActionsHealthyFeature.cs +++ b/ModpackUpdater.Apps.Manager/Features/Tools/CheckAllActionsHealthyFeature.cs @@ -17,10 +17,10 @@ internal class CheckAllActionsHealthyFeature : PluginFunction, IPluginFeaturePro protected override async Task ExecuteFunctionAsync(PluginFunctionParameter? @params) { - if (@params is not MainApiParameters p || p.Api.Model.CurrentGridRows is null) + if (@params is not MainApiParameters p) return null; - await SharedFunctions.CheckActionHealthy(p.Api, [.. p.Api.Model.CurrentGridRows]); + await SharedFunctions.CheckActionHealthy(p.Api, [.. p.Api.Model.CurrentGridRows.View]); return null; } diff --git a/ModpackUpdater.Apps.Manager/Features/Tools/ClearDirectLinksFeature.cs b/ModpackUpdater.Apps.Manager/Features/Tools/ClearDirectLinksFeature.cs index 29336b6..6b565c3 100644 --- a/ModpackUpdater.Apps.Manager/Features/Tools/ClearDirectLinksFeature.cs +++ b/ModpackUpdater.Apps.Manager/Features/Tools/ClearDirectLinksFeature.cs @@ -17,8 +17,8 @@ internal class ClearDirectLinksFeature : PluginFunction, IPluginFeatureProvider< protected override Task ExecuteFunctionAsync(PluginFunctionParameter? @params) { - if (@params is MainApiParameters p && p.Api.Model.CurrentGridRows is not null) - SharedFunctions.ClearDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows]); + if (@params is MainApiParameters p) + SharedFunctions.ClearDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows.View]); return Task.FromResult(null); } } diff --git a/ModpackUpdater.Apps.Manager/Features/Tools/UpdateDirectLinksFeature.cs b/ModpackUpdater.Apps.Manager/Features/Tools/UpdateDirectLinksFeature.cs index 70cac79..4bfe1f2 100644 --- a/ModpackUpdater.Apps.Manager/Features/Tools/UpdateDirectLinksFeature.cs +++ b/ModpackUpdater.Apps.Manager/Features/Tools/UpdateDirectLinksFeature.cs @@ -17,10 +17,10 @@ internal class UpdateDirectLinksFeature : PluginFunction, IPluginFeatureProvider protected override async Task ExecuteFunctionAsync(PluginFunctionParameter? @params) { - if (@params is not MainApiParameters p || p.Api.Model.CurrentGridRows is null) + if (@params is not MainApiParameters p) return null; - await SharedFunctions.FindNewDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows]); + await SharedFunctions.FindNewDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows.View]); return null; } diff --git a/ModpackUpdater.Apps.Manager/ModpackUpdater.Apps.Manager.csproj b/ModpackUpdater.Apps.Manager/ModpackUpdater.Apps.Manager.csproj index ad77235..f39cf98 100644 --- a/ModpackUpdater.Apps.Manager/ModpackUpdater.Apps.Manager.csproj +++ b/ModpackUpdater.Apps.Manager/ModpackUpdater.Apps.Manager.csproj @@ -50,6 +50,7 @@ + diff --git a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml index 0c6bb29..0f98d33 100644 --- a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml +++ b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml @@ -120,6 +120,18 @@ + + + + + + - - - - - - @@ -172,7 +172,7 @@ Grid.Row="1" x:Name="DataGridActions" VerticalAlignment="Stretch" - ItemsSource="{Binding CurrentGridRows}" + ItemsSource="{Binding CurrentGridRows.View}" SelectedItem="{Binding SelectedGridRow}"> diff --git a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs index 06108c3..357b306 100644 --- a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs +++ b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; +using DynamicData; using ModpackUpdater.Apps.Manager.Api; using ModpackUpdater.Apps.Manager.Api.Model; using ModpackUpdater.Apps.Manager.Api.Plugins.Features; @@ -129,7 +130,7 @@ public partial class MainWindow : Window, IMainApi private async void Window_OnLoaded(object? sender, RoutedEventArgs e) { var updater = new AppUpdates(Program.UpdateUrl, this); - updater.OnDownloadProgramUpdate += (o, args) => Model.Progress.Start(); + updater.OnDownloadProgramUpdate += (o, _) => Model.Progress.Start(); await updater.UpdateApp(); Model.Progress.Stop(); @@ -239,7 +240,7 @@ public partial class MainWindow : Window, IMainApi return; } - rows.Add(new MainWindowGridRow(action, rootInfos)); + rows.List.Add(new MainWindowGridRow(action, rootInfos)); } private void ButtonRemoveAction_OnClick(object? sender, RoutedEventArgs e) @@ -262,6 +263,6 @@ public partial class MainWindow : Window, IMainApi return; } - rows.Remove(row); + rows.List.Remove(row); } } \ No newline at end of file diff --git a/ModpackUpdater.Apps.Manager/Ui/Models/DynamicDataView.cs b/ModpackUpdater.Apps.Manager/Ui/Models/DynamicDataView.cs new file mode 100644 index 0000000..a61d16b --- /dev/null +++ b/ModpackUpdater.Apps.Manager/Ui/Models/DynamicDataView.cs @@ -0,0 +1,38 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using DynamicData; + +namespace ModpackUpdater.Apps.Manager.Ui.Models; + +public class DynamicDataView : INotifyPropertyChanged where T : notnull +{ + public event PropertyChangedEventHandler? PropertyChanged; + + private string? searchText; + private readonly Subject searchTextSubject = new(); + + public SourceList List { get; } = new(); + public ReadOnlyObservableCollection View { get; } + + public DynamicDataView(Func> predicate) + { + List.Connect() + .Filter(searchTextSubject/*.Throttle(TimeSpan.FromMilliseconds(250))*/.Select(predicate)) + .Bind(out var view) + .Subscribe(); + searchTextSubject?.OnNext(searchText); + View = view; + } + + public string? SearchText + { + get => searchText; + set + { + searchText = value; + searchTextSubject.OnNext(value); + } + } +} \ No newline at end of file diff --git a/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowViewModel.cs b/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowViewModel.cs index 832e129..bae0f33 100644 --- a/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowViewModel.cs +++ b/ModpackUpdater.Apps.Manager/Ui/Models/MainWindow/MainWindowViewModel.cs @@ -11,13 +11,13 @@ public class MainWindowViewModel : INotifyPropertyChanged public event PropertyChangedEventHandler? PropertyChanged; private ObservableCollection? currentTreeNodes; - private ObservableCollection? currentGridRows; private MainWindowTreeNode? selectedTreeNode; private IWorkspace? currentWorkspace; public bool IsUpdate => selectedTreeNode is ActionSetTreeNode node && node.Infos is UpdateInfo; public ProgressInfos Progress { get; } = new(); public MainWindowGridRow? SelectedGridRow { get; set; } + public DynamicDataView CurrentGridRows { get; } = new(FilterGridRows); [AlsoNotifyFor(nameof(CurrentTreeNodes))] public IWorkspace? CurrentWorkspace @@ -48,24 +48,23 @@ public class MainWindowViewModel : INotifyPropertyChanged } } - [AlsoNotifyFor(nameof(CurrentGridRows))] public MainWindowTreeNode? SelectedTreeNode { get => selectedTreeNode; set { - currentGridRows = null; selectedTreeNode = value; + CurrentGridRows.List.Edit(list => + { + list.Clear(); + if (CurrentWorkspace?.InstallInfos != null && selectedTreeNode is ActionSetTreeNode node) + list.AddRange(node.Infos.Actions.Select(n => new MainWindowGridRow(n, CurrentWorkspace.InstallInfos))); + }); } } - public ObservableCollection? CurrentGridRows + private static Func FilterGridRows(string? searchText) { - get - { - if (currentGridRows == null && CurrentWorkspace?.InstallInfos != null && selectedTreeNode is ActionSetTreeNode node) - currentGridRows = [.. node.Infos.Actions.Select(n => new MainWindowGridRow(n, CurrentWorkspace.InstallInfos))]; - return currentGridRows; - } + return n => string.IsNullOrWhiteSpace(searchText) || true; } } \ No newline at end of file diff --git a/ModpackUpdater.Apps.Manager/Ui/Models/UpdatesCollectorViewMode/UpdatesCollectorViewModel.cs b/ModpackUpdater.Apps.Manager/Ui/Models/UpdatesCollectorViewMode/UpdatesCollectorViewModel.cs index 15d627a..eaa8d95 100644 --- a/ModpackUpdater.Apps.Manager/Ui/Models/UpdatesCollectorViewMode/UpdatesCollectorViewModel.cs +++ b/ModpackUpdater.Apps.Manager/Ui/Models/UpdatesCollectorViewMode/UpdatesCollectorViewModel.cs @@ -1,5 +1,9 @@ using System.Collections.ObjectModel; using System.ComponentModel; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using DynamicData; using PropertyChanged; namespace ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode; @@ -9,6 +13,10 @@ public class UpdatesCollectorViewModel : INotifyPropertyChanged public event PropertyChangedEventHandler? PropertyChanged; public ProgressInfos Progress { get; } = new(); - public string? SearchText { get; set; } - public ObservableCollection Updates { get; } = []; + public DynamicDataView Updates { get; } = new(FilterUpdates); + + private static Func FilterUpdates(string? searchText) + { + return n => string.IsNullOrWhiteSpace(searchText) || (n.Origin.Name != null && n.Origin.Name.Contains(searchText, StringComparison.InvariantCultureIgnoreCase)); + } } \ No newline at end of file diff --git a/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml b/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml index e0d3986..830369a 100644 --- a/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml +++ b/ModpackUpdater.Apps.Manager/Ui/UpdatesCollectorView.axaml @@ -26,15 +26,14 @@ + Text="{Binding Updates.SearchText}"/> + ItemsSource="{Binding Updates.View}"> @@ -43,8 +42,7 @@ ColumnDefinitions="20*,20*,20*,Auto" ColumnSpacing="6" RowSpacing="6" - Margin="3" - IsVisible="{Binding Visible}"> + Margin="3">