Skip to content

Commit

Permalink
Add search (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
giuinktse7 authored Jul 25, 2022
1 parent e160922 commit 1a7519c
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 19 deletions.
3 changes: 2 additions & 1 deletion LapisItemEditor/LapisItemEditor.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
Expand All @@ -11,6 +11,7 @@
<PackageReference Include="Avalonia.Desktop" Version="0.10.7" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.7" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.7" />
<PackageReference Include="FuzzySharp" Version="2.0.2" />
</ItemGroup>

<ItemGroup>
Expand Down
85 changes: 84 additions & 1 deletion LapisItemEditor/ViewModels/Main/ItemListViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,112 @@
using DynamicData;
using DynamicData.Binding;
using System.Reactive.Linq;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Input;

namespace LapisItemEditor.ViewModels

{


public sealed class ItemListViewModel : ViewModelBase
{
private SourceList<ItemModel> _items;
private ReadOnlyObservableCollection<ItemModel> _observableItems;

private bool searchForServerId;
private string searchQuery;
public ReactiveCommand<ItemModel, ItemModel> ItemTypeSelected { get; }

public ICommand Search { get; }

public string SearchQuery { get => searchQuery; set => this.RaiseAndSetIfChanged(ref searchQuery, value); }
public bool SearchForServerId { get => searchForServerId; set => this.RaiseAndSetIfChanged(ref searchForServerId, value); }


public class ItemModelComparer<T> : IEqualityComparer<ItemModel>
{
Func<ItemModel, T> f;
public ItemModelComparer(Func<ItemModel, T> f)
{
this.f = f;
}

public bool Equals(ItemModel? x, ItemModel? y)
{
return f(x).Equals(f(y));
}

public int GetHashCode([DisallowNull] ItemModel obj)
{
return (int)obj.ClientId;
}
}

public int getIndex(uint id, bool clientId = true)
{
var item = new ItemModel();
Func<ItemModel, uint> f;
if (clientId)
{
f = (a) => a.ClientId;
item.ClientId = id;
}
else
{
f = (a) => a.ServerId;
item.ServerId = id;
}

return Items.AsObservableList().Items.IndexOf(item, new ItemModelComparer<uint>(f));
}


public ItemListViewModel()
{
ItemTypeSelected = ReactiveCommand.Create<ItemModel, ItemModel>(itemModel => { return itemModel; });
Items = new SourceList<ItemModel>();

var searchFilter = this.WhenValueChanged(x => x.SearchQuery)
.Select(SearchNamePredicate);

var sorter = this.WhenValueChanged(x => x.SearchQuery)
.Select(SortComparer);

var loader = Items
.Connect()
.Sort(SortExpressionComparer<ItemModel>.Ascending(x => x.ServerId))
.Filter(searchFilter)
.Sort(sorter)
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _observableItems)
.DisposeMany().Subscribe();
}

private Func<ItemModel, bool> SearchNamePredicate(string query)
{
bool isInt = int.TryParse(query, out var result);
if (isInt || query == null || query.Length < 3)
{
return model => true;
}

return model => FuzzySharp.Fuzz.Ratio(query, model.Name) >= 0.8;
}

private IComparer<ItemModel> SortComparer(string query)
{
bool isInt = int.TryParse(query, out var result);
if (isInt || query == null || query.Length < 3)
{
return SortExpressionComparer<ItemModel>.Ascending(x => x.ServerId);
}
else
{
return SortExpressionComparer<ItemModel>.Descending(model => FuzzySharp.Fuzz.Ratio(query, model.Name));
}
}

public SourceList<ItemModel> Items
{
get => _items;
Expand Down
41 changes: 25 additions & 16 deletions LapisItemEditor/Views/Main/ItemListView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,29 @@
</RecyclingElementFactory>
</UserControl.Resources>

<Border
BorderThickness="1"
BorderBrush="{DynamicResource SystemControlHighlightBaseMediumLowBrush}"
Margin="0 0 16 0"
Padding="16">
<ScrollViewer
Name="scroller"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Visible">
<ItemsRepeater
Name="repeater"
Background="Transparent"
Items="{Binding ObservableItems}"
ItemTemplate="{StaticResource elementFactory}"/>
</ScrollViewer>
</Border>

<Grid RowDefinitions="Auto,*" Margin="0 0 16 0">
<StackPanel>
<TextBox Grid.Row="0" Name="search_box" KeyUp="searchBoxKeyUp" Padding="16"/>
<CheckBox VerticalAlignment="Center" IsChecked="{Binding SearchForServerId}">Server ID</CheckBox>
</StackPanel>

<Border
Grid.Row="1"
BorderThickness="1"
BorderBrush="{DynamicResource SystemControlHighlightBaseMediumLowBrush}"
Padding="16">
<ScrollViewer
Name="scroller"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Visible">
<ItemsRepeater
Name="repeater"
Background="Transparent"
Items="{Binding ObservableItems}"
ItemTemplate="{StaticResource elementFactory}"/>
</ScrollViewer>
</Border>
</Grid>

</UserControl>
72 changes: 71 additions & 1 deletion LapisItemEditor/Views/Main/ItemListView.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,83 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
using LapisItemEditor.ViewModels;
using static LapisItemEditor.ViewModels.ItemListViewModel;

namespace LapisItemEditor.Views
{
public partial class ItemListView : ReactiveUserControl<WelcomeViewModel>

public partial class ItemListView : ReactiveUserControl<ItemListViewModel>
{

public static Action Debounce(Action f, int milliseconds = 300)
{
var last = 0;
return () =>
{
var current = Interlocked.Increment(ref last);

Task.Delay(milliseconds).ContinueWith(task =>
{
if (current == last)
{
Dispatcher.UIThread.Post(() => f());
}
task.Dispose();
});
};
}
private ScrollViewer scrollView;
private Action debouncedSearch;
private Action debouncedWrapper;

public TextBox SearchBox => this.FindControl<TextBox>("search_box");


public ItemListView()
{
InitializeComponent();

scrollView = this.FindControl<ScrollViewer>("scroller");
debouncedWrapper = Debounce(search, 300);
}

private void search()
{
if (ViewModel == null)
{
return;
}

ViewModel.SearchQuery = SearchBox.Text;

if (int.TryParse(SearchBox.Text, out var result))
{
bool useClientId = !ViewModel.SearchForServerId;
var index = ViewModel.getIndex((uint)result, useClientId);

if (index != -1)
{
scrollView.Offset = new Avalonia.Point(0, index * 51);
}
}
else
{
scrollView.Offset = new Avalonia.Point(0, 0);
}
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);

var a = SearchBox;

}

public void OnSelectTemplateKey(object sender, SelectTemplateEventArgs e)
Expand All @@ -28,5 +90,13 @@ public void OnSelectTemplateKey(object sender, SelectTemplateEventArgs e)

e.TemplateKey = "defaultKey";
}



private void searchBoxKeyUp(object sender, KeyEventArgs e)
{
debouncedWrapper();
}

}
}

0 comments on commit 1a7519c

Please sign in to comment.