ui(manager): improve search bindings
This commit is contained in:
@@ -70,7 +70,7 @@ internal static class SharedFunctions
|
|||||||
foreach (var update in updates)
|
foreach (var update in updates)
|
||||||
{
|
{
|
||||||
var sourceTag = update.AvailableVersions[update.NewVersion].Tag;
|
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;
|
row.SourceTag = sourceTag;
|
||||||
else
|
else
|
||||||
update.Origin.SourceTag = sourceTag;
|
update.Origin.SourceTag = sourceTag;
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ internal class CheckAllActionsHealthyFeature : PluginFunction, IPluginFeaturePro
|
|||||||
|
|
||||||
protected override async Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params)
|
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;
|
return null;
|
||||||
|
|
||||||
await SharedFunctions.CheckActionHealthy(p.Api, [.. p.Api.Model.CurrentGridRows]);
|
await SharedFunctions.CheckActionHealthy(p.Api, [.. p.Api.Model.CurrentGridRows.View]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ internal class ClearDirectLinksFeature : PluginFunction, IPluginFeatureProvider<
|
|||||||
|
|
||||||
protected override Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params)
|
protected override Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params)
|
||||||
{
|
{
|
||||||
if (@params is MainApiParameters p && p.Api.Model.CurrentGridRows is not null)
|
if (@params is MainApiParameters p)
|
||||||
SharedFunctions.ClearDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows]);
|
SharedFunctions.ClearDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows.View]);
|
||||||
return Task.FromResult<object?>(null);
|
return Task.FromResult<object?>(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ internal class UpdateDirectLinksFeature : PluginFunction, IPluginFeatureProvider
|
|||||||
|
|
||||||
protected override async Task<object?> ExecuteFunctionAsync(PluginFunctionParameter? @params)
|
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;
|
return null;
|
||||||
|
|
||||||
await SharedFunctions.FindNewDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows]);
|
await SharedFunctions.FindNewDirectLinks(p.Api, [.. p.Api.Model.CurrentGridRows.View]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
|
<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="MessageBox.Avalonia" Version="3.3.0" />
|
||||||
<PackageReference Include="EPPlus" Version="8.2.1" />
|
<PackageReference Include="EPPlus" Version="8.2.1" />
|
||||||
<PackageReference Include="NGitLab" Version="11.0.1" />
|
<PackageReference Include="NGitLab" Version="11.0.1" />
|
||||||
|
|||||||
@@ -120,6 +120,18 @@
|
|||||||
<StackPanel
|
<StackPanel
|
||||||
Orientation="Horizontal">
|
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 -->
|
<!-- Button: Add action -->
|
||||||
<pilz:ImageButton
|
<pilz:ImageButton
|
||||||
x:Name="ButtonAddAction"
|
x:Name="ButtonAddAction"
|
||||||
@@ -135,18 +147,6 @@
|
|||||||
ImageSource="{x:Static local:MainWindow.ButtonImageRemoveAction}"
|
ImageSource="{x:Static local:MainWindow.ButtonImageRemoveAction}"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
Click="ButtonRemoveAction_OnClick"/>
|
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>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
x:Name="DataGridActions"
|
x:Name="DataGridActions"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
ItemsSource="{Binding CurrentGridRows}"
|
ItemsSource="{Binding CurrentGridRows.View}"
|
||||||
SelectedItem="{Binding SelectedGridRow}">
|
SelectedItem="{Binding SelectedGridRow}">
|
||||||
|
|
||||||
<DataGrid.ContextMenu>
|
<DataGrid.ContextMenu>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
|
using DynamicData;
|
||||||
using ModpackUpdater.Apps.Manager.Api;
|
using ModpackUpdater.Apps.Manager.Api;
|
||||||
using ModpackUpdater.Apps.Manager.Api.Model;
|
using ModpackUpdater.Apps.Manager.Api.Model;
|
||||||
using ModpackUpdater.Apps.Manager.Api.Plugins.Features;
|
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)
|
private async void Window_OnLoaded(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var updater = new AppUpdates(Program.UpdateUrl, this);
|
var updater = new AppUpdates(Program.UpdateUrl, this);
|
||||||
updater.OnDownloadProgramUpdate += (o, args) => Model.Progress.Start();
|
updater.OnDownloadProgramUpdate += (o, _) => Model.Progress.Start();
|
||||||
await updater.UpdateApp();
|
await updater.UpdateApp();
|
||||||
Model.Progress.Stop();
|
Model.Progress.Stop();
|
||||||
|
|
||||||
@@ -239,7 +240,7 @@ public partial class MainWindow : Window, IMainApi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.Add(new MainWindowGridRow(action, rootInfos));
|
rows.List.Add(new MainWindowGridRow(action, rootInfos));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ButtonRemoveAction_OnClick(object? sender, RoutedEventArgs e)
|
private void ButtonRemoveAction_OnClick(object? sender, RoutedEventArgs e)
|
||||||
@@ -262,6 +263,6 @@ public partial class MainWindow : Window, IMainApi
|
|||||||
return;
|
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;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
private ObservableCollection<MainWindowTreeNode>? currentTreeNodes;
|
private ObservableCollection<MainWindowTreeNode>? currentTreeNodes;
|
||||||
private ObservableCollection<MainWindowGridRow>? currentGridRows;
|
|
||||||
private MainWindowTreeNode? selectedTreeNode;
|
private MainWindowTreeNode? selectedTreeNode;
|
||||||
private IWorkspace? currentWorkspace;
|
private IWorkspace? currentWorkspace;
|
||||||
|
|
||||||
public bool IsUpdate => selectedTreeNode is ActionSetTreeNode node && node.Infos is UpdateInfo;
|
public bool IsUpdate => selectedTreeNode is ActionSetTreeNode node && node.Infos is UpdateInfo;
|
||||||
public ProgressInfos Progress { get; } = new();
|
public ProgressInfos Progress { get; } = new();
|
||||||
public MainWindowGridRow? SelectedGridRow { get; set; }
|
public MainWindowGridRow? SelectedGridRow { get; set; }
|
||||||
|
public DynamicDataView<MainWindowGridRow> CurrentGridRows { get; } = new(FilterGridRows);
|
||||||
|
|
||||||
[AlsoNotifyFor(nameof(CurrentTreeNodes))]
|
[AlsoNotifyFor(nameof(CurrentTreeNodes))]
|
||||||
public IWorkspace? CurrentWorkspace
|
public IWorkspace? CurrentWorkspace
|
||||||
@@ -48,24 +48,23 @@ public class MainWindowViewModel : INotifyPropertyChanged
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[AlsoNotifyFor(nameof(CurrentGridRows))]
|
|
||||||
public MainWindowTreeNode? SelectedTreeNode
|
public MainWindowTreeNode? SelectedTreeNode
|
||||||
{
|
{
|
||||||
get => selectedTreeNode;
|
get => selectedTreeNode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
currentGridRows = null;
|
|
||||||
selectedTreeNode = value;
|
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
|
return n => string.IsNullOrWhiteSpace(searchText) || true;
|
||||||
{
|
|
||||||
if (currentGridRows == null && CurrentWorkspace?.InstallInfos != null && selectedTreeNode is ActionSetTreeNode node)
|
|
||||||
currentGridRows = [.. node.Infos.Actions.Select(n => new MainWindowGridRow(n, CurrentWorkspace.InstallInfos))];
|
|
||||||
return currentGridRows;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Reactive;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Reactive.Subjects;
|
||||||
|
using DynamicData;
|
||||||
using PropertyChanged;
|
using PropertyChanged;
|
||||||
|
|
||||||
namespace ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode;
|
namespace ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode;
|
||||||
@@ -9,6 +13,10 @@ public class UpdatesCollectorViewModel : INotifyPropertyChanged
|
|||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
public ProgressInfos Progress { get; } = new();
|
public ProgressInfos Progress { get; } = new();
|
||||||
public string? SearchText { get; set; }
|
public DynamicDataView<ModUpdateInfo> Updates { get; } = new(FilterUpdates);
|
||||||
public ObservableCollection<ModUpdateInfo> Updates { get; } = [];
|
|
||||||
|
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
|
<TextBox
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Watermark="Search"
|
Watermark="Search"
|
||||||
Text="{Binding SearchText}"
|
Text="{Binding Updates.SearchText}"/>
|
||||||
TextChanged="TextBoxSearch_OnTextChanged"/>
|
|
||||||
|
|
||||||
<!-- ScrollViewer: Updates -->
|
<!-- ScrollViewer: Updates -->
|
||||||
<ScrollViewer
|
<ScrollViewer
|
||||||
Grid.Row="1">
|
Grid.Row="1">
|
||||||
|
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
ItemsSource="{Binding Updates}">
|
ItemsSource="{Binding Updates.View}">
|
||||||
|
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
@@ -43,8 +42,7 @@
|
|||||||
ColumnDefinitions="20*,20*,20*,Auto"
|
ColumnDefinitions="20*,20*,20*,Auto"
|
||||||
ColumnSpacing="6"
|
ColumnSpacing="6"
|
||||||
RowSpacing="6"
|
RowSpacing="6"
|
||||||
Margin="3"
|
Margin="3">
|
||||||
IsVisible="{Binding Visible}">
|
|
||||||
|
|
||||||
<!-- Label: Name -->
|
<!-- Label: Name -->
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using DynamicData;
|
||||||
using ModpackUpdater.Apps.Manager.Api.Model;
|
using ModpackUpdater.Apps.Manager.Api.Model;
|
||||||
using ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode;
|
using ModpackUpdater.Apps.Manager.Ui.Models.UpdatesCollectorViewMode;
|
||||||
using ModpackUpdater.Manager;
|
using ModpackUpdater.Manager;
|
||||||
@@ -36,7 +37,7 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
|
|||||||
if (updates == null || updates.Length == 0 || updates[0].Tag == action.SourceTag)
|
if (updates == null || updates.Length == 0 || updates[0].Tag == action.SourceTag)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Model.Updates.Add(new(updates, action));
|
Model.Updates.List.Add(new(updates, action));
|
||||||
|
|
||||||
if (IsClosed)
|
if (IsClosed)
|
||||||
break;
|
break;
|
||||||
@@ -47,7 +48,7 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
|
|||||||
|
|
||||||
protected override object GetResult()
|
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)
|
private async void Me_OnLoaded(object? sender, RoutedEventArgs e)
|
||||||
@@ -55,18 +56,9 @@ public partial class UpdatesCollectorView : AvaloniaFlyoutBase
|
|||||||
await FindUpdates();
|
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)
|
private void ButtonRemoveUpdate_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is Button button && button.DataContext is ModUpdateInfo update)
|
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