mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-02-18 00:17:43 +01:00
Auto-scroll process queue
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
AllowAutoHide="False">
|
||||
<ItemsControl ItemsSource="{Binding Queue}">
|
||||
<ItemsControl Name="QueueListControl" ItemsSource="{Binding Queue}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using ApplicationServices;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Threading;
|
||||
using DataLayer;
|
||||
using LibationFileManager;
|
||||
using LibationUiBase;
|
||||
@@ -94,6 +95,42 @@ namespace LibationAvalonia.Views
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Auto-Scroll Current Item Into View
|
||||
protected override void OnDataContextBeginUpdate()
|
||||
{
|
||||
if (DataContext is ProcessQueueViewModel vm)
|
||||
{
|
||||
vm.ProcessStart -= Book_ProcessStart;
|
||||
}
|
||||
base.OnDataContextBeginUpdate();
|
||||
}
|
||||
|
||||
protected override void OnDataContextEndUpdate()
|
||||
{
|
||||
if (DataContext is ProcessQueueViewModel vm)
|
||||
{
|
||||
vm.ProcessStart += Book_ProcessStart;
|
||||
}
|
||||
base.OnDataContextEndUpdate();
|
||||
}
|
||||
|
||||
private void Book_ProcessStart(object? sender, ProcessBookViewModel e)
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (Queue?.IndexOf(e) is int newtBookIndex && newtBookIndex > 0 && QueueListControl.Presenter?.Panel is VirtualizingStackPanel panel && itemIsVisible(newtBookIndex - 1, panel))
|
||||
{
|
||||
// Only scroll the new item into view if the previous item is visible.
|
||||
// This allows users to scroll through the queue without being interrupted.
|
||||
QueueListControl.ScrollIntoView(newtBookIndex);
|
||||
}
|
||||
});
|
||||
|
||||
static bool itemIsVisible(int newtBookIndex, VirtualizingStackPanel panel)
|
||||
=> panel.FirstRealizedIndex <= newtBookIndex && panel.LastRealizedIndex >= newtBookIndex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void NumericUpDown_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Avalonia.Input.Key.Enter && sender is Avalonia.Input.IInputElement input) input.Focus();
|
||||
|
||||
@@ -264,7 +264,8 @@ public class ProcessQueueViewModel : ReactiveObject
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public event EventHandler<ProcessBookViewModel>? ProcessStart;
|
||||
public event EventHandler<ProcessBookViewModel>? ProcessEnd;
|
||||
private async Task QueueLoop()
|
||||
{
|
||||
try
|
||||
@@ -288,6 +289,7 @@ public class ProcessQueueViewModel : ReactiveObject
|
||||
|
||||
Serilog.Log.Logger.Information("Begin processing queued item: '{item_LibraryBook}'", nextBook.LibraryBook);
|
||||
SpeedLimit = nextBook.Configuration.DownloadSpeedLimit / 1024m / 1024;
|
||||
ProcessStart?.Invoke(this, nextBook);
|
||||
var result = await nextBook.ProcessOneAsync();
|
||||
|
||||
Serilog.Log.Logger.Information("Completed processing queued item: '{item_LibraryBook}' with result: {result}", nextBook.LibraryBook, result);
|
||||
@@ -310,6 +312,7 @@ public class ProcessQueueViewModel : ReactiveObject
|
||||
MessageBoxIcon.Asterisk);
|
||||
shownServiceOutageMessage = true;
|
||||
}
|
||||
ProcessEnd?.Invoke(this, nextBook);
|
||||
}
|
||||
Serilog.Log.Logger.Information("Completed processing queue");
|
||||
|
||||
|
||||
@@ -38,9 +38,26 @@ internal partial class ProcessQueueControl : UserControl
|
||||
logDGV.EnableHeadersVisualStyles = !Application.IsDarkModeEnabled;
|
||||
ViewModel.PropertyChanged += ProcessQueue_PropertyChanged;
|
||||
ViewModel.LogEntries.CollectionChanged += LogEntries_CollectionChanged;
|
||||
ViewModel.ProcessStart += Book_ProcessStart;
|
||||
ProcessQueue_PropertyChanged(this, new PropertyChangedEventArgs(null));
|
||||
}
|
||||
|
||||
private void Book_ProcessStart(object? sender, ProcessBookViewModel e)
|
||||
{
|
||||
Invoke(() =>
|
||||
{
|
||||
if (ViewModel.Queue?.IndexOf(e) is int newtBookIndex && newtBookIndex > 0 && itemIsVisible(newtBookIndex - 1))
|
||||
{
|
||||
// Only scroll the new item into view if the previous item is visible.
|
||||
// This allows users to scroll through the queue without being interrupted.
|
||||
virtualFlowControl2.ScrollIntoView(newtBookIndex);
|
||||
}
|
||||
});
|
||||
|
||||
bool itemIsVisible(int newtBookIndex)
|
||||
=> virtualFlowControl2.FirstRealizedIndex <= newtBookIndex && virtualFlowControl2.LastRealizedIndex >= newtBookIndex;
|
||||
}
|
||||
|
||||
private void LogEntries_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (!IsDisposed && e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
|
||||
|
||||
@@ -14,6 +14,17 @@ namespace LibationWinForms.ProcessQueue
|
||||
/// Triggered when one of the <see cref="ProcessBookControl"/>'s buttons has been clicked
|
||||
/// </summary>
|
||||
public event EventHandler<string>? ButtonClicked;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the first realized element, or -1 if no elements are realized.
|
||||
/// </summary>
|
||||
public int FirstRealizedIndex { get; private set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the last realized element, or -1 if no elements are realized.
|
||||
/// </summary>
|
||||
public int LastRealizedIndex { get; private set; } = -1;
|
||||
|
||||
public IList? Items { get; private set; }
|
||||
|
||||
private object? m_OldContext;
|
||||
@@ -199,6 +210,27 @@ namespace LibationWinForms.ProcessQueue
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scrolls the specified item into view.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item.</param>
|
||||
public void ScrollIntoView(int index)
|
||||
{
|
||||
if (index < 0 || index >= VirtualControlCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
int firstVisible = FirstVisibleVirtualIndex;
|
||||
int lastVisible = firstVisible + (DisplayHeight / VirtualControlHeight) - 1;
|
||||
if (index < firstVisible)
|
||||
{
|
||||
SetScrollPosition(index * VirtualControlHeight);
|
||||
}
|
||||
else if (index > lastVisible)
|
||||
{
|
||||
int newScrollPos = (index - (lastVisible - firstVisible)) * VirtualControlHeight;
|
||||
SetScrollPosition(newScrollPos);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculated the virtual controls that are in view at the current scroll position and windows size,
|
||||
/// positions <see cref="panel1"/> to simulate scroll activity, then fires updates the controls with
|
||||
@@ -229,6 +261,9 @@ namespace LibationWinForms.ProcessQueue
|
||||
{
|
||||
BookControls[i].Visible = i < numVisible;
|
||||
}
|
||||
|
||||
FirstRealizedIndex = firstVisible;
|
||||
LastRealizedIndex = firstVisible + numVisible - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user