Merge pull request #489 from Noggog/percent

Percent Struct
This commit is contained in:
Timothy Baldridge 2020-02-08 07:11:20 -07:00 committed by GitHub
commit 2ffc20411d
31 changed files with 547 additions and 141 deletions

View File

@ -79,8 +79,8 @@ namespace Wabbajack.Common
if (line.Length <= 4 || line[3] != '%')
continue;
int.TryParse(line.Substring(0, 3), out var percent);
Utils.Status($"Extracting {name} - {line.Trim()}", percent);
int.TryParse(line.Substring(0, 3), out var percentInt);
Utils.Status($"Extracting {name} - {line.Trim()}", Percent.FactoryPutInRange(percentInt / 100d));
}
}
catch (Exception e)
@ -176,8 +176,8 @@ namespace Wabbajack.Common
if (line.Length <= 4 || line[3] != '%') continue;
int.TryParse(line.Substring(0, 3), out var percent);
Utils.Status($"Extracting {name} - {line.Trim()}", percent);
int.TryParse(line.Substring(0, 3), out var percentInt);
Utils.Status($"Extracting {name} - {line.Trim()}", Percent.FactoryPutInRange(percentInt / 100d));
}
}
catch (Exception)
@ -188,7 +188,7 @@ namespace Wabbajack.Common
if (p.ExitCode == 0)
{
Utils.Status($"Extracting {name} - 100%", 100, alsoLog: true);
Utils.Status($"Extracting {name} - 100%", Percent.One, alsoLog: true);
return;
}
Utils.Error(new _7zipReturnError(p.ExitCode, source, dest, p.StandardOutput.ReadToEnd()));

View File

@ -44,9 +44,9 @@ namespace Wabbajack.Common
}
if (_queue != null)
_queue.Report(_message, (int) (_inner.Position * 100 / _inner.Length));
_queue.Report(_message, Percent.FactoryPutInRange(_inner.Position, _inner.Length));
else
Utils.Status(_message, (int) (_inner.Position * 100 / _inner.Length));
Utils.Status(_message, Percent.FactoryPutInRange(_inner.Position, _inner.Length));
}
public override void Write(byte[] buffer, int offset, int count)

View File

@ -15,8 +15,8 @@ namespace Wabbajack.Common
private Subject<int> _maxStep = new Subject<int>();
public IObservable<int> MaxStep => _maxStep;
private Subject<float> _progress = new Subject<float>();
public IObservable<float> Progress => _progress;
private Subject<Percent> _progress = new Subject<Percent>();
public IObservable<Percent> Progress => _progress;
private int _internalCurrentStep;
private int _internalMaxStep;
@ -37,24 +37,24 @@ namespace Wabbajack.Common
Utils.Log(name);
_step.OnNext(_internalCurrentStep);
_stepName.OnNext(name);
MakeUpdate(0.0f);
MakeUpdate(Percent.Zero);
}
private float OverAllStatus(float sub_status)
private Percent OverAllStatus(Percent sub_status)
{
var per_step = 1.0f / _internalMaxStep;
var macro = _internalCurrentStep * per_step;
return macro + (per_step * sub_status);
return Percent.FactoryPutInRange(macro + (per_step * sub_status));
}
public void MakeUpdate(float progress)
public void MakeUpdate(Percent progress)
{
_progress.OnNext(OverAllStatus(progress));
}
public void MakeUpdate(int max, int curr)
{
MakeUpdate((float)curr / (max == 0 ? 1 : max));
MakeUpdate(Percent.FactoryPutInRange(curr, max == 0 ? 1 : max));
}
}

View File

@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Wabbajack.Common
{
public struct Percent : IComparable, IEquatable<Percent>
{
public static readonly Percent One = new Percent(1);
public static readonly Percent Zero = new Percent(0);
public readonly double Value;
public Percent Inverse => new Percent(1 - this.Value, check: false);
private Percent(double d, bool check)
{
if (!check || InRange(d))
{
this.Value = d;
}
else
{
throw new ArgumentException("Element out of range: " + d);
}
}
public Percent(double d)
: this(d, check: true)
{
}
public Percent(int i)
{
if (i < 0)
{
Value = 0;
}
else if (i > 100)
{
Value = 1;
}
else
{
Value = i / 100d;
}
}
public static bool InRange(double d)
{
return d >= 0 || d <= 1;
}
public static Percent operator +(Percent c1, Percent c2)
{
return new Percent(c1.Value + c2.Value);
}
public static Percent operator *(Percent c1, Percent c2)
{
return new Percent(c1.Value * c2.Value);
}
public static Percent operator -(Percent c1, Percent c2)
{
return new Percent(c1.Value - c2.Value);
}
public static Percent operator /(Percent c1, Percent c2)
{
return new Percent(c1.Value / c2.Value);
}
public static implicit operator double(Percent c1)
{
return c1.Value;
}
public static Percent FactoryPutInRange(double d)
{
if (double.IsNaN(d) || double.IsInfinity(d))
{
throw new ArgumentException();
}
if (d < 0)
{
return Percent.Zero;
}
else if (d > 1)
{
return Percent.One;
}
return new Percent(d, check: false);
}
public static Percent FactoryPutInRange(int cur, int max)
{
return FactoryPutInRange(1.0d * cur / max);
}
public static Percent FactoryPutInRange(long cur, long max)
{
return FactoryPutInRange(1.0d * cur / max);
}
public static Percent AverageFromPercents(params Percent[] ps)
{
double percent = 0;
foreach (var p in ps)
{
percent += p.Value;
}
return new Percent(percent / ps.Length, check: false);
}
public static Percent MultFromPercents(params Percent[] ps)
{
double percent = 1;
foreach (var p in ps)
{
percent *= p.Value;
}
return new Percent(percent, check: false);
}
public override bool Equals(object obj)
{
if (!(obj is Percent rhs)) return false;
return Equals(rhs);
}
public bool Equals(Percent other)
{
return this.Value == other.Value;
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
public override string ToString()
{
return ToString(0);
}
public string ToString(string format)
{
return $"{(Value * 100).ToString(format)}%";
}
public string ToString(byte numDigits)
{
switch (numDigits)
{
case 0:
return ToString("n0");
case 1:
return ToString("n1");
case 2:
return ToString("n2");
case 3:
return ToString("n3");
case 4:
return ToString("n4");
case 5:
return ToString("n5");
case 6:
return ToString("n6");
default:
throw new NotImplementedException();
}
}
public int CompareTo(object obj)
{
if (obj is Percent rhs)
{
return this.Value.CompareTo(rhs.Value);
}
return 0;
}
public static bool TryParse(string str, out Percent p)
{
if (double.TryParse(str, out double d))
{
if (InRange(d))
{
p = new Percent(d);
return true;
}
}
p = default(Percent);
return false;
}
}
}

View File

@ -126,7 +126,7 @@ namespace Wabbajack.Common
}
}
public static void Status(string msg, int progress = 0, bool alsoLog = false)
public static void Status(string msg, Percent progress, bool alsoLog = false)
{
WorkQueue.AsyncLocalCurrentQueue.Value?.Report(msg, progress);
if (alsoLog)
@ -135,6 +135,11 @@ namespace Wabbajack.Common
}
}
public static void Status(string msg, bool alsoLog = false)
{
Status(msg, Percent.Zero, alsoLog: alsoLog);
}
public static void CatchAndLog(Action a)
{
try
@ -262,7 +267,7 @@ namespace Wabbajack.Common
if (read == 0) break;
totalRead += read;
ostream.Write(buffer, 0, read);
Status(status, (int) (totalRead * 100 / maxSize));
Status(status, Percent.FactoryPutInRange(totalRead, maxSize));
}
}
public static string xxHash(this byte[] data, bool nullOnIOError = false)
@ -764,7 +769,7 @@ namespace Wabbajack.Common
var lst = coll.ToList();
lst.DoIndexed((idx, i) =>
{
Status(msg, idx * 100 / lst.Count);
Status(msg, Percent.FactoryPutInRange(idx, lst.Count));
f(i);
});
}

View File

@ -116,7 +116,7 @@ namespace Wabbajack.Common
{
while (true)
{
Report("Waiting", 0, false);
Report("Waiting", Percent.Zero, false);
if (_shutdown.IsCancellationRequested) return;
@ -150,7 +150,7 @@ namespace Wabbajack.Common
{
Utils.Error($"Could not remove thread from workpool with CPU ID {cpuID}");
}
Report("Shutting down", 0, false);
Report("Shutting down", Percent.Zero, false);
_cpuCountSubj.OnNext((_tasks.Count, DesiredNumWorkers));
return;
}
@ -165,13 +165,12 @@ namespace Wabbajack.Common
}
}
public void Report(string msg, int progress, bool isWorking = true)
public void Report(string msg, Percent progress, bool isWorking = true)
{
_Status.OnNext(
new CPUStatus
{
Progress = progress,
ProgressPercent = progress / 100f,
ProgressPercent = progress,
Msg = msg,
ID = _cpuId.Value,
IsWorking = isWorking
@ -193,8 +192,7 @@ namespace Wabbajack.Common
public class CPUStatus
{
public int Progress { get; internal set; }
public float ProgressPercent { get; internal set; }
public Percent ProgressPercent { get; internal set; }
public string Msg { get; internal set; }
public int ID { get; internal set; }
public bool IsWorking { get; internal set; }

View File

@ -19,12 +19,12 @@ namespace Wabbajack.Lib
protected StatusUpdateTracker UpdateTracker { get; private set; }
private Subject<float> _percentCompleted { get; } = new Subject<float>();
private Subject<Percent> _percentCompleted { get; } = new Subject<Percent>();
/// <summary>
/// The current progress of the entire processing system on a scale of 0.0 to 1.0
/// </summary>
public IObservable<float> PercentCompleted => _percentCompleted;
public IObservable<Percent> PercentCompleted => _percentCompleted;
private Subject<string> _textStatus { get; } = new Subject<string>();
@ -51,7 +51,7 @@ namespace Wabbajack.Lib
// WorkQueue settings
public BehaviorSubject<bool> ManualCoreLimit = new BehaviorSubject<bool>(true);
public BehaviorSubject<byte> MaxCores = new BehaviorSubject<byte>(byte.MaxValue);
public BehaviorSubject<double> TargetUsagePercent = new BehaviorSubject<double>(1.0d);
public BehaviorSubject<Percent> TargetUsagePercent = new BehaviorSubject<Percent>(Percent.One);
protected void ConfigureProcessor(int steps, IObservable<int> numThreads = null)
{

View File

@ -54,7 +54,7 @@ namespace Wabbajack.Lib
public void Status(string msg)
{
Queue.Report(msg, 0);
Queue.Report(msg, Percent.Zero);
}
public static void Error(string msg)

View File

@ -47,7 +47,7 @@ namespace Wabbajack.Lib
public void Status(string msg)
{
Queue.Report(msg, 0);
Queue.Report(msg, Percent.Zero);
}
public void Error(string msg)
@ -183,7 +183,7 @@ namespace Wabbajack.Lib
await vFiles.GroupBy(f => f.FromFile)
.PDoIndexed(queue, (idx, group) =>
{
Utils.Status("Installing files", idx * 100 / vFiles.Count);
Utils.Status("Installing files", Percent.FactoryPutInRange(idx, vFiles.Count));
var firstDest = Path.Combine(OutputFolder, group.First().To);
CopyFile(group.Key.StagedPath, firstDest, true);

View File

@ -124,7 +124,7 @@ namespace Wabbajack.Lib.Downloaders
var secs = times.Download - times.CurrentTime;
for (int x = 0; x < secs; x++)
{
Utils.Status($"Waiting for {secs} at the request of {downloader.SiteName}", x * 100 / secs);
Utils.Status($"Waiting for {secs} at the request of {downloader.SiteName}", Percent.FactoryPutInRange(x, secs));
await Task.Delay(1000);
}
Utils.Status("Retrying download");

View File

@ -133,7 +133,7 @@ namespace Wabbajack.Lib.Downloaders
var max_chunks = info.depot_list[0].file_list[0].chunk_count;
foreach (var chunk in info.depot_list[0].file_list[0].chunk_list.OrderBy(c => c.index))
{
Utils.Status($"Downloading {a.Name}", chunk.index * 100 / max_chunks);
Utils.Status($"Downloading {a.Name}", Percent.FactoryPutInRange(chunk.index, max_chunks));
var got = await client.GetAsync(
$"https://content.cdp.bethesda.net/{collected.CDPProductId}/{collected.CDPPropertiesId}/{chunk.sha}");
var data = await got.Content.ReadAsByteArrayAsync();

View File

@ -101,7 +101,7 @@ namespace Wabbajack.Lib.Downloaders
long totalRead = 0;
var bufferSize = 1024 * 32;
Utils.Status($"Starting Download {a?.Name ?? Url}", 0);
Utils.Status($"Starting Download {a?.Name ?? Url}", Percent.Zero);
var response = await client.GetAsync(Url, HttpCompletionOption.ResponseHeadersRead);
TOP:
@ -179,7 +179,7 @@ TOP:
read_this_cycle += read;
if (read == 0) break;
Utils.Status($"Downloading {a.Name}", (int)(totalRead * 100 / contentSize));
Utils.Status($"Downloading {a.Name}", Percent.FactoryPutInRange(totalRead, contentSize));
fs.Write(buffer, 0, read);
totalRead += read;

View File

@ -61,7 +61,7 @@ namespace Wabbajack.Lib.FileUploader
long sent = 0;
using (var iqueue = new WorkQueue(MAX_CONNECTIONS))
{
iqueue.Report("Starting Upload", 1);
iqueue.Report("Starting Upload", Percent.One);
await Blocks(fsize)
.PMap(iqueue, async block_idx =>
{

View File

@ -18,7 +18,7 @@ namespace Wabbajack.Lib
/// <summary>
/// The current progress of the entire processing system on a scale of 0.0 to 1.0
/// </summary>
IObservable<float> PercentCompleted { get; }
IObservable<Percent> PercentCompleted { get; }
/// <summary>
/// The current status of the processor as a text string

View File

@ -20,6 +20,10 @@ namespace Wabbajack
new IntDownCastConverter(),
typeof(IBindingTypeConverter)
);
Locator.CurrentMutable.RegisterConstant(
new PercentToDoubleConverter(),
typeof(IBindingTypeConverter)
);
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Newtonsoft.Json;
using Wabbajack.Common;
namespace Wabbajack
{
public class PercentJsonConverter : JsonConverter<Percent>
{
public override Percent ReadJson(JsonReader reader, Type objectType, [AllowNull] Percent existingValue, bool hasExistingValue, JsonSerializer serializer)
{
double d = (double)reader.Value;
return Percent.FactoryPutInRange(d);
}
public override void WriteJson(JsonWriter writer, [AllowNull] Percent value, JsonSerializer serializer)
{
writer.WriteValue(value.Value);
}
}
}

View File

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using ReactiveUI;
using Wabbajack.Common;
namespace Wabbajack
{
public class PercentToDoubleConverter : IBindingTypeConverter
{
public int GetAffinityForObjects(Type fromType, Type toType)
{
if (toType == typeof(double)) return 1;
if (toType == typeof(double?)) return 1;
if (toType == typeof(Percent)) return 1;
if (toType == typeof(Percent?)) return 1;
return 0;
}
public bool TryConvert(object from, Type toType, object conversionHint, out object result)
{
if (toType == typeof(double))
{
if (from is Percent p)
{
result = p.Value;
return true;
}
result = 0d;
return false;
}
if (toType == typeof(double?))
{
if (from is Percent p)
{
result = p.Value;
return true;
}
if (from == null)
{
result = default(double?);
return true;
}
result = default(double?);
return false;
}
if (toType == typeof(Percent))
{
if (from is double d)
{
result = Percent.FactoryPutInRange(d);
return true;
}
result = Percent.Zero;
return false;
}
if (toType == typeof(Percent?))
{
if (from is double d)
{
result = Percent.FactoryPutInRange(d);
return true;
}
if (from == null)
{
result = default(Percent?);
return true;
}
result = Percent.Zero;
return false;
}
result = null;
return false;
}
}
}

View File

@ -82,8 +82,8 @@ namespace Wabbajack
private byte _MaxCores = byte.MaxValue;
public byte MaxCores { get => _MaxCores; set => this.RaiseAndSetIfChanged(ref _MaxCores, value); }
private double _TargetUsage = 1.0d;
public double TargetUsage { get => _TargetUsage; set => this.RaiseAndSetIfChanged(ref _TargetUsage, value); }
private Percent _TargetUsage = Percent.One;
public Percent TargetUsage { get => _TargetUsage; set => this.RaiseAndSetIfChanged(ref _TargetUsage, value); }
public void AttachToBatchProcessor(ABatchProcessor processor)
{

View File

@ -65,6 +65,7 @@ namespace Wabbajack
.ToObservableChangeSet(x => x.ID)
.Batch(TimeSpan.FromMilliseconds(50), RxApp.TaskpoolScheduler)
.EnsureUniqueChanges()
.ObserveOnGuiThread()
.TransformAndCache(
onAdded: (key, cpu) => new CPUDisplayVM(cpu),
onUpdated: (change, vm) => vm.AbsorbStatus(change.Current))
@ -72,7 +73,6 @@ namespace Wabbajack
.AutoRefresh(x => x.StartTime)
.Filter(i => i.IsWorking && i.ID != WorkQueue.UnassignedCpuId)
.Sort(SortExpressionComparer<CPUDisplayVM>.Ascending(s => s.StartTime))
.ObserveOnGuiThread()
.Bind(list)
.Subscribe();
}

View File

@ -20,7 +20,7 @@ namespace Wabbajack
[Reactive]
public string Msg { get; set; }
[Reactive]
public float ProgressPercent { get; set; }
public Percent ProgressPercent { get; set; }
public CPUDisplayVM()
{

View File

@ -40,8 +40,8 @@ namespace Wabbajack
private readonly ObservableAsPropertyHelper<bool> _compiling;
public bool Compiling => _compiling.Value;
private readonly ObservableAsPropertyHelper<float> _percentCompleted;
public float PercentCompleted => _percentCompleted.Value;
private readonly ObservableAsPropertyHelper<Percent> _percentCompleted;
public Percent PercentCompleted => _percentCompleted.Value;
public ObservableCollectionExtended<CPUDisplayVM> StatusList { get; } = new ObservableCollectionExtended<CPUDisplayVM>();
@ -169,9 +169,9 @@ namespace Wabbajack
{
if (compiler == null)
{
return Observable.Return<float>(completed != null ? 1f : 0f);
return Observable.Return<Percent>(completed != null ? Percent.One : Percent.Zero);
}
return compiler.PercentCompleted.StartWith(0);
return compiler.PercentCompleted.StartWith(Percent.Zero);
})
.Switch()
.Debounce(TimeSpan.FromMilliseconds(25), RxApp.MainThreadScheduler)

View File

@ -68,8 +68,8 @@ namespace Wabbajack
private readonly ObservableAsPropertyHelper<string> _modListName;
public string ModListName => _modListName.Value;
private readonly ObservableAsPropertyHelper<float> _percentCompleted;
public float PercentCompleted => _percentCompleted.Value;
private readonly ObservableAsPropertyHelper<Percent> _percentCompleted;
public Percent PercentCompleted => _percentCompleted.Value;
public ObservableCollectionExtended<CPUDisplayVM> StatusList { get; } = new ObservableCollectionExtended<CPUDisplayVM>();
public ObservableCollectionExtended<IStatusMessage> Log => MWVM.Log;
@ -238,9 +238,9 @@ namespace Wabbajack
{
if (installer == null)
{
return Observable.Return<float>(completed != null ? 1f : 0f);
return Observable.Return<Percent>(completed != null ? Percent.One : Percent.Zero);
}
return installer.PercentCompleted.StartWith(0f);
return installer.PercentCompleted.StartWith(Percent.Zero);
})
.Switch()
.Debounce(TimeSpan.FromMilliseconds(25), RxApp.MainThreadScheduler)

View File

@ -34,7 +34,7 @@ namespace Wabbajack
public string Location { get; }
[Reactive]
public double ProgressPercent { get; private set; }
public Percent ProgressPercent { get; private set; }
[Reactive]
public bool IsBroken { get; private set; }
@ -144,7 +144,7 @@ namespace Wabbajack
private async Task<bool> Download()
{
ProgressPercent = 0d;
ProgressPercent = Percent.Zero;
using (var queue = new WorkQueue(1))
using (queue.Status.Select(i => i.ProgressPercent)
.Subscribe(percent => ProgressPercent = percent))

View File

@ -0,0 +1,35 @@
<rxui:ReactiveUserControl
x:Class="Wabbajack.CpuLineView"
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:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rxui="http://reactiveui.net"
d:DesignHeight="65"
d:DesignWidth="400"
x:TypeArguments="local:CPUDisplayVM"
mc:Ignorable="d">
<Grid Background="{StaticResource WindowBackgroundBrush}">
<mahapps:MetroProgressBar
x:Name="BackgroundProgressBar"
Background="Transparent"
BorderThickness="0"
Foreground="{StaticResource DarkPrimaryVariantBrush}"
Maximum="1" />
<Grid Height="1" VerticalAlignment="Bottom">
<mahapps:MetroProgressBar
x:Name="ThinProgressBar"
Background="Transparent"
BorderThickness="0"
Foreground="{StaticResource DarkSecondaryBrush}"
Maximum="1" />
</Grid>
<TextBlock
x:Name="Text"
Margin="0,0,0,2"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />
</Grid>
</rxui:ReactiveUserControl>

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
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;
using ReactiveUI;
namespace Wabbajack
{
/// <summary>
/// Interaction logic for CpuLineView.xaml
/// </summary>
public partial class CpuLineView : ReactiveUserControl<CPUDisplayVM>
{
public CpuLineView()
{
InitializeComponent();
this.WhenActivated(dispose =>
{
this.WhenAny(x => x.ViewModel.ProgressPercent)
.Select(x => x.Value)
.BindToStrict(this, x => x.BackgroundProgressBar.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.ProgressPercent)
.Select(x => x.Value)
.BindToStrict(this, x => x.BackgroundProgressBar.Opacity)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.ProgressPercent)
.Select(x => x.Value)
.BindToStrict(this, x => x.ThinProgressBar.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.Msg)
.BindToStrict(this, x => x.Text.Text)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.Msg)
.BindToStrict(this, x => x.Text.ToolTip)
.DisposeWith(dispose);
});
}
}
}

View File

@ -4,14 +4,13 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wabbajack"
xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
x:TypeArguments="local:ICpuStatusVM"
mc:Ignorable="d">
<Grid x:Name="ControlGrid" Background="Transparent">
<Rectangle Fill="{StaticResource HeatedBorderBrush}" Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
<Rectangle x:Name="HeatedBorderRect" Fill="{StaticResource HeatedBorderBrush}" />
<Border BorderBrush="Transparent" BorderThickness="1" />
<Grid Margin="1" Background="{StaticResource DarkBackgroundBrush}">
<Grid.RowDefinitions>
@ -25,29 +24,7 @@
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="{StaticResource WindowBackgroundBrush}">
<mahapps:MetroProgressBar
Background="Transparent"
BorderThickness="0"
Foreground="{StaticResource DarkPrimaryVariantBrush}"
Maximum="1"
Opacity="{Binding ProgressPercent, Mode=OneWay}"
Value="{Binding ProgressPercent, Mode=OneWay}" />
<Grid Height="1" VerticalAlignment="Bottom">
<mahapps:MetroProgressBar
Background="Transparent"
BorderThickness="0"
Foreground="{StaticResource DarkSecondaryBrush}"
Maximum="1"
Value="{Binding ProgressPercent, Mode=OneWay}" />
</Grid>
<TextBlock
Margin="0,0,0,2"
Text="{Binding Msg, Mode=OneWay}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap"
ToolTip="{Binding Msg, Mode=OneWay}" />
</Grid>
<local:CpuLineView ViewModel="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

View File

@ -18,6 +18,7 @@ using ReactiveUI.Fody.Helpers;
using Wabbajack.Lib;
using System.Windows.Controls.Primitives;
using System.Reactive.Linq;
using Wabbajack.Common;
namespace Wabbajack
{
@ -26,13 +27,13 @@ namespace Wabbajack
/// </summary>
public partial class CpuView : UserControlRx<ICpuStatusVM>
{
public double ProgressPercent
public Percent ProgressPercent
{
get => (double)GetValue(ProgressPercentProperty);
get => (Percent)GetValue(ProgressPercentProperty);
set => SetValue(ProgressPercentProperty, value);
}
public static readonly DependencyProperty ProgressPercentProperty = DependencyProperty.Register(nameof(ProgressPercent), typeof(double), typeof(CpuView),
new FrameworkPropertyMetadata(default(double)));
public static readonly DependencyProperty ProgressPercentProperty = DependencyProperty.Register(nameof(ProgressPercent), typeof(Percent), typeof(CpuView),
new FrameworkPropertyMetadata(default(Percent), WireNotifyPropertyChanged));
public MainSettings SettingsHook
{
@ -55,28 +56,32 @@ namespace Wabbajack
this.WhenAny(x => x.SettingsHook.Performance.Manual)
.StartWith(true),
resultSelector: (over, manual) => over && !manual)
.Subscribe(showing =>
{
SettingsBar.Visibility = showing ? Visibility.Visible : Visibility.Collapsed;
})
.Select(showing => showing ? Visibility.Visible : Visibility.Collapsed)
.BindToStrict(this, x => x.SettingsBar.Visibility)
.DisposeWith(disposable);
this.WhenAny(x => x.ViewModel.StatusList)
.BindToStrict(this, x => x.CpuListControl.ItemsSource)
.DisposeWith(disposable);
this.OneWayBindStrict(this.ViewModel, x => x.StatusList, x => x.CpuListControl.ItemsSource)
this.Bind(this.ViewModel, x => x.MWVM.Settings.Performance.TargetUsage, x => x.TargetPercentageSlider.Value)
.DisposeWith(disposable);
this.BindStrict(this.ViewModel, x => x.MWVM.Settings.Performance.TargetUsage, x => x.TargetPercentageSlider.Value)
.DisposeWith(disposable);
this.OneWayBindStrict(this.ViewModel, x => x.MWVM.Settings.Performance.TargetUsage, x => x.PercentageText.Text, x => $"{x.ToString("f2")}%")
this.WhenAny(x => x.ViewModel.MWVM.Settings.Performance.TargetUsage)
.Select(p => p.ToString(0))
.BindToStrict(this, x => x.PercentageText.Text)
.DisposeWith(disposable);
this.WhenAny(x => x.ViewModel.CurrentCpuCount)
.DistinctUntilChanged()
.ObserveOnGuiThread()
.Subscribe(x =>
{
this.CpuCountText.Text = $"{x.CurrentCPUs} / {x.DesiredCPUs}";
})
.Select(x => $"{x.CurrentCPUs} / {x.DesiredCPUs}")
.BindToStrict(this, x => x.CpuCountText.Text)
.DisposeWith(disposable);
// Progress
this.WhenAny(x => x.ProgressPercent)
.Select(p => p.Value)
.BindToStrict(this, x => x.HeatedBorderRect.Opacity)
.DisposeWith(disposable);
});
}

View File

@ -1,4 +1,4 @@
<local:UserControlRx
<local:UserControlRx
x:Class="Wabbajack.TopProgressView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@ -19,26 +19,17 @@
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Rectangle
x:Name="OverhangShadowRect"
Grid.Row="2"
Height="25"
VerticalAlignment="Top"
IsHitTestVisible="False"
Visibility="{Binding OverhangShadow, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource bool2VisibilityConverter}}">
IsHitTestVisible="False">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#AA000000" />
<GradientStop Offset="1" Color="#00000000" />
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding ShadowMargin, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Value="True">
<Setter Property="Margin" Value="6,0" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<Rectangle Grid.Row="1" Fill="{StaticResource BackgroundBrush}" />
<mahapps:MetroProgressBar
@ -51,8 +42,7 @@
Background="Transparent"
BorderBrush="Transparent"
Foreground="{StaticResource PrimaryVariantBrush}"
Maximum="1"
Value="{Binding ProgressPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
Maximum="1">
<mahapps:MetroProgressBar.Effect>
<BlurEffect Radius="25" />
</mahapps:MetroProgressBar.Effect>
@ -69,9 +59,7 @@
Grid.ColumnSpan="4"
Background="#AA121212"
BorderThickness="0"
Maximum="1"
Opacity="{Binding ProgressOpacityPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Value="{Binding ProgressPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
Maximum="1">
<mahapps:MetroProgressBar.Foreground>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Offset="0" Color="{StaticResource DarkerPrimaryVariant}" />
@ -85,8 +73,7 @@
Grid.ColumnSpan="4"
Background="Transparent"
BorderThickness="0"
Maximum="1"
Value="{Binding ProgressPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
Maximum="1">
<mahapps:MetroProgressBar.Foreground>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#33000000" />
@ -95,6 +82,7 @@
</mahapps:MetroProgressBar.Foreground>
</mahapps:MetroProgressBar>
<TextBlock
x:Name="StatePrefixText"
Grid.Column="0"
Width="130"
Margin="0,0,0,0"
@ -102,21 +90,19 @@
FontFamily="Lucida Sans"
FontWeight="Black"
Foreground="{StaticResource ComplementaryBrush}"
Text="{Binding StatePrefixTitle, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
TextAlignment="Right"
Visibility="{Binding StatePrefixTitle, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource IsNotNullVisibilityConverter}}" />
TextAlignment="Right" />
<Rectangle
x:Name="PrefixSpacerRect"
Grid.Column="0"
Width="50"
Visibility="{Binding StatePrefixTitle, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource IsNotNullVisibilityConverter}, ConverterParameter=False}" />
Width="50" />
<TextBlock
x:Name="TitleText"
Grid.Column="1"
Margin="15,0,0,0"
VerticalAlignment="Center"
FontFamily="Lucida Sans"
FontSize="25"
FontWeight="Black"
Text="{Binding Title, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
FontWeight="Black" />
<ContentControl Grid.Column="2" />
</Grid>
<mahapps:MetroProgressBar
@ -128,8 +114,7 @@
Background="Transparent"
BorderBrush="Transparent"
Foreground="{StaticResource SecondaryBrush}"
Maximum="1"
Value="{Binding ProgressPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
Maximum="1">
<mahapps:MetroProgressBar.Effect>
<BlurEffect Radius="8" />
</mahapps:MetroProgressBar.Effect>
@ -143,8 +128,7 @@
Background="Transparent"
BorderBrush="Transparent"
Foreground="{StaticResource SecondaryBrush}"
Maximum="1"
Value="{Binding ProgressPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
Maximum="1">
<mahapps:MetroProgressBar.Effect>
<BlurEffect Radius="15" />
</mahapps:MetroProgressBar.Effect>
@ -158,8 +142,7 @@
Background="Transparent"
BorderBrush="Transparent"
Foreground="{StaticResource SecondaryBrush}"
Maximum="1"
Value="{Binding ProgressPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
Maximum="1" />
<mahapps:MetroProgressBar
x:Name="BottomProgressBarHighlight"
Grid.Row="1"
@ -168,8 +151,7 @@
VerticalAlignment="Top"
Background="Transparent"
BorderBrush="Transparent"
Maximum="1"
Value="{Binding ProgressPercent, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
Maximum="1">
<mahapps:MetroProgressBar.Foreground>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#CCFFFFFF" />

View File

@ -1,4 +1,4 @@
using System.Reactive.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Controls;
using ReactiveUI;
@ -28,7 +28,7 @@ namespace Wabbajack
set => SetValue(TitleProperty, value);
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(TopProgressView),
new FrameworkPropertyMetadata(default(string)));
new FrameworkPropertyMetadata(default(string), WireNotifyPropertyChanged));
public string StatePrefixTitle
{
@ -36,7 +36,7 @@ namespace Wabbajack
set => SetValue(StatePrefixTitleProperty, value);
}
public static readonly DependencyProperty StatePrefixTitleProperty = DependencyProperty.Register(nameof(StatePrefixTitle), typeof(string), typeof(TopProgressView),
new FrameworkPropertyMetadata(default(string)));
new FrameworkPropertyMetadata(default(string), WireNotifyPropertyChanged));
public bool OverhangShadow
{
@ -44,7 +44,7 @@ namespace Wabbajack
set => SetValue(OverhangShadowProperty, value);
}
public static readonly DependencyProperty OverhangShadowProperty = DependencyProperty.Register(nameof(OverhangShadow), typeof(bool), typeof(TopProgressView),
new FrameworkPropertyMetadata(true));
new FrameworkPropertyMetadata(true, WireNotifyPropertyChanged));
public bool ShadowMargin
{
@ -52,10 +52,7 @@ namespace Wabbajack
set => SetValue(ShadowMarginProperty, value);
}
public static readonly DependencyProperty ShadowMarginProperty = DependencyProperty.Register(nameof(ShadowMargin), typeof(bool), typeof(TopProgressView),
new FrameworkPropertyMetadata(true));
[Reactive]
public double ProgressOpacityPercent { get; private set; }
new FrameworkPropertyMetadata(true, WireNotifyPropertyChanged));
public TopProgressView()
{
@ -63,11 +60,53 @@ namespace Wabbajack
this.WhenActivated(dispose =>
{
this.WhenAny(x => x.ProgressPercent)
.Select(x =>
{
return 0.3 + x * 0.7;
})
.Subscribe(x => ProgressOpacityPercent = x)
.Select(x => 0.3 + x * 0.7)
.BindToStrict(this, x => x.LargeProgressBar.Opacity)
.DisposeWith(dispose);
this.WhenAny(x => x.ProgressPercent)
.BindToStrict(this, x => x.LargeProgressBar.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.ProgressPercent)
.BindToStrict(this, x => x.BottomProgressBarDarkGlow.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.ProgressPercent)
.BindToStrict(this, x => x.LargeProgressBarTopGlow.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.ProgressPercent)
.BindToStrict(this, x => x.BottomProgressBarBrightGlow1.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.ProgressPercent)
.BindToStrict(this, x => x.BottomProgressBarBrightGlow2.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.ProgressPercent)
.BindToStrict(this, x => x.BottomProgressBar.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.ProgressPercent)
.BindToStrict(this, x => x.BottomProgressBarHighlight.Value)
.DisposeWith(dispose);
this.WhenAny(x => x.OverhangShadow)
.Select(x => x ? Visibility.Visible : Visibility.Collapsed)
.BindToStrict(this, x => x.OverhangShadowRect.Visibility)
.DisposeWith(dispose);
this.WhenAny(x => x.ShadowMargin)
.DistinctUntilChanged()
.Select(x => x ? new Thickness(6, 0, 6, 0) : new Thickness(0))
.BindToStrict(this, x => x.OverhangShadowRect.Margin)
.DisposeWith(dispose);
this.WhenAny(x => x.Title)
.BindToStrict(this, x => x.TitleText.Text)
.DisposeWith(dispose);
this.WhenAny(x => x.StatePrefixTitle)
.Select(x => x == null ? Visibility.Visible : Visibility.Collapsed)
.BindToStrict(this, x => x.PrefixSpacerRect.Visibility)
.DisposeWith(dispose);
this.WhenAny(x => x.StatePrefixTitle)
.Select(x => x == null ? Visibility.Collapsed : Visibility.Visible)
.BindToStrict(this, x => x.StatePrefixText.Visibility)
.DisposeWith(dispose);
this.WhenAny(x => x.StatePrefixTitle)
.BindToStrict(this, x => x.StatePrefixText.Text)
.DisposeWith(dispose);
});
}

View File

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using MahApps.Metro.Controls;
using Newtonsoft.Json;
using Wabbajack.Common;
using Wabbajack.Lib.LibCefHelpers;
using Application = System.Windows.Application;
@ -48,6 +50,13 @@ namespace Wabbajack
}).FireAndForget();
// Load settings
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Converters = new List<JsonConverter>
{
new PercentJsonConverter()
}
};
if (CLIArguments.NoSettings || !MainSettings.TryLoadTypicalSettings(out var settings))
{
_settings = new MainSettings();

View File

@ -15,6 +15,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ReactiveUI;
using Wabbajack.Common;
namespace Wabbajack
{
@ -65,7 +66,7 @@ namespace Wabbajack
.DisposeWith(disposable);
this.BindStrict(this.ViewModel, x => x.TargetUsage, x => x.TargetUsageSpinner.Value)
.DisposeWith(disposable);
this.BindStrict(this.ViewModel, x => x.TargetUsage, x => x.TargetUsageSlider.Value)
this.Bind(this.ViewModel, x => x.TargetUsage, x => x.TargetUsageSlider.Value)
.DisposeWith(disposable);
});
}