ui(manager): improve search bindings
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -17,10 +17,10 @@ internal class CheckAllActionsHealthyFeature : PluginFunction, IPluginFeaturePro
|
||||
|
||||
protected override async Task<object?> 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;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ internal class ClearDirectLinksFeature : PluginFunction, IPluginFeatureProvider<
|
||||
|
||||
protected override Task<object?> 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<object?>(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ internal class UpdateDirectLinksFeature : PluginFunction, IPluginFeatureProvider
|
||||
|
||||
protected override async Task<object?> 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;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
|
||||
<PackageReference Include="DynamicData" Version="9.4.1" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
|
||||
<PackageReference Include="EPPlus" Version="8.2.1" />
|
||||
<PackageReference Include="NGitLab" Version="11.0.1" />
|
||||
|
||||
@@ -120,6 +120,18 @@
|
||||
<StackPanel
|
||||
Orientation="Horizontal">
|
||||
|
||||
<!-- TextBox: Version -->
|
||||
<TextBox
|
||||
Margin="3, 0, 3, 0"
|
||||
Width="100"
|
||||
Text="{Binding Version}"/>
|
||||
|
||||
<!-- CheckBox: Is public -->
|
||||
<CheckBox
|
||||
Margin="3, 0, 3, 0"
|
||||
Content="{x:Static langRes:GeneralLangRes.Public}"
|
||||
IsChecked="{Binding IsPublic}"/>
|
||||
|
||||
<!-- Button: Add action -->
|
||||
<pilz:ImageButton
|
||||
x:Name="ButtonAddAction"
|
||||
@@ -135,18 +147,6 @@
|
||||
ImageSource="{x:Static local:MainWindow.ButtonImageRemoveAction}"
|
||||
Background="Transparent"
|
||||
Click="ButtonRemoveAction_OnClick"/>
|
||||
|
||||
<!-- TextBox: Version -->
|
||||
<TextBox
|
||||
Margin="3, 0, 3, 0"
|
||||
Width="100"
|
||||
Text="{Binding Version}"/>
|
||||
|
||||
<!-- CheckBox: Is public -->
|
||||
<CheckBox
|
||||
Margin="3, 0, 3, 0"
|
||||
Content="{x:Static langRes:GeneralLangRes.Public}"
|
||||
IsChecked="{Binding IsPublic}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
Grid.Row="1"
|
||||
x:Name="DataGridActions"
|
||||
VerticalAlignment="Stretch"
|
||||
ItemsSource="{Binding CurrentGridRows}"
|
||||
ItemsSource="{Binding CurrentGridRows.View}"
|
||||
SelectedItem="{Binding SelectedGridRow}">
|
||||
|
||||
<DataGrid.ContextMenu>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
38
ModpackUpdater.Apps.Manager/Ui/Models/DynamicDataView.cs
Normal file
38
ModpackUpdater.Apps.Manager/Ui/Models/DynamicDataView.cs
Normal file
@@ -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<T> : INotifyPropertyChanged where T : notnull
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private string? searchText;
|
||||
private readonly Subject<string?> searchTextSubject = new();
|
||||
|
||||
public SourceList<T> List { get; } = new();
|
||||
public ReadOnlyObservableCollection<T> View { get; }
|
||||
|
||||
public DynamicDataView(Func<string?, Func<T, bool>> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,13 @@ public class MainWindowViewModel : INotifyPropertyChanged
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private ObservableCollection<MainWindowTreeNode>? currentTreeNodes;
|
||||
private ObservableCollection<MainWindowGridRow>? 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<MainWindowGridRow> 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<MainWindowGridRow>? CurrentGridRows
|
||||
private static Func<MainWindowGridRow, bool> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<ModUpdateInfo> Updates { get; } = [];
|
||||
public DynamicDataView<ModUpdateInfo> Updates { get; } = new(FilterUpdates);
|
||||
|
||||
private static Func<ModUpdateInfo, bool> FilterUpdates(string? searchText)
|
||||
{
|
||||
return n => string.IsNullOrWhiteSpace(searchText) || (n.Origin.Name != null && n.Origin.Name.Contains(searchText, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
}
|
||||
@@ -26,15 +26,14 @@
|
||||
<TextBox
|
||||
Grid.Row="0"
|
||||
Watermark="Search"
|
||||
Text="{Binding SearchText}"
|
||||
TextChanged="TextBoxSearch_OnTextChanged"/>
|
||||
Text="{Binding Updates.SearchText}"/>
|
||||
|
||||
<!-- ScrollViewer: Updates -->
|
||||
<ScrollViewer
|
||||
Grid.Row="1">
|
||||
|
||||
<ItemsControl
|
||||
ItemsSource="{Binding Updates}">
|
||||
ItemsSource="{Binding Updates.View}">
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -43,8 +42,7 @@
|
||||
ColumnDefinitions="20*,20*,20*,Auto"
|
||||
ColumnSpacing="6"
|
||||
RowSpacing="6"
|
||||
Margin="3"
|
||||
IsVisible="{Binding Visible}">
|
||||
Margin="3">
|
||||
|
||||
<!-- Label: Name -->
|
||||
<TextBlock
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using DynamicData;
|
||||
using ModpackUpdater.Apps.Manager.Api.Model;
|
||||
using ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode;
|
||||
using ModpackUpdater.Manager;
|
||||
@@ -36,7 +37,7 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
|
||||
if (updates == null || updates.Length == 0 || updates[0].Tag == action.SourceTag)
|
||||
continue;
|
||||
|
||||
Model.Updates.Add(new(updates, action));
|
||||
Model.Updates.List.Add(new(updates, action));
|
||||
|
||||
if (IsClosed)
|
||||
break;
|
||||
@@ -47,7 +48,7 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
|
||||
|
||||
protected override object GetResult()
|
||||
{
|
||||
return new ModUpdates(Model.Updates);
|
||||
return new ModUpdates([.. Model.Updates.List.Items]);
|
||||
}
|
||||
|
||||
private async void Me_OnLoaded(object? sender, RoutedEventArgs e)
|
||||
@@ -55,18 +56,9 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
|
||||
await FindUpdates();
|
||||
}
|
||||
|
||||
private void TextBoxSearch_OnTextChanged(object? sender, TextChangedEventArgs e)
|
||||
{
|
||||
var searchString = Model.SearchText?.Trim().ToLowerInvariant();
|
||||
var hasNoSearch = string.IsNullOrWhiteSpace(searchString);
|
||||
|
||||
foreach (var item in Model.Updates)
|
||||
item.Visible = hasNoSearch || (item.Origin.Name != null && item.Origin.Name.Contains(searchString!, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
private void ButtonRemoveUpdate_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.DataContext is ModUpdateInfo update)
|
||||
Model.Updates.Remove(update);
|
||||
Model.Updates.List.Remove(update);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user