Skip to content

Commit

Permalink
Improve/fix UI in a few ways
Browse files Browse the repository at this point in the history
Fixed a bug where the tames list could overwrite a user's custom search list.
Improve responsiveness of UI when altering searches.
Add display of the number of creatures that match a search.
  • Loading branch information
coldino committed Nov 15, 2018
1 parent 3fbe8d0 commit a6874d1
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 10 deletions.
5 changes: 3 additions & 2 deletions LarkatorGUI/Converters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var v = (bool)value;
return v ? Visibility.Visible : Visibility.Collapsed;
if (value is bool asBool) return asBool ? Visibility.Visible : Visibility.Collapsed;
if (value is int asInt) return asInt != 0 ? Visibility.Visible : Visibility.Collapsed;
return Visibility.Collapsed;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
Expand Down
114 changes: 114 additions & 0 deletions LarkatorGUI/DebounceDispatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System;
using System.Windows.Threading;

namespace LarkatorGUI
{
/// <summary>
/// Provides Debounce() and Throttle() methods.
/// Use these methods to ensure that events aren't handled too frequently.
///
/// Throttle() ensures that events are throttled by the interval specified.
/// Only the last event in the interval sequence of events fires.
///
/// Debounce() fires an event only after the specified interval has passed
/// in which no other pending event has fired. Only the last event in the
/// sequence is fired.
///
/// From https://weblog.west-wind.com/posts/2017/Jul/02/Debouncing-and-Throttling-Dispatcher-Events.
/// </summary>
public class DebounceDispatcher
{
private DispatcherTimer timer;
private DateTime timerStarted { get; set; } = DateTime.UtcNow.AddYears(-1);

/// <summary>
/// Debounce an event by resetting the event timeout every time the event is
/// fired. The behavior is that the Action passed is fired only after events
/// stop firing for the given timeout period.
///
/// Use Debounce when you want events to fire only after events stop firing
/// after the given interval timeout period.
///
/// Wrap the logic you would normally use in your event code into
/// the Action you pass to this method to debounce the event.
/// Example: https://gist.github.com/RickStrahl/0519b678f3294e27891f4d4f0608519a
/// </summary>
/// <param name="interval">Timeout in Milliseconds</param>
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
/// <param name="param">optional parameter</param>
/// <param name="priority">optional priorty for the dispatcher</param>
/// <param name="disp">optional dispatcher. If not passed or null CurrentDispatcher is used.</param>
public void Debounce(int interval, Action<object> action,
object param = null,
DispatcherPriority priority = DispatcherPriority.ApplicationIdle,
Dispatcher disp = null)
{
// kill pending timer and pending ticks
timer?.Stop();
timer = null;

if (disp == null)
disp = Dispatcher.CurrentDispatcher;

// timer is recreated for each event and effectively
// resets the timeout. Action only fires after timeout has fully
// elapsed without other events firing in between
timer = new DispatcherTimer(TimeSpan.FromMilliseconds(interval), priority, (s, e) =>
{
if (timer == null)
return;

timer?.Stop();
timer = null;
action.Invoke(param);
}, disp);

timer.Start();
}

/// <summary>
/// This method throttles events by allowing only 1 event to fire for the given
/// timeout period. Only the last event fired is handled - all others are ignored.
/// Throttle will fire events every timeout ms even if additional events are pending.
///
/// Use Throttle where you need to ensure that events fire at given intervals.
/// </summary>
/// <param name="interval">Timeout in Milliseconds</param>
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
/// <param name="param">optional parameter</param>
/// <param name="priority">optional priorty for the dispatcher</param>
/// <param name="disp">optional dispatcher. If not passed or null CurrentDispatcher is used.</param>
public void Throttle(int interval, Action<object> action,
object param = null,
DispatcherPriority priority = DispatcherPriority.ApplicationIdle,
Dispatcher disp = null)
{
// kill pending timer and pending ticks
timer?.Stop();
timer = null;

if (disp == null)
disp = Dispatcher.CurrentDispatcher;

var curTime = DateTime.UtcNow;

// if timeout is not up yet - adjust timeout to fire
// with potentially new Action parameters
if (curTime.Subtract(timerStarted).TotalMilliseconds < interval)
interval -= (int)curTime.Subtract(timerStarted).TotalMilliseconds;

timer = new DispatcherTimer(TimeSpan.FromMilliseconds(interval), priority, (s, e) =>
{
if (timer == null)
return;

timer?.Stop();
timer = null;
action.Invoke(param);
}, disp);

timer.Start();
timerStarted = curTime;
}
}
}
1 change: 1 addition & 0 deletions LarkatorGUI/LarkatorGUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
<Compile Include="CalibrationWindow.xaml.cs">
<DependentUpon>CalibrationWindow.xaml</DependentUpon>
</Compile>
<Compile Include="DebounceDispatcher.cs" />
<Compile Include="DinoViewModel.cs" />
<Compile Include="DummyMainWindow.cs" />
<Compile Include="ExternalToolsException.cs" />
Expand Down
7 changes: 6 additions & 1 deletion LarkatorGUI/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<TextBlock Grid.Row="0" FontSize="24" TextAlignment="Center" Text="The Chase" Margin="0"/>
Expand Down Expand Up @@ -380,7 +381,11 @@
</DataGrid.Columns>
</DataGrid>

<StackPanel Grid.Row="2" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center">
<TextBlock Grid.Row="2" HorizontalAlignment="Center" Visibility="{Binding ShowCounts, Converter={StaticResource BoolToVisibilityConverter}}">
Matching <TextBlock Text="{Binding ResultMatchingCount}"/> of <TextBlock Text="{Binding ResultTotalCount}"/> creatures
</TextBlock>

<StackPanel Grid.Row="3" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center">
<StackPanel x:Name="devButtons" Visibility="Collapsed" Orientation="Horizontal" Margin="0,0,32,0">
<fa:ImageAwesome Icon="Bug" Height="30" Width="30" Margin="8" Foreground="#88FFFF00" ToolTip="Add dummy map points" MouseDown="Dev_DummyData_Click"/>
<fa:ImageAwesome Icon="Crop" Height="30" Width="30" Margin="8" Foreground="#88FFFF00" ToolTip="Calibration" MouseDown="Dev_Calibration_Click"/>
Expand Down
60 changes: 53 additions & 7 deletions LarkatorGUI/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,33 @@ public string SearchText
set { SetValue(SearchTextProperty, value); }
}

public int ResultMatchingCount
{
get { return (int)GetValue(ResultMatchingCountProperty); }
set { SetValue(ResultMatchingCountProperty, value); }
}

public int ResultTotalCount
{
get { return (int)GetValue(ResultTotalCountProperty); }
set { SetValue(ResultTotalCountProperty, value); }
}

public bool ShowCounts
{
get { return (bool)GetValue(ShowCountsProperty); }
set { SetValue(ShowCountsProperty, value); }
}

public static readonly DependencyProperty ShowCountsProperty =
DependencyProperty.Register("ShowCounts", typeof(bool), typeof(MainWindow), new PropertyMetadata(false));

public static readonly DependencyProperty ResultTotalCountProperty =
DependencyProperty.Register("ResultTotalCount", typeof(int), typeof(MainWindow), new PropertyMetadata(0));

public static readonly DependencyProperty ResultMatchingCountProperty =
DependencyProperty.Register("ResultMatchingCount", typeof(int), typeof(MainWindow), new PropertyMetadata(0));

public static readonly DependencyProperty SearchTextProperty =
DependencyProperty.Register("SearchText", typeof(string), typeof(MainWindow), new PropertyMetadata(""));

Expand Down Expand Up @@ -147,6 +174,8 @@ public string SearchText
string nameSearchArg;
bool nameSearchRunning;
private string lastArk;
private DebounceDispatcher refreshSearchesTimer = new DebounceDispatcher();
private DebounceDispatcher settingsSaveTimer = new DebounceDispatcher();

public MainWindow()
{
Expand Down Expand Up @@ -601,13 +630,16 @@ private void ResultList_Sorting(object sender, DataGridSortingEventArgs e)

private void MarkSearchesChanged()
{
SaveSearches();
settingsSaveTimer.Debounce(1000, o => SaveSearches());
}

private void SaveSearches()
{
Properties.Settings.Default.SavedSearches = JsonConvert.SerializeObject(ListSearches);
Properties.Settings.Default.Save();
if (!ShowTames)
{
Properties.Settings.Default.SavedSearches = JsonConvert.SerializeObject(ListSearches);
Properties.Settings.Default.Save();
}
}

private void LoadSavedSearches()
Expand Down Expand Up @@ -653,26 +685,35 @@ private void UpdateSearchResults(IList<SearchCriteria> searches)
// Find dinos that match the given searches
var found = new List<Dino>();
var sourceDinos = ShowTames ? arkReader.TamedDinos : arkReader.WildDinos;
var total = 0;
foreach (var search in searches)
{
if (String.IsNullOrWhiteSpace(search.Species))
{
foreach (var speciesDinos in sourceDinos.Values)
{
found.AddRange(speciesDinos);
total += speciesDinos.Count;
}
}
else
{
if (sourceDinos.ContainsKey(search.Species))
{
var dinoList = sourceDinos[search.Species];
found.AddRange(dinoList.Where(d => search.Matches(d)));
total += dinoList.Count;
}
}
}

ListResults.Clear();
foreach (var result in found)
ListResults.Add(result);

ShowCounts = true;
ResultTotalCount = ShowTames ? sourceDinos.Sum(species => species.Value.Count()) : total;
ResultMatchingCount = ListResults.Count;
}

((CollectionViewSource)Resources["OrderedResults"]).View.Refresh();
Expand Down Expand Up @@ -707,10 +748,15 @@ private async Task PerformConversion()

private void UpdateCurrentSearch()
{
var search = (SearchCriteria)searchesList.SelectedItem;
var searches = new List<SearchCriteria>();
if (search != null) searches.Add(search);
UpdateSearchResults(searches);
void updateSearch(object o)
{
var search = (SearchCriteria)searchesList.SelectedItem;
var searches = new List<SearchCriteria>();
if (search != null) searches.Add(search);
UpdateSearchResults(searches);
}

refreshSearchesTimer.Debounce(250, updateSearch);
}

void IDropTarget.DragOver(IDropInfo dropInfo)
Expand Down

0 comments on commit a6874d1

Please sign in to comment.