mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge branch 'master' into login-manager
This commit is contained in:
commit
698a419973
@ -144,7 +144,7 @@ namespace Wabbajack.CacheServer
|
||||
{
|
||||
foreach (var list in modlists)
|
||||
{
|
||||
var modlist_path = Path.Combine(Consts.ModListDownloadFolder, list.Links.MachineURL + ".wabbajack");
|
||||
var modlist_path = Path.Combine(Consts.ModListDownloadFolder, list.Links.MachineURL + ExtensionManager.Extension);
|
||||
|
||||
if (list.NeedsDownload(modlist_path))
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.HashFunction.xxHash;
|
||||
using System.Diagnostics;
|
||||
@ -329,7 +329,7 @@ namespace Wabbajack.Common
|
||||
return new DynamicIniData(new FileIniDataParser().ReadData(new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(file)))));
|
||||
}
|
||||
|
||||
public static void ToCERAS<T>(this T obj, string filename, ref SerializerConfig config)
|
||||
public static void ToCERAS<T>(this T obj, string filename, SerializerConfig config)
|
||||
{
|
||||
var ceras = new CerasSerializer(config);
|
||||
byte[] buffer = null;
|
||||
@ -343,7 +343,7 @@ namespace Wabbajack.Common
|
||||
}
|
||||
}
|
||||
|
||||
public static T FromCERAS<T>(this Stream data, ref SerializerConfig config)
|
||||
public static T FromCERAS<T>(this Stream data, SerializerConfig config)
|
||||
{
|
||||
var ceras = new CerasSerializer(config);
|
||||
byte[] bytes = data.ReadAll();
|
||||
@ -630,6 +630,44 @@ namespace Wabbajack.Common
|
||||
return await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
public static async Task PMap<TI>(this IEnumerable<TI> coll, WorkQueue queue,
|
||||
Func<TI, Task> f)
|
||||
{
|
||||
var colllst = coll.ToList();
|
||||
|
||||
var remainingTasks = colllst.Count;
|
||||
|
||||
var tasks = colllst.Select(i =>
|
||||
{
|
||||
var tc = new TaskCompletionSource<bool>();
|
||||
queue.QueueTask(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await f(i);
|
||||
tc.SetResult(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tc.SetException(ex);
|
||||
}
|
||||
Interlocked.Decrement(ref remainingTasks);
|
||||
});
|
||||
return tc.Task;
|
||||
}).ToList();
|
||||
|
||||
// To avoid thread starvation, we'll start to help out in the work queue
|
||||
if (WorkQueue.WorkerThread)
|
||||
while (remainingTasks > 0)
|
||||
if (queue.Queue.TryTake(out var a, 500))
|
||||
{
|
||||
WorkQueue.AsyncLocalCurrentQueue.Value = WorkQueue.ThreadLocalCurrentQueue.Value;
|
||||
await a();
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
public static async Task PMap<TI>(this IEnumerable<TI> coll, WorkQueue queue, Action<TI> f)
|
||||
{
|
||||
await coll.PMap(queue, i =>
|
||||
|
@ -22,6 +22,7 @@ namespace Wabbajack.Lib
|
||||
public abstract class ACompiler : ABatchProcessor
|
||||
{
|
||||
public string ModListName, ModListAuthor, ModListDescription, ModListImage, ModListWebsite, ModListReadme;
|
||||
public bool ReadmeIsWebsite;
|
||||
public string WabbajackVersion;
|
||||
|
||||
protected static string _vfsCacheName = "vfs_compile_cache.bin";
|
||||
@ -94,8 +95,10 @@ namespace Wabbajack.Lib
|
||||
ModList.Readme = $"readme{readme.Extension}";
|
||||
}
|
||||
|
||||
ModList.ReadmeIsWebsite = ReadmeIsWebsite;
|
||||
|
||||
//ModList.ToJSON(Path.Combine(ModListOutputFolder, "modlist.json"));
|
||||
ModList.ToCERAS(Path.Combine(ModListOutputFolder, "modlist"), ref CerasConfig.Config);
|
||||
ModList.ToCERAS(Path.Combine(ModListOutputFolder, "modlist"), CerasConfig.Config);
|
||||
|
||||
if (File.Exists(ModListOutputFile))
|
||||
File.Delete(ModListOutputFile);
|
||||
|
@ -78,7 +78,7 @@ namespace Wabbajack.Lib
|
||||
return e.FromJSON<ModList>();
|
||||
}
|
||||
using (var e = entry.Open())
|
||||
return e.FromCERAS<ModList>(ref CerasConfig.Config);
|
||||
return e.FromCERAS<ModList>(CerasConfig.Config);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,25 +8,31 @@ namespace Wabbajack.Lib
|
||||
{
|
||||
public class CerasConfig
|
||||
{
|
||||
public static SerializerConfig Config = new SerializerConfig
|
||||
{
|
||||
KnownTypes =
|
||||
{
|
||||
typeof(ModList), typeof(Game), typeof(Directive), typeof(IgnoredDirectly),
|
||||
typeof(NoMatch), typeof(InlineFile), typeof(PropertyType), typeof(CleanedESM),
|
||||
typeof(RemappedInlineFile), typeof(FromArchive), typeof(CreateBSA), typeof(PatchedFromArchive),
|
||||
typeof(SourcePatch), typeof(MergedPatch), typeof(Archive), typeof(IndexedArchive), typeof(IndexedEntry),
|
||||
typeof(IndexedArchiveEntry), typeof(BSAIndexedEntry), typeof(VirtualFile),
|
||||
typeof(ArchiveStateObject), typeof(FileStateObject), typeof(IDownloader),
|
||||
typeof(IUrlDownloader), typeof(AbstractDownloadState), typeof(ManualDownloader.State),
|
||||
typeof(DropboxDownloader), typeof(GoogleDriveDownloader.State), typeof(HTTPDownloader.State),
|
||||
typeof(MegaDownloader.State), typeof(ModDBDownloader.State), typeof(NexusDownloader.State),
|
||||
typeof(BSAStateObject), typeof(BSAFileStateObject), typeof(BA2StateObject), typeof(BA2DX10EntryState),
|
||||
typeof(BA2FileEntryState), typeof(MediaFireDownloader.State), typeof(ArchiveMeta),
|
||||
typeof(PropertyFile), typeof(SteamMeta), typeof(SteamWorkshopDownloader), typeof(SteamWorkshopDownloader.State),
|
||||
typeof(LoversLabDownloader.State), typeof(GameFileSourceDownloader.State)
|
||||
public static readonly SerializerConfig Config;
|
||||
|
||||
}
|
||||
};
|
||||
static CerasConfig()
|
||||
{
|
||||
Config = new SerializerConfig
|
||||
{
|
||||
KnownTypes =
|
||||
{
|
||||
typeof(ModList), typeof(Game), typeof(Directive), typeof(IgnoredDirectly),
|
||||
typeof(NoMatch), typeof(InlineFile), typeof(PropertyType), typeof(CleanedESM),
|
||||
typeof(RemappedInlineFile), typeof(FromArchive), typeof(CreateBSA), typeof(PatchedFromArchive),
|
||||
typeof(SourcePatch), typeof(MergedPatch), typeof(Archive), typeof(IndexedArchive), typeof(IndexedEntry),
|
||||
typeof(IndexedArchiveEntry), typeof(BSAIndexedEntry), typeof(VirtualFile),
|
||||
typeof(ArchiveStateObject), typeof(FileStateObject), typeof(IDownloader),
|
||||
typeof(IUrlDownloader), typeof(AbstractDownloadState), typeof(ManualDownloader.State),
|
||||
typeof(DropboxDownloader), typeof(GoogleDriveDownloader.State), typeof(HTTPDownloader.State),
|
||||
typeof(MegaDownloader.State), typeof(ModDBDownloader.State), typeof(NexusDownloader.State),
|
||||
typeof(BSAStateObject), typeof(BSAFileStateObject), typeof(BA2StateObject), typeof(BA2DX10EntryState),
|
||||
typeof(BA2FileEntryState), typeof(MediaFireDownloader.State), typeof(ArchiveMeta),
|
||||
typeof(PropertyFile), typeof(SteamMeta), typeof(SteamWorkshopDownloader), typeof(SteamWorkshopDownloader.State),
|
||||
typeof(LoversLabDownloader.State), typeof(GameFileSourceDownloader.State)
|
||||
|
||||
},
|
||||
};
|
||||
Config.VersionTolerance.Mode = VersionToleranceMode.Standard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ namespace Wabbajack.Lib
|
||||
public string Website;
|
||||
|
||||
/// <summary>
|
||||
/// Hash of the readme
|
||||
/// readme path or website
|
||||
/// </summary>
|
||||
public string Readme;
|
||||
|
||||
@ -113,6 +113,10 @@ namespace Wabbajack.Lib
|
||||
.Take(Environment.ProcessorCount)
|
||||
.Sum(a => a.Size) * 2;
|
||||
|
||||
/// <summary>
|
||||
/// Whether readme is a website
|
||||
/// </summary>
|
||||
public bool ReadmeIsWebsite;
|
||||
}
|
||||
|
||||
public class Directive
|
||||
|
@ -44,6 +44,15 @@ namespace Wabbajack
|
||||
.Unit();
|
||||
}
|
||||
|
||||
public static IObservable<Unit> EndingExecution(this IReactiveCommand cmd)
|
||||
{
|
||||
return cmd.IsExecuting
|
||||
.DistinctUntilChanged()
|
||||
.Pairwise()
|
||||
.Where(x => x.Previous && !x.Current)
|
||||
.Unit();
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
@ -15,11 +15,12 @@ namespace Wabbajack.Lib.LibCefHelpers
|
||||
{
|
||||
public static class Helpers
|
||||
{
|
||||
private static readonly Task _initTask;
|
||||
|
||||
/// <summary>
|
||||
/// We bundle the cef libs inside the .exe, we need to extract them before loading any wpf code that requires them
|
||||
/// </summary>
|
||||
public static void ExtractLibs()
|
||||
private static async Task ExtractLibs()
|
||||
{
|
||||
if (File.Exists("cefglue.7z") && File.Exists("libcef.dll")) return;
|
||||
|
||||
@ -30,9 +31,21 @@ namespace Wabbajack.Lib.LibCefHelpers
|
||||
Utils.Log("Extracting libCef files");
|
||||
}
|
||||
using (var wq = new WorkQueue(1))
|
||||
FileExtractor.ExtractAll(wq, "cefglue.7z", ".");
|
||||
|
||||
{
|
||||
await FileExtractor.ExtractAll(wq, "cefglue.7z", ".");
|
||||
}
|
||||
}
|
||||
|
||||
static Helpers()
|
||||
{
|
||||
_initTask = Task.Run(ExtractLibs);
|
||||
}
|
||||
|
||||
public static Task Initialize()
|
||||
{
|
||||
return _initTask;
|
||||
}
|
||||
|
||||
public static HttpClient GetClient(IEnumerable<Cookie> cookies, string referer)
|
||||
{
|
||||
var container = ToCookieContainer(cookies);
|
||||
@ -65,7 +78,6 @@ namespace Wabbajack.Lib.LibCefHelpers
|
||||
return (await visitor.Task).Where(c => c.Domain.EndsWith(domainEnding)).ToArray();
|
||||
}
|
||||
|
||||
|
||||
private class CookieVisitor : CefCookieVisitor
|
||||
{
|
||||
TaskCompletionSource<List<Cookie>> _source = new TaskCompletionSource<List<Cookie>>();
|
||||
@ -92,8 +104,6 @@ namespace Wabbajack.Lib.LibCefHelpers
|
||||
if (disposing)
|
||||
_source.SetResult(Cookies);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class Cookie
|
||||
|
@ -117,7 +117,7 @@ namespace Wabbajack.Lib
|
||||
|
||||
if (Directory.Exists(lootPath))
|
||||
{
|
||||
lootFiles = Directory.EnumerateFiles(lootPath, "userlist.yaml", SearchOption.AllDirectories)
|
||||
lootFiles = Directory.EnumerateFiles(lootPath, "userlist.yaml", SearchOption.AllDirectories)
|
||||
.Where(p => p.FileExists())
|
||||
.Select(p => new RawSourceFile(VFS.Index.ByRootPath[p])
|
||||
{ Path = Path.Combine(Consts.LOOTFolderFilesDir, p.RelativeTo(lootPath)) });
|
||||
@ -415,8 +415,9 @@ namespace Wabbajack.Lib
|
||||
var absolutePaths = AllFiles.ToDictionary(e => e.Path, e => e.AbsolutePath);
|
||||
await groups.PMap(Queue, group => BuildArchivePatches(group.Key, group, absolutePaths));
|
||||
|
||||
if (InstallDirectives.OfType<PatchedFromArchive>().FirstOrDefault(f => f.PatchID == null) != null)
|
||||
Error("Missing patches after generation, this should not happen");
|
||||
var firstFailedPatch = InstallDirectives.OfType<PatchedFromArchive>().FirstOrDefault(f => f.PatchID == null);
|
||||
if (firstFailedPatch != null)
|
||||
Error($"Missing patches after generation, this should not happen. First failure: {firstFailedPatch.FullPath}");
|
||||
}
|
||||
|
||||
private async Task BuildArchivePatches(string archiveSha, IEnumerable<PatchedFromArchive> group,
|
||||
|
@ -318,5 +318,41 @@ namespace Wabbajack.Lib
|
||||
|
||||
File.WriteAllText(Path.Combine(OutputFolder, directive.To), data);
|
||||
}
|
||||
|
||||
public static IErrorResponse CheckValidInstallPath(string path)
|
||||
{
|
||||
var ret = Utils.IsDirectoryPathValid(path);
|
||||
if (!ret.Succeeded) return ret;
|
||||
|
||||
if (!Directory.Exists(path)) return ErrorResponse.Success;
|
||||
|
||||
// Check folder does not have a wabbajack modlist
|
||||
foreach (var file in Directory.EnumerateFiles(path, DirectoryEnumerationOptions.Recursive))
|
||||
{
|
||||
if (!File.Exists(file)) continue;
|
||||
if (System.IO.Path.GetExtension(file).Equals(ExtensionManager.Extension))
|
||||
{
|
||||
return ErrorResponse.Fail($"Cannot install into a folder with a wabbajack modlist inside of it.");
|
||||
}
|
||||
}
|
||||
|
||||
// Check folder is either empty, or a likely valid previous install
|
||||
if (!Directory.IsEmpty(path))
|
||||
{
|
||||
// Some probably naive check, but should be a good starting point to improve later
|
||||
if (!Directory.EnumerateFiles(path).Any(file =>
|
||||
{
|
||||
var fileName = Path.GetFileName(file);
|
||||
if (fileName.Equals("ModOrganizer.exe", StringComparison.OrdinalIgnoreCase)) return true;
|
||||
if (fileName.Equals("ModOrganizer.ini", StringComparison.OrdinalIgnoreCase)) return true;
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
return ErrorResponse.Fail($"Cannot install into a non-empty folder that does not look like a previous WJ installation.");
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorResponse.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ namespace Wabbajack.Test
|
||||
protected TestUtils utils { get; set; }
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
public async Task TestInitialize()
|
||||
{
|
||||
Helpers.ExtractLibs();
|
||||
await Helpers.Initialize();
|
||||
Consts.TestMode = true;
|
||||
|
||||
utils = new TestUtils();
|
||||
|
@ -24,9 +24,9 @@ namespace Wabbajack.Test
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
public async Task Setup()
|
||||
{
|
||||
Helpers.ExtractLibs();
|
||||
await Helpers.Initialize();
|
||||
Utils.LogMessages.OfType<IInfo>().Subscribe(onNext: msg => TestContext.WriteLine(msg.ShortDescription));
|
||||
Utils.LogMessages.OfType<IUserIntervention>().Subscribe(msg =>
|
||||
TestContext.WriteLine("ERROR: User intervetion required: " + msg.ShortDescription));
|
||||
|
@ -4,7 +4,6 @@ using System.Reflection;
|
||||
using System.Windows;
|
||||
using MahApps.Metro;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
@ -15,7 +14,7 @@ namespace Wabbajack
|
||||
{
|
||||
public App()
|
||||
{
|
||||
Helpers.ExtractLibs();
|
||||
// Do initialization in MainWindow ctor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ namespace Wabbajack
|
||||
public string Author { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Website { get; set; }
|
||||
public bool ReadmeIsWebsite { get; set; }
|
||||
public string Readme { get; set; }
|
||||
public string SplashScreen { get; set; }
|
||||
}
|
||||
|
@ -3748,59 +3748,4 @@
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
</Style>
|
||||
<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>
|
||||
|
21
Wabbajack/Util/AsyncLazy.cs
Normal file
21
Wabbajack/Util/AsyncLazy.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public class AsyncLazy<T> : Lazy<Task<T>>
|
||||
{
|
||||
public AsyncLazy(Func<T> valueFactory) :
|
||||
base(() => Task.Factory.StartNew(valueFactory))
|
||||
{
|
||||
}
|
||||
|
||||
public AsyncLazy(Func<Task<T>> taskFactory) :
|
||||
base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.StatusFeed;
|
||||
@ -46,20 +47,18 @@ namespace Wabbajack
|
||||
public IReactiveCommand BackCommand { get; }
|
||||
public IReactiveCommand GoToModlistCommand { get; }
|
||||
public IReactiveCommand CloseWhenCompleteCommand { get; }
|
||||
public IReactiveCommand BeginCommand { get; }
|
||||
|
||||
public FilePickerVM OutputLocation { get; }
|
||||
|
||||
private readonly ObservableAsPropertyHelper<IUserIntervention> _ActiveGlobalUserIntervention;
|
||||
public IUserIntervention ActiveGlobalUserIntervention => _ActiveGlobalUserIntervention.Value;
|
||||
|
||||
private readonly ObservableAsPropertyHelper<bool> _Completed;
|
||||
public bool Completed => _Completed.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks whether compilation has begun
|
||||
/// </summary>
|
||||
[Reactive]
|
||||
public bool CompilationMode { get; set; }
|
||||
public bool StartedCompilation { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ErrorResponse? Completed { get; set; }
|
||||
|
||||
public CompilerVM(MainWindowVM mainWindowVM)
|
||||
{
|
||||
@ -137,7 +136,8 @@ namespace Wabbajack
|
||||
execute: () =>
|
||||
{
|
||||
mainWindowVM.ActivePane = mainWindowVM.ModeSelectionVM;
|
||||
CompilationMode = false;
|
||||
StartedCompilation = false;
|
||||
Completed = null;
|
||||
},
|
||||
canExecute: this.WhenAny(x => x.Compiling)
|
||||
.Select(x => !x));
|
||||
@ -166,15 +166,6 @@ namespace Wabbajack
|
||||
.Subscribe()
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
_Completed = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Compiling),
|
||||
this.WhenAny(x => x.CompilationMode),
|
||||
resultSelector: (installing, installingMode) =>
|
||||
{
|
||||
return installingMode && !installing;
|
||||
})
|
||||
.ToProperty(this, nameof(Completed));
|
||||
|
||||
_percentCompleted = this.WhenAny(x => x.Compiler.ActiveCompilation)
|
||||
.StartWith(default(ACompiler))
|
||||
.CombineLatest(
|
||||
@ -183,21 +174,37 @@ namespace Wabbajack
|
||||
{
|
||||
if (compiler == null)
|
||||
{
|
||||
return Observable.Return<float>(completed ? 1f : 0f);
|
||||
return Observable.Return<float>(completed != null ? 1f : 0f);
|
||||
}
|
||||
return compiler.PercentCompleted;
|
||||
return compiler.PercentCompleted.StartWith(0);
|
||||
})
|
||||
.Switch()
|
||||
.Debounce(TimeSpan.FromMilliseconds(25))
|
||||
.ToProperty(this, nameof(PercentCompleted));
|
||||
|
||||
// When sub compiler begins an install, mark state variable
|
||||
this.WhenAny(x => x.Compiler.BeginCommand)
|
||||
.Select(x => x?.StartingExecution() ?? Observable.Empty<Unit>())
|
||||
.Switch()
|
||||
BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: this.WhenAny(x => x.Compiler.CanCompile)
|
||||
.Switch(),
|
||||
execute: async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.Compiler.Compile();
|
||||
Completed = ErrorResponse.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Completed = ErrorResponse.Fail(ex);
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Error(ex, $"Compiler error");
|
||||
}
|
||||
});
|
||||
|
||||
// When sub compiler begins a compile, mark state variable
|
||||
BeginCommand.StartingExecution()
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
CompilationMode = true;
|
||||
StartedCompilation = true;
|
||||
})
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
@ -218,14 +225,16 @@ namespace Wabbajack
|
||||
.ToProperty(this, nameof(ActiveGlobalUserIntervention));
|
||||
|
||||
CloseWhenCompleteCommand = ReactiveCommand.Create(
|
||||
canExecute: this.WhenAny(x => x.Completed),
|
||||
canExecute: this.WhenAny(x => x.Completed)
|
||||
.Select(x => x != null),
|
||||
execute: () =>
|
||||
{
|
||||
MWVM.ShutdownApplication();
|
||||
});
|
||||
|
||||
GoToModlistCommand = ReactiveCommand.Create(
|
||||
canExecute: this.WhenAny(x => x.Completed),
|
||||
canExecute: this.WhenAny(x => x.Completed)
|
||||
.Select(x => x != null),
|
||||
execute: () =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(OutputLocation.TargetPath))
|
||||
|
@ -1,4 +1,6 @@
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using ReactiveUI;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
@ -6,10 +8,10 @@ namespace Wabbajack
|
||||
{
|
||||
public interface ISubCompilerVM
|
||||
{
|
||||
IReactiveCommand BeginCommand { get; }
|
||||
ACompiler ActiveCompilation { get; }
|
||||
|
||||
ModlistSettingsEditorVM ModlistSettings { get; }
|
||||
void Unload();
|
||||
IObservable<bool> CanCompile { get; }
|
||||
Task Compile();
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
@ -38,6 +39,8 @@ namespace Wabbajack
|
||||
[Reactive]
|
||||
public StatusUpdateTracker StatusTracker { get; private set; }
|
||||
|
||||
public IObservable<bool> CanCompile { get; }
|
||||
|
||||
public MO2CompilerVM(CompilerVM parent)
|
||||
{
|
||||
Parent = parent;
|
||||
@ -93,8 +96,8 @@ namespace Wabbajack
|
||||
|
||||
// Load custom modlist settings per MO2 profile
|
||||
_modlistSettings = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModListLocation.ErrorState),
|
||||
this.WhenAny(x => x.ModListLocation.TargetPath),
|
||||
(this).WhenAny(x => x.ModListLocation.ErrorState),
|
||||
(this).WhenAny(x => x.ModListLocation.TargetPath),
|
||||
resultSelector: (state, path) => (State: state, Path: path))
|
||||
// A short throttle is a quick hack to make the above changes "atomic"
|
||||
.Throttle(TimeSpan.FromMilliseconds(25))
|
||||
@ -119,67 +122,16 @@ namespace Wabbajack
|
||||
.ObserveOnGuiThread()
|
||||
.ToProperty(this, nameof(ModlistSettings));
|
||||
|
||||
// Wire start command
|
||||
BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModListLocation.InError),
|
||||
this.WhenAny(x => x.DownloadLocation.InError),
|
||||
parent.WhenAny(x => x.OutputLocation.InError),
|
||||
this.WhenAny(x => x.ModlistSettings)
|
||||
.Select(x => x?.InError ?? Observable.Return(false))
|
||||
.Switch(),
|
||||
resultSelector: (ml, down, output, modlistSettings) => !ml && !down && !output && !modlistSettings)
|
||||
.ObserveOnGuiThread(),
|
||||
execute: async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string outputFile;
|
||||
if (string.IsNullOrWhiteSpace(parent.OutputLocation.TargetPath))
|
||||
{
|
||||
outputFile = MOProfile + ExtensionManager.Extension;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFile = Path.Combine(parent.OutputLocation.TargetPath, MOProfile + ExtensionManager.Extension);
|
||||
}
|
||||
ActiveCompilation = new MO2Compiler(
|
||||
mo2Folder: Mo2Folder,
|
||||
mo2Profile: MOProfile,
|
||||
outputFile: outputFile)
|
||||
{
|
||||
ModListName = ModlistSettings.ModListName,
|
||||
ModListAuthor = ModlistSettings.AuthorText,
|
||||
ModListDescription = ModlistSettings.Description,
|
||||
ModListImage = ModlistSettings.ImagePath.TargetPath,
|
||||
ModListWebsite = ModlistSettings.Website,
|
||||
ModListReadme = ModlistSettings.ReadMeText.TargetPath,
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Error(ex, $"Compiler error");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await ActiveCompilation.Begin();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Error(ex, $"Compiler error");
|
||||
}
|
||||
finally
|
||||
{
|
||||
StatusTracker = null;
|
||||
ActiveCompilation.Dispose();
|
||||
ActiveCompilation = null;
|
||||
}
|
||||
|
||||
});
|
||||
CanCompile = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModListLocation.InError),
|
||||
this.WhenAny(x => x.DownloadLocation.InError),
|
||||
parent.WhenAny(x => x.OutputLocation.InError),
|
||||
this.WhenAny(x => x.ModlistSettings)
|
||||
.Select(x => x?.InError ?? Observable.Return(false))
|
||||
.Switch(),
|
||||
resultSelector: (ml, down, output, modlistSettings) => !ml && !down && !output && !modlistSettings)
|
||||
.Publish()
|
||||
.RefCount();
|
||||
|
||||
// Load settings
|
||||
_settings = parent.MWVM.Settings.Compiler.MO2Compilation;
|
||||
@ -193,11 +145,11 @@ namespace Wabbajack
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
// If Mo2 folder changes and download location is empty, set it for convenience
|
||||
this.WhenAny(x => x.Mo2Folder)
|
||||
(this).WhenAny(x => x.Mo2Folder)
|
||||
.DelayInitial(TimeSpan.FromMilliseconds(100))
|
||||
.Where(x => Directory.Exists(x))
|
||||
.FilterSwitch(
|
||||
this.WhenAny(x => x.DownloadLocation.Exists)
|
||||
(this).WhenAny(x => x.DownloadLocation.Exists)
|
||||
.Invert())
|
||||
// A skip is needed to ignore the initial signal when the FilterSwitch turns on
|
||||
.Skip(1)
|
||||
@ -214,5 +166,42 @@ namespace Wabbajack
|
||||
_settings.LastCompiledProfileLocation = ModListLocation.TargetPath;
|
||||
ModlistSettings?.Save();
|
||||
}
|
||||
|
||||
public async Task Compile()
|
||||
{
|
||||
string outputFile;
|
||||
if (string.IsNullOrWhiteSpace(Parent.OutputLocation.TargetPath))
|
||||
{
|
||||
outputFile = MOProfile + ExtensionManager.Extension;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFile = Path.Combine(Parent.OutputLocation.TargetPath, MOProfile + ExtensionManager.Extension);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ActiveCompilation = new MO2Compiler(
|
||||
mo2Folder: Mo2Folder,
|
||||
mo2Profile: MOProfile,
|
||||
outputFile: outputFile)
|
||||
{
|
||||
ModListName = ModlistSettings.ModListName,
|
||||
ModListAuthor = ModlistSettings.AuthorText,
|
||||
ModListDescription = ModlistSettings.Description,
|
||||
ModListImage = ModlistSettings.ImagePath.TargetPath,
|
||||
ModListWebsite = ModlistSettings.Website,
|
||||
ModListReadme = ModlistSettings.ReadmeIsWebsite ? ModlistSettings.ReadmeWebsite : ModlistSettings.ReadmeFilePath.TargetPath,
|
||||
ReadmeIsWebsite = ModlistSettings.ReadmeIsWebsite,
|
||||
};
|
||||
await ActiveCompilation.Begin();
|
||||
}
|
||||
finally
|
||||
{
|
||||
StatusTracker = null;
|
||||
ActiveCompilation.Dispose();
|
||||
ActiveCompilation = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using DynamicData;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
@ -22,13 +24,22 @@ namespace Wabbajack
|
||||
|
||||
public FilePickerVM ImagePath { get; }
|
||||
|
||||
public FilePickerVM ReadMeText { get; }
|
||||
public FilePickerVM ReadmeFilePath { get; }
|
||||
|
||||
[Reactive]
|
||||
public string ReadmeWebsite { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string Website { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool ReadmeIsWebsite { get; set; }
|
||||
|
||||
public IObservable<bool> InError { get; }
|
||||
|
||||
public ICommand SwapToTextReadmeCommand { get; }
|
||||
public ICommand SwapToWebsiteReadmeCommand { get; }
|
||||
|
||||
public ModlistSettingsEditorVM(CompilationModlistSettings settings)
|
||||
{
|
||||
this._settings = settings;
|
||||
@ -38,19 +49,24 @@ namespace Wabbajack
|
||||
PathType = FilePickerVM.PathTypeOptions.File,
|
||||
};
|
||||
ImagePath.Filters.Add(new CommonFileDialogFilter("Banner image", "*.png"));
|
||||
ReadMeText = new FilePickerVM()
|
||||
ReadmeFilePath = new FilePickerVM()
|
||||
{
|
||||
PathType = FilePickerVM.PathTypeOptions.File,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty,
|
||||
};
|
||||
ReadMeText.Filters.Add(new CommonFileDialogFilter("Text", "*.txt"));
|
||||
ReadmeFilePath.Filters.Add(new CommonFileDialogFilter("Text", "*.txt"));
|
||||
ReadmeFilePath.Filters.Add(new CommonFileDialogFilter("HTML File", "*.html"));
|
||||
|
||||
InError = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ImagePath.ErrorState).Select(err => err.Failed),
|
||||
this.WhenAny(x => x.ReadMeText.ErrorState).Select(err => err.Failed),
|
||||
resultSelector: (img, readme) => img || readme)
|
||||
this.WhenAny(x => x.ReadmeFilePath.ErrorState).Select(err => err.Failed),
|
||||
this.WhenAny(x => x.ReadmeIsWebsite),
|
||||
resultSelector: (img, readme, isWebsite) => img || (readme && !isWebsite))
|
||||
.Publish()
|
||||
.RefCount();
|
||||
|
||||
SwapToTextReadmeCommand = ReactiveCommand.Create(() => ReadmeIsWebsite = false);
|
||||
SwapToWebsiteReadmeCommand = ReactiveCommand.Create(() => ReadmeIsWebsite = true);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
@ -61,7 +77,15 @@ namespace Wabbajack
|
||||
ModListName = _settings.ModListName;
|
||||
}
|
||||
Description = _settings.Description;
|
||||
ReadMeText.TargetPath = _settings.Readme;
|
||||
ReadmeIsWebsite = _settings.ReadmeIsWebsite;
|
||||
if (ReadmeIsWebsite)
|
||||
{
|
||||
ReadmeWebsite = _settings.Readme;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadmeFilePath.TargetPath = _settings.Readme;
|
||||
}
|
||||
ImagePath.TargetPath = _settings.SplashScreen;
|
||||
Website = _settings.Website;
|
||||
}
|
||||
@ -71,7 +95,15 @@ namespace Wabbajack
|
||||
_settings.Author = AuthorText;
|
||||
_settings.ModListName = ModListName;
|
||||
_settings.Description = Description;
|
||||
_settings.Readme = ReadMeText.TargetPath;
|
||||
_settings.ReadmeIsWebsite = ReadmeIsWebsite;
|
||||
if (ReadmeIsWebsite)
|
||||
{
|
||||
_settings.Readme = ReadmeWebsite;
|
||||
}
|
||||
else
|
||||
{
|
||||
_settings.Readme = ReadmeFilePath.TargetPath;
|
||||
}
|
||||
_settings.SplashScreen = ImagePath.TargetPath;
|
||||
_settings.Website = Website;
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ namespace Wabbajack
|
||||
[Reactive]
|
||||
public StatusUpdateTracker StatusTracker { get; private set; }
|
||||
|
||||
public IObservable<bool> CanCompile { get; }
|
||||
|
||||
public VortexCompilerVM(CompilerVM parent)
|
||||
{
|
||||
Parent = parent;
|
||||
@ -77,7 +79,7 @@ namespace Wabbajack
|
||||
};
|
||||
|
||||
// Load custom ModList settings when game type changes
|
||||
_modListSettings = this.WhenAny(x => x.SelectedGame)
|
||||
_modListSettings = (this).WhenAny(x => x.SelectedGame)
|
||||
.Select(game =>
|
||||
{
|
||||
if (game == null) return null;
|
||||
@ -97,67 +99,16 @@ namespace Wabbajack
|
||||
.ObserveOnGuiThread()
|
||||
.ToProperty(this, nameof(ModlistSettings));
|
||||
|
||||
// Wire start command
|
||||
BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: Observable.CombineLatest(
|
||||
this.WhenAny(x => x.GameLocation.InError),
|
||||
this.WhenAny(x => x.DownloadsLocation.InError),
|
||||
this.WhenAny(x => x.StagingLocation.InError),
|
||||
this.WhenAny(x => x.ModlistSettings)
|
||||
.Select(x => x?.InError ?? Observable.Return(false))
|
||||
.Switch(),
|
||||
(g, d, s, ml) => !g && !d && !s && !ml)
|
||||
.ObserveOnGuiThread(),
|
||||
execute: async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string outputFile = $"{ModlistSettings.ModListName}{ExtensionManager.Extension}";
|
||||
if (!string.IsNullOrWhiteSpace(parent.OutputLocation.TargetPath))
|
||||
{
|
||||
outputFile = Path.Combine(parent.OutputLocation.TargetPath, outputFile);
|
||||
}
|
||||
ActiveCompilation = new VortexCompiler(
|
||||
game: SelectedGame.Game,
|
||||
gamePath: GameLocation.TargetPath,
|
||||
vortexFolder: VortexCompiler.TypicalVortexFolder(),
|
||||
downloadsFolder: DownloadsLocation.TargetPath,
|
||||
stagingFolder: StagingLocation.TargetPath,
|
||||
outputFile: outputFile)
|
||||
{
|
||||
ModListName = ModlistSettings.ModListName,
|
||||
ModListAuthor = ModlistSettings.AuthorText,
|
||||
ModListDescription = ModlistSettings.Description,
|
||||
ModListImage = ModlistSettings.ImagePath.TargetPath,
|
||||
ModListWebsite = ModlistSettings.Website,
|
||||
ModListReadme = ModlistSettings.ReadMeText.TargetPath,
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Error(ex, $"Compiler error");
|
||||
return;
|
||||
}
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await ActiveCompilation.Begin();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Error(ex, $"Compiler error");
|
||||
}
|
||||
finally
|
||||
{
|
||||
StatusTracker = null;
|
||||
ActiveCompilation.Dispose();
|
||||
ActiveCompilation = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
CanCompile = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.GameLocation.InError),
|
||||
this.WhenAny(x => x.DownloadsLocation.InError),
|
||||
this.WhenAny(x => x.StagingLocation.InError),
|
||||
this.WhenAny(x => x.ModlistSettings)
|
||||
.Select(x => x?.InError ?? Observable.Return(false))
|
||||
.Switch(),
|
||||
(g, d, s, ml) => !g && !d && !s && !ml)
|
||||
.Publish()
|
||||
.RefCount();
|
||||
|
||||
// Load settings
|
||||
_settings = parent.MWVM.Settings.Compiler.VortexCompilation;
|
||||
@ -167,7 +118,7 @@ namespace Wabbajack
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
// Load custom game settings when game type changes
|
||||
this.WhenAny(x => x.SelectedGame)
|
||||
(this).WhenAny(x => x.SelectedGame)
|
||||
.Select(game => _settings.ModlistSettings.TryCreate(game.Game))
|
||||
.Pairwise()
|
||||
.Subscribe(pair =>
|
||||
@ -230,5 +181,40 @@ namespace Wabbajack
|
||||
var gogGame = GOGHandler.Instance.Games.FirstOrDefault(g => g.Game.HasValue && g.Game == SelectedGame.Game);
|
||||
GameLocation.TargetPath = gogGame?.Path;
|
||||
}
|
||||
|
||||
public async Task Compile()
|
||||
{
|
||||
string outputFile = $"{ModlistSettings.ModListName}{ExtensionManager.Extension}";
|
||||
if (!string.IsNullOrWhiteSpace(Parent.OutputLocation.TargetPath))
|
||||
{
|
||||
outputFile = Path.Combine(Parent.OutputLocation.TargetPath, outputFile);
|
||||
}
|
||||
try
|
||||
{
|
||||
ActiveCompilation = new VortexCompiler(
|
||||
game: SelectedGame.Game,
|
||||
gamePath: GameLocation.TargetPath,
|
||||
vortexFolder: VortexCompiler.TypicalVortexFolder(),
|
||||
downloadsFolder: DownloadsLocation.TargetPath,
|
||||
stagingFolder: StagingLocation.TargetPath,
|
||||
outputFile: outputFile)
|
||||
{
|
||||
ModListName = ModlistSettings.ModListName,
|
||||
ModListAuthor = ModlistSettings.AuthorText,
|
||||
ModListDescription = ModlistSettings.Description,
|
||||
ModListImage = ModlistSettings.ImagePath.TargetPath,
|
||||
ModListWebsite = ModlistSettings.Website,
|
||||
ModListReadme = ModlistSettings.ReadmeIsWebsite ? ModlistSettings.ReadmeWebsite : ModlistSettings.ReadmeFilePath.TargetPath,
|
||||
ReadmeIsWebsite = ModlistSettings.ReadmeIsWebsite,
|
||||
};
|
||||
await ActiveCompilation.Begin();
|
||||
}
|
||||
finally
|
||||
{
|
||||
StatusTracker = null;
|
||||
ActiveCompilation.Dispose();
|
||||
ActiveCompilation = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,12 @@ namespace Wabbajack
|
||||
public interface ISubInstallerVM
|
||||
{
|
||||
InstallerVM Parent { get; }
|
||||
IReactiveCommand BeginCommand { get; }
|
||||
AInstaller ActiveInstallation { get; }
|
||||
void Unload();
|
||||
bool SupportsAfterInstallNavigation { get; }
|
||||
void AfterInstallNavigation();
|
||||
int ConfigVisualVerticalOffset { get; }
|
||||
IObservable<bool> CanInstall { get; }
|
||||
Task Install();
|
||||
}
|
||||
}
|
||||
|
@ -46,11 +46,11 @@ namespace Wabbajack
|
||||
private readonly ObservableAsPropertyHelper<bool> _installing;
|
||||
public bool Installing => _installing.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks whether installation has begun
|
||||
/// </summary>
|
||||
[Reactive]
|
||||
public bool InstallingMode { get; set; }
|
||||
public bool StartedInstallation { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ErrorResponse? Completed { get; set; }
|
||||
|
||||
private readonly ObservableAsPropertyHelper<ImageSource> _image;
|
||||
public ImageSource Image => _image.Value;
|
||||
@ -82,9 +82,6 @@ namespace Wabbajack
|
||||
private readonly ObservableAsPropertyHelper<IUserIntervention> _ActiveGlobalUserIntervention;
|
||||
public IUserIntervention ActiveGlobalUserIntervention => _ActiveGlobalUserIntervention.Value;
|
||||
|
||||
private readonly ObservableAsPropertyHelper<bool> _Completed;
|
||||
public bool Completed => _Completed.Value;
|
||||
|
||||
// Command properties
|
||||
public IReactiveCommand ShowReportCommand { get; }
|
||||
public IReactiveCommand OpenReadmeCommand { get; }
|
||||
@ -92,6 +89,7 @@ namespace Wabbajack
|
||||
public IReactiveCommand BackCommand { get; }
|
||||
public IReactiveCommand CloseWhenCompleteCommand { get; }
|
||||
public IReactiveCommand GoToInstallCommand { get; }
|
||||
public IReactiveCommand BeginCommand { get; }
|
||||
|
||||
public InstallerVM(MainWindowVM mainWindowVM)
|
||||
{
|
||||
@ -163,7 +161,7 @@ namespace Wabbajack
|
||||
.Select(modList => modList?.ReportHTML)
|
||||
.ToProperty(this, nameof(HTMLReport));
|
||||
_installing = this.WhenAny(x => x.Installer.ActiveInstallation)
|
||||
.Select(compilation => compilation != null)
|
||||
.Select(i => i != null)
|
||||
.ObserveOnGuiThread()
|
||||
.ToProperty(this, nameof(Installing));
|
||||
_TargetManager = this.WhenAny(x => x.ModList)
|
||||
@ -182,21 +180,13 @@ namespace Wabbajack
|
||||
BackCommand = ReactiveCommand.Create(
|
||||
execute: () =>
|
||||
{
|
||||
InstallingMode = false;
|
||||
StartedInstallation = false;
|
||||
Completed = null;
|
||||
mainWindowVM.ActivePane = mainWindowVM.ModeSelectionVM;
|
||||
},
|
||||
canExecute: this.WhenAny(x => x.Installing)
|
||||
.Select(x => !x));
|
||||
|
||||
_Completed = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Installing),
|
||||
this.WhenAny(x => x.InstallingMode),
|
||||
resultSelector: (installing, installingMode) =>
|
||||
{
|
||||
return installingMode && !installing;
|
||||
})
|
||||
.ToProperty(this, nameof(Completed));
|
||||
|
||||
_percentCompleted = this.WhenAny(x => x.Installer.ActiveInstallation)
|
||||
.StartWith(default(AInstaller))
|
||||
.CombineLatest(
|
||||
@ -205,9 +195,9 @@ namespace Wabbajack
|
||||
{
|
||||
if (installer == null)
|
||||
{
|
||||
return Observable.Return<float>(completed ? 1f : 0f);
|
||||
return Observable.Return<float>(completed != null ? 1f : 0f);
|
||||
}
|
||||
return installer.PercentCompleted;
|
||||
return installer.PercentCompleted.StartWith(0f);
|
||||
})
|
||||
.Switch()
|
||||
.Debounce(TimeSpan.FromMilliseconds(25))
|
||||
@ -273,7 +263,7 @@ namespace Wabbajack
|
||||
// Define commands
|
||||
ShowReportCommand = ReactiveCommand.Create(ShowReport);
|
||||
OpenReadmeCommand = ReactiveCommand.Create(
|
||||
execute: OpenReadmeWindow,
|
||||
execute: () => this.ModList?.OpenReadmeWindow(),
|
||||
canExecute: this.WhenAny(x => x.ModList)
|
||||
.Select(modList => !string.IsNullOrEmpty(modList?.Readme))
|
||||
.ObserveOnGuiThread());
|
||||
@ -285,11 +275,11 @@ namespace Wabbajack
|
||||
|
||||
_progressTitle = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Installing),
|
||||
this.WhenAny(x => x.InstallingMode),
|
||||
resultSelector: (installing, mode) =>
|
||||
this.WhenAny(x => x.StartedInstallation),
|
||||
resultSelector: (installing, started) =>
|
||||
{
|
||||
if (!installing) return "Configuring";
|
||||
return mode ? "Installing" : "Installed";
|
||||
return started ? "Installing" : "Installed";
|
||||
})
|
||||
.ToProperty(this, nameof(ProgressTitle));
|
||||
|
||||
@ -317,13 +307,39 @@ namespace Wabbajack
|
||||
.Subscribe()
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: this.WhenAny(x => x.Installer.CanInstall)
|
||||
.Switch(),
|
||||
execute: async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.Installer.Install();
|
||||
Completed = ErrorResponse.Success;
|
||||
try
|
||||
{
|
||||
this.ModList?.OpenReadmeWindow();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Error(ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log(ex.StackTrace);
|
||||
Utils.Log(ex.ToString());
|
||||
Utils.Log($"{ex.Message} - Can't continue");
|
||||
Completed = ErrorResponse.Fail(ex);
|
||||
}
|
||||
});
|
||||
|
||||
// When sub installer begins an install, mark state variable
|
||||
this.WhenAny(x => x.Installer.BeginCommand)
|
||||
.Select(x => x?.StartingExecution() ?? Observable.Empty<Unit>())
|
||||
.Switch()
|
||||
BeginCommand.StartingExecution()
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
InstallingMode = true;
|
||||
StartedInstallation = true;
|
||||
})
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
@ -344,7 +360,8 @@ namespace Wabbajack
|
||||
.ToProperty(this, nameof(ActiveGlobalUserIntervention));
|
||||
|
||||
CloseWhenCompleteCommand = ReactiveCommand.Create(
|
||||
canExecute: this.WhenAny(x => x.Completed),
|
||||
canExecute: this.WhenAny(x => x.Completed)
|
||||
.Select(x => x != null),
|
||||
execute: () =>
|
||||
{
|
||||
MWVM.ShutdownApplication();
|
||||
@ -352,7 +369,8 @@ namespace Wabbajack
|
||||
|
||||
GoToInstallCommand = ReactiveCommand.Create(
|
||||
canExecute: Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Completed),
|
||||
this.WhenAny(x => x.Completed)
|
||||
.Select(x => x != null),
|
||||
this.WhenAny(x => x.Installer.SupportsAfterInstallNavigation),
|
||||
resultSelector: (complete, supports) => complete && supports),
|
||||
execute: () =>
|
||||
@ -367,31 +385,5 @@ namespace Wabbajack
|
||||
File.WriteAllText(file, HTMLReport);
|
||||
Process.Start(file);
|
||||
}
|
||||
|
||||
private void OpenReadmeWindow()
|
||||
{
|
||||
if (string.IsNullOrEmpty(ModList.Readme)) return;
|
||||
using (var fs = new FileStream(ModListLocation.TargetPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var entry = ar.GetEntry(ModList.Readme);
|
||||
if (entry == null)
|
||||
{
|
||||
Utils.Log($"Tried to open a non-existant readme: {ModList.Readme}");
|
||||
return;
|
||||
}
|
||||
using (var e = entry.Open())
|
||||
{
|
||||
e.CopyTo(ms);
|
||||
}
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
using (var reader = new StreamReader(ms))
|
||||
{
|
||||
var viewer = new TextViewer(reader.ReadToEnd(), ModList.Name);
|
||||
viewer.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace Wabbajack
|
||||
{
|
||||
public InstallerVM Parent { get; }
|
||||
|
||||
public IReactiveCommand BeginCommand { get; }
|
||||
public IObservable<bool> CanInstall { get; }
|
||||
|
||||
[Reactive]
|
||||
public AInstaller ActiveInstallation { get; private set; }
|
||||
@ -48,7 +48,7 @@ namespace Wabbajack
|
||||
PromptTitle = "Select Installation Directory",
|
||||
};
|
||||
Location.AdditionalError = this.WhenAny(x => x.Location.TargetPath)
|
||||
.Select(x => Utils.IsDirectoryPathValid(x));
|
||||
.Select(x => MO2Installer.CheckValidInstallPath(x));
|
||||
DownloadLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.Off,
|
||||
@ -58,61 +58,13 @@ namespace Wabbajack
|
||||
DownloadLocation.AdditionalError = this.WhenAny(x => x.DownloadLocation.TargetPath)
|
||||
.Select(x => Utils.IsDirectoryPathValid(x));
|
||||
|
||||
BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Location.InError),
|
||||
this.WhenAny(x => x.DownloadLocation.InError),
|
||||
installerVM.WhenAny(x => x.ModListLocation.InError),
|
||||
resultSelector: (loc, modlist, download) =>
|
||||
{
|
||||
return !loc && !download && !modlist;
|
||||
})
|
||||
.ObserveOnGuiThread(),
|
||||
execute: async () =>
|
||||
CanInstall = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Location.InError),
|
||||
this.WhenAny(x => x.DownloadLocation.InError),
|
||||
installerVM.WhenAny(x => x.ModListLocation.InError),
|
||||
resultSelector: (loc, modlist, download) =>
|
||||
{
|
||||
AInstaller installer;
|
||||
|
||||
try
|
||||
{
|
||||
installer = new MO2Installer(
|
||||
archive: installerVM.ModListLocation.TargetPath,
|
||||
modList: installerVM.ModList.SourceModList,
|
||||
outputFolder: Location.TargetPath,
|
||||
downloadFolder: DownloadLocation.TargetPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log(ex.StackTrace);
|
||||
Utils.Log(ex.ToString());
|
||||
Utils.Log($"{ex.Message} - Can't continue");
|
||||
ActiveInstallation = null;
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
IDisposable subscription = null;
|
||||
try
|
||||
{
|
||||
var workTask = installer.Begin();
|
||||
ActiveInstallation = installer;
|
||||
await workTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log(ex.StackTrace);
|
||||
Utils.Log(ex.ToString());
|
||||
Utils.Log($"{ex.Message} - Can't continue");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Dispose of CPU tracking systems
|
||||
subscription?.Dispose();
|
||||
ActiveInstallation = null;
|
||||
}
|
||||
});
|
||||
return !loc && !download && !modlist;
|
||||
});
|
||||
|
||||
// Have Installation location updates modify the downloads location if empty
|
||||
@ -131,7 +83,7 @@ namespace Wabbajack
|
||||
_CurrentSettings = installerVM.WhenAny(x => x.ModListLocation.TargetPath)
|
||||
.Select(path => path == null ? null : installerVM.MWVM.Settings.Installer.Mo2ModlistSettings.TryCreate(path))
|
||||
.ToProperty(this, nameof(CurrentSettings));
|
||||
this.WhenAny(x => x.CurrentSettings)
|
||||
(this).WhenAny(x => x.CurrentSettings)
|
||||
.Pairwise()
|
||||
.Subscribe(settingsPair =>
|
||||
{
|
||||
@ -184,5 +136,29 @@ namespace Wabbajack
|
||||
{
|
||||
Process.Start("explorer.exe", Location.TargetPath);
|
||||
}
|
||||
|
||||
public async Task Install()
|
||||
{
|
||||
var installer = new MO2Installer(
|
||||
archive: Parent.ModListLocation.TargetPath,
|
||||
modList: Parent.ModList.SourceModList,
|
||||
outputFolder: Location.TargetPath,
|
||||
downloadFolder: DownloadLocation.TargetPath);
|
||||
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var workTask = installer.Begin();
|
||||
ActiveInstallation = installer;
|
||||
await workTask;
|
||||
return ErrorResponse.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveInstallation = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,6 @@ namespace Wabbajack
|
||||
{
|
||||
public InstallerVM Parent { get; }
|
||||
|
||||
public IReactiveCommand BeginCommand { get; }
|
||||
|
||||
[Reactive]
|
||||
public AInstaller ActiveInstallation { get; private set; }
|
||||
|
||||
@ -33,6 +31,8 @@ namespace Wabbajack
|
||||
|
||||
public int ConfigVisualVerticalOffset => 0;
|
||||
|
||||
public IObservable<bool> CanInstall { get; }
|
||||
|
||||
public VortexInstallerVM(InstallerVM installerVM)
|
||||
{
|
||||
Parent = installerVM;
|
||||
@ -40,60 +40,11 @@ namespace Wabbajack
|
||||
_TargetGame = installerVM.WhenAny(x => x.ModList.SourceModList.GameType)
|
||||
.ToProperty(this, nameof(TargetGame));
|
||||
|
||||
BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: Observable.CombineLatest(
|
||||
this.WhenAny(x => x.TargetGame)
|
||||
.Select(game => VortexCompiler.IsActiveVortexGame(game)),
|
||||
installerVM.WhenAny(x => x.ModListLocation.InError),
|
||||
resultSelector: (isVortexGame, modListErr) => isVortexGame && !modListErr),
|
||||
execute: async () =>
|
||||
{
|
||||
AInstaller installer;
|
||||
|
||||
try
|
||||
{
|
||||
var download = VortexCompiler.RetrieveDownloadLocation(TargetGame);
|
||||
var staging = VortexCompiler.RetrieveStagingLocation(TargetGame);
|
||||
installer = new VortexInstaller(
|
||||
archive: installerVM.ModListLocation.TargetPath,
|
||||
modList: installerVM.ModList.SourceModList,
|
||||
outputFolder: staging,
|
||||
downloadFolder: download);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log(ex.StackTrace);
|
||||
Utils.Log(ex.ToString());
|
||||
Utils.Log($"{ex.Message} - Can't continue");
|
||||
ActiveInstallation = null;
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
IDisposable subscription = null;
|
||||
try
|
||||
{
|
||||
var workTask = installer.Begin();
|
||||
ActiveInstallation = installer;
|
||||
await workTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log(ex.StackTrace);
|
||||
Utils.Log(ex.ToString());
|
||||
Utils.Log($"{ex.Message} - Can't continue");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Dispose of CPU tracking systems
|
||||
subscription?.Dispose();
|
||||
ActiveInstallation = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
CanInstall = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.TargetGame)
|
||||
.Select(game => VortexCompiler.IsActiveVortexGame(game)),
|
||||
installerVM.WhenAny(x => x.ModListLocation.InError),
|
||||
resultSelector: (isVortexGame, modListErr) => isVortexGame && !modListErr);
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
@ -104,5 +55,32 @@ namespace Wabbajack
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task Install()
|
||||
{
|
||||
AInstaller installer;
|
||||
|
||||
var download = VortexCompiler.RetrieveDownloadLocation(TargetGame);
|
||||
var staging = VortexCompiler.RetrieveStagingLocation(TargetGame);
|
||||
installer = new VortexInstaller(
|
||||
archive: Parent.ModListLocation.TargetPath,
|
||||
modList: Parent.ModList.SourceModList,
|
||||
outputFolder: staging,
|
||||
downloadFolder: download);
|
||||
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var workTask = installer.Begin();
|
||||
ActiveInstallation = installer;
|
||||
await workTask;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveInstallation = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ namespace Wabbajack
|
||||
public readonly Lazy<InstallerVM> Installer;
|
||||
public readonly Lazy<ModListGalleryVM> Gallery;
|
||||
public readonly ModeSelectionVM ModeSelectionVM;
|
||||
public readonly WebBrowserVM WebBrowserVM;
|
||||
public readonly UserInterventionHandlers UserInterventionHandlers;
|
||||
public readonly LoginManagerVM LoginManagerVM;
|
||||
|
||||
@ -61,7 +60,6 @@ namespace Wabbajack
|
||||
Compiler = new Lazy<CompilerVM>(() => new CompilerVM(this));
|
||||
Gallery = new Lazy<ModListGalleryVM>(() => new ModListGalleryVM(this));
|
||||
ModeSelectionVM = new ModeSelectionVM(this);
|
||||
WebBrowserVM = new WebBrowserVM();
|
||||
UserInterventionHandlers = new UserInterventionHandlers(this);
|
||||
LoginManagerVM = new LoginManagerVM(this);
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace Wabbajack
|
||||
Metadata = metadata;
|
||||
IsBroken = metadata.ValidationSummary.HasFailures;
|
||||
OpenWebsiteCommand = ReactiveCommand.Create(() => Process.Start($"https://www.wabbajack.org/modlist/{Metadata.Links.MachineURL}"));
|
||||
ExecuteCommand = ReactiveCommand.CreateFromObservable<Unit, bool>(
|
||||
ExecuteCommand = ReactiveCommand.CreateFromObservable<Unit, Unit>(
|
||||
canExecute: this.WhenAny(x => x.IsBroken).Select(x => !x),
|
||||
execute: (unit) =>
|
||||
Observable.Return(unit)
|
||||
@ -63,20 +63,46 @@ namespace Wabbajack
|
||||
}
|
||||
return exists;
|
||||
})
|
||||
.Where(exists => exists)
|
||||
// Do any install page swap over on GUI thread
|
||||
.ObserveOnGuiThread()
|
||||
.Do(exists =>
|
||||
.Select(_ =>
|
||||
{
|
||||
if (exists)
|
||||
{
|
||||
_parent.MWVM.OpenInstaller(Path.GetFullPath(Location));
|
||||
}
|
||||
}));
|
||||
_parent.MWVM.OpenInstaller(Path.GetFullPath(Location));
|
||||
|
||||
// Wait for modlist member to be filled, then open its readme
|
||||
return _parent.MWVM.Installer.Value.WhenAny(x => x.ModList)
|
||||
.NotNull()
|
||||
.Take(1)
|
||||
.Do(modList =>
|
||||
{
|
||||
try
|
||||
{
|
||||
modList.OpenReadmeWindow();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Error(ex);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Switch()
|
||||
.Unit());
|
||||
|
||||
_Exists = Observable.Interval(TimeSpan.FromSeconds(0.5))
|
||||
.Unit()
|
||||
.StartWith(Unit.Default)
|
||||
.Select(_ => !metadata.NeedsDownload(Location))
|
||||
.Select(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return !metadata.NeedsDownload(Location);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.ToProperty(this, nameof(Exists));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Reactive;
|
||||
@ -82,5 +83,38 @@ namespace Wabbajack
|
||||
.Replay(1)
|
||||
.RefCount();
|
||||
}
|
||||
|
||||
public void OpenReadmeWindow()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Readme)) return;
|
||||
if (SourceModList.ReadmeIsWebsite)
|
||||
{
|
||||
Process.Start(Readme);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var fs = new FileStream(ModListPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var entry = ar.GetEntry(Readme);
|
||||
if (entry == null)
|
||||
{
|
||||
Utils.Log($"Tried to open a non-existant readme: {Readme}");
|
||||
return;
|
||||
}
|
||||
using (var e = entry.Open())
|
||||
{
|
||||
e.CopyTo(ms);
|
||||
}
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
using (var reader = new StreamReader(ms))
|
||||
{
|
||||
var viewer = new TextViewer(reader.ReadToEnd(), Name);
|
||||
viewer.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace Wabbajack
|
||||
{
|
||||
CancellationTokenSource cancel = new CancellationTokenSource();
|
||||
var oldPane = MainWindow.ActivePane;
|
||||
var vm = new WebBrowserVM();
|
||||
var vm = await WebBrowserVM.GetNew();
|
||||
MainWindow.ActivePane = vm;
|
||||
vm.BackCommand = ReactiveCommand.Create(() =>
|
||||
{
|
||||
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
using Xilium.CefGlue.WPF;
|
||||
|
||||
namespace Wabbajack
|
||||
@ -20,10 +21,17 @@ namespace Wabbajack
|
||||
[Reactive]
|
||||
public IReactiveCommand BackCommand { get; set; }
|
||||
|
||||
public WebBrowserVM(string url = "http://www.wabbajack.org")
|
||||
private WebBrowserVM(string url = "http://www.wabbajack.org")
|
||||
{
|
||||
Browser.Address = url;
|
||||
Instructions = "Wabbajack Web Browser";
|
||||
}
|
||||
|
||||
public static async Task<WebBrowserVM> GetNew(string url = "http://www.wabbajack.org")
|
||||
{
|
||||
// Make sure libraries are extracted first
|
||||
await Helpers.Initialize();
|
||||
return new WebBrowserVM(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
116
Wabbajack/Views/Common/AttentionBorder.xaml
Normal file
116
Wabbajack/Views/Common/AttentionBorder.xaml
Normal file
@ -0,0 +1,116 @@
|
||||
<UserControl
|
||||
x:Class="Wabbajack.AttentionBorder"
|
||||
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">
|
||||
<Border BorderThickness="1">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<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" />
|
||||
<Condition Binding="{Binding Failure, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Value="False" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
AutoReverse="True"
|
||||
RepeatBehavior="Forever"
|
||||
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
|
||||
To="{StaticResource Secondary}"
|
||||
Duration="0:0:1.5" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
<BeginStoryboard>
|
||||
<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>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsVisible, RelativeSource={RelativeSource Self}}" Value="True" />
|
||||
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="False" />
|
||||
<Condition Binding="{Binding Failure, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Value="True" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
AutoReverse="True"
|
||||
RepeatBehavior="Forever"
|
||||
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
|
||||
To="#ff0026"
|
||||
Duration="0:0:1.5" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
AutoReverse="True"
|
||||
RepeatBehavior="Forever"
|
||||
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
|
||||
To="#540914"
|
||||
Duration="0:0:1.5" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</MultiDataTrigger.EnterActions>
|
||||
<MultiDataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
|
||||
To="#700d1c"
|
||||
Duration="0:0:0.1" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
|
||||
To="#1c0307"
|
||||
Duration="0:0:0.1" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</MultiDataTrigger.ExitActions>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<ContentPresenter Content="{Binding DisplayContent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
|
||||
</Border>
|
||||
</UserControl>
|
44
Wabbajack/Views/Common/AttentionBorder.xaml.cs
Normal file
44
Wabbajack/Views/Common/AttentionBorder.xaml.cs
Normal file
@ -0,0 +1,44 @@
|
||||
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 AttentionBorder.xaml
|
||||
/// </summary>
|
||||
public partial class AttentionBorder : UserControl
|
||||
{
|
||||
public object DisplayContent
|
||||
{
|
||||
get => (object)GetValue(DisplayContentProperty);
|
||||
set => SetValue(DisplayContentProperty, value);
|
||||
}
|
||||
public static readonly DependencyProperty DisplayContentProperty = DependencyProperty.Register(nameof(DisplayContent), typeof(object), typeof(AttentionBorder),
|
||||
new FrameworkPropertyMetadata(default(object)));
|
||||
|
||||
public bool Failure
|
||||
{
|
||||
get => (bool)GetValue(FailureProperty);
|
||||
set => SetValue(FailureProperty, value);
|
||||
}
|
||||
public static readonly DependencyProperty FailureProperty = DependencyProperty.Register(nameof(Failure), typeof(bool), typeof(AttentionBorder),
|
||||
new FrameworkPropertyMetadata(default(bool)));
|
||||
|
||||
public AttentionBorder()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -65,7 +65,7 @@
|
||||
x:Name="LargeProgressBar"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="4"
|
||||
Background="#88121212"
|
||||
Background="#AA121212"
|
||||
BorderThickness="0"
|
||||
Maximum="1"
|
||||
Opacity="{Binding ProgressOpacityPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
||||
|
@ -9,93 +9,103 @@
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<Border ClipToBounds="True" Style="{StaticResource AttentionBorderStyle}">
|
||||
<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="Compilation Complete">
|
||||
<TextBlock.Effect>
|
||||
<DropShadowEffect BlurRadius="25" Opacity="0.5" />
|
||||
</TextBlock.Effect>
|
||||
</TextBlock>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center">
|
||||
<local:AttentionBorder ClipToBounds="True" Failure="{Binding Completed.Failed}">
|
||||
<local:AttentionBorder.DisplayContent>
|
||||
<Grid Margin="5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="3*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
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"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Main Menu" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{Binding CompilerSupportsAfterCompileNavigation, Converter={StaticResource bool2VisibilityConverter}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
Width="55"
|
||||
Height="55"
|
||||
Command="{Binding GoToModlistCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconMaterial
|
||||
Width="25"
|
||||
Height="25"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
Kind="FolderMove" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
VerticalAlignment="Bottom"
|
||||
FontFamily="Lucida Sans"
|
||||
FontSize="22"
|
||||
FontWeight="Black">
|
||||
<TextBlock.Effect>
|
||||
<DropShadowEffect BlurRadius="25" Opacity="0.5" />
|
||||
</TextBlock.Effect>
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="Compilation Complete" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Completed.Failed}" Value="True">
|
||||
<Setter Property="Text" Value="Compilation Failed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Go To Modlist" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<!--<Button
|
||||
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 CompilerSupportsAfterCompileNavigation, Converter={StaticResource bool2VisibilityConverter}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
Width="55"
|
||||
Height="55"
|
||||
Command="{Binding GoToModlistCommand}"
|
||||
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="Go To Modlist" />
|
||||
</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}"
|
||||
@ -117,23 +127,24 @@
|
||||
<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" />
|
||||
<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>
|
||||
</Grid>
|
||||
</Border>
|
||||
</local:AttentionBorder.DisplayContent>
|
||||
</local:AttentionBorder>
|
||||
</UserControl>
|
||||
|
@ -150,12 +150,69 @@
|
||||
<TextBox Style="{StaticResource ValueStyle}" Text="{Binding Website, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBlock
|
||||
Margin="{StaticResource TitleMargin}"
|
||||
Text="Readme Path"
|
||||
ToolTip="Path to a readme file." />
|
||||
<local:FilePicker
|
||||
PickerVM="{Binding ReadMeText}"
|
||||
Style="{StaticResource PickerStyle}"
|
||||
Text="Readme"
|
||||
ToolTip="Path to a readme file." />
|
||||
<Grid Margin="0,0,0,6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:FilePicker
|
||||
Grid.Column="0"
|
||||
Height="27"
|
||||
Margin="0,0,3,0"
|
||||
VerticalAlignment="Center"
|
||||
PickerVM="{Binding ReadmeFilePath}"
|
||||
ToolTip="Path to a readme file."
|
||||
Visibility="{Binding ReadmeIsWebsite, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}" />
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
Height="27"
|
||||
Margin="0,0,3,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding ReadmeWebsite}"
|
||||
ToolTip="Readme website"
|
||||
Visibility="{Binding ReadmeIsWebsite, Converter={StaticResource bool2VisibilityConverter}}" />
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Width="27"
|
||||
Command="{Binding SwapToWebsiteReadmeCommand}"
|
||||
ToolTip="Set readme to be a website">
|
||||
<Button.Style>
|
||||
<Style BasedOn="{StaticResource IconBareButtonStyle}" TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ReadmeIsWebsite}" Value="True">
|
||||
<Setter Property="Foreground" Value="{StaticResource SecondaryBrush}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
<icon:PackIconMaterial
|
||||
Width="20"
|
||||
Height="20"
|
||||
Kind="Web" />
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Width="27"
|
||||
Command="{Binding SwapToTextReadmeCommand}"
|
||||
ToolTip="Source readme from a local file (txt | html)">
|
||||
<Button.Style>
|
||||
<Style BasedOn="{StaticResource IconBareButtonStyle}" TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ReadmeIsWebsite}" Value="False">
|
||||
<Setter Property="Foreground" Value="{StaticResource SecondaryBrush}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
<icon:PackIconMaterial
|
||||
Width="20"
|
||||
Height="20"
|
||||
Kind="File" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<Border
|
||||
@ -174,7 +231,7 @@
|
||||
Margin="35,0,35,0"
|
||||
VerticalAlignment="Center"
|
||||
ClipToBounds="False"
|
||||
Visibility="{Binding CompilationMode, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
||||
Visibility="{Binding StartedCompilation, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
@ -232,7 +289,7 @@
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="5"
|
||||
Command="{Binding Compiler.BeginCommand}" />
|
||||
Command="{Binding BeginCommand}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
@ -240,7 +297,7 @@
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="5"
|
||||
Margin="5"
|
||||
Visibility="{Binding CompilationMode, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
|
||||
Visibility="{Binding StartedCompilation, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="4*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
@ -251,15 +308,14 @@
|
||||
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>
|
||||
<local:CompilationCompleteView Grid.Column="2" Visibility="{Binding Completed, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Collapsed}" />
|
||||
<local:AttentionBorder Grid.Column="2" Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}}">
|
||||
<local:AttentionBorder.DisplayContent>
|
||||
<Grid>
|
||||
<local:ConfirmationInterventionView DataContext="{Binding ActiveGlobalUserIntervention}" Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsTypeVisibilityConverter}, ConverterParameter={x:Type common:ConfirmationIntervention}}" />
|
||||
</Grid>
|
||||
</local:AttentionBorder.DisplayContent>
|
||||
</local:AttentionBorder>
|
||||
<local:CompilationCompleteView Grid.Column="2" Visibility="{Binding Completed, Converter={StaticResource IsNotNullVisibilityConverter}, FallbackValue=Collapsed}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
@ -9,136 +9,147 @@
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<Border ClipToBounds="True" Style="{StaticResource AttentionBorderStyle}">
|
||||
<Grid Margin="5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="3*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="4"
|
||||
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">
|
||||
<local:AttentionBorder ClipToBounds="True" Failure="{Binding Completed.Failed}">
|
||||
<local:AttentionBorder.DisplayContent>
|
||||
<Grid Margin="5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="3*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Width="50"
|
||||
Height="50"
|
||||
Command="{Binding BackCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconMaterial
|
||||
Width="25"
|
||||
Height="25"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
Kind="ArrowLeft" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="4"
|
||||
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="50"
|
||||
Height="50"
|
||||
Command="{Binding GoToInstallCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconMaterial
|
||||
Width="23"
|
||||
Height="23"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
Kind="FolderMove" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
VerticalAlignment="Bottom"
|
||||
FontFamily="Lucida Sans"
|
||||
FontSize="22"
|
||||
FontWeight="Black">
|
||||
<TextBlock.Effect>
|
||||
<DropShadowEffect BlurRadius="25" Opacity="0.5" />
|
||||
</TextBlock.Effect>
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="Installation Complete" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Completed.Failed}" Value="True">
|
||||
<Setter Property="Text" Value="Installation Failed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="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="50"
|
||||
Height="50"
|
||||
Command="{Binding OpenReadmeCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconFontAwesome
|
||||
Width="25"
|
||||
Height="25"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
Kind="ReadmeBrands" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
Grid.Row="0"
|
||||
Width="50"
|
||||
Height="50"
|
||||
Command="{Binding BackCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconMaterial
|
||||
Width="25"
|
||||
Height="25"
|
||||
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"
|
||||
Margin="0,10,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Readme" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="3"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
Width="50"
|
||||
Height="50"
|
||||
Command="{Binding CloseWhenCompleteCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconMaterial
|
||||
Width="25"
|
||||
Height="25"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
Kind="Check" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{Binding InstallerSupportsAfterInstallNavigation, Converter={StaticResource bool2VisibilityConverter}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
Width="50"
|
||||
Height="50"
|
||||
Command="{Binding GoToInstallCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconMaterial
|
||||
Width="23"
|
||||
Height="23"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
Kind="FolderMove" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Install Folder" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Close" />
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
Width="50"
|
||||
Height="50"
|
||||
Command="{Binding OpenReadmeCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconFontAwesome
|
||||
Width="25"
|
||||
Height="25"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
Kind="ReadmeBrands" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Readme" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="3"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
Width="50"
|
||||
Height="50"
|
||||
Command="{Binding CloseWhenCompleteCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}">
|
||||
<icon:PackIconMaterial
|
||||
Width="25"
|
||||
Height="25"
|
||||
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>
|
||||
</Grid>
|
||||
</Border>
|
||||
</local:AttentionBorder.DisplayContent>
|
||||
</local:AttentionBorder>
|
||||
</UserControl>
|
||||
|
@ -206,7 +206,7 @@
|
||||
ToolTip="Pause Installation"
|
||||
Margin="0,0,0,5"
|
||||
Width="50"
|
||||
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}}">
|
||||
Visibility="{Binding StartedInstallation, Converter={StaticResource bool2VisibilityConverter}}">
|
||||
<icon:PackIconMaterial
|
||||
Kind="Pause" />
|
||||
</Button>
|
||||
@ -214,7 +214,7 @@
|
||||
ToolTip="Stop Installation"
|
||||
Margin="0,0,0,5"
|
||||
Width="50"
|
||||
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}}" >
|
||||
Visibility="{Binding StartedInstallation, Converter={StaticResource bool2VisibilityConverter}}" >
|
||||
<icon:PackIconFontAwesome
|
||||
Width="25"
|
||||
Height="25"
|
||||
@ -240,7 +240,7 @@
|
||||
Grid.Row="2"
|
||||
Margin="5,0,5,5"
|
||||
ClipToBounds="True"
|
||||
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
||||
Visibility="{Binding StartedInstallation, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="4" />
|
||||
@ -339,7 +339,7 @@
|
||||
Margin="5"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
||||
Visibility="{Binding StartedInstallation, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
@ -388,13 +388,13 @@
|
||||
Margin="0,0,25,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding Installer.BeginCommand, Mode=OneWay}" />
|
||||
Command="{Binding BeginCommand, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
Margin="5,0,5,5"
|
||||
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
|
||||
Visibility="{Binding StartedInstallation, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="4*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
@ -405,16 +405,15 @@
|
||||
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}" />
|
||||
<local:AttentionBorder Grid.Column="2" Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}}">
|
||||
<local:AttentionBorder.DisplayContent>
|
||||
<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>
|
||||
</local:AttentionBorder.DisplayContent>
|
||||
</local:AttentionBorder>
|
||||
<local:InstallationCompleteView Grid.Column="2" Visibility="{Binding Completed, Converter={StaticResource IsNotNullVisibilityConverter}, FallbackValue=Collapsed}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using MahApps.Metro.Controls;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
using Application = System.Windows.Application;
|
||||
using Utils = Wabbajack.Common.Utils;
|
||||
|
||||
@ -25,22 +27,26 @@ namespace Wabbajack
|
||||
Wabbajack.Common.Utils.Error(((Exception)e.ExceptionObject), "Uncaught error");
|
||||
};
|
||||
|
||||
var appPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
|
||||
try
|
||||
{
|
||||
if (!ExtensionManager.IsAssociated() || ExtensionManager.NeedsUpdating(appPath))
|
||||
{
|
||||
ExtensionManager.Associate(appPath);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Log($"ExtensionManager had an exception:\n{e}");
|
||||
}
|
||||
|
||||
|
||||
Wabbajack.Common.Utils.Log($"Wabbajack Build - {ThisAssembly.Git.Sha}");
|
||||
|
||||
// Run some init tasks in background
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Helpers.Initialize();
|
||||
var appPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
|
||||
try
|
||||
{
|
||||
if (!ExtensionManager.IsAssociated() || ExtensionManager.NeedsUpdating(appPath))
|
||||
{
|
||||
ExtensionManager.Associate(appPath);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Log($"ExtensionManager had an exception:\n{e}");
|
||||
}
|
||||
}).FireAndForget();
|
||||
|
||||
// Load settings
|
||||
string[] args = Environment.GetCommandLineArgs();
|
||||
if ((args.Length > 1 && args[1] == "nosettings")
|
||||
@ -58,6 +64,18 @@ namespace Wabbajack
|
||||
// Set datacontext
|
||||
_mwvm = new MainWindowVM(this, _settings);
|
||||
DataContext = _mwvm;
|
||||
|
||||
// Bring window to the front if it isn't already
|
||||
this.Initialized += (s, e) =>
|
||||
{
|
||||
this.Activate();
|
||||
this.Topmost = true;
|
||||
this.Focus();
|
||||
};
|
||||
this.ContentRendered += (s, e) =>
|
||||
{
|
||||
this.Topmost = false;
|
||||
};
|
||||
}
|
||||
|
||||
public void Init(MainWindowVM vm, MainSettings settings)
|
||||
|
@ -172,11 +172,15 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="Views\Common\AttentionBorder.xaml.cs">
|
||||
<DependentUpon>AttentionBorder.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Converters\IsTypeVisibilityConverter.cs" />
|
||||
<Compile Include="UnderMaintenanceOverlay.xaml.cs">
|
||||
<DependentUpon>UnderMaintenanceOverlay.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UserInterventions\ShowLoginManager.cs" />
|
||||
<Compile Include="Util\AsyncLazy.cs" />
|
||||
<Compile Include="View Models\CPUDisplayVM.cs" />
|
||||
<Compile Include="View Models\LoginManagerVM.cs" />
|
||||
<Compile Include="Views\Compilers\CompilationCompleteView.xaml.cs">
|
||||
@ -278,6 +282,10 @@
|
||||
<Compile Include="Views\WebBrowserView.xaml.cs">
|
||||
<DependentUpon>WebBrowserView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Page Include="Views\Common\AttentionBorder.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="UnderMaintenanceOverlay.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
Loading…
Reference in New Issue
Block a user