Merge pull request #183 from Noggog/some-styling

Some Styling
This commit is contained in:
Timothy Baldridge 2019-11-17 06:56:05 -07:00 committed by GitHub
commit 702de2cfbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2203 additions and 1640 deletions

View File

@ -60,6 +60,32 @@ namespace Wabbajack
.Switch();
}
/// <summary>
/// Convenience operator to subscribe to the source observable, only when a second "switch" observable is on.
/// When the switch is on, the source will be subscribed to, and its updates passed through.
/// When the switch is off, the subscription to the source observable will be stopped, and no signal will be published.
/// </summary>
/// <param name="source">Source observable to subscribe to if on</param>
/// <param name="filterSwitch">On/Off signal of whether to subscribe to source observable</param>
/// <param name="valueOnOff">Value to fire when switching off</param>
/// <returns>Observable that publishes data from source, if the switch is on.</returns>
public static IObservable<T> FilterSwitch<T>(this IObservable<T> source, IObservable<bool> filterSwitch, T valueWhenOff)
{
return filterSwitch
.DistinctUntilChanged()
.Select(on =>
{
if (on)
{
return source;
}
else
{
return Observable.Return<T>(valueWhenOff);
}
})
.Switch();
}
/// Inspiration:
/// http://reactivex.io/documentation/operators/debounce.html

File diff suppressed because it is too large Load Diff

View File

@ -43,13 +43,13 @@ namespace Wabbajack
{
this.ModlistLocation = new FilePickerVM()
{
DoExistsCheck = true,
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
PathType = FilePickerVM.PathTypeOptions.File,
PromptTitle = "Select Modlist"
};
this.DownloadLocation = new FilePickerVM()
{
DoExistsCheck = true,
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
PathType = FilePickerVM.PathTypeOptions.Folder,
PromptTitle = "Select Download Location",
};

View File

@ -36,7 +36,7 @@ namespace Wabbajack
this.settings = settings;
this.ImagePath = new FilePickerVM()
{
DoExistsCheck = false,
ExistCheckOption = FilePickerVM.ExistCheckOptions.IfNotEmpty,
PathType = FilePickerVM.PathTypeOptions.File,
Filters =
{
@ -46,7 +46,7 @@ namespace Wabbajack
this.ReadMeText = new FilePickerVM()
{
PathType = FilePickerVM.PathTypeOptions.File,
DoExistsCheck = false,
ExistCheckOption = FilePickerVM.ExistCheckOptions.IfNotEmpty,
};
}

View File

@ -56,19 +56,19 @@ namespace Wabbajack
{
this.GameLocation = new FilePickerVM()
{
DoExistsCheck = true,
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
PathType = FilePickerVM.PathTypeOptions.Folder,
PromptTitle = "Select Game Folder Location"
};
this.DownloadsLocation = new FilePickerVM()
{
DoExistsCheck = true,
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
PathType = FilePickerVM.PathTypeOptions.Folder,
PromptTitle = "Select Downloads Folder"
};
this.StagingLocation = new FilePickerVM()
{
DoExistsCheck = true,
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
PathType = FilePickerVM.PathTypeOptions.Folder,
PromptTitle = "Select Staging Folder"
};

View File

@ -23,6 +23,13 @@ namespace Wabbajack
Folder
}
public enum ExistCheckOptions
{
Off,
IfNotEmpty,
On
}
public object Parent { get; }
[Reactive]
@ -38,7 +45,7 @@ namespace Wabbajack
public PathTypeOptions PathType { get; set; }
[Reactive]
public bool DoExistsCheck { get; set; }
public ExistCheckOptions ExistCheckOption { get; set; }
[Reactive]
public IObservable<IErrorResponse> AdditionalError { get; set; }
@ -63,28 +70,60 @@ namespace Wabbajack
this.SetTargetPathCommand = ConstructTypicalPickerCommand();
// Check that file exists
this._Exists = Observable.Interval(TimeSpan.FromSeconds(3))
.FilterSwitch(
Observable.CombineLatest(
this.WhenAny(x => x.PathType),
this.WhenAny(x => x.DoExistsCheck),
resultSelector: (type, doExists) => type != PathTypeOptions.Off && doExists))
.Unit()
// Also do it when fields change
.Merge(this.WhenAny(x => x.PathType).Unit())
.Merge(this.WhenAny(x => x.DoExistsCheck).Unit())
.CombineLatest(
this.WhenAny(x => x.DoExistsCheck),
this.WhenAny(x => x.PathType),
this.WhenAny(x => x.TargetPath)
var existsCheckTuple = Observable.CombineLatest(
this.WhenAny(x => x.ExistCheckOption),
this.WhenAny(x => x.PathType),
this.WhenAny(x => x.TargetPath)
// Dont want to debounce the initial value, because we know it's null
.Skip(1)
.Debounce(TimeSpan.FromMilliseconds(200)),
resultSelector: (_, DoExists, Type, Path) => (DoExists, Type, Path))
.Debounce(TimeSpan.FromMilliseconds(200))
.StartWith(default(string)),
resultSelector: (ExistsOption, Type, Path) => (ExistsOption, Type, Path))
.Publish()
.RefCount();
this._Exists = Observable.Interval(TimeSpan.FromSeconds(3))
// Only check exists on timer if desired
.FilterSwitch(existsCheckTuple
.Select(t =>
{
// Don't do exists type if we don't know what path type we're tracking
if (t.Type == PathTypeOptions.Off) return false;
switch (t.ExistsOption)
{
case ExistCheckOptions.Off:
return false;
case ExistCheckOptions.IfNotEmpty:
return !string.IsNullOrWhiteSpace(t.Path);
case ExistCheckOptions.On:
return true;
default:
throw new NotImplementedException();
}
}))
.Unit()
// Also check though, when fields change
.Merge(this.WhenAny(x => x.PathType).Unit())
.Merge(this.WhenAny(x => x.ExistCheckOption).Unit())
.Merge(this.WhenAny(x => x.TargetPath).Unit())
// Signaled to check, get latest params for actual use
.CombineLatest(existsCheckTuple,
resultSelector: (_, tuple) => tuple)
// Refresh exists
.Select(t =>
{
if (!t.DoExists) return true;
switch (t.ExistsOption)
{
case ExistCheckOptions.IfNotEmpty:
if (string.IsNullOrWhiteSpace(t.Path)) return true;
break;
case ExistCheckOptions.On:
break;
case ExistCheckOptions.Off:
default:
return true;
}
switch (t.Type)
{
case PathTypeOptions.Either:

View File

@ -99,7 +99,7 @@ namespace Wabbajack
this.Location = new FilePickerVM()
{
DoExistsCheck = false,
ExistCheckOption = FilePickerVM.ExistCheckOptions.Off,
PathType = FilePickerVM.PathTypeOptions.Folder,
PromptTitle = "Select Installation Directory",
};
@ -107,7 +107,7 @@ namespace Wabbajack
.Select(x => Utils.IsDirectoryPathValid(x));
this.DownloadLocation = new FilePickerVM()
{
DoExistsCheck = false,
ExistCheckOption = FilePickerVM.ExistCheckOptions.Off,
PathType = FilePickerVM.PathTypeOptions.Folder,
PromptTitle = "Select a location for MO2 downloads",
};

View File

@ -1,4 +1,4 @@
<UserControl
<local:UserControlRx
x:Class="Wabbajack.DetailImageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@ -97,7 +97,8 @@
FontWeight="Bold"
Style="{StaticResource BackgroundBlurStyle}"
Text="{Binding Title, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
TextWrapping="WrapWithOverflow">
TextWrapping="WrapWithOverflow"
Visibility="{Binding ShowTitle, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource bool2VisibilityHiddenConverter}}">
<TextBlock.Effect>
<BlurEffect Radius="85" />
</TextBlock.Effect>
@ -113,7 +114,8 @@
FontSize="30"
FontWeight="Bold"
Style="{StaticResource BackgroundBlurStyle}"
TextWrapping="WrapWithOverflow">
TextWrapping="WrapWithOverflow"
Visibility="{Binding ShowAuthor, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource bool2VisibilityHiddenConverter}}">
<TextBlock.Effect>
<BlurEffect Radius="55" />
</TextBlock.Effect>
@ -130,7 +132,8 @@
FontSize="65"
FontWeight="Bold"
Text="{Binding Title, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
TextWrapping="WrapWithOverflow">
TextWrapping="WrapWithOverflow"
Visibility="{Binding ShowTitle, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource bool2VisibilityHiddenConverter}}">
<TextBlock.Effect>
<DropShadowEffect />
</TextBlock.Effect>
@ -142,7 +145,8 @@
FontFamily="Lucida Sans"
FontSize="30"
FontWeight="Bold"
TextWrapping="Wrap">
TextWrapping="Wrap"
Visibility="{Binding ShowAuthor, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource bool2VisibilityHiddenConverter}}">
<TextBlock.Effect>
<DropShadowEffect />
</TextBlock.Effect>
@ -163,7 +167,8 @@
Style="{StaticResource BackgroundBlurStyle}"
Text="{Binding Description, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
TextAlignment="Right"
TextWrapping="Wrap">
TextWrapping="Wrap"
Visibility="{Binding ShowDescription, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource bool2VisibilityHiddenConverter}}">
<TextBlock.Effect>
<BlurEffect Radius="55" />
</TextBlock.Effect>
@ -179,7 +184,8 @@
FontSize="16"
Text="{Binding Description, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
TextAlignment="Right"
TextWrapping="Wrap">
TextWrapping="Wrap"
Visibility="{Binding ShowDescription, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource bool2VisibilityHiddenConverter}}">
<TextBlock.Effect>
<DropShadowEffect />
</TextBlock.Effect>
@ -192,4 +198,4 @@
Grid.ColumnSpan="2"
Fill="Transparent" />
</Grid>
</UserControl>
</local:UserControlRx>

View File

@ -1,6 +1,8 @@
using System;
using ReactiveUI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
@ -18,7 +20,7 @@ namespace Wabbajack
/// <summary>
/// Interaction logic for DetailImageView.xaml
/// </summary>
public partial class DetailImageView : UserControl
public partial class DetailImageView : UserControlRx
{
public ImageSource Image
{
@ -42,7 +44,7 @@ namespace Wabbajack
set => SetValue(TitleProperty, value);
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(DetailImageView),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, WireNotifyPropertyChanged));
public string Author
{
@ -50,7 +52,7 @@ namespace Wabbajack
set => SetValue(AuthorProperty, value);
}
public static readonly DependencyProperty AuthorProperty = DependencyProperty.Register(nameof(Author), typeof(string), typeof(DetailImageView),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, WireNotifyPropertyChanged));
public string Description
{
@ -58,11 +60,32 @@ namespace Wabbajack
set => SetValue(DescriptionProperty, value);
}
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(nameof(Description), typeof(string), typeof(DetailImageView),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, WireNotifyPropertyChanged));
private readonly ObservableAsPropertyHelper<bool> _ShowAuthor;
public bool ShowAuthor => _ShowAuthor.Value;
private readonly ObservableAsPropertyHelper<bool> _ShowDescription;
public bool ShowDescription => _ShowDescription.Value;
private readonly ObservableAsPropertyHelper<bool> _ShowTitle;
public bool ShowTitle => _ShowTitle.Value;
public DetailImageView()
{
InitializeComponent();
this._ShowAuthor = this.WhenAny(x => x.Author)
.Select(x => !string.IsNullOrWhiteSpace(x))
.ToProperty(this, nameof(this.ShowAuthor));
this._ShowDescription = this.WhenAny(x => x.Description)
.Select(x => !string.IsNullOrWhiteSpace(x))
.ToProperty(this, nameof(this.ShowDescription));
this._ShowTitle = this.WhenAny(x => x.Title)
.Select(x => !string.IsNullOrWhiteSpace(x))
.ToProperty(this, nameof(this.ShowTitle));
}
}
}

View File

@ -10,36 +10,90 @@
d:DesignWidth="400"
BorderBrush="{StaticResource DarkBackgroundBrush}"
mc:Ignorable="d">
<Grid>
<Grid ClipToBounds="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="36" />
</Grid.ColumnDefinitions>
<Border
x:Name="BackgroundCornerFillIn"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0,0,5,0"
Background="{StaticResource TextBoxBackground}"
CornerRadius="3" />
<TextBox
Grid.Column="0"
Margin="0,0,-4,0"
Margin="5,1,-2,1"
VerticalContentAlignment="Center"
Background="{StaticResource DarkBackgroundBrush}"
Text="{Binding TargetPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ShowTextBoxInput}" />
<icon:PackIconMaterial
Margin="0,4,4,4"
Padding="0,3"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{StaticResource WarningBrush}"
Kind="Circle"
ToolTip="{Binding ErrorTooltip}"
Visibility="{Binding InError, Converter={StaticResource bool2VisibilityConverter}}" />
<Button
Grid.Column="1"
Command="{Binding SetTargetPathCommand}"
ToolTip="Set target path">
<icon:PackIconMaterial
Width="16"
Height="12"
Margin="4"
Kind="DotsHorizontal" />
</Button>
<Grid Grid.Column="1" HorizontalAlignment="Right">
<Border
Margin="3,1,0,1"
HorizontalAlignment="Right"
Background="{StaticResource WarningBrush}"
CornerRadius="3"
ToolTip="{Binding ErrorTooltip}">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Width" Value="25" />
<Style.Triggers>
<DataTrigger Binding="{Binding InError}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Width"
To="33"
Duration="0:0:0.1">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Width"
To="25"
Duration="0:0:0.1">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<Border
Width="30"
HorizontalAlignment="Left"
Background="{StaticResource TextBoxBackground}"
CornerRadius="3">
<Button Command="{Binding SetTargetPathCommand}" ToolTip="Set target path">
<icon:PackIconMaterial
Width="16"
Height="12"
Margin="4"
Kind="DotsHorizontal" />
</Button>
<Border.Effect>
<DropShadowEffect
BlurRadius="3"
Direction="0"
Opacity="0.5"
ShadowDepth="2" />
</Border.Effect>
</Border>
</Grid>
</Grid>
</UserControl>

View File

@ -85,7 +85,10 @@
<Setter Property="FontSize" Value="15" />
<Setter Property="Margin" Value="0,0,0,6" />
</Style>
<Style x:Key="PickerStyle" TargetType="local:FilePicker">
<Style
x:Key="PickerStyle"
BasedOn="{StaticResource MainFilePickerStyle}"
TargetType="local:FilePicker">
<Setter Property="Margin" Value="0,0,0,6" />
</Style>
</StackPanel.Resources>

View File

@ -46,7 +46,7 @@
ToolTip="The game you wish to target">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
<TextBlock Margin="6,2" Text="{Binding DisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>