From fa504a1b1631f6ca8f9195235070ee15bc11d59e Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Tue, 5 Nov 2019 19:39:18 -0600 Subject: [PATCH 1/5] DictionaryExt. Other Ext files migrated to Common --- Wabbajack.Common/Extensions/DictionaryExt.cs | 25 +++ Wabbajack.Common/Extensions/RxExt.cs | 172 ++++++++++++++++++ .../Extensions/TaskExt.cs | 0 Wabbajack.Common/Wabbajack.Common.csproj | 3 + Wabbajack.Lib/Wabbajack.Lib.csproj | 1 - Wabbajack/Extensions/ReactiveUIExt.cs | 156 ---------------- 6 files changed, 200 insertions(+), 157 deletions(-) create mode 100644 Wabbajack.Common/Extensions/DictionaryExt.cs create mode 100644 Wabbajack.Common/Extensions/RxExt.cs rename {Wabbajack.Lib => Wabbajack.Common}/Extensions/TaskExt.cs (100%) diff --git a/Wabbajack.Common/Extensions/DictionaryExt.cs b/Wabbajack.Common/Extensions/DictionaryExt.cs new file mode 100644 index 00000000..cd2ea840 --- /dev/null +++ b/Wabbajack.Common/Extensions/DictionaryExt.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack +{ + public static class DictionaryExt + { + public static V TryCreate(this IDictionary dict, K key) + where V : new() + { + return dict.TryCreate(key, () => new V()); + } + + public static V TryCreate(this IDictionary dict, K key, Func create) + { + if (dict.TryGetValue(key, out var val)) return val; + var ret = create(); + dict[key] = ret; + return ret; + } + } +} diff --git a/Wabbajack.Common/Extensions/RxExt.cs b/Wabbajack.Common/Extensions/RxExt.cs new file mode 100644 index 00000000..8bf082d7 --- /dev/null +++ b/Wabbajack.Common/Extensions/RxExt.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack +{ + public static class RxExt + { + /// + /// Convenience function that discards events that are null + /// + /// + /// + /// Source events that are not null + public static IObservable NotNull(this IObservable source) + where T : class + { + return source.Where(u => u != null); + } + + /// + /// Converts any observable to type Unit. Useful for when you care that a signal occurred, + /// but don't care about what its value is downstream. + /// + /// An observable that returns Unit anytime the source signal fires an event. + public static IObservable Unit(this IObservable source) + { + return source.Select(_ => System.Reactive.Unit.Default); + } + + /// + /// 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. + /// + /// Source observable to subscribe to if on + /// On/Off signal of whether to subscribe to source observable + /// Observable that publishes data from source, if the switch is on. + public static IObservable FilterSwitch(this IObservable source, IObservable filterSwitch) + { + return filterSwitch + .DistinctUntilChanged() + .Select(on => + { + if (on) + { + return source; + } + else + { + return Observable.Empty(); + } + }) + .Switch(); + } + + + /// Inspiration: + /// http://reactivex.io/documentation/operators/debounce.html + /// https://stackoverflow.com/questions/20034476/how-can-i-use-reactive-extensions-to-throttle-events-using-a-max-window-size + public static IObservable Debounce(this IObservable source, TimeSpan interval, IScheduler scheduler = null) + { + scheduler = scheduler ?? Scheduler.Default; + return Observable.Create(o => + { + var hasValue = false; + bool throttling = false; + T value = default; + + var dueTimeDisposable = new SerialDisposable(); + + void internalCallback() + { + if (hasValue) + { + // We have another value that came in to fire. + // Reregister for callback + dueTimeDisposable.Disposable = scheduler.Schedule(interval, internalCallback); + o.OnNext(value); + value = default; + hasValue = false; + } + else + { + // Nothing to do, throttle is complete. + throttling = false; + } + } + + return source.Subscribe( + onNext: (x) => + { + if (!throttling) + { + // Fire initial value + o.OnNext(x); + // Mark that we're throttling + throttling = true; + // Register for callback when throttle is complete + dueTimeDisposable.Disposable = scheduler.Schedule(interval, internalCallback); + } + else + { + // In the middle of throttle + // Save value and return + hasValue = true; + value = x; + } + }, + onError: o.OnError, + onCompleted: o.OnCompleted); + }); + } + + public static IObservable SelectTask(this IObservable source, Func task) + { + return source + .SelectMany(async i => + { + await task(i).ConfigureAwait(false); + return System.Reactive.Unit.Default; + }); + } + + public static IObservable SelectTask(this IObservable source, Func task) + { + return source + .SelectMany(async _ => + { + await task().ConfigureAwait(false); + return System.Reactive.Unit.Default; + }); + } + + public static IObservable SelectTask(this IObservable source, Func> task) + { + return source + .SelectMany(_ => task()); + } + + public static IObservable SelectTask(this IObservable source, Func> task) + { + return source + .SelectMany(x => task(x)); + } + + public static IObservable DoTask(this IObservable source, Func task) + { + return source + .SelectMany(async (x) => + { + await task(x).ConfigureAwait(false); + return x; + }); + } + + public static IObservable WhereCastable(this IObservable source) + where R : class + where T : class + { + return source + .Select(x => x as R) + .NotNull(); + } + } +} diff --git a/Wabbajack.Lib/Extensions/TaskExt.cs b/Wabbajack.Common/Extensions/TaskExt.cs similarity index 100% rename from Wabbajack.Lib/Extensions/TaskExt.cs rename to Wabbajack.Common/Extensions/TaskExt.cs diff --git a/Wabbajack.Common/Wabbajack.Common.csproj b/Wabbajack.Common/Wabbajack.Common.csproj index f8a4ba0f..9485a93e 100644 --- a/Wabbajack.Common/Wabbajack.Common.csproj +++ b/Wabbajack.Common/Wabbajack.Common.csproj @@ -92,7 +92,10 @@ + + + diff --git a/Wabbajack.Lib/Wabbajack.Lib.csproj b/Wabbajack.Lib/Wabbajack.Lib.csproj index 09184a29..433b08cd 100644 --- a/Wabbajack.Lib/Wabbajack.Lib.csproj +++ b/Wabbajack.Lib/Wabbajack.Lib.csproj @@ -116,7 +116,6 @@ - diff --git a/Wabbajack/Extensions/ReactiveUIExt.cs b/Wabbajack/Extensions/ReactiveUIExt.cs index 372794a8..878b564c 100644 --- a/Wabbajack/Extensions/ReactiveUIExt.cs +++ b/Wabbajack/Extensions/ReactiveUIExt.cs @@ -31,18 +31,6 @@ namespace Wabbajack return This.WhenAny(property1, selector: x => x.GetValue()); } - /// - /// Convenience function that discards events that are null - /// - /// - /// - /// Source events that are not null - public static IObservable NotNull(this IObservable source) - where T : class - { - return source.Where(u => u != null); - } - /// /// Convenience wrapper to observe following calls on the GUI thread. /// @@ -51,42 +39,6 @@ namespace Wabbajack return source.ObserveOn(RxApp.MainThreadScheduler); } - /// - /// Converts any observable to type Unit. Useful for when you care that a signal occurred, - /// but don't care about what its value is downstream. - /// - /// An observable that returns Unit anytime the source signal fires an event. - public static IObservable Unit(this IObservable source) - { - return source.Select(_ => System.Reactive.Unit.Default); - } - - /// - /// 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. - /// - /// Source observable to subscribe to if on - /// On/Off signal of whether to subscribe to source observable - /// Observable that publishes data from source, if the switch is on. - public static IObservable FilterSwitch(this IObservable source, IObservable filterSwitch) - { - return filterSwitch - .DistinctUntilChanged() - .Select(on => - { - if (on) - { - return source; - } - else - { - return Observable.Empty(); - } - }) - .Switch(); - } - public static IObservable StartingExecution(this IReactiveCommand cmd) { return cmd.IsExecuting @@ -95,114 +47,6 @@ namespace Wabbajack .Unit(); } - /// Inspiration: - /// http://reactivex.io/documentation/operators/debounce.html - /// https://stackoverflow.com/questions/20034476/how-can-i-use-reactive-extensions-to-throttle-events-using-a-max-window-size - public static IObservable Debounce(this IObservable source, TimeSpan interval, IScheduler scheduler = null) - { - scheduler = scheduler ?? Scheduler.Default; - return Observable.Create(o => - { - var hasValue = false; - bool throttling = false; - T value = default; - - var dueTimeDisposable = new SerialDisposable(); - - void internalCallback() - { - if (hasValue) - { - // We have another value that came in to fire. - // Reregister for callback - dueTimeDisposable.Disposable = scheduler.Schedule(interval, internalCallback); - o.OnNext(value); - value = default; - hasValue = false; - } - else - { - // Nothing to do, throttle is complete. - throttling = false; - } - } - - return source.Subscribe( - onNext: (x) => - { - if (!throttling) - { - // Fire initial value - o.OnNext(x); - // Mark that we're throttling - throttling = true; - // Register for callback when throttle is complete - dueTimeDisposable.Disposable = scheduler.Schedule(interval, internalCallback); - } - else - { - // In the middle of throttle - // Save value and return - hasValue = true; - value = x; - } - }, - onError: o.OnError, - onCompleted: o.OnCompleted); - }); - } - - public static IObservable SelectTask(this IObservable source, Func task) - { - return source - .SelectMany(async i => - { - await task(i).ConfigureAwait(false); - return System.Reactive.Unit.Default; - }); - } - - public static IObservable SelectTask(this IObservable source, Func task) - { - return source - .SelectMany(async _ => - { - await task().ConfigureAwait(false); - return System.Reactive.Unit.Default; - }); - } - - public static IObservable SelectTask(this IObservable source, Func> task) - { - return source - .SelectMany(_ => task()); - } - - public static IObservable SelectTask(this IObservable source, Func> task) - { - return source - .SelectMany(x => task(x)); - } - - public static IObservable DoTask(this IObservable source, Func task) - { - return source - .SelectMany(async (x) => - { - await task(x).ConfigureAwait(false); - return x; - }); - } - - public static IObservable WhereCastable(this IObservable source) - where R : class - where T : class - { - return source - .Select(x => x as R) - .NotNull(); - } - /// 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 From 471704bbe3ce3ae6c9bdcdfa9d63af2431fde506 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Tue, 5 Nov 2019 21:10:43 -0600 Subject: [PATCH 2/5] FilePicker: Boolean logic error /w AdditionalError check --- Wabbajack/Views/FilePicker.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Wabbajack/Views/FilePicker.xaml.cs b/Wabbajack/Views/FilePicker.xaml.cs index 5ddad5d0..502f0ae0 100644 --- a/Wabbajack/Views/FilePicker.xaml.cs +++ b/Wabbajack/Views/FilePicker.xaml.cs @@ -193,7 +193,7 @@ namespace Wabbajack this._InError = Observable.CombineLatest( this.WhenAny(x => x.Exists), this.WhenAny(x => x.AdditionalError) - .Select(err => !err?.Succeeded ?? true), + .Select(err => !err?.Succeeded ?? false), resultSelector: (exist, err) => !exist || err) .ToProperty(this, nameof(this.InError)); From becf1c0ecd35d102b5b52ee84390877c65d35f41 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Tue, 5 Nov 2019 21:17:57 -0600 Subject: [PATCH 3/5] Bugfix for CompileVM using splat logging Was vestigial change of me experimenting with Splat. Didn't roll back all the way --- Wabbajack/View Models/CompilerVM.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Wabbajack/View Models/CompilerVM.cs b/Wabbajack/View Models/CompilerVM.cs index 73e79878..f3116ac5 100644 --- a/Wabbajack/View Models/CompilerVM.cs +++ b/Wabbajack/View Models/CompilerVM.cs @@ -1,15 +1,10 @@ -using ReactiveUI; +using ReactiveUI; using ReactiveUI.Fody.Helpers; -using Splat; using System; -using System.Collections.Generic; -using System.ComponentModel; using System.IO; using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; using System.Windows.Media.Imaging; using Wabbajack.Common; @@ -93,7 +88,7 @@ namespace Wabbajack this.Mo2Folder = Path.GetDirectoryName(Path.GetDirectoryName(profile_folder)); if (!File.Exists(Path.Combine(this.Mo2Folder, "ModOrganizer.exe"))) { - this.Log().Error($"Error! No ModOrganizer2.exe found in {this.Mo2Folder}"); + Utils.Log($"Error! No ModOrganizer2.exe found in {this.Mo2Folder}"); } this.MOProfile = Path.GetFileName(profile_folder); @@ -131,7 +126,7 @@ namespace Wabbajack catch (Exception ex) { while (ex.InnerException != null) ex = ex.InnerException; - this.Log().Warn(ex, "Can't continue"); + Utils.Log($"Can't continue: {ex.ExceptionToString()}"); } finally { @@ -141,7 +136,7 @@ namespace Wabbajack } else { - this.Log().Warn("Cannot compile modlist: no valid Mod Organizer profile directory selected."); + Utils.Log("Cannot compile modlist: no valid Mod Organizer profile directory selected."); UIReady = true; } } From 9e8ea6d281ed02d9524f72a533ab1eb12a97a220 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Tue, 5 Nov 2019 21:22:38 -0600 Subject: [PATCH 4/5] Initial settings systems Waiting to do position/size implementation until new ModeSelection systems are finished --- Wabbajack.Lib/UI/UIUtils.cs | 3 +- Wabbajack/Settings.cs | 70 +++++++++++++++++++++ Wabbajack/View Models/CompilerVM.cs | 36 ++++++++++- Wabbajack/View Models/InstallerVM.cs | 37 ++++++----- Wabbajack/View Models/MainWindowVM.cs | 13 ++-- Wabbajack/Views/CompilerView.xaml | 2 +- Wabbajack/Views/MainWindow.xaml.cs | 15 +++-- Wabbajack/Views/ModeSelectionWindow.xaml.cs | 25 ++++++-- Wabbajack/Wabbajack.csproj | 1 + 9 files changed, 160 insertions(+), 42 deletions(-) create mode 100644 Wabbajack/Settings.cs diff --git a/Wabbajack.Lib/UI/UIUtils.cs b/Wabbajack.Lib/UI/UIUtils.cs index 13471acb..a3fc6c24 100644 --- a/Wabbajack.Lib/UI/UIUtils.cs +++ b/Wabbajack.Lib/UI/UIUtils.cs @@ -92,10 +92,11 @@ namespace Wabbajack.Lib } } - public static string OpenFileDialog(string filter) + public static string OpenFileDialog(string filter, string initialDirectory = null) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = filter; + ofd.InitialDirectory = initialDirectory; if (ofd.ShowDialog() == DialogResult.OK) return ofd.FileName; return null; diff --git a/Wabbajack/Settings.cs b/Wabbajack/Settings.cs new file mode 100644 index 00000000..34b2b1d7 --- /dev/null +++ b/Wabbajack/Settings.cs @@ -0,0 +1,70 @@ +using Newtonsoft.Json; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack +{ + [JsonObject(MemberSerialization.OptOut)] + public class MainSettings + { + private static string Filename = "settings.json"; + + public double PosX { get; set; } + public double PosY { get; set; } + public double Height { get; set; } + public double Width { get; set; } + public string LastInstalledListLocation { get; set; } + public Dictionary InstallationSettings { get; } = new Dictionary(); + public string LastCompiledProfileLocation { get; set; } + public Dictionary CompilationSettings { get; } = new Dictionary(); + + [JsonIgnoreAttribute] + private Subject _saveSignal = new Subject(); + public IObservable SaveSignal => _saveSignal; + + public static MainSettings LoadSettings() + { + if (!File.Exists(Filename)) return new MainSettings(); + return JsonConvert.DeserializeObject(File.ReadAllText(Filename)); + } + + public static void SaveSettings(MainSettings settings) + { + settings._saveSignal.OnNext(Unit.Default); + + // Might add this if people are putting save work on other threads or other + // things that delay the operation. + //settings._saveSignal.OnCompleted(); + //await settings._saveSignal; + + File.WriteAllText(Filename, JsonConvert.SerializeObject(settings, Formatting.Indented)); + } + } + + public class InstallationSettings + { + public string InstallationLocation { get; set; } + public string DownloadLocation { get; set; } + } + + public class CompilationSettings + { + public string ModListName { get; set; } + public string Author { get; set; } + public string Description { get; set; } + public string Website { get; set; } + public string Readme { get; set; } + public string SplashScreen { get; set; } + public string Location { get; set; } + public string DownloadLocation { get; set; } + } +} diff --git a/Wabbajack/View Models/CompilerVM.cs b/Wabbajack/View Models/CompilerVM.cs index f3116ac5..0eefd80e 100644 --- a/Wabbajack/View Models/CompilerVM.cs +++ b/Wabbajack/View Models/CompilerVM.cs @@ -1,4 +1,4 @@ -using ReactiveUI; +using ReactiveUI; using ReactiveUI.Fody.Helpers; using System; using System.IO; @@ -44,7 +44,7 @@ namespace Wabbajack public BitmapImage Image => _Image.Value; [Reactive] - public string NexusSiteURL { get; set; } + public string Website { get; set; } [Reactive] public string ReadMeText { get; set; } @@ -80,6 +80,36 @@ namespace Wabbajack .ToProperty(this, nameof(this.Image)); ConfigureForBuild(source); + + // Load settings + CompilationSettings settings = this.MWVM.Settings.CompilationSettings.TryCreate(source); + this.AuthorName = settings.Author; + this.ModListName = settings.ModListName; + this.Summary = settings.Description; + this.ReadMeText = settings.Readme; + this.ImagePath = settings.SplashScreen; + this.Website = settings.Website; + if (!string.IsNullOrWhiteSpace(settings.DownloadLocation)) + { + this.DownloadLocation = settings.DownloadLocation; + } + if (!string.IsNullOrWhiteSpace(settings.Location)) + { + this.Location = settings.Location; + } + this.MWVM.Settings.SaveSignal + .Subscribe(_ => + { + settings.Author = this.AuthorName; + settings.ModListName = this.ModListName; + settings.Description = this.Summary; + settings.Readme = this.ReadMeText; + settings.SplashScreen = this.ImagePath; + settings.Website = this.Website; + settings.Location = this.Location; + settings.DownloadLocation = this.DownloadLocation; + }) + .DisposeWith(this.CompositeDisposable); } private void ConfigureForBuild(string location) @@ -109,7 +139,7 @@ namespace Wabbajack ModListAuthor = this.AuthorName, ModListDescription = this.Summary, ModListImage = this.ImagePath, - ModListWebsite = this.NexusSiteURL, + ModListWebsite = this.Website, ModListReadme = this.ReadMeText, }; await Task.Run(() => diff --git a/Wabbajack/View Models/InstallerVM.cs b/Wabbajack/View Models/InstallerVM.cs index 319aebc5..e0f38c2d 100644 --- a/Wabbajack/View Models/InstallerVM.cs +++ b/Wabbajack/View Models/InstallerVM.cs @@ -1,32 +1,18 @@ using Syroot.Windows.IO; using System; using ReactiveUI; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; -using System.Net.Http; -using System.Reactive.Subjects; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reflection; using System.Threading; using System.Windows; -using System.Windows.Input; using System.Windows.Media.Imaging; -using System.Windows.Threading; using Wabbajack.Common; -using Wabbajack.Lib.Downloaders; -using Wabbajack.Lib.NexusApi; -using DynamicData; -using DynamicData.Binding; -using System.Reactive; -using System.Text; using Wabbajack.Lib; -using Splat; using ReactiveUI.Fody.Helpers; namespace Wabbajack @@ -96,7 +82,7 @@ namespace Wabbajack public IReactiveCommand OpenReadmeCommand { get; } public IReactiveCommand VisitWebsiteCommand { get; } - public InstallerVM(MainWindowVM mainWindowVM) + public InstallerVM(MainWindowVM mainWindowVM, string source) { if (Path.GetDirectoryName(Assembly.GetEntryAssembly().Location.ToLower()) == KnownFolders.Downloads.Path.ToLower()) { @@ -110,13 +96,26 @@ namespace Wabbajack } this.MWVM = mainWindowVM; + this.ModListPath = source; + + // Load settings + InstallationSettings settings = this.MWVM.Settings.InstallationSettings.TryCreate(source); + this.Location = settings.InstallationLocation; + this.DownloadLocation = settings.DownloadLocation; + this.MWVM.Settings.SaveSignal + .Subscribe(_ => + { + settings.InstallationLocation = this.Location; + settings.DownloadLocation = this.DownloadLocation; + }) + .DisposeWith(this.CompositeDisposable); this._ModList = this.WhenAny(x => x.ModListPath) .ObserveOn(RxApp.TaskpoolScheduler) - .Select(source => + .Select(modListPath => { - if (source == null) return default(ModListVM); - var modList = Installer.LoadFromFile(source); + if (modListPath == null) return default(ModListVM); + var modList = Installer.LoadFromFile(modListPath); if (modList == null) { MessageBox.Show("Invalid Modlist, or file not found.", "Invalid Modlist", MessageBoxButton.OK, @@ -133,7 +132,7 @@ namespace Wabbajack }); return default(ModListVM); } - return new ModListVM(modList, source); + return new ModListVM(modList, modListPath); }) .ObserveOnGuiThread() .StartWith(default(ModListVM)) diff --git a/Wabbajack/View Models/MainWindowVM.cs b/Wabbajack/View Models/MainWindowVM.cs index 613334bd..94fea828 100644 --- a/Wabbajack/View Models/MainWindowVM.cs +++ b/Wabbajack/View Models/MainWindowVM.cs @@ -26,6 +26,8 @@ namespace Wabbajack { public MainWindow MainWindow { get; } + public MainSettings Settings { get; } + private readonly ObservableAsPropertyHelper _ActivePane; public ViewModel ActivePane => _ActivePane.Value; @@ -34,7 +36,6 @@ namespace Wabbajack public ObservableCollectionExtended StatusList { get; } = new ObservableCollectionExtended(); - private Subject _logSubj = new Subject(); public ObservableCollectionExtended Log { get; } = new ObservableCollectionExtended(); [Reactive] @@ -43,11 +44,12 @@ namespace Wabbajack private readonly Lazy _Compiler; private readonly Lazy _Installer; - public MainWindowVM(RunMode mode, string source, MainWindow mainWindow) + public MainWindowVM(RunMode mode, string source, MainWindow mainWindow, MainSettings settings) { this.Mode = mode; this.MainWindow = mainWindow; - this._Installer = new Lazy(() => new InstallerVM(this)); + this.Settings = settings; + this._Installer = new Lazy(() => new InstallerVM(this, source)); this._Compiler = new Lazy(() => new CompilerVM(this, source)); // Set up logging @@ -82,11 +84,6 @@ namespace Wabbajack } }) .ToProperty(this, nameof(this.ActivePane)); - this.WhenAny(x => x.ActivePane) - .ObserveOn(RxApp.TaskpoolScheduler) - .WhereCastable() - .Subscribe(vm => vm.ModListPath = source) - .DisposeWith(this.CompositeDisposable); // Compile progress updates and populate ObservableCollection WorkQueue.Status diff --git a/Wabbajack/Views/CompilerView.xaml b/Wabbajack/Views/CompilerView.xaml index bd87a568..6b82a824 100644 --- a/Wabbajack/Views/CompilerView.xaml +++ b/Wabbajack/Views/CompilerView.xaml @@ -106,7 +106,7 @@ Text="{Binding Summary}" TextWrapping="Wrap" /> - + public partial class ModeSelectionWindow : Window { - private List _lists; + MainSettings settings; public ModeSelectionWindow() { @@ -30,6 +31,7 @@ namespace Wabbajack var discordIcon = UIUtils.BitmapImageFromResource("Wabbajack.Resources.Icons.discord.png"); Discord.Source = discordIcon; + settings = MainSettings.LoadSettings(); DataContext = new ModeSelectionWindowVM(); } @@ -37,7 +39,9 @@ namespace Wabbajack { OpenMainWindow( RunMode.Compile, - UIUtils.OpenFileDialog("MO2 Modlist(modlist.txt)|modlist.txt")); + UIUtils.OpenFileDialog( + "MO2 Modlist(modlist.txt)|modlist.txt", + initialDirectory: settings.LastCompiledProfileLocation)); } private void InstallModlist_Click(object sender, RoutedEventArgs e) @@ -57,7 +61,18 @@ namespace Wabbajack { if (file == null) return; ShutdownOnClose = false; - var window = new MainWindow(mode, file); + switch (mode) + { + case RunMode.Compile: + settings.LastCompiledProfileLocation = Path.GetDirectoryName(file); + break; + case RunMode.Install: + settings.LastInstalledListLocation = Path.GetDirectoryName(file); + break; + default: + break; + } + var window = new MainWindow(mode, file, settings); window.Left = this.Left; window.Top = this.Top; window.Show(); @@ -90,7 +105,9 @@ namespace Wabbajack private void InstallFromList_Click(object sender, RoutedEventArgs e) { OpenMainWindow(RunMode.Install, - UIUtils.OpenFileDialog($"*{ExtensionManager.Extension}|*{ExtensionManager.Extension}")); + UIUtils.OpenFileDialog( + $"*{ExtensionManager.Extension}|*{ExtensionManager.Extension}", + initialDirectory: settings.LastInstalledListLocation)); } } } diff --git a/Wabbajack/Wabbajack.csproj b/Wabbajack/Wabbajack.csproj index 6c4a51e8..c65221a0 100644 --- a/Wabbajack/Wabbajack.csproj +++ b/Wabbajack/Wabbajack.csproj @@ -163,6 +163,7 @@ + From c54d7dea8f0e44ae37703316ae841ec0867b7651 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Tue, 5 Nov 2019 21:50:24 -0600 Subject: [PATCH 5/5] Extracted support from MahApps to use their textbox watermarks --- Wabbajack/Themes/Styles.xaml | 130 +++++++++++++++++++++++------- Wabbajack/Views/CompilerView.xaml | 2 + 2 files changed, 103 insertions(+), 29 deletions(-) diff --git a/Wabbajack/Themes/Styles.xaml b/Wabbajack/Themes/Styles.xaml index 14e242b2..689fc4ca 100644 --- a/Wabbajack/Themes/Styles.xaml +++ b/Wabbajack/Themes/Styles.xaml @@ -3,6 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Wabbajack" + xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" xmlns:darkBlendTheme="clr-namespace:DarkBlendTheme" mc:Ignorable="d"> @@ -838,49 +839,120 @@ -