Slideshow update thread removed in favor of Rx

Will want to replace the throttle /w a debounce call.
Also will want to remove unnecessary subject once command IsExecuting callbacks are researched
This commit is contained in:
Justin Swanson 2019-10-12 16:04:14 -05:00
parent c808f9a4ed
commit ff2010134c
5 changed files with 68 additions and 52 deletions

View File

@ -22,18 +22,17 @@ using Wabbajack.NexusApi;
using Wabbajack.UI;
using DynamicData;
using DynamicData.Binding;
using System.Reactive;
namespace Wabbajack
{
public enum TaskMode { INSTALLING, BUILDING }
internal class AppState : ViewModel, IDataErrorInfo
public class AppState : ViewModel, IDataErrorInfo
{
public const bool GcCollect = true;
private SlideShow _slideShow;
public bool installing = false;
private string _mo2Folder;
private readonly BitmapImage _wabbajackLogo = UIUtils.BitmapImageFromResource("Wabbajack.UI.banner.png");
@ -59,7 +58,7 @@ namespace Wabbajack
private BitmapImage _SplashScreenImage;
public BitmapImage SplashScreenImage { get => _SplashScreenImage; set => this.RaiseAndSetIfChanged(ref _SplashScreenImage, value); }
private BitmapImage _NextIcon = UIUtils.BitmapImageFromResource("Wabbajack.UI.Icons.next.png");
public BitmapImage NextIcon { get => _NextIcon; set => this.RaiseAndSetIfChanged(ref _NextIcon, value); }
@ -69,6 +68,9 @@ namespace Wabbajack
private string _HTMLReport;
public string HTMLReport { get => _HTMLReport; set => this.RaiseAndSetIfChanged(ref _HTMLReport, value); }
private bool _Installing;
public bool Installing { get => _Installing; set => this.RaiseAndSetIfChanged(ref _Installing, value); }
// Command properties
public IReactiveCommand ChangePathCommand => ReactiveCommand.Create(ExecuteChangePath);
public IReactiveCommand ChangeDownloadPathCommand => ReactiveCommand.Create(ExecuteChangeDownloadPath);
@ -77,7 +79,10 @@ namespace Wabbajack
public IReactiveCommand VisitNexusSiteCommand => ReactiveCommand.Create(VisitNexusSite);
public IReactiveCommand OpenReadmeCommand { get; }
public IReactiveCommand OpenModListPropertiesCommand => ReactiveCommand.Create(OpenModListProperties);
public IReactiveCommand SlideShowNextItemCommand { get; }
// ToDo
// This subject is not desirable. Need to research why command's IsExecuting observable not firing, as we'd prefer to hook onto that instead
private Subject<Unit> _slideshowCommandTriggeredSubject = new Subject<Unit>();
public ReactiveCommand<Unit, Unit> SlideShowNextItemCommand => ReactiveCommand.Create(() => _slideshowCommandTriggeredSubject.OnNext(Unit.Default));
public AppState()
{
@ -121,7 +126,6 @@ namespace Wabbajack
.DisposeWith(this.CompositeDisposable);
_slideShow = new SlideShow(this, true);
this.SlideShowNextItemCommand = ReactiveCommand.Create(_slideShow.UpdateSlideShowItem);
// Update splashscreen when modlist changes
Observable.CombineLatest(
@ -170,10 +174,24 @@ namespace Wabbajack
.Subscribe(bitmap => this.SplashScreenImage = bitmap)
.DisposeWith(this.CompositeDisposable);
// Trigger a slideshow update if enabled
this.WhenAny(x => x.EnableSlideShow)
.Skip(1) // Don't fire initially
.WhenAny(enable => enable)
/// Wire slideshow updates
// Merge all the sources that trigger a slideshow update
Observable.Merge(
// If the natural timer fires
Observable.Interval(TimeSpan.FromSeconds(10)).Unit(),
// If user requests one manually
_slideshowCommandTriggeredSubject)
// When enabled, fire an initial signal
.StartWith(Unit.Default)
// Only subscribe to slideshow triggers if enabled and installing
.FilterSwitch(
Observable.CombineLatest(
this.WhenAny(x => x.EnableSlideShow),
this.WhenAny(x => x.Installing),
resultSelector: (enabled, installing) => enabled && installing))
// Don't ever update more than once every half second. ToDo: Update to debounce
.Throttle(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => _slideShow.UpdateSlideShowItem())
.DisposeWith(this.CompositeDisposable);
@ -192,17 +210,8 @@ namespace Wabbajack
.Bind(this.Status)
.Subscribe()
.DisposeWith(this.CompositeDisposable);
slideshowThread = new Thread(UpdateLoop)
{
Priority = ThreadPriority.BelowNormal,
IsBackground = true
};
slideshowThread.Start();
}
public DateTime lastSlideShowUpdate = new DateTime();
public ObservableCollection<string> Log { get; } = new ObservableCollection<string>();
private string _Location;
@ -316,8 +325,7 @@ namespace Wabbajack
private string _SplashScreenSummary;
public string SplashScreenSummary { get => _SplashScreenSummary; set => this.RaiseAndSetIfChanged(ref _SplashScreenSummary, value); }
private bool _splashShowNSFW = false;
public bool SplashShowNSFW { get => _splashShowNSFW; set => this.RaiseAndSetIfChanged(ref _splashShowNSFW, value); }
private readonly Thread slideshowThread = null;
public bool SplashShowNSFW { get => _splashShowNSFW; set => this.RaiseAndSetIfChanged(ref _splashShowNSFW, value); }
public string Error => "Error";
@ -355,23 +363,6 @@ namespace Wabbajack
return validationMessage;
}
private void UpdateLoop()
{
while (Running)
{
if (_slideShow.SlidesQueue.Any())
{
if (DateTime.Now - lastSlideShowUpdate > TimeSpan.FromSeconds(10))
{
_slideShow.UpdateSlideShowItem();
}
}
Thread.Sleep(1000);
}
}
public bool Running { get; set; } = true;
public void LogMsg(string msg)
{
Application.Current.Dispatcher.Invoke(() => Log.Add(msg));
@ -425,7 +416,7 @@ namespace Wabbajack
UIReady = false;
if (Mode == TaskMode.INSTALLING)
{
installing = true;
this.Installing = true;
var installer = new Installer(this.ModListPath, this.ModList, Location)
{
DownloadFolder = DownloadLocation
@ -447,9 +438,7 @@ namespace Wabbajack
finally
{
UIReady = true;
Running = false;
installing = false;
slideshowThread.Abort();
this.Installing = false;
}
})
{

View File

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reactive;
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Kernel;
@ -29,6 +30,38 @@ namespace Wabbajack
return source.ObserveOn(RxApp.MainThreadScheduler);
}
public static IObservable<Unit> Unit<T>(this IObservable<T> source)
{
return source.Select(_ => System.Reactive.Unit.Default);
}
/// <summary>
/// Convenience operator to subscribe to the source observable, only when a second "switch" observable is on.
/// When the switch is on, the source will be subscribed to, and its updates passed through.
/// When the switch is off, the subscription to the source observable will be stopped, and no signal will be published.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">Source observable to subscribe to if on</param>
/// <param name="filterSwitch">On/Off signal of whether to subscribe to source observable</param>
/// <returns>Observable that publishes data from source, if the switch is on.</returns>
public static IObservable<T> FilterSwitch<T>(this IObservable<T> source, IObservable<bool> filterSwitch)
{
return filterSwitch
.DistinctUntilChanged()
.Select(on =>
{
if (on)
{
return source;
}
else
{
return Observable.Empty<T>();
}
})
.Switch();
}
/// These snippets were provided by RolandPheasant (author of DynamicData)
/// They'll be going into the official library at some point, but are here for now.
#region Dynamic Data EnsureUniqueChanges

View File

@ -149,7 +149,7 @@
Height="30"
HorizontalAlignment="Right"
Command="{Binding SlideShowNextItemCommand}"
ToolTip="Spamming this button will result in problems">
ToolTip="Skip to next slide">
<DockPanel>
<Image Source="{Binding NextIcon}" Stretch="Fill" />
</DockPanel>

View File

@ -68,7 +68,6 @@ namespace Wabbajack
MessageBoxImage.Error);
Dispatcher.Invoke(() =>
{
context.Running = false;
ExitWhenClosing = false;
var window = new ModeSelectionWindow
{

View File

@ -74,7 +74,7 @@ namespace Wabbajack.UI
public void UpdateSlideShowItem()
{
if (!_appState.EnableSlideShow || !_appState.installing || SlidesQueue.Count==0) return;
if (SlidesQueue.Count == 0) return;
var slide = SlidesQueue.Peek();
while (CachedSlides.Count >= MaxCacheSize)
@ -98,11 +98,8 @@ namespace Wabbajack.UI
_appState.SplashScreenImage = _appState._noneImage;
if (slide.ImageURL != null && slide.Image != null)
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
if (!CachedSlides.ContainsKey(slide.ModID)) return;
_appState.SplashScreenImage = slide.Image;
});
if (!CachedSlides.ContainsKey(slide.ModID)) return;
_appState.SplashScreenImage = slide.Image;
}
_appState.SplashScreenModName = slide.ModName;
@ -111,8 +108,6 @@ namespace Wabbajack.UI
_appState._nexusSiteURL = slide.ModURL;
}
_appState.lastSlideShowUpdate = DateTime.Now;
SlidesQueue.Dequeue();
QueueRandomSlide(false, true);
}