Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 71 additions & 54 deletions StabilityMatrix.Avalonia/Views/Dialogs/LaunchOptionsDialog.axaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
<controls:UserControlBase xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dialogs="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Dialogs"
xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core"
xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
d:DataContext="{x:Static mocks:DesignData.LaunchOptionsViewModel}"
x:DataType="dialogs:LaunchOptionsViewModel"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="650"
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.LaunchOptionsDialog">
<controls:UserControlBase
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.LaunchOptionsDialog"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dialogs="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Dialogs"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core"
xmlns:ui="using:FluentAvalonia.UI.Controls"
d:DataContext="{x:Static mocks:DesignData.LaunchOptionsViewModel}"
d:DesignHeight="650"
d:DesignWidth="500"
x:DataType="dialogs:LaunchOptionsViewModel"
mc:Ignorable="d">
<controls:UserControlBase.Resources>

<converters:LaunchOptionConverter x:Key="LaunchOptionConverter" />
Expand All @@ -21,64 +24,67 @@
<converters:LaunchOptionConverter />
<converters:LaunchOptionIntDoubleConverter />
</converters:ValueConverterGroup>

</controls:UserControlBase.Resources>

<Grid MinWidth="400" RowDefinitions="Auto,*" Margin="8">

<Grid
MinWidth="400"
Margin="8"
RowDefinitions="Auto,*">
<StackPanel
HorizontalAlignment="Stretch"
Spacing="4"
Margin="0,0,0,16"
Orientation="Vertical">
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="4">
<!-- Title -->
<TextBlock
Margin="16"
FontSize="24"
FontWeight="Bold"
Margin="16"
Text="{Binding Title}"
TextWrapping="Wrap" />
<!-- Search box -->
<TextBox
HorizontalAlignment="Stretch"
x:Name="SearchBox"
Margin="8,0"
Watermark="{x:Static lang:Resources.Label_SearchEllipsis}"
Text="{Binding SearchText, Mode=TwoWay}"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
IsVisible="{Binding IsSearchBoxEnabled}"
x:Name="SearchBox">
Text="{Binding SearchText, Mode=TwoWay}"
Watermark="{x:Static lang:Resources.Label_SearchEllipsis}">
<TextBox.InnerRightContent>
<ui:SymbolIcon Symbol="Find" Margin="0,0,8,0" />
<ui:SymbolIcon Margin="0,0,8,0" Symbol="Find" />
</TextBox.InnerRightContent>
</TextBox>
</StackPanel>

<!-- Option Cards -->
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ItemsControl
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Padding="8"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding FilteredCards}">

<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

<ItemsControl.DataTemplates>
<controls:LaunchOptionCardTemplateSelector>
<!-- Int type card (textboxes) -->
<DataTemplate x:DataType="models:LaunchOptionCard" x:Key="{x:Static models:LaunchOptionType.Int}">
<DataTemplate x:Key="{x:Static models:LaunchOptionType.Int}" x:DataType="models:LaunchOptionCard">
<controls:Card>
<StackPanel
HorizontalAlignment="Stretch"
Margin="8,0,8,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<TextBlock
Margin="0,8"
FontSize="16"
FontWeight="Bold"
Margin="0,8"
Text="{Binding Title}"
TextWrapping="Wrap" />
<ItemsControl ItemsSource="{Binding Options}">
Expand All @@ -87,73 +93,84 @@
<StackPanel HorizontalAlignment="Stretch" Orientation="Vertical">
<Label Content="{Binding Name}" />
<ui:NumberBox
HorizontalAlignment="Stretch"
Margin="8"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
PlaceholderText="{Binding DefaultValue, Mode=OneWay, Converter={StaticResource LaunchOptionConverter}}"
SpinButtonPlacementMode="Compact"
ValidationMode="Disabled"
Value="{Binding OptionValue, Converter={StaticResource LaunchOptionIntDoubleConverter}, Mode=TwoWay}"
VerticalAlignment="Stretch" />
Value="{Binding OptionValue, Converter={StaticResource LaunchOptionIntDoubleConverter}, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</controls:Card>
</DataTemplate>

<!-- String type card (textboxes) -->
<DataTemplate DataType="{x:Type models:LaunchOptionCard}" x:Key="{x:Static models:LaunchOptionType.String}">
<DataTemplate x:Key="{x:Static models:LaunchOptionType.String}" DataType="{x:Type models:LaunchOptionCard}">
<controls:Card Margin="0,8">
<StackPanel
HorizontalAlignment="Stretch"
Margin="8,0,8,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<TextBlock
Margin="0,8"
FontSize="16"
FontWeight="Bold"
Margin="0,8"
Text="{Binding Title}"
TextWrapping="Wrap" />
<ItemsControl ItemsSource="{Binding Options}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch" Orientation="Vertical">
<Label Content="{Binding Name}" />
<!--PlaceholderEnabled="{Binding HasDefaultValue}"-->
<!-- PlaceholderEnabled="{Binding HasDefaultValue}" -->
<TextBox
HorizontalAlignment="Stretch"
Margin="8"
Watermark="{Binding DefaultValue}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Text="{Binding OptionValue, Converter={StaticResource LaunchOptionConverter}}"
VerticalAlignment="Stretch" />
Watermark="{Binding DefaultValue}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</controls:Card>
</DataTemplate>

<!-- Bool type card (checkboxes) -->
<DataTemplate DataType="{x:Type models:LaunchOptionCard}" x:Key="{x:Static models:LaunchOptionType.Bool}">
<DataTemplate x:Key="{x:Static models:LaunchOptionType.Bool}" DataType="{x:Type models:LaunchOptionCard}">
<controls:Card Margin="0,8">
<StackPanel
HorizontalAlignment="Left"
Margin="8,0,8,0"
HorizontalAlignment="Left"
Orientation="Vertical">
<TextBlock
Margin="0,8"
FontSize="16"
FontWeight="Bold"
Margin="0,8"
Text="{Binding Title}"
TextWrapping="Wrap" />
<StackPanel Orientation="Horizontal">
<StackPanel IsVisible="{Binding !IsSingleSelect}" Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Options}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding OptionValue, Converter={StaticResource LaunchOptionConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!-- Mutually exclusive options are rendered as radio buttons -->
<StackPanel IsVisible="{Binding IsSingleSelect}" Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Options}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox
Content="{Binding Name}"
<RadioButton
Content="{Binding Name}"
GroupName="{Binding GroupName}"
IsChecked="{Binding OptionValue, Converter={StaticResource LaunchOptionConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
Expand All @@ -168,5 +185,5 @@
</ScrollViewer>

</Grid>

</controls:UserControlBase>
31 changes: 19 additions & 12 deletions StabilityMatrix.Core/Models/LaunchOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ public class LaunchOption
public required string Name { get; init; }

public LaunchOptionType Type { get; init; } = LaunchOptionType.Bool;

[JsonIgnore]
public object? DefaultValue { get; init; }

[JsonIgnore]
public bool HasDefaultValue => DefaultValue != null;

/// <summary>
/// When set, this option is exclusive with other options sharing the same GroupName
/// (rendered as radio buttons instead of checkboxes).
/// </summary>
[JsonIgnore]
public string? GroupName { get; init; }

[JsonConverter(typeof(LaunchOptionValueJsonConverter))]
public object? OptionValue { get; set; }

Expand All @@ -29,11 +36,9 @@ public bool IsEmptyOrDefault()
return Type switch
{
LaunchOptionType.Bool => OptionValue == null,
LaunchOptionType.Int => OptionValue == null ||
(int?) OptionValue == (int?) DefaultValue,
LaunchOptionType.String => OptionValue == null ||
(string?) OptionValue == (string?) DefaultValue,
_ => throw new ArgumentOutOfRangeException()
LaunchOptionType.Int => OptionValue == null || (int?)OptionValue == (int?)DefaultValue,
LaunchOptionType.String => OptionValue == null || (string?)OptionValue == (string?)DefaultValue,
_ => throw new ArgumentOutOfRangeException(),
};
}

Expand All @@ -48,7 +53,7 @@ public bool IsEmptyOrDefault()
LaunchOptionType.Bool => bool.TryParse(value, out var boolValue) ? boolValue : null,
LaunchOptionType.Int => int.TryParse(value, out var intValue) ? intValue : null,
LaunchOptionType.String => value,
_ => throw new ArgumentException($"Unknown option type {type}")
_ => throw new ArgumentException($"Unknown option type {type}"),
};
}

Expand All @@ -63,17 +68,19 @@ public bool IsEmptyOrDefault()
switch (Type)
{
case LaunchOptionType.Bool:
return (bool?) OptionValue == true ? Name : null;
return (bool?)OptionValue == true ? Name : null;
case LaunchOptionType.Int:
return (int?) OptionValue != null ? $"{Name} {OptionValue}" : null;
return (int?)OptionValue != null ? $"{Name} {OptionValue}" : null;
case LaunchOptionType.String:
var valueString = (string?) OptionValue;
var valueString = (string?)OptionValue;
// Special case empty string name to not do quoting (for custom launch args)
if (Name == "")
{
return valueString;
}
return string.IsNullOrWhiteSpace(valueString) ? null : $"{Name} {ProcessRunner.Quote(valueString)}";
return string.IsNullOrWhiteSpace(valueString)
? null
: $"{Name} {ProcessRunner.Quote(valueString)}";
default:
throw new ArgumentOutOfRangeException();
}
Expand Down
Loading
Loading