Merge pull request #255 from Noggog/intervention-frontend

Intervention frontend
This commit is contained in:
Timothy Baldridge
2019-12-10 22:55:34 -07:00
committed by GitHub
36 changed files with 916 additions and 183 deletions

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Wabbajack.Common.StatusFeed
{
/// <summary>
/// Defines a message that requires user interaction. The user must perform some action
/// or make a choice.
/// </summary>
public interface IUserIntervention : IStatusMessage
{
/// <summary>
/// The user didn't make a choice, so this action should be aborted
/// </summary>
void Cancel();
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using ReactiveUI;
namespace Wabbajack.Common
{
public abstract class AUserIntervention : ReactiveObject, IUserIntervention
{
public DateTime Timestamp { get; } = DateTime.Now;
public abstract string ShortDescription { get; }
public abstract string ExtendedDescription { get; }
private bool _handled;
public bool Handled { get => _handled; set => this.RaiseAndSetIfChanged(ref _handled, value); }
public int CpuID { get; } = WorkQueue.CpuId;
public abstract void Cancel();
public ICommand CancelCommand { get; }
public AUserIntervention()
{
CancelCommand = ReactiveCommand.Create(() => Cancel());
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using ReactiveUI;
namespace Wabbajack.Common
{
public abstract class ConfirmationIntervention : AUserIntervention
{
public enum Choice
{
Continue,
Abort
}
private TaskCompletionSource<Choice> _source = new TaskCompletionSource<Choice>();
public Task<Choice> Task => _source.Task;
public ICommand ConfirmCommand { get; }
public ConfirmationIntervention()
{
ConfirmCommand = ReactiveCommand.Create(() => Confirm());
}
public override void Cancel()
{
Handled = true;
_source.SetResult(Choice.Abort);
}
public void Confirm()
{
Handled = true;
_source.SetResult(Choice.Continue);
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ReactiveUI;
using Wabbajack.Common.StatusFeed;
namespace Wabbajack.Common
{
/// <summary>
/// Defines a message that requires user interaction. The user must perform some action
/// or make a choice.
/// </summary>
public interface IUserIntervention : IStatusMessage, IReactiveObject
{
/// <summary>
/// The user didn't make a choice, so this action should be aborted
/// </summary>
void Cancel();
/// <summary>
/// Whether the interaction has been handled and no longer needs attention
/// Note: This needs to be Reactive so that users can monitor its status
/// </summary>
bool Handled { get; }
/// <summary>
/// WorkQueue job ID that is blocking on this intervention
/// </summary>
int CpuID { get; }
}
}

View File

@ -114,6 +114,7 @@
<Compile Include="SplittingStream.cs" /> <Compile Include="SplittingStream.cs" />
<Compile Include="StatusFeed\AErrorMessage.cs" /> <Compile Include="StatusFeed\AErrorMessage.cs" />
<Compile Include="StatusFeed\AStatusMessage.cs" /> <Compile Include="StatusFeed\AStatusMessage.cs" />
<Compile Include="StatusFeed\Interventions\AUserIntervention.cs" />
<Compile Include="StatusFeed\Errors\7zipReturnError.cs" /> <Compile Include="StatusFeed\Errors\7zipReturnError.cs" />
<Compile Include="StatusFeed\Errors\FileExtractionError.cs" /> <Compile Include="StatusFeed\Errors\FileExtractionError.cs" />
<Compile Include="StatusFeed\Errors\GenericException.cs" /> <Compile Include="StatusFeed\Errors\GenericException.cs" />
@ -122,8 +123,9 @@
<Compile Include="StatusFeed\IError.cs" /> <Compile Include="StatusFeed\IError.cs" />
<Compile Include="StatusFeed\IException.cs" /> <Compile Include="StatusFeed\IException.cs" />
<Compile Include="StatusFeed\IInfo.cs" /> <Compile Include="StatusFeed\IInfo.cs" />
<Compile Include="StatusFeed\Interventions\ConfirmationIntervention.cs" />
<Compile Include="StatusFeed\IStatusMessage.cs" /> <Compile Include="StatusFeed\IStatusMessage.cs" />
<Compile Include="StatusFeed\IUserIntervention.cs" /> <Compile Include="StatusFeed\Interventions\IUserIntervention.cs" />
<Compile Include="StatusFileStream.cs" /> <Compile Include="StatusFileStream.cs" />
<Compile Include="StatusUpdate.cs" /> <Compile Include="StatusUpdate.cs" />
<Compile Include="SteamHandler.cs" /> <Compile Include="SteamHandler.cs" />

View File

@ -14,7 +14,10 @@ namespace Wabbajack.Common
internal BlockingCollection<Action> internal BlockingCollection<Action>
Queue = new BlockingCollection<Action>(new ConcurrentStack<Action>()); Queue = new BlockingCollection<Action>(new ConcurrentStack<Action>());
[ThreadStatic] private static int CpuId; public const int UnassignedCpuId = -1;
[ThreadStatic] private static int _cpuId = UnassignedCpuId;
public static int CpuId => _cpuId;
internal static bool WorkerThread => CurrentQueue != null; internal static bool WorkerThread => CurrentQueue != null;
[ThreadStatic] internal static WorkQueue CurrentQueue; [ThreadStatic] internal static WorkQueue CurrentQueue;
@ -24,6 +27,11 @@ namespace Wabbajack.Common
public static List<Thread> Threads { get; private set; } public static List<Thread> Threads { get; private set; }
// This is currently a lie, as it wires to the Utils singleton stream This is still good to have,
// so that logic related to a single WorkQueue can subscribe to this dummy member so that If/when we
// implement log messages in a non-singleton fashion, they will already be wired up properly.
public IObservable<IStatusMessage> LogMessages => Utils.LogMessages;
public WorkQueue(int threadCount = 0) public WorkQueue(int threadCount = 0)
{ {
StartThreads(threadCount == 0 ? Environment.ProcessorCount : threadCount); StartThreads(threadCount == 0 ? Environment.ProcessorCount : threadCount);
@ -48,7 +56,7 @@ namespace Wabbajack.Common
private void ThreadBody(int idx) private void ThreadBody(int idx)
{ {
CpuId = idx; _cpuId = idx;
CurrentQueue = this; CurrentQueue = this;
while (true) while (true)
@ -67,7 +75,7 @@ namespace Wabbajack.Common
Progress = progress, Progress = progress,
ProgressPercent = progress / 100f, ProgressPercent = progress / 100f,
Msg = msg, Msg = msg,
ID = CpuId, ID = _cpuId,
IsWorking = isWorking IsWorking = isWorking
}); });
} }

View File

@ -1,9 +1,11 @@
using System; using System;
using System.IO; using System.IO;
using System.Reactive.Disposables;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Wabbajack.Common; using Wabbajack.Common;
using Wabbajack.Common.StatusFeed;
using Wabbajack.VirtualFileSystem; using Wabbajack.VirtualFileSystem;
namespace Wabbajack.Lib namespace Wabbajack.Lib
@ -15,6 +17,7 @@ namespace Wabbajack.Lib
public void Dispose() public void Dispose()
{ {
Queue?.Shutdown(); Queue?.Shutdown();
_subs.Dispose();
} }
public Context VFS { get; private set; } public Context VFS { get; private set; }
@ -38,6 +41,9 @@ namespace Wabbajack.Lib
private Subject<CPUStatus> _queueStatus { get; } = new Subject<CPUStatus>(); private Subject<CPUStatus> _queueStatus { get; } = new Subject<CPUStatus>();
public IObservable<CPUStatus> QueueStatus => _queueStatus; public IObservable<CPUStatus> QueueStatus => _queueStatus;
private Subject<IStatusMessage> _logMessages { get; } = new Subject<IStatusMessage>();
public IObservable<IStatusMessage> LogMessages => _logMessages;
private Subject<bool> _isRunning { get; } = new Subject<bool>(); private Subject<bool> _isRunning { get; } = new Subject<bool>();
public IObservable<bool> IsRunning => _isRunning; public IObservable<bool> IsRunning => _isRunning;
@ -46,6 +52,8 @@ namespace Wabbajack.Lib
private int _configured; private int _configured;
private int _started; private int _started;
private readonly CompositeDisposable _subs = new CompositeDisposable();
protected void ConfigureProcessor(int steps, int threads = 0) protected void ConfigureProcessor(int steps, int threads = 0)
{ {
if (1 == Interlocked.CompareExchange(ref _configured, 1, 1)) if (1 == Interlocked.CompareExchange(ref _configured, 1, 1))
@ -54,7 +62,10 @@ namespace Wabbajack.Lib
} }
Queue = new WorkQueue(threads); Queue = new WorkQueue(threads);
UpdateTracker = new StatusUpdateTracker(steps); UpdateTracker = new StatusUpdateTracker(steps);
Queue.Status.Subscribe(_queueStatus); Queue.Status.Subscribe(_queueStatus)
.DisposeWith(_subs);
Queue.LogMessages.Subscribe(_logMessages)
.DisposeWith(_subs);
UpdateTracker.Progress.Subscribe(_percentCompleted); UpdateTracker.Progress.Subscribe(_percentCompleted);
UpdateTracker.StepName.Subscribe(_textStatus); UpdateTracker.StepName.Subscribe(_textStatus);
VFS = new Context(Queue) { UpdateTracker = UpdateTracker }; VFS = new Context(Queue) { UpdateTracker = UpdateTracker };

View File

@ -9,11 +9,8 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web; using System.Web;
using Wabbajack.Common; using Wabbajack.Common;
using Wabbajack.Common.StatusFeed;
using Wabbajack.Common.StatusFeed.Errors;
using Wabbajack.Lib.LibCefHelpers; using Wabbajack.Lib.LibCefHelpers;
using Wabbajack.Lib.Validation; using Wabbajack.Lib.Validation;
using Wabbajack.Lib.WebAutomation;
using Xilium.CefGlue.Common; using Xilium.CefGlue.Common;
using File = Alphaleonis.Win32.Filesystem.File; using File = Alphaleonis.Win32.Filesystem.File;
@ -183,7 +180,7 @@ namespace Wabbajack.Lib.Downloaders
} }
} }
public class RequestLoversLabLogin : AStatusMessage, IUserIntervention public class RequestLoversLabLogin : AUserIntervention
{ {
public override string ShortDescription => "Getting LoversLab information"; public override string ShortDescription => "Getting LoversLab information";
public override string ExtendedDescription { get; } public override string ExtendedDescription { get; }
@ -193,10 +190,13 @@ namespace Wabbajack.Lib.Downloaders
public void Resume(Helpers.Cookie[] cookies) public void Resume(Helpers.Cookie[] cookies)
{ {
Handled = true;
_source.SetResult(cookies); _source.SetResult(cookies);
} }
public void Cancel()
public override void Cancel()
{ {
Handled = true;
_source.SetCanceled(); _source.SetCanceled();
} }
} }

View File

@ -10,7 +10,6 @@ using Wabbajack.Common;
using Wabbajack.Lib.CompilationSteps.CompilationErrors; using Wabbajack.Lib.CompilationSteps.CompilationErrors;
using Wabbajack.Lib.Downloaders; using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.NexusApi; using Wabbajack.Lib.NexusApi;
using Wabbajack.Lib.StatusMessages;
using Wabbajack.Lib.Validation; using Wabbajack.Lib.Validation;
using Directory = Alphaleonis.Win32.Filesystem.Directory; using Directory = Alphaleonis.Win32.Filesystem.Directory;
using File = Alphaleonis.Win32.Filesystem.File; using File = Alphaleonis.Win32.Filesystem.File;

View File

@ -3,11 +3,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Wabbajack.Common.StatusFeed; using Wabbajack.Common;
namespace Wabbajack.Lib.NexusApi namespace Wabbajack.Lib.NexusApi
{ {
public class RequestNexusAuthorization : AStatusMessage, IUserIntervention public class RequestNexusAuthorization : AUserIntervention
{ {
public override string ShortDescription => "Getting User's Nexus API Key"; public override string ShortDescription => "Getting User's Nexus API Key";
public override string ExtendedDescription { get; } public override string ExtendedDescription { get; }
@ -17,10 +17,13 @@ namespace Wabbajack.Lib.NexusApi
public void Resume(string apikey) public void Resume(string apikey)
{ {
Handled = true;
_source.SetResult(apikey); _source.SetResult(apikey);
} }
public void Cancel()
public override void Cancel()
{ {
Handled = true;
_source.SetCanceled(); _source.SetCanceled();
} }
} }

View File

@ -3,24 +3,16 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Wabbajack.Common.StatusFeed; using Wabbajack.Common;
namespace Wabbajack.Lib.StatusMessages namespace Wabbajack.Lib
{ {
public class ConfirmUpdateOfExistingInstall : AStatusMessage, IUserIntervention public class ConfirmUpdateOfExistingInstall : ConfirmationIntervention
{ {
public enum Choice
{
Continue,
Abort
}
public string OutputFolder { get; set; } public string OutputFolder { get; set; }
public string ModListName { get; set; } public string ModListName { get; set; }
public override string ShortDescription { get; } = "Do you want to overwrite existing files?";
private TaskCompletionSource<Choice> _source = new TaskCompletionSource<Choice>(); public override string ShortDescription { get; } = "Do you want to overwrite existing files?";
public Task<Choice> Task => _source.Task;
public override string ExtendedDescription public override string ExtendedDescription
{ {
@ -29,15 +21,5 @@ namespace Wabbajack.Lib.StatusMessages
Any files that exist in {OutputFolder} will be changed to match the files found in the {ModListName} modlist. This means that save games will be removed, custom settings Any files that exist in {OutputFolder} will be changed to match the files found in the {ModListName} modlist. This means that save games will be removed, custom settings
will be reverted. Are you sure you wish to continue?"; will be reverted. Are you sure you wish to continue?";
} }
public void Cancel()
{
_source.SetResult(Choice.Abort);
}
public void Confirm()
{
_source.SetResult(Choice.Continue);
}
} }
} }

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace Wabbajack
{
public class IsTypeVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(Visibility))
throw new InvalidOperationException($"The target must be of type {nameof(Visibility)}");
if (!(parameter is Type paramType))
{
throw new ArgumentException();
}
if (value == null) return Visibility.Collapsed;
return paramType.Equals(value.GetType()) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -20,8 +20,8 @@ namespace Wabbajack
public InstallerSettings Installer { get; set; } = new InstallerSettings(); public InstallerSettings Installer { get; set; } = new InstallerSettings();
public CompilerSettings Compiler { get; set; } = new CompilerSettings(); public CompilerSettings Compiler { get; set; } = new CompilerSettings();
[JsonIgnoreAttribute]
private Subject<Unit> _saveSignal = new Subject<Unit>(); private Subject<Unit> _saveSignal = new Subject<Unit>();
[JsonIgnoreAttribute]
public IObservable<Unit> SaveSignal => _saveSignal; public IObservable<Unit> SaveSignal => _saveSignal;
public static bool TryLoadTypicalSettings(out MainSettings settings) public static bool TryLoadTypicalSettings(out MainSettings settings)
@ -58,6 +58,7 @@ namespace Wabbajack
{ {
public string InstallationLocation { get; set; } public string InstallationLocation { get; set; }
public string DownloadLocation { get; set; } public string DownloadLocation { get; set; }
public bool AutomaticallyOverrideExistingInstall { get; set; }
} }
public class CompilerSettings public class CompilerSettings

View File

@ -15,6 +15,7 @@
<local:InverseBooleanConverter x:Key="InverseBooleanConverter" /> <local:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<local:IsNotNullVisibilityConverter x:Key="IsNotNullVisibilityConverter" /> <local:IsNotNullVisibilityConverter x:Key="IsNotNullVisibilityConverter" />
<local:EqualsToBoolConverter x:Key="EqualsToBoolConverter" /> <local:EqualsToBoolConverter x:Key="EqualsToBoolConverter" />
<local:IsTypeVisibilityConverter x:Key="IsTypeVisibilityConverter" />
<!-- Colors --> <!-- Colors -->
<Color x:Key="WindowBackgroundColor">#121212</Color> <Color x:Key="WindowBackgroundColor">#121212</Color>
@ -43,6 +44,7 @@
<Color x:Key="Secondary">#03DAC6</Color> <Color x:Key="Secondary">#03DAC6</Color>
<Color x:Key="DarkSecondary">#0e8f83</Color> <Color x:Key="DarkSecondary">#0e8f83</Color>
<Color x:Key="DarkerSecondary">#095952</Color> <Color x:Key="DarkerSecondary">#095952</Color>
<Color x:Key="SecondaryBackground">#042421</Color>
<Color x:Key="OffWhiteSeconday">#cef0ed</Color> <Color x:Key="OffWhiteSeconday">#cef0ed</Color>
<Color x:Key="LightSecondary">#8cede5</Color> <Color x:Key="LightSecondary">#8cede5</Color>
<Color x:Key="IntenseSecondary">#00ffe7</Color> <Color x:Key="IntenseSecondary">#00ffe7</Color>
@ -115,6 +117,7 @@
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}" /> <SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}" />
<SolidColorBrush x:Key="DarkSecondaryBrush" Color="{StaticResource DarkSecondary}" /> <SolidColorBrush x:Key="DarkSecondaryBrush" Color="{StaticResource DarkSecondary}" />
<SolidColorBrush x:Key="DarkerSecondaryBrush" Color="{StaticResource DarkerSecondary}" /> <SolidColorBrush x:Key="DarkerSecondaryBrush" Color="{StaticResource DarkerSecondary}" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="{StaticResource SecondaryBackground}" />
<SolidColorBrush x:Key="OffWhiteSecondayBrush" Color="{StaticResource OffWhiteSeconday}" /> <SolidColorBrush x:Key="OffWhiteSecondayBrush" Color="{StaticResource OffWhiteSeconday}" />
<SolidColorBrush x:Key="LightSecondaryBrush" Color="{StaticResource LightSecondary}" /> <SolidColorBrush x:Key="LightSecondaryBrush" Color="{StaticResource LightSecondary}" />
<SolidColorBrush x:Key="IntenseSecondaryBrush" Color="{StaticResource IntenseSecondary}" /> <SolidColorBrush x:Key="IntenseSecondaryBrush" Color="{StaticResource IntenseSecondary}" />
@ -3708,4 +3711,58 @@
</Style> </Style>
<Style BasedOn="{StaticResource MainFilePickerStyle}" TargetType="{x:Type local:FilePicker}" /> <Style BasedOn="{StaticResource MainFilePickerStyle}" TargetType="{x:Type local:FilePicker}" />
<!-- User Intervention Background -->
<Style x:Key="AttentionBorderStyle" TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Background" Value="{StaticResource WindowBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource DarkerSecondaryBrush}" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsVisible, RelativeSource={RelativeSource Self}}" Value="True" />
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard x:Name="BorderGlow">
<Storyboard>
<ColorAnimation
AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
To="{StaticResource Secondary}"
Duration="0:0:1.5" />
</Storyboard>
</BeginStoryboard>
<BeginStoryboard x:Name="BackgroundGlow">
<Storyboard>
<ColorAnimation
AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="{StaticResource SecondaryBackground}"
Duration="0:0:1.5" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
To="{StaticResource DarkerSecondary}"
Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="{StaticResource WindowBackgroundColor}"
Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary> </ResourceDictionary>

View File

@ -11,8 +11,11 @@ namespace Wabbajack
{ {
public interface ISubInstallerVM public interface ISubInstallerVM
{ {
InstallerVM Parent { get; }
IReactiveCommand BeginCommand { get; } IReactiveCommand BeginCommand { get; }
AInstaller ActiveInstallation { get; } AInstaller ActiveInstallation { get; }
void Unload(); void Unload();
bool SupportsAfterInstallNavigation { get; }
void AfterInstallNavigation();
} }
} }

View File

@ -19,7 +19,6 @@ using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using Wabbajack.Common.StatusFeed; using Wabbajack.Common.StatusFeed;
using System.Reactive; using System.Reactive;
using Wabbajack.Common.StatusFeed;
namespace Wabbajack namespace Wabbajack
{ {
@ -79,11 +78,19 @@ namespace Wabbajack
private readonly ObservableAsPropertyHelper<ModManager?> _TargetManager; private readonly ObservableAsPropertyHelper<ModManager?> _TargetManager;
public ModManager? TargetManager => _TargetManager.Value; public ModManager? TargetManager => _TargetManager.Value;
private readonly ObservableAsPropertyHelper<IUserIntervention> _ActiveGlobalUserIntervention;
public IUserIntervention ActiveGlobalUserIntervention => _ActiveGlobalUserIntervention.Value;
private readonly ObservableAsPropertyHelper<bool> _Completed;
public bool Completed => _Completed.Value;
// Command properties // Command properties
public IReactiveCommand ShowReportCommand { get; } public IReactiveCommand ShowReportCommand { get; }
public IReactiveCommand OpenReadmeCommand { get; } public IReactiveCommand OpenReadmeCommand { get; }
public IReactiveCommand VisitWebsiteCommand { get; } public IReactiveCommand VisitWebsiteCommand { get; }
public IReactiveCommand BackCommand { get; } public IReactiveCommand BackCommand { get; }
public IReactiveCommand CloseWhenCompleteCommand { get; }
public IReactiveCommand GoToInstallCommand { get; }
public InstallerVM(MainWindowVM mainWindowVM) public InstallerVM(MainWindowVM mainWindowVM)
{ {
@ -293,6 +300,48 @@ namespace Wabbajack
InstallingMode = true; InstallingMode = true;
}) })
.DisposeWith(CompositeDisposable); .DisposeWith(CompositeDisposable);
// Listen for user interventions, and compile a dynamic list of all unhandled ones
var activeInterventions = this.WhenAny(x => x.Installer.ActiveInstallation)
.SelectMany(c => c?.LogMessages ?? Observable.Empty<IStatusMessage>())
.WhereCastable<IStatusMessage, IUserIntervention>()
.ToObservableChangeSet()
.AutoRefresh(i => i.Handled)
.Filter(i => !i.Handled)
.AsObservableList();
// Find the top intervention /w no CPU ID to be marked as "global"
_ActiveGlobalUserIntervention = activeInterventions.Connect()
.Filter(x => x.CpuID == WorkQueue.UnassignedCpuId)
.QueryWhenChanged(query => query.FirstOrDefault())
.ObserveOnGuiThread()
.ToProperty(this, nameof(ActiveGlobalUserIntervention));
_Completed = Observable.CombineLatest(
this.WhenAny(x => x.Installing),
this.WhenAny(x => x.InstallingMode),
resultSelector: (installing, installingMode) =>
{
return installingMode && !installing;
})
.ToProperty(this, nameof(Completed));
CloseWhenCompleteCommand = ReactiveCommand.Create(
canExecute: this.WhenAny(x => x.Completed),
execute: () =>
{
MWVM.ShutdownApplication();
});
GoToInstallCommand = ReactiveCommand.Create(
canExecute: Observable.CombineLatest(
this.WhenAny(x => x.Completed),
this.WhenAny(x => x.Installer.SupportsAfterInstallNavigation),
resultSelector: (complete, supports) => complete && supports),
execute: () =>
{
Installer.AfterInstallNavigation();
});
} }
private void ShowReport() private void ShowReport()

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
@ -15,7 +16,7 @@ namespace Wabbajack
{ {
public class MO2InstallerVM : ViewModel, ISubInstallerVM public class MO2InstallerVM : ViewModel, ISubInstallerVM
{ {
private InstallerVM _installerVM; public InstallerVM Parent { get; }
public IReactiveCommand BeginCommand { get; } public IReactiveCommand BeginCommand { get; }
@ -29,9 +30,14 @@ namespace Wabbajack
public FilePickerVM DownloadLocation { get; } public FilePickerVM DownloadLocation { get; }
public bool SupportsAfterInstallNavigation => true;
[Reactive]
public bool AutomaticallyOverwrite { get; set; }
public MO2InstallerVM(InstallerVM installerVM) public MO2InstallerVM(InstallerVM installerVM)
{ {
_installerVM = installerVM; Parent = installerVM;
Location = new FilePickerVM() Location = new FilePickerVM()
{ {
@ -131,11 +137,31 @@ namespace Wabbajack
if (settingsPair.Current == null) return; if (settingsPair.Current == null) return;
Location.TargetPath = settingsPair.Current.InstallationLocation; Location.TargetPath = settingsPair.Current.InstallationLocation;
DownloadLocation.TargetPath = settingsPair.Current.DownloadLocation; DownloadLocation.TargetPath = settingsPair.Current.DownloadLocation;
AutomaticallyOverwrite = settingsPair.Current.AutomaticallyOverrideExistingInstall;
}) })
.DisposeWith(CompositeDisposable); .DisposeWith(CompositeDisposable);
installerVM.MWVM.Settings.SaveSignal installerVM.MWVM.Settings.SaveSignal
.Subscribe(_ => SaveSettings(CurrentSettings)) .Subscribe(_ => SaveSettings(CurrentSettings))
.DisposeWith(CompositeDisposable); .DisposeWith(CompositeDisposable);
// Hook onto user interventions, and intercept MO2 specific ones for customization
this.WhenAny(x => x.ActiveInstallation.LogMessages)
.Switch()
.Subscribe(x =>
{
switch (x)
{
case ConfirmUpdateOfExistingInstall c:
if (AutomaticallyOverwrite)
{
c.Confirm();
}
break;
default:
break;
}
})
.DisposeWith(CompositeDisposable);
} }
public void Unload() public void Unload()
@ -145,10 +171,16 @@ namespace Wabbajack
private void SaveSettings(Mo2ModlistInstallationSettings settings) private void SaveSettings(Mo2ModlistInstallationSettings settings)
{ {
_installerVM.MWVM.Settings.Installer.LastInstalledListLocation = _installerVM.ModListLocation.TargetPath; Parent.MWVM.Settings.Installer.LastInstalledListLocation = Parent.ModListLocation.TargetPath;
if (settings == null) return; if (settings == null) return;
settings.InstallationLocation = Location.TargetPath; settings.InstallationLocation = Location.TargetPath;
settings.DownloadLocation = DownloadLocation.TargetPath; settings.DownloadLocation = DownloadLocation.TargetPath;
settings.AutomaticallyOverrideExistingInstall = AutomaticallyOverwrite;
}
public void AfterInstallNavigation()
{
Process.Start("explorer.exe", Location.TargetPath);
} }
} }
} }

View File

@ -13,6 +13,8 @@ namespace Wabbajack
{ {
public class VortexInstallerVM : ViewModel, ISubInstallerVM public class VortexInstallerVM : ViewModel, ISubInstallerVM
{ {
public InstallerVM Parent { get; }
public IReactiveCommand BeginCommand { get; } public IReactiveCommand BeginCommand { get; }
[Reactive] [Reactive]
@ -27,8 +29,12 @@ namespace Wabbajack
private readonly ObservableAsPropertyHelper<Game> _TargetGame; private readonly ObservableAsPropertyHelper<Game> _TargetGame;
public Game TargetGame => _TargetGame.Value; public Game TargetGame => _TargetGame.Value;
public bool SupportsAfterInstallNavigation => false;
public VortexInstallerVM(InstallerVM installerVM) public VortexInstallerVM(InstallerVM installerVM)
{ {
Parent = installerVM;
_TargetGame = installerVM.WhenAny(x => x.ModList.SourceModList.GameType) _TargetGame = installerVM.WhenAny(x => x.ModList.SourceModList.GameType)
.ToProperty(this, nameof(TargetGame)); .ToProperty(this, nameof(TargetGame));
@ -91,5 +97,10 @@ namespace Wabbajack
public void Unload() public void Unload()
{ {
} }
public void AfterInstallNavigation()
{
throw new NotImplementedException();
}
} }
} }

View File

@ -12,9 +12,6 @@ using System.Windows.Threading;
using Wabbajack.Common; using Wabbajack.Common;
using Wabbajack.Common.StatusFeed; using Wabbajack.Common.StatusFeed;
using Wabbajack.Lib; using Wabbajack.Lib;
using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.NexusApi;
using Wabbajack.Lib.StatusMessages;
namespace Wabbajack namespace Wabbajack
{ {
@ -105,5 +102,16 @@ namespace Wabbajack
ActivePane = installer; ActivePane = installer;
installer.ModListLocation.TargetPath = path; installer.ModListLocation.TargetPath = path;
} }
public void ShutdownApplication()
{
Dispose();
Settings.PosX = MainWindow.Left;
Settings.PosY = MainWindow.Top;
Settings.Width = MainWindow.Width;
Settings.Height = MainWindow.Height;
MainSettings.SaveSettings(Settings);
Application.Current.Shutdown();
}
} }
} }

View File

@ -8,10 +8,8 @@ using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
using ReactiveUI; using ReactiveUI;
using Wabbajack.Common; using Wabbajack.Common;
using Wabbajack.Common.StatusFeed;
using Wabbajack.Lib.Downloaders; using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.NexusApi; using Wabbajack.Lib.NexusApi;
using Wabbajack.Lib.StatusMessages;
namespace Wabbajack namespace Wabbajack
{ {
@ -54,22 +52,10 @@ namespace Wabbajack
MainWindow.ActivePane = oldPane; MainWindow.ActivePane = oldPane;
} }
public void Handle(ConfirmUpdateOfExistingInstall msg)
{
var result = MessageBox.Show(msg.ExtendedDescription, msg.ShortDescription, MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
msg.Confirm();
else
msg.Cancel();
}
public async Task Handle(IUserIntervention msg) public async Task Handle(IUserIntervention msg)
{ {
switch (msg) switch (msg)
{ {
case ConfirmUpdateOfExistingInstall c:
Handle(c);
break;
case RequestNexusAuthorization c: case RequestNexusAuthorization c:
await WrapBrowserJob(msg, async (vm, cancel) => await WrapBrowserJob(msg, async (vm, cancel) =>
{ {
@ -84,6 +70,8 @@ namespace Wabbajack
c.Resume(data); c.Resume(data);
}); });
break; break;
case ConfirmationIntervention c:
break;
default: default:
throw new NotImplementedException($"No handler for {msg}"); throw new NotImplementedException($"No handler for {msg}");
} }

View File

@ -1,44 +1,17 @@
<UserControl <UserControl
x:Class="Wabbajack.LogCpuView" x:Class="Wabbajack.CpuView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wabbajack" xmlns:local="clr-namespace:Wabbajack"
xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="250" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
BorderThickness="0"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Rectangle Fill="{StaticResource HeatedBorderBrush}" Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="500" />
</Grid.ColumnDefinitions>
<Rectangle
Grid.Column="0"
Fill="{StaticResource HeatedBorderBrush}"
Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
<ListBox <ListBox
Grid.Column="0"
local:AutoScrollBehavior.ScrollOnNewItem="True"
BorderBrush="Transparent"
BorderThickness="1"
ItemsSource="{Binding Log}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ShortDescription}" TextWrapping="WrapWithOverflow" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Rectangle
Grid.Column="2"
Fill="{StaticResource HeatedBorderBrush}"
Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
<ListBox
Grid.Column="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
BorderBrush="Transparent" BorderBrush="Transparent"
BorderThickness="1" BorderThickness="1"

View File

@ -1,22 +1,34 @@
using System.Windows; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Wabbajack namespace Wabbajack
{ {
/// <summary> /// <summary>
/// Interaction logic for LogCpuView.xaml /// Interaction logic for CpuView.xaml
/// </summary> /// </summary>
public partial class LogCpuView : UserControl public partial class CpuView : UserControl
{ {
public double ProgressPercent public double ProgressPercent
{ {
get => (double)GetValue(ProgressPercentProperty); get => (double)GetValue(ProgressPercentProperty);
set => SetValue(ProgressPercentProperty, value); set => SetValue(ProgressPercentProperty, value);
} }
public static readonly DependencyProperty ProgressPercentProperty = DependencyProperty.Register(nameof(ProgressPercent), typeof(double), typeof(LogCpuView), public static readonly DependencyProperty ProgressPercentProperty = DependencyProperty.Register(nameof(ProgressPercent), typeof(double), typeof(CpuView),
new FrameworkPropertyMetadata(default(double))); new FrameworkPropertyMetadata(default(double)));
public LogCpuView() public CpuView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@ -0,0 +1,26 @@
<UserControl
x:Class="Wabbajack.LogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wabbajack"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<Rectangle Fill="{StaticResource HeatedBorderBrush}" Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
<ListBox
local:AutoScrollBehavior.ScrollOnNewItem="True"
BorderBrush="Transparent"
BorderThickness="1"
ItemsSource="{Binding Log}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ShortDescription}" TextWrapping="WrapWithOverflow" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Wabbajack
{
/// <summary>
/// Interaction logic for LogView.xaml
/// </summary>
public partial class LogView : UserControl
{
public double ProgressPercent
{
get => (double)GetValue(ProgressPercentProperty);
set => SetValue(ProgressPercentProperty, value);
}
public static readonly DependencyProperty ProgressPercentProperty = DependencyProperty.Register(nameof(ProgressPercent), typeof(double), typeof(LogView),
new FrameworkPropertyMetadata(default(double)));
public LogView()
{
InitializeComponent();
}
}
}

View File

@ -2,6 +2,7 @@
x:Class="Wabbajack.CompilerView" x:Class="Wabbajack.CompilerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="clr-namespace:Wabbajack.Common;assembly=Wabbajack.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks" xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:local="clr-namespace:Wabbajack" xmlns:local="clr-namespace:Wabbajack"
@ -240,7 +241,24 @@
Grid.ColumnSpan="5" Grid.ColumnSpan="5"
Margin="5" Margin="5"
Visibility="{Binding Compiling, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}"> Visibility="{Binding Compiling, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
<local:LogCpuView ProgressPercent="{Binding PercentCompleted}" /> <Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<local:LogView Grid.Column="0" ProgressPercent="{Binding PercentCompleted, Mode=OneWay}" />
<local:CpuView
Grid.Column="2"
ProgressPercent="{Binding PercentCompleted, Mode=OneWay}"
Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}, ConverterParameter=False}" />
<Border
Grid.Column="2"
Style="{StaticResource AttentionBorderStyle}"
Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}}">
<Grid>
<local:ConfirmationInterventionView DataContext="{Binding ActiveGlobalUserIntervention}" Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsTypeVisibilityConverter}, ConverterParameter={x:Type common:ConfirmationIntervention}}" />
</Grid>
</Border>
</Grid> </Grid>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -0,0 +1,139 @@
<UserControl
x:Class="Wabbajack.InstallationCompleteView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:local="clr-namespace:Wabbajack"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Border Style="{StaticResource AttentionBorderStyle}" ClipToBounds="True">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
FontFamily="Lucida Sans"
FontSize="22"
FontWeight="Black"
Text="Installation Complete">
<TextBlock.Effect>
<DropShadowEffect BlurRadius="25" Opacity="0.5" />
</TextBlock.Effect>
</TextBlock>
<Grid
Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Width="55"
Height="55"
Command="{Binding BackCommand}"
Style="{StaticResource CircleButtonStyle}">
<icon:PackIconMaterial
Width="28"
Height="28"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
Kind="ArrowLeft" />
</Button>
<TextBlock
Grid.Row="1"
Margin="0,10,0,0"
HorizontalAlignment="Center"
Text="Main Menu" />
</Grid>
<Grid
Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center"
Visibility="{Binding InstallerSupportsAfterInstallNavigation, Converter={StaticResource bool2VisibilityConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button
Width="55"
Height="55"
Command="{Binding GoToInstallCommand}"
Style="{StaticResource CircleButtonStyle}">
<icon:PackIconMaterial
Width="25"
Height="25"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
Kind="FolderMove" />
</Button>
<TextBlock
Grid.Row="1"
Margin="0,10,0,0"
HorizontalAlignment="Center"
Text="Open Install Folder" />
</Grid>
<Grid
Grid.Row="1"
Grid.Column="2"
VerticalAlignment="Center"
Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--<Button
Width="55"
Height="55"
Background="{StaticResource PrimaryVariantBrush}"
BorderBrush="{StaticResource PrimaryVariantBrush}"
IsHitTestVisible="False"
Style="{StaticResource CircleButtonStyle}">
<Button.Effect>
<BlurEffect Radius="35" />
</Button.Effect>
</Button>
<Button
Width="55"
Height="55"
Background="{StaticResource SecondaryBrush}"
BorderBrush="{StaticResource SecondaryBrush}"
IsHitTestVisible="False"
Style="{StaticResource CircleButtonStyle}">
<Button.Effect>
<BlurEffect Radius="15" />
</Button.Effect>
</Button>-->
<Button
Width="55"
Height="55"
Command="{Binding CloseWhenCompleteCommand}"
Style="{StaticResource CircleButtonStyle}">
<icon:PackIconMaterial
Width="30"
Height="30"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
Kind="Check" />
</Button>
<TextBlock
Grid.Row="1"
Margin="0,10,0,0"
HorizontalAlignment="Center"
Text="Close" />
</Grid>
</Grid>
</Border>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Wabbajack
{
/// <summary>
/// Interaction logic for InstallationCompleteView.xaml
/// </summary>
public partial class InstallationCompleteView : UserControl
{
public InstallationCompleteView()
{
InitializeComponent();
}
}
}

View File

@ -2,8 +2,10 @@
x:Class="Wabbajack.InstallationView" x:Class="Wabbajack.InstallationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="clr-namespace:Wabbajack.Common;assembly=Wabbajack.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks" xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:lib="clr-namespace:Wabbajack.Lib;assembly=Wabbajack.Lib"
xmlns:local="clr-namespace:Wabbajack" xmlns:local="clr-namespace:Wabbajack"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DataContext="{d:DesignInstance local:InstallerVM}" d:DataContext="{d:DesignInstance local:InstallerVM}"
@ -24,7 +26,7 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="47" /> <RowDefinition Height="47" />
<RowDefinition Height="4*" /> <RowDefinition Height="4*" />
<RowDefinition Height="*" MinHeight="150" /> <RowDefinition Height="*" MinHeight="175" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Rectangle <Rectangle
x:Name="BorderEdgeFadeDown" x:Name="BorderEdgeFadeDown"
@ -287,49 +289,19 @@
Background="Transparent" Background="Transparent"
VerticalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}"> Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
<Grid> <ContentPresenter
<Grid.ColumnDefinitions> Margin="0"
<ColumnDefinition Width="Auto" MinWidth="120" /> VerticalAlignment="Center"
<ColumnDefinition Width="20" /> Content="{Binding Installer}">
<ColumnDefinition Width="*" /> <ContentPresenter.Resources>
</Grid.ColumnDefinitions> <DataTemplate DataType="{x:Type local:MO2InstallerVM}">
<Grid.RowDefinitions> <local:MO2InstallerConfigView />
<RowDefinition Height="*" /> </DataTemplate>
<RowDefinition Height="40" /> <DataTemplate DataType="{x:Type local:VortexInstallerVM}">
<RowDefinition Height="80" /> <local:VortexInstallerConfigView />
<RowDefinition Height="*" /> </DataTemplate>
</Grid.RowDefinitions> </ContentPresenter.Resources>
<TextBlock </ContentPresenter>
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontSize="14"
Text="Target Modlist"
TextAlignment="Center" />
<local:FilePicker
Grid.Row="1"
Grid.Column="2"
Height="30"
VerticalAlignment="Center"
DataContext="{Binding ModListLocation}"
FontSize="14" />
<ContentPresenter
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="0"
Content="{Binding Installer}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type local:MO2InstallerVM}">
<local:MO2InstallerConfigView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:VortexInstallerVM}">
<local:VortexInstallerConfigView />
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</ScrollViewer> </ScrollViewer>
<local:BeginButton <local:BeginButton
Grid.Column="2" Grid.Column="2"
@ -343,7 +315,26 @@
Grid.Row="2" Grid.Row="2"
Margin="5,0,5,5" Margin="5,0,5,5"
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}"> Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
<local:LogCpuView ProgressPercent="{Binding PercentCompleted, Mode=OneWay}" /> <Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<local:LogView Grid.Column="0" ProgressPercent="{Binding PercentCompleted, Mode=OneWay}" />
<local:CpuView
Grid.Column="2"
ProgressPercent="{Binding PercentCompleted, Mode=OneWay}"
Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}, ConverterParameter=False}" />
<Border
Grid.Column="2"
Style="{StaticResource AttentionBorderStyle}"
Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}}">
<Grid>
<local:ConfirmationInterventionView DataContext="{Binding ActiveGlobalUserIntervention}" Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsTypeVisibilityConverter}, ConverterParameter={x:Type common:ConfirmationIntervention}}" />
<local:ConfirmUpdateOfExistingInstallView Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsTypeVisibilityConverter}, ConverterParameter={x:Type lib:ConfirmUpdateOfExistingInstall}}" />
</Grid>
</Border>
<local:InstallationCompleteView Grid.Column="2" Visibility="{Binding Completed, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Collapsed}" />
</Grid> </Grid>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -3,6 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:local="clr-namespace:Wabbajack" xmlns:local="clr-namespace:Wabbajack"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450" d:DesignHeight="450"
@ -15,11 +16,29 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="40" /> <RowDefinition Height="40" />
<RowDefinition Height="40" /> <RowDefinition Height="40" />
<RowDefinition Height="40" />
<RowDefinition Height="20" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontSize="14"
Text="Target Modlist"
TextAlignment="Center" />
<local:FilePicker
Grid.Row="1"
Grid.Column="2"
Height="30"
VerticalAlignment="Center"
DataContext="{Binding Parent.ModListLocation}"
FontSize="14" />
<TextBlock
Grid.Row="2"
Grid.Column="0" Grid.Column="0"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
@ -27,14 +46,14 @@
Text="Installation Location" Text="Installation Location"
TextAlignment="Center" /> TextAlignment="Center" />
<local:FilePicker <local:FilePicker
Grid.Row="0" Grid.Row="2"
Grid.Column="2" Grid.Column="2"
Height="30" Height="30"
VerticalAlignment="Center" VerticalAlignment="Center"
DataContext="{Binding Location}" DataContext="{Binding Location}"
FontSize="14" /> FontSize="14" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
@ -42,11 +61,29 @@
Text="Download Location" Text="Download Location"
TextAlignment="Center" /> TextAlignment="Center" />
<local:FilePicker <local:FilePicker
Grid.Row="1" Grid.Row="3"
Grid.Column="2" Grid.Column="2"
Height="30" Height="30"
VerticalAlignment="Center" VerticalAlignment="Center"
DataContext="{Binding DownloadLocation}" DataContext="{Binding DownloadLocation}"
FontSize="14" /> FontSize="14" />
<CheckBox
Grid.Row="4"
Grid.Column="2"
HorizontalAlignment="Right"
Content="Overwrite Installation"
IsChecked="{Binding AutomaticallyOverwrite}"
ToolTip="If installing over an existing installation, automatically replace it without asking permission.">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Opacity" Value="0.6" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Opacity" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -9,6 +9,28 @@
d:DesignWidth="800" d:DesignWidth="800"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid> <Grid>
<!-- Nothing so far --> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="120" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontSize="14"
Text="Target Modlist"
TextAlignment="Center" />
<local:FilePicker
Grid.Row="0"
Grid.Column="2"
Height="30"
VerticalAlignment="Center"
DataContext="{Binding Parent.ModListLocation}"
FontSize="14" />
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -0,0 +1,58 @@
<UserControl
x:Class="Wabbajack.ConfirmUpdateOfExistingInstallView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wabbajack"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="5*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="0,0,0,5"
FontFamily="Lucida Sans"
FontSize="14"
FontWeight="Bold"
Text="{Binding ActiveGlobalUserIntervention.ShortDescription}"
TextWrapping="WrapWithOverflow" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Text="{Binding ActiveGlobalUserIntervention.ExtendedDescription}"
TextWrapping="WrapWithOverflow" />
<CheckBox
Grid.Row="2"
Grid.Column="2"
Margin="4"
HorizontalAlignment="Right"
Content="Remember"
IsChecked="{Binding Installer.AutomaticallyOverwrite}"
ToolTip="If installing over an existing installation next time, automatically replace it without asking permission." />
<Button
Grid.Row="3"
Grid.Column="0"
Command="{Binding ActiveGlobalUserIntervention.CancelCommand}"
Content="Cancel" />
<Button
Grid.Row="3"
Grid.Column="2"
Command="{Binding ActiveGlobalUserIntervention.ConfirmCommand}"
Content="Confirm" />
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Wabbajack
{
/// <summary>
/// Interaction logic for ConfirmUpdateOfExistingInstallView.xaml
/// </summary>
public partial class ConfirmUpdateOfExistingInstallView : UserControl
{
public ConfirmUpdateOfExistingInstallView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,49 @@
<UserControl
x:Class="Wabbajack.ConfirmationInterventionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wabbajack"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="0,0,0,5"
FontFamily="Lucida Sans"
FontSize="14"
FontWeight="Bold"
Text="{Binding ShortDescription}"
TextWrapping="WrapWithOverflow" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Text="{Binding ExtendedDescription}"
TextWrapping="WrapWithOverflow" />
<Button
Grid.Row="2"
Grid.Column="0"
Command="{Binding CancelCommand}"
Content="Cancel" />
<Button
Grid.Row="2"
Grid.Column="2"
Command="{Binding ConfirmCommand}"
Content="Confirm" />
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Wabbajack
{
/// <summary>
/// Interaction logic for ConfirmationInterventionView.xaml
/// </summary>
public partial class ConfirmationInterventionView : UserControl
{
public ConfirmationInterventionView()
{
InitializeComponent();
}
}
}

View File

@ -15,8 +15,6 @@ namespace Wabbajack
private MainWindowVM _mwvm; private MainWindowVM _mwvm;
private MainSettings _settings; private MainSettings _settings;
internal bool ExitWhenClosing = true;
public MainWindow() public MainWindow()
{ {
// Wire any unhandled crashing exceptions to log before exiting // Wire any unhandled crashing exceptions to log before exiting
@ -93,16 +91,7 @@ namespace Wabbajack
private void Window_Closing(object sender, CancelEventArgs e) private void Window_Closing(object sender, CancelEventArgs e)
{ {
_mwvm.Dispose(); _mwvm.ShutdownApplication();
_settings.PosX = Left;
_settings.PosY = Top;
_settings.Width = Width;
_settings.Height = Height;
MainSettings.SaveSettings(_settings);
if (ExitWhenClosing)
{
Application.Current.Shutdown();
}
} }
} }
} }

View File

@ -172,7 +172,20 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="Converters\IsTypeVisibilityConverter.cs" />
<Compile Include="Views\Installers\InstallationCompleteView.xaml.cs">
<DependentUpon>InstallationCompleteView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Interventions\ConfirmationInterventionView.xaml.cs">
<DependentUpon>ConfirmationInterventionView.xaml</DependentUpon>
</Compile>
<Compile Include="Converters\EqualsToBoolConverter.cs" /> <Compile Include="Converters\EqualsToBoolConverter.cs" />
<Compile Include="Views\Common\CpuView.xaml.cs">
<DependentUpon>CpuView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Common\LogView.xaml.cs">
<DependentUpon>LogView.xaml</DependentUpon>
</Compile>
<Compile Include="View Models\UserInterventionHandlers.cs" /> <Compile Include="View Models\UserInterventionHandlers.cs" />
<Compile Include="View Models\WebBrowserVM.cs" /> <Compile Include="View Models\WebBrowserVM.cs" />
<Compile Include="Views\Installers\MO2InstallerConfigView.xaml.cs"> <Compile Include="Views\Installers\MO2InstallerConfigView.xaml.cs">
@ -186,6 +199,9 @@
<Compile Include="Views\Common\HeatedBackgroundView.xaml.cs"> <Compile Include="Views\Common\HeatedBackgroundView.xaml.cs">
<DependentUpon>HeatedBackgroundView.xaml</DependentUpon> <DependentUpon>HeatedBackgroundView.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\Interventions\ConfirmUpdateOfExistingInstallView.xaml.cs">
<DependentUpon>ConfirmUpdateOfExistingInstallView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\LinksView.xaml.cs"> <Compile Include="Views\LinksView.xaml.cs">
<DependentUpon>LinksView.xaml</DependentUpon> <DependentUpon>LinksView.xaml</DependentUpon>
</Compile> </Compile>
@ -234,9 +250,6 @@
</Compile> </Compile>
<Compile Include="View Models\Compilers\CompilerVM.cs" /> <Compile Include="View Models\Compilers\CompilerVM.cs" />
<Compile Include="View Models\MainWindowVM.cs" /> <Compile Include="View Models\MainWindowVM.cs" />
<Compile Include="Views\Common\LogCpuView.xaml.cs">
<DependentUpon>LogCpuView.xaml</DependentUpon>
</Compile>
<Compile Include="Converters\LeftMarginMultiplierConverter.cs" /> <Compile Include="Converters\LeftMarginMultiplierConverter.cs" />
<Compile Include="Util\TreeViewItemExtensions.cs" /> <Compile Include="Util\TreeViewItemExtensions.cs" />
<Compile Include="View Models\SlideShow.cs" /> <Compile Include="View Models\SlideShow.cs" />
@ -256,6 +269,22 @@
<Compile Include="Views\WebBrowserView.xaml.cs"> <Compile Include="Views\WebBrowserView.xaml.cs">
<DependentUpon>WebBrowserView.xaml</DependentUpon> <DependentUpon>WebBrowserView.xaml</DependentUpon>
</Compile> </Compile>
<Page Include="Views\Installers\InstallationCompleteView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Interventions\ConfirmationInterventionView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Common\CpuView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Common\LogView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Installers\MO2InstallerConfigView.xaml"> <Page Include="Views\Installers\MO2InstallerConfigView.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@ -264,6 +293,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\Interventions\ConfirmUpdateOfExistingInstallView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\LinksView.xaml"> <Page Include="Views\LinksView.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@ -304,10 +337,6 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\Common\LogCpuView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\MainWindow.xaml"> <Page Include="Views\MainWindow.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>