Compare commits

...
2 Commits
Author SHA1 Message Date
amacocianandGitHub 6a6eea5d02 Implement search bar for builds and skills (#50)
* Add search bars to builds and skills

* Fix search bar not appearing.
Improve search function.

* Increment version

* Rename version check so it can be added to status checks
2022-08-18 21:26:57 +02:00
amacocianandGitHub 8fe9eeb32f Icon downloader bugfixes (#47)
* Fix icon download to properly finish
2022-08-16 19:47:25 +02:00
13 changed files with 261 additions and 37 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ on:
jobs:
build:
check_version:
strategy:
matrix:
@@ -44,8 +44,8 @@ namespace Daybreak.Controls
private bool canNavigate;
[GenerateDependencyProperty(InitialValue = true)]
private bool controlsEnabled;
[GenerateDependencyProperty]
private bool browserSupported;
[GenerateDependencyProperty(InitialValue = null)]
private bool? browserSupported;
[GenerateDependencyProperty]
private bool addressBarReadonly;
[GenerateDependencyProperty]
+35
View File
@@ -0,0 +1,35 @@
<UserControl x:Class="Daybreak.Controls.SearchTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls"
x:Name="_this"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid>
<TextBox Foreground="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=Foreground, Mode=OneWay}"
FontSize="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontSize, Mode=OneWay}"
FontFamily="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontFamily, Mode=OneWay}"
FontStretch="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontStretch, Mode=OneWay}"
FontWeight="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontWeight, Mode=OneWay}"
FontStyle="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontStyle, Mode=OneWay}"
TextChanged="TextBox_TextChanged"
Background="Transparent"/>
<TextBlock Margin="5, 0, 0, 0"
Text="Search"
Background="Transparent"
Opacity="0.5"
IsHitTestVisible="False"
FontSize="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontSize, Mode=OneWay}"
FontFamily="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontFamily, Mode=OneWay}"
FontStretch="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontStretch, Mode=OneWay}"
FontWeight="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontWeight, Mode=OneWay}"
FontStyle="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=FontStyle, Mode=OneWay}"
Foreground="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=Foreground, Mode=OneWay}"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=PlaceholderVisibility, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
</UserControl>
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.Extensions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
namespace Daybreak.Controls
{
/// <summary>
/// Interaction logic for SearchTextBox.xaml
/// </summary>
public partial class SearchTextBox : UserControl
{
public event EventHandler<string> TextChanged;
[GenerateDependencyProperty(InitialValue = true)]
private bool placeholderVisibility;
[GenerateDependencyProperty]
private string searchText;
public SearchTextBox()
{
this.InitializeComponent();
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var searchText = e.Source.As<TextBox>().Text;
if (searchText.IsNullOrWhiteSpace())
{
this.PlaceholderVisibility = true;
}
else
{
this.PlaceholderVisibility = false;
}
this.SearchText = searchText;
this.TextChanged?.Invoke(this, searchText);
}
}
}
+24 -8
View File
@@ -157,14 +157,30 @@
<local:ChromiumBrowserWrapper x:Name="SkillBrowser" ControlsEnabled="False" Width="0" AddressBarReadonly="True" CanNavigate="True"></local:ChromiumBrowserWrapper>
</Grid>
<Grid Grid.Column="1" Grid.RowSpan="6">
<ListView x:Name="SkillsListView" Background="Transparent" Width="0"
ItemsSource="{Binding ElementName=_this, Path=AvailableSkills, Mode=OneWay}" MouseDoubleClick="ListView_MouseDoubleClick">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="16" Foreground="White" TextWrapping="Wrap" Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid x:Name="SkillListContainer">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<local:SearchTextBox FontSize="20"
Foreground="White"
Background="Transparent"
SearchText="{Binding ElementName=_this, Path=SkillSearchText, Mode=TwoWay}"
TextChanged="SearchTextBox_TextChanged"></local:SearchTextBox>
<ScrollViewer VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Disabled"
Grid.Row="1"
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
<ListView x:Name="SkillsListView" Background="Transparent"
ItemsSource="{Binding ElementName=_this, Path=AvailableSkills, Mode=OneWay}" MouseDoubleClick="ListView_MouseDoubleClick">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="16" Foreground="White" TextWrapping="Wrap" Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</Grid>
</Grid>
</Grid>
</UserControl>
@@ -14,6 +14,7 @@ using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using Utils;
namespace Daybreak.Controls
{
@@ -36,6 +37,8 @@ namespace Daybreak.Controls
public event EventHandler BuildChanged;
[GenerateDependencyProperty]
private string skillSearchText;
[GenerateDependencyProperty]
private Profession primaryProfession;
[GenerateDependencyProperty]
@@ -84,6 +87,9 @@ namespace Daybreak.Controls
this.SkillTemplate5.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate6.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate7.InitializeSkillTemplate(iconRetriever);
this.HideSkillListView();
this.HideInfoBrowser();
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
@@ -207,6 +213,9 @@ namespace Daybreak.Controls
var possibleSkills = Skill.Skills
.Where(s => s.Profession == this.PrimaryProfession || s.Profession == this.SecondaryProfession || s.Profession == Profession.None)
.Where(s => s != Skill.NoSkill)
.Where(s => this.SkillSearchText.IsNullOrWhiteSpace() ?
true :
StringUtils.MatchesSearchString(s.Name.Replace("\"", "").Replace("!", ""), this.SkillSearchText.Replace("\"", "").Replace("!", "")))
.OrderBy(s => s.Name);
this.AvailableSkills.ClearAnd().AddRange(possibleSkills);
@@ -297,7 +306,7 @@ namespace Daybreak.Controls
if (this.SkillBrowser.BrowserSupported is true)
{
this.SkillBrowser.Width = 400;
this.SkillsListView.Width = 0;
this.SkillListContainer.Width = 0;
}
}
@@ -312,12 +321,12 @@ namespace Daybreak.Controls
private void ShowSkillListView()
{
this.SkillBrowser.Width = 0;
this.SkillsListView.Width = 400;
this.SkillListContainer.Width = 400;
}
private void HideSkillListView()
{
this.SkillsListView.Width = 0;
this.SkillListContainer.Width = 0;
}
private void HelpButtonPrimary_Clicked(object sender, System.EventArgs e)
@@ -363,6 +372,7 @@ namespace Daybreak.Controls
var skill = sender.As<SkillTemplate>().DataContext.As<Skill>();
if (skill == Skill.NoSkill)
{
this.SkillSearchText = string.Empty;
this.ShowSkillListView();
this.selectingSkillTemplate = sender.As<SkillTemplate>();
}
@@ -374,6 +384,11 @@ namespace Daybreak.Controls
e.Handled = true;
}
private void SearchTextBox_TextChanged(object sender, string e)
{
this.LoadSkills();
}
private void SkillTemplate_RemoveClicked(object sender, System.EventArgs e)
{
sender.As<SkillTemplate>().DataContext = Skill.NoSkill;
@@ -415,5 +430,16 @@ namespace Daybreak.Controls
sender.As<ListView>().Items.Count;
}
}
private void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
if (sender is not ScrollViewer scrollViewer)
{
return;
}
e.Handled = true;
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Extensions;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
@@ -19,7 +20,7 @@ namespace Daybreak.Converters
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return GetVisibility(value);
return this.GetVisibility(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
@@ -29,17 +30,24 @@ namespace Daybreak.Converters
private object GetVisibility(object value)
{
if (!(value is bool))
return DependencyProperty.UnsetValue;
bool objValue = (bool)value;
if (value is not bool)
{
return this.IsHidden ?
Visibility.Hidden :
Visibility.Collapsed;
}
var objValue = value.Cast<bool>();
if ((objValue && TriggerValue && IsHidden) || (!objValue && !TriggerValue && IsHidden))
{
return Visibility.Hidden;
}
if ((objValue && TriggerValue && !IsHidden) || (!objValue && !TriggerValue && !IsHidden))
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
}
+1 -1
View File
@@ -10,7 +10,7 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.3.6</Version>
<Version>0.9.4</Version>
</PropertyGroup>
<ItemGroup>
+2 -2
View File
@@ -104,8 +104,8 @@
</Grid>
<wcl:Border OnResize="Border_OnResize" Grid.RowSpan="2" Active="True"></wcl:Border>
<webview:WebView2 x:Name="BackgroundWebView"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Visibility="Collapsed"
Grid.Row="1"></webview:WebView2>
</Grid>
@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
@@ -98,11 +99,40 @@ namespace Daybreak.Services.IconRetrieve
return;
}
this.iconBrowser.InitializeWebView(this.browserWrapper, this.cancellationTokenSource.Token);
var progressIncrement = 100d / Skill.Skills.Count();
var incomplete = false;
var progressValue = 0d;
foreach (var skill in Skill.Skills.OrderBy(s => s.Name))
var skillsToDownload = new List<Skill>();
foreach(var skill in Skill.Skills.OrderBy(s => s.Name))
{
if (skill == Skill.NoSkill)
{
continue;
}
var logger = this.logger.CreateScopedLogger(nameof(this.DownloadIcons), skill.Name);
logger.LogInformation("Verifying if icon exists");
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Checking(skill.Name, progressValue);
if ((await this.iconCache.GetIconUri(skill)).ExtractValue() is not null)
{
progressValue += progressIncrement;
await Task.Delay(1);
continue;
}
skillsToDownload.Add(skill);
}
if (skillsToDownload.Count == 0)
{
this.logger.LogInformation("No icons missing. Stopping download");
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Finished;
return;
}
this.iconBrowser.InitializeWebView(this.browserWrapper, this.cancellationTokenSource.Token);
var incomplete = false;
foreach (var skill in skillsToDownload)
{
if (this.cancellationTokenSource?.IsCancellationRequested is null or true)
{
@@ -114,17 +144,7 @@ namespace Daybreak.Services.IconRetrieve
{
progressValue += progressIncrement;
continue;
}
var logger = this.logger.CreateScopedLogger(nameof(this.DownloadIcons), skill.Name);
logger.LogInformation("Verifying if icon exists");
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Checking(skill.Name, progressValue);
if ((await this.iconCache.GetIconUri(skill)).ExtractValue() is not null)
{
logger.LogInformation("Icon exists");
progressValue += progressIncrement;
continue;
}
}
logger.LogInformation("Downloading icon");
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Downloading(skill.Name, progressValue);
@@ -156,6 +176,12 @@ namespace Daybreak.Services.IconRetrieve
}
else
{
Application.Current.Dispatcher.Invoke(() =>
{
this.browserWrapper.IsEnabled = false;
this.browserWrapper.Dispose();
});
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Finished;
this.DownloadComplete = true;
}
}
+53
View File
@@ -0,0 +1,53 @@
using System;
using System.Linq;
namespace Utils
{
public static class StringUtils
{
public static int DamerauLevenshteinDistance(string s, string t)
{
var (height, width) = (s.Length + 1, t.Length + 1);
var matrix = new int[height, width];
for (var j = 0; j < height; j++) { matrix[j, 0] = j; };
for (var i = 0; i < width; i++) { matrix[0, i] = i; };
for (var j = 1; j < height; j++)
{
for (var i = 1; i < width; i++)
{
var cost = (s[j - 1] == t[i - 1]) ? 0 : 1;
var insertion = matrix[j, i - 1] + 1;
var deletion = matrix[j - 1, i] + 1;
var substitution = matrix[j - 1, i - 1] + cost;
var distance = Math.Min(insertion, Math.Min(deletion, substitution));
if (j > 1 && i > 1 && s[j - 1] == t[i - 2] && s[j - 2] == t[i - 1])
{
distance = Math.Min(distance, matrix[j - 2, i - 2] + cost);
}
matrix[j, i] = distance;
}
}
return matrix[height - 1, width - 1];
}
/// <summary>
/// Returns true if stringToSearch is somewhat close to searchString.
/// </summary>
/// <param name="stringToSearch"></param>
/// <param name="searchString"></param>
/// <returns>True if strings match.</returns>
public static bool MatchesSearchString(string stringToSearch, string searchString)
{
// Return true if either the distance between the entire text and the searchstring is small enough, or if any of the words are close to the search string.
return DamerauLevenshteinDistance(stringToSearch.ToLower()[..Math.Min(stringToSearch.Length, searchString.Length)], searchString.ToLower()) < 3 ||
stringToSearch.Split(' ').Any(word => DamerauLevenshteinDistance(word.ToLower()[..Math.Min(word.Length, searchString.Length)], searchString.ToLower()) < 3);
}
}
}
+6 -1
View File
@@ -10,6 +10,7 @@
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
@@ -17,7 +18,11 @@
Clicked="BackButton_Clicked"></controls:BackButton>
<controls:AddButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5"
Clicked="AddButton_Clicked"></controls:AddButton>
<ListView Grid.Row="1" Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=BuildEntries, Mode=OneWay}"
<controls:SearchTextBox Grid.Row="1"
FontSize="24"
Foreground="White"
TextChanged="SearchTextBox_TextChanged"></controls:SearchTextBox>
<ListView Grid.Row="2" Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=BuildEntries, Mode=OneWay}"
MouseDoubleClick="ListView_MouseDoubleClick" HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
+14 -1
View File
@@ -2,9 +2,12 @@
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.ViewManagement;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Extensions;
using System.Linq;
using System.Windows.Controls;
using Utils;
namespace Daybreak.Views
{
@@ -16,6 +19,8 @@ namespace Daybreak.Views
private readonly IViewManager viewManager;
private readonly IBuildTemplateManager buildTemplateManager;
private IEnumerable<BuildEntry> buildEntries;
public ObservableCollection<BuildEntry> BuildEntries { get; } = new ObservableCollection<BuildEntry>();
public BuildsListView(
@@ -30,7 +35,8 @@ namespace Daybreak.Views
private void LoadBuilds()
{
this.BuildEntries.ClearAnd().AddRange(this.buildTemplateManager.GetBuilds());
this.buildEntries = this.buildTemplateManager.GetBuilds();
this.BuildEntries.ClearAnd().AddRange(this.buildEntries);
}
private void ListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
@@ -54,5 +60,12 @@ namespace Daybreak.Views
this.buildTemplateManager.RemoveBuild(e);
this.LoadBuilds();
}
private void SearchTextBox_TextChanged(object sender, string e)
{
this.BuildEntries.Clear();
this.BuildEntries.AddRange(
this.buildEntries.Where(b => StringUtils.MatchesSearchString(b.Name, e)));
}
}
}