diff --git a/Build Tools/build.sh b/Build Tools/build.sh new file mode 100755 index 00000000..3836d21c --- /dev/null +++ b/Build Tools/build.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail +# Simple cross-platform build script for Avalonia UI and CLI +# Usage: ./build.sh + +if [[ $# -lt 1 ]]; then + echo "Usage: $0 " + exit 1 +fi +OUTDIR="$1" +SOLUTION_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$SOLUTION_DIR" + +# Build UI (Avalonia) self-contained single-file (no trimming for compatibility) +publish_ui(){ + local rid="$1" + local dest="$OUTDIR/UI/$rid" + dotnet publish MinishCapRandomizerUI.Avalonia/MinishCapRandomizerUI.Avalonia.csproj -c Release -r "$rid" \ + -p:PublishSingleFile=true -p:SelfContained=true \ + -p:EnableCompressionInSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o "$dest" +} + +# Build CLI +publish_cli(){ + local rid="$1" + local dest="$OUTDIR/CLI/$rid" + dotnet publish MinishCapRandomizerCLI/MinishCapRandomizerCLI.csproj -c Release -r "$rid" \ + -p:PublishSingleFile=true -p:SelfContained=true -o "$dest" +} + +for rid in linux-x64 linux-arm64 win-x64 win-arm64; do + publish_ui "$rid" + publish_cli "$rid" +done + +echo "Builds published to $OUTDIR" diff --git a/MinishCapRandomizerCLI/MinishCapRandomizerCLI.csproj b/MinishCapRandomizerCLI/MinishCapRandomizerCLI.csproj index 14bf05da..a8b1cf06 100644 --- a/MinishCapRandomizerCLI/MinishCapRandomizerCLI.csproj +++ b/MinishCapRandomizerCLI/MinishCapRandomizerCLI.csproj @@ -35,15 +35,18 @@ + - - Always - - - - - - + + Patches\%(RecursiveDir)%(Filename)%(Extension) + PreserveNewest + PreserveNewest + + + Language Raws\%(RecursiveDir)%(Filename)%(Extension) + PreserveNewest + PreserveNewest + @@ -52,24 +55,12 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/MinishCapRandomizerUI.Avalonia/App.axaml b/MinishCapRandomizerUI.Avalonia/App.axaml new file mode 100644 index 00000000..8ba80589 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/App.axaml @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MinishCapRandomizerUI.Avalonia/App.axaml.cs b/MinishCapRandomizerUI.Avalonia/App.axaml.cs new file mode 100644 index 00000000..4907f039 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/App.axaml.cs @@ -0,0 +1,35 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; + +namespace MinishCapRandomizerUI.Avalonia; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } + + public void ApplyThemePreference(string preference) + { + RequestedThemeVariant = preference switch + { + "Light" => ThemeVariant.Light, + "Dark" => ThemeVariant.Dark, + _ => ThemeVariant.Default // System + }; + } +} + diff --git a/MinishCapRandomizerUI/DrawConstants/Constants.cs b/MinishCapRandomizerUI.Avalonia/DrawConstants/Constants.cs similarity index 56% rename from MinishCapRandomizerUI/DrawConstants/Constants.cs rename to MinishCapRandomizerUI.Avalonia/DrawConstants/Constants.cs index b194777a..42dfd1c4 100644 --- a/MinishCapRandomizerUI/DrawConstants/Constants.cs +++ b/MinishCapRandomizerUI.Avalonia/DrawConstants/Constants.cs @@ -1,35 +1,32 @@ -namespace MinishCapRandomizerUI.DrawConstants; +using Avalonia.Media; +using Avalonia; + +namespace MinishCapRandomizerUI.Avalonia.DrawConstants; public static class Constants { public static int TopRowAboveSpacing => 15; public static int FirstElementInRowX => 10; public static int WidthMargin => 10; - public static int CategorySpacing => 20; - public static int CategoryLabelAlignX => 17; - public static int CategoryLabelAlignY => -8; - public static int CategoryWidth => 760; - public static BorderStyle CategoryBorderStyle => BorderStyle.FixedSingle; - public static Color DefaultBackgroundColor => Color.White; - public static Color DefaultButtonBackgroundColor => Color.Transparent; //Never heard of this color but it matches on the UI + public static Thickness CategoryBorderThickness => new(1); + public static IBrush CategoryBorderBrush => Brushes.Gray; + public static IBrush DefaultBackgroundBrush => Brushes.White; + public static IBrush DefaultButtonBackgroundBrush => Brushes.Transparent; public static bool CategoryLabelsUseAutosize => true; - public static bool LabelsAndCheckboxesUseAutoEllipsis => true; public static int DefaultStartingPaneX => 6; - public static int DefaultStartingPaneY => 15; + public static double SpecialScaling = 1; - public static double SpecialScaling = 1; //Some controls use this as a scaling factor, set during UI building based on DPI + public static bool UseMnemonic => false; - public static bool UseMnemonic => false; //This is used so that way escape characters like & are used literally - public const int TotalColorPickersPerRow = 1; public const int TotalNumberBoxesPerRow = 2; public const int TotalDropdownsPerRow = 2; @@ -37,5 +34,5 @@ public static class Constants public const int TooltipInitialShowDelayMs = 400; public const int TooltipRepeatDelayMs = 400; - public const int TooltipDisplayLengthMs = 30000; //This is a hard limit of WinForms, sucks but oh well + public const int TooltipDisplayLengthMs = 30000; } diff --git a/MinishCapRandomizerUI.Avalonia/Elements/ColorPickerWrapper.cs b/MinishCapRandomizerUI.Avalonia/Elements/ColorPickerWrapper.cs new file mode 100644 index 00000000..de0ad112 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/Elements/ColorPickerWrapper.cs @@ -0,0 +1,367 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Layout; +using RandomizerCore.Randomizer.Logic.Options; +using MinishCapRandomizerUI.Avalonia.DrawConstants; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace MinishCapRandomizerUI.Avalonia.Elements; + +public class ColorPickerWrapper : WrapperBase, ILogicOptionObserver +{ + private const int NameTextWidth = 125; + private const int CheckboxWidth = 125; + private const int PreviewTextWidth = 55; + private const int ButtonWidth = 100; + private const int PictureBoxWidth = 60; + private const int Height = 23; + private const string CheckboxText = "Use Random Color"; + private const string SelectColorText = "Select Color"; + private const string SelectRandomColorText = "Pick Random"; + private const string UseDefaultColorText = "Use Default"; + private const string ColorPreviewText = "Preview:"; + + private static readonly int ElementWidthInternal = CheckboxWidth + NameTextWidth + PreviewTextWidth + PictureBoxWidth + 3 * ButtonWidth + 7 * Constants.WidthMargin; + + private readonly LogicColorPicker _picker; + + private TextBlock? _nameLabel; + private CheckBox? _useRandomCheckbox; + private Button? _selectColorButton; + private Button? _selectRandomColorButton; + private Button? _useDefaultColorButton; + private TextBlock? _previewLabel; + private Border? _colorPreview; + + public ColorPickerWrapper(LogicColorPicker picker) : base(ElementWidthInternal, Height, picker.SettingGroup, picker.SettingPage) + { + _picker = picker; + _picker.RegisterObserver(this); + } + + public override IList BuildControls(int initialX, int initialY) + { + if (_nameLabel != null && _useRandomCheckbox != null && _selectColorButton != null && _selectRandomColorButton != null && + _useDefaultColorButton != null && _previewLabel != null && _colorPreview != null) + { + return new List{ _nameLabel, _useRandomCheckbox, _selectColorButton, _selectRandomColorButton, _useDefaultColorButton, _previewLabel, _colorPreview }; + } + + _nameLabel = new TextBlock + { + Text = _picker.NiceName + ":", + VerticalAlignment = VerticalAlignment.Center, + Width = NameTextWidth + }; + ToolTip.SetTip(_nameLabel, _picker.DescriptionText); + + _useRandomCheckbox = new CheckBox + { + Content = CheckboxText, + IsChecked = _picker.UseRandomColor, + Width = CheckboxWidth, + VerticalAlignment = VerticalAlignment.Center + }; + ToolTip.SetTip(_useRandomCheckbox, "If enabled, a random color will be selected on seed generation"); + _useRandomCheckbox.IsCheckedChanged += (_, __) => + { + _picker.UseRandomColor = _useRandomCheckbox.IsChecked == true; + UpdatePreviewColor(); + UpdateButtonEnablement(); + _picker.NotifyChildren(); + }; + + _selectColorButton = new Button + { + Content = SelectColorText, + Width = ButtonWidth + }; + ToolTip.SetTip(_selectColorButton, "Opens the system color picker dialog"); + _selectColorButton.Click += async (_, __) => await OpenSystemColorDialogAsync(); + + _selectRandomColorButton = new Button + { + Content = SelectRandomColorText, + Width = ButtonWidth + }; + ToolTip.SetTip(_selectRandomColorButton, "A random color is selected now and shown in the preview"); + _selectRandomColorButton.Click += (_, __) => + { + _picker.PickRandomColor(); + UpdatePreviewColor(); + _picker.NotifyChildren(); + }; + + _useDefaultColorButton = new Button + { + Content = UseDefaultColorText, + Width = ButtonWidth + }; + ToolTip.SetTip(_useDefaultColorButton, "Resets the selected color back to its default"); + _useDefaultColorButton.Click += (_, __) => + { + _picker.DefinedColor = _picker.BaseColor; + UpdatePreviewColor(); + _picker.NotifyChildren(); + }; + + _previewLabel = new TextBlock + { + Text = ColorPreviewText, + VerticalAlignment = VerticalAlignment.Center, + Width = PreviewTextWidth + }; + + _colorPreview = new Border + { + Width = PictureBoxWidth, + Height = Height - 4, + BorderBrush = Brushes.Gray, + BorderThickness = new Thickness(1) + }; + + UpdatePreviewColor(); + UpdateButtonEnablement(); + + return new List{ _nameLabel, _useRandomCheckbox, _selectColorButton, _selectRandomColorButton, _useDefaultColorButton, _previewLabel, _colorPreview }; + } + + private async Task OpenSystemColorDialogAsync() + { + if (_useRandomCheckbox?.IsChecked == true) return; + + var c = _picker.DefinedColor; + System.Drawing.Color? selected = null; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + selected = await Task.Run(() => ShowWindowsColorDialog(c)); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + selected = await ShowMacColorDialogAsync(c); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + selected = await ShowLinuxColorDialogAsync(c); + } + + if (selected.HasValue) + { + _picker.DefinedColor = System.Drawing.Color.FromArgb(255, selected.Value.R, selected.Value.G, selected.Value.B); + _picker.UseRandomColor = false; + if (_useRandomCheckbox != null) _useRandomCheckbox.IsChecked = false; + UpdatePreviewColor(); + _picker.NotifyChildren(); + } + } + + private static System.Drawing.Color? ShowWindowsColorDialog(System.Drawing.Color initial) + { + try + { + const int CC_RGBINIT = 0x00000001; + const int CC_FULLOPEN = 0x00000002; + + var customColors = Marshal.AllocHGlobal(sizeof(int) * 16); + try + { + var cc = new CHOOSECOLOR(); + cc.lStructSize = Marshal.SizeOf(); + cc.hwndOwner = IntPtr.Zero; + cc.rgbResult = (initial.B << 16) | (initial.G << 8) | initial.R; + cc.lpCustColors = customColors; + cc.Flags = CC_RGBINIT | CC_FULLOPEN; + + if (ChooseColor(ref cc)) + { + int rgb = cc.rgbResult; + var r = (byte)(rgb & 0xFF); + var g = (byte)((rgb >> 8) & 0xFF); + var b = (byte)((rgb >> 16) & 0xFF); + return System.Drawing.Color.FromArgb(255, r, g, b); + } + } + finally + { + Marshal.FreeHGlobal(customColors); + } + } + catch + { + } + return null; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct CHOOSECOLOR + { + public int lStructSize; + public IntPtr hwndOwner; + public IntPtr hInstance; + public int rgbResult; + public IntPtr lpCustColors; + public int Flags; + public IntPtr lCustData; + public IntPtr lpfnHook; + public string? lpTemplateName; + } + + [DllImport("comdlg32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool ChooseColor(ref CHOOSECOLOR cc); + + private static async Task ShowMacColorDialogAsync(System.Drawing.Color initial) + { + try + { + int ri = initial.R * 257, gi = initial.G * 257, bi = initial.B * 257; + var psi = new ProcessStartInfo + { + FileName = "osascript", + ArgumentList = { "-e", $"choose color default color {{{ri},{gi},{bi}}}" }, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + using var p = Process.Start(psi); + if (p == null) return null; + string output = await p.StandardOutput.ReadToEndAsync(); + await p.WaitForExitAsync(); + if (p.ExitCode != 0) return null; + var parts = output.Trim().Split(','); + if (parts.Length >= 3 && + int.TryParse(parts[0].Trim(), out var r16) && + int.TryParse(parts[1].Trim(), out var g16) && + int.TryParse(parts[2].Trim(), out var b16)) + { + byte r = (byte)Math.Clamp(r16 / 257, 0, 255); + byte g = (byte)Math.Clamp(g16 / 257, 0, 255); + byte b = (byte)Math.Clamp(b16 / 257, 0, 255); + return System.Drawing.Color.FromArgb(255, r, g, b); + } + } + catch + { + } + return null; + } + + private static async Task ShowLinuxColorDialogAsync(System.Drawing.Color initial) + { + var initHex = $"#{initial.R:X2}{initial.G:X2}{initial.B:X2}"; + var result = await RunProcessAndCaptureAsync("zenity", new[] { "--color-selection", "--show-palette", "--color", initHex }); + if (result.success) + { + var text = result.output.Trim(); + if (text.StartsWith("#")) + { + if (TryParseHexColor(text, out var c)) return c; + } + else if (text.StartsWith("rgb", StringComparison.OrdinalIgnoreCase)) + { + var inner = text.Trim().TrimStart('r','g','b','(').TrimEnd(')'); + var parts = inner.Split(','); + if (parts.Length >= 3 && + int.TryParse(parts[0], out var r) && + int.TryParse(parts[1], out var g) && + int.TryParse(parts[2], out var b)) + { + return System.Drawing.Color.FromArgb(255, ClampByte(r), ClampByte(g), ClampByte(b)); + } + } + } + result = await RunProcessAndCaptureAsync("kdialog", new[] { "--getcolor", initHex }); + if (result.success) + { + var text = result.output.Trim(); + if (TryParseHexColor(text, out var c)) return c; + } + return null; + } + + private static byte ClampByte(int v) => (byte)Math.Clamp(v, 0, 255); + + private static bool TryParseHexColor(string hex, out System.Drawing.Color color) + { + color = System.Drawing.Color.Empty; + if (string.IsNullOrWhiteSpace(hex)) return false; + var t = hex.Trim(); + if (t.StartsWith("#")) t = t[1..]; + if (t.Length == 6 && + byte.TryParse(t.Substring(0, 2), System.Globalization.NumberStyles.HexNumber, null, out var r) && + byte.TryParse(t.Substring(2, 2), System.Globalization.NumberStyles.HexNumber, null, out var g) && + byte.TryParse(t.Substring(4, 2), System.Globalization.NumberStyles.HexNumber, null, out var b)) + { + color = System.Drawing.Color.FromArgb(255, r, g, b); + return true; + } + return false; + } + + private static async Task<(bool success, string output)> RunProcessAndCaptureAsync(string fileName, string[] args) + { + try + { + var psi = new ProcessStartInfo + { + FileName = fileName, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + foreach (var a in args) psi.ArgumentList.Add(a); + using var p = Process.Start(psi); + if (p == null) return (false, string.Empty); + string output = await p.StandardOutput.ReadToEndAsync(); + await p.WaitForExitAsync(); + return (p.ExitCode == 0, output); + } + catch + { + return (false, string.Empty); + } + } + + private static Queue? _recent; + private void EnqueueRecent(Color c) + { + _recent ??= new Queue(); + if (_recent.Count >= 10) _recent.Dequeue(); + _recent.Enqueue(c); + } + + private void UpdatePreviewColor() + { + if (_colorPreview == null) return; + var c = _picker.DefinedColor; + if (_useRandomCheckbox?.IsChecked == true) + { + _colorPreview.Background = new SolidColorBrush(Colors.Transparent); + } + else + { + _colorPreview.Background = new SolidColorBrush(Color.FromRgb(c.R, c.G, c.B)); + } + } + + private void UpdateButtonEnablement() + { + var disabled = _useRandomCheckbox?.IsChecked == true; + if (_selectColorButton != null) _selectColorButton.IsEnabled = !disabled; + if (_selectRandomColorButton != null) _selectRandomColorButton.IsEnabled = !disabled; + if (_useDefaultColorButton != null) _useDefaultColorButton.IsEnabled = !disabled; + } + + public void NotifyObserver() + { + if (_useRandomCheckbox != null) + _useRandomCheckbox.IsChecked = _picker.UseRandomColor; + UpdateButtonEnablement(); + UpdatePreviewColor(); + } +} diff --git a/MinishCapRandomizerUI.Avalonia/Elements/DropdownWrapper.cs b/MinishCapRandomizerUI.Avalonia/Elements/DropdownWrapper.cs new file mode 100644 index 00000000..6e35b159 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/Elements/DropdownWrapper.cs @@ -0,0 +1,73 @@ +using System.Linq; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using MinishCapRandomizerUI.Avalonia.DrawConstants; +using RandomizerCore.Randomizer.Logic.Options; + +namespace MinishCapRandomizerUI.Avalonia.Elements; + +public class DropdownWrapper : WrapperBase, ILogicOptionObserver +{ + private const int TextWidth = 160; + private const int DropdownWidth = 0; + private const int DropdownHeight = 23; + private static readonly int ElementWidthInternal = TextWidth + 160 + Constants.WidthMargin; + private const int ElementHeightInternal = DropdownHeight; + + private TextBlock? _label; + private ComboBox? _comboBox; + private readonly LogicDropdown _dropdown; + + public DropdownWrapper(LogicDropdown dropdown) : base(ElementWidthInternal, ElementHeightInternal, dropdown.SettingGroup, dropdown.SettingPage) + { + _dropdown = dropdown; + _dropdown.RegisterObserver(this); + } + + public override IList BuildControls(int initialX, int initialY) + { + if (_label != null && _comboBox != null) + return new List { _label, _comboBox }; + + _label = new TextBlock { + Text = _dropdown.NiceName + ":", + VerticalAlignment = VerticalAlignment.Center, + TextWrapping = TextWrapping.Wrap, + FontSize = 11 + }; + if (!string.IsNullOrWhiteSpace(_dropdown.DescriptionText)) + ToolTip.SetTip(_label, _dropdown.DescriptionText); + + _comboBox = new ComboBox{ + MinWidth = 160, + Width = 160, + HorizontalAlignment = HorizontalAlignment.Left, + FontSize = 11 + }; + var items = _dropdown.SelectionOptionNames.ToList(); + _comboBox.ItemsSource = items; + var selectedName = _dropdown.OptionsToNames[_dropdown.Selection]; + _comboBox.SelectedItem = selectedName; + if (_comboBox.SelectedItem == null && items.Count > 0) + _comboBox.SelectedIndex = 0; + if (!string.IsNullOrWhiteSpace(_dropdown.DescriptionText)) + ToolTip.SetTip(_comboBox, _dropdown.DescriptionText); + _comboBox.SelectionChanged += (_, __) => + { + if (_comboBox.SelectedItem is string s) + { + _dropdown.Selection = _dropdown.NamesToOptions[s]; + _dropdown.NotifyChildren(); + } + }; + return new List{ _label, _comboBox }; + } + + public void NotifyObserver() + { + if (_comboBox == null) return; + var selectionName = _dropdown.OptionsToNames[_dropdown.Selection]; + _comboBox.SelectedItem = selectionName; + } +} diff --git a/MinishCapRandomizerUI.Avalonia/Elements/FlagWrapper.cs b/MinishCapRandomizerUI.Avalonia/Elements/FlagWrapper.cs new file mode 100644 index 00000000..d1bdb97a --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/Elements/FlagWrapper.cs @@ -0,0 +1,39 @@ +using Avalonia.Controls; +using Avalonia.Media; +using MinishCapRandomizerUI.Avalonia.DrawConstants; +using MinishCapRandomizerUI.Avalonia.Elements; +using RandomizerCore.Randomizer.Logic.Options; + +namespace MinishCapRandomizerUI.Avalonia.Elements; + +public class FlagWrapper : WrapperBase, ILogicOptionObserver +{ + private const int WidthInternal = 200; + private const int HeightInternal = 18; + private CheckBox? _checkBox; + private readonly LogicFlag _flag; + + public FlagWrapper(LogicFlag flag) : base(WidthInternal, HeightInternal, flag.SettingGroup, flag.SettingPage) + { + _flag = flag; + _flag.RegisterObserver(this); + } + + public bool IsFigurineHuntFlag => _flag.Name == "FIGURINE_HUNT" || _flag.NiceName == "Figurine Hunt"; + + public override IList BuildControls(int initialX, int initialY) + { + if (_checkBox != null) return new List{ _checkBox }; + var label = new TextBlock{ Text = _flag.NiceName, TextWrapping = TextWrapping.Wrap, MaxWidth = 220 }; + _checkBox = new CheckBox { Content = label, IsChecked = _flag.Active }; + if (!string.IsNullOrWhiteSpace(_flag.DescriptionText)) + ToolTip.SetTip(_checkBox, _flag.DescriptionText); + _checkBox.IsCheckedChanged += (_, __) => { _flag.Active = _checkBox.IsChecked == true; _flag.NotifyChildren(); }; + return new List{ _checkBox }; + } + + public void NotifyObserver() + { + if (_checkBox != null) _checkBox.IsChecked = _flag.Active; + } +} diff --git a/MinishCapRandomizerUI.Avalonia/Elements/NumberBoxWrapper.cs b/MinishCapRandomizerUI.Avalonia/Elements/NumberBoxWrapper.cs new file mode 100644 index 00000000..ea15f302 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/Elements/NumberBoxWrapper.cs @@ -0,0 +1,67 @@ +using Avalonia.Controls; +using MinishCapRandomizerUI.Avalonia.Elements; +using MinishCapRandomizerUI.Avalonia.DrawConstants; +using RandomizerCore.Randomizer.Logic.Options; +using System; + +namespace MinishCapRandomizerUI.Avalonia.Elements; + +public class NumberBoxWrapper : WrapperBase, ILogicOptionObserver +{ + private const int TextWidth = 180; + private const int NumberBoxWidth = 90; + private TextBlock? _label; + private TextBox? _textBox; + private readonly LogicNumberBox _numberBox; + + public NumberBoxWrapper(LogicNumberBox numberBox) : base(TextWidth + NumberBoxWidth + Constants.WidthMargin, 20, numberBox.SettingGroup, numberBox.SettingPage) + { + _numberBox = numberBox; + _numberBox.RegisterObserver(this); + } + + public bool IsFigurineRelated => _numberBox.Name.StartsWith("FIGURINE", StringComparison.OrdinalIgnoreCase) + || _numberBox.NiceName.Contains("Figurine", StringComparison.OrdinalIgnoreCase); + + public override IList BuildControls(int initialX, int initialY) + { + if (_label != null && _textBox != null) return new List{ _label, _textBox }; + _label = new TextBlock{ Text = _numberBox.NiceName + ":", Width = TextWidth }; + if (!string.IsNullOrWhiteSpace(_numberBox.DescriptionText)) + ToolTip.SetTip(_label, _numberBox.DescriptionText); + _textBox = new TextBox{ Text = _numberBox.Value.ToString(), Width = NumberBoxWidth }; + if (!string.IsNullOrWhiteSpace(_numberBox.DescriptionText)) + ToolTip.SetTip(_textBox, _numberBox.DescriptionText); + _textBox.PropertyChanged += (_, e) => + { + if (e.Property == TextBox.TextProperty) + { + if (string.IsNullOrWhiteSpace(_textBox.Text)) + { + _numberBox.Value = _numberBox.DefaultValue; + _textBox.Text = _numberBox.DefaultValue.ToString(); + _numberBox.NotifyChildren(); + return; + } + if (byte.TryParse(_textBox.Text, out var val)) + { + if (val < _numberBox.MinValue) val = _numberBox.MinValue; + if (val > _numberBox.MaxValue) val = _numberBox.MaxValue; + _numberBox.Value = val; + _textBox.Text = _numberBox.Value.ToString(); + _numberBox.NotifyChildren(); + } + else + { + _textBox.Text = _numberBox.Value.ToString(); + } + } + }; + return new List{ _label, _textBox }; + } + + public void NotifyObserver() + { + if (_textBox != null) _textBox.Text = _numberBox.Value.ToString(); + } +} diff --git a/MinishCapRandomizerUI.Avalonia/Elements/WrappedLogicOptionFactory.cs b/MinishCapRandomizerUI.Avalonia/Elements/WrappedLogicOptionFactory.cs new file mode 100644 index 00000000..43ca0641 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/Elements/WrappedLogicOptionFactory.cs @@ -0,0 +1,302 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Visuals; +using MinishCapRandomizerUI.Avalonia.DrawConstants; +using RandomizerCore.Randomizer.Logic.Options; + +namespace MinishCapRandomizerUI.Avalonia.Elements; + +public static class WrappedLogicOptionFactory +{ + private enum RowKind { Flags, Inputs, Color, Mixed } + + public static List BuildGenericWrappedLogicOptions(List logicOptions) + { + var wrappedOptions = new List(); + foreach (var option in logicOptions) + { + switch (option) + { + case LogicFlag flag: wrappedOptions.Add(new FlagWrapper(flag)); break; + case LogicDropdown dd: wrappedOptions.Add(new DropdownWrapper(dd)); break; + case LogicNumberBox nb: wrappedOptions.Add(new NumberBoxWrapper(nb)); break; + case LogicColorPicker cp: wrappedOptions.Add(new ColorPickerWrapper(cp)); break; + } + } + return wrappedOptions; + } + + public static List BuildGenericWrappedLogicOptions(IEnumerable logicOptions) + { + var wrappedOptions = new List(); + foreach (var option in logicOptions) + { + switch (option) + { + case LogicFlag flag: wrappedOptions.Add(new FlagWrapper(flag)); break; + case LogicDropdown dd: wrappedOptions.Add(new DropdownWrapper(dd)); break; + case LogicNumberBox nb: wrappedOptions.Add(new NumberBoxWrapper(nb)); break; + case LogicColorPicker cp: wrappedOptions.Add(new ColorPickerWrapper(cp)); break; + } + } + return wrappedOptions; + } + + public static Control BuildGroupContainer(string groupName, IEnumerable elements) + { + var elementsList = elements.ToList(); + bool isItemPoolTab = elementsList.Any() && elementsList.First().Page?.Contains("Item Pool", StringComparison.OrdinalIgnoreCase) == true; + + var reordered = elementsList.OrderBy(e => e switch { + DropdownWrapper => 0, + FlagWrapper => 1, + NumberBoxWrapper => 2, + ColorPickerWrapper => 3, + _ => 4 + }).ToList(); + + if (groupName.Contains("Figurine", StringComparison.OrdinalIgnoreCase)) + { + var figFlag = reordered.OfType().FirstOrDefault(f => f.IsFigurineHuntFlag); + if (figFlag != null) + { + reordered.Remove(figFlag); + var figInputs = reordered.Where(w => (w as NumberBoxWrapper)?.IsFigurineRelated == true).ToList(); + foreach (var fi in figInputs) reordered.Remove(fi); + reordered.Insert(0, figFlag); + var insertAt = 1; + foreach (var fi in figInputs) reordered.Insert(insertAt++, fi); + } + } + + + var rows = new List<(RowKind Kind, List Items)>(); + + foreach (var el in reordered) + { + var isFlag = el is FlagWrapper; + var isInput = el is DropdownWrapper || el is NumberBoxWrapper; + var isColor = el is ColorPickerWrapper; + + if (isColor) + { + rows.Add((RowKind.Color, new List{el})); + continue; + } + + if (isFlag) + { + var last = rows.LastOrDefault(); + if (last.Items != null && last.Kind == RowKind.Flags && last.Items.Count < 3) + { + last.Items.Add(el); + rows[^1] = last; + } + else + { + rows.Add((RowKind.Flags, new List{el})); + } + continue; + } + + if (isInput) + { + var last = rows.LastOrDefault(); + if (last.Items != null && last.Kind == RowKind.Inputs && last.Items.Count < 2) + { + last.Items.Add(el); + rows[^1] = last; + } + else + { + rows.Add((RowKind.Inputs, new List{el})); + } + continue; + } + + rows.Add((RowKind.Mixed, new List{el})); + } + + double measuredLabelWidth = 0.0; + foreach (var row in rows) + { + foreach (var item in row.Items) + { + var controls = item.BuildControls(0, 0); + var label = controls.OfType().FirstOrDefault(); + if (label != null) + { + label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + var w = label.DesiredSize.Width; + if (w > measuredLabelWidth) measuredLabelWidth = w; + } + } + } + + var preferredCap = isItemPoolTab ? 320.0 : 240.0; + measuredLabelWidth = Math.Min(measuredLabelWidth + 8.0, preferredCap); + if (measuredLabelWidth < 60.0) measuredLabelWidth = 60.0; + + var mainGrid = new Grid { HorizontalAlignment = HorizontalAlignment.Stretch }; + mainGrid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(measuredLabelWidth, GridUnitType.Pixel))); + mainGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto)); + mainGrid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(measuredLabelWidth, GridUnitType.Pixel))); + mainGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto)); + mainGrid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(1, GridUnitType.Star))); + + int gridRow = 0; + foreach (var row in rows) + { + mainGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto)); + + if (row.Kind == RowKind.Flags) + { + var flagsPanel = new Grid { HorizontalAlignment = HorizontalAlignment.Stretch }; + for (int i = 0; i < Math.Max(1, row.Items.Count); i++) flagsPanel.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Star)); + for (int i = 0; i < row.Items.Count; i++) + { + var item = row.Items[i]; + var ctrls = item.BuildControls(0, 0); + var panel = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 6, HorizontalAlignment = HorizontalAlignment.Left }; + foreach (var c in ctrls) panel.Children.Add(c); + Grid.SetColumn(panel, i); + flagsPanel.Children.Add(panel); + } + Grid.SetRow(flagsPanel, gridRow); + Grid.SetColumnSpan(flagsPanel, 5); + mainGrid.Children.Add(flagsPanel); + } + else if (row.Kind == RowKind.Inputs) + { + for (int i = 0; i < row.Items.Count && i < 2; i++) + { + var item = row.Items[i]; + var controls = item.BuildControls(0, 0); + var label = controls.OfType().FirstOrDefault(); + var control = controls.FirstOrDefault(c => !(c is TextBlock)); + + if (label != null) + { + label.FontSize = 11; + label.MinWidth = 60; + label.MaxWidth = measuredLabelWidth; + label.TextWrapping = TextWrapping.NoWrap; + label.TextTrimming = TextTrimming.CharacterEllipsis; + label.Margin = new Thickness(0, 0, 6, 0); + label.VerticalAlignment = VerticalAlignment.Center; + label.HorizontalAlignment = HorizontalAlignment.Right; + label.TextAlignment = TextAlignment.Right; + Grid.SetRow(label, gridRow); + Grid.SetColumn(label, i * 2); + mainGrid.Children.Add(label); + } + + if (control != null) + { + if (control is ComboBox cb) + { + cb.HorizontalAlignment = HorizontalAlignment.Left; + cb.Width = 160; + cb.FontSize = 11; + cb.Margin = new Thickness(0, 0, 6, 0); + } + else if (control is TextBox tb) + { + tb.HorizontalAlignment = HorizontalAlignment.Left; + tb.Width = 160; + tb.FontSize = 11; + tb.Margin = new Thickness(0, 0, 6, 0); + } + + Grid.SetRow(control, gridRow); + Grid.SetColumn(control, i * 2 + 1); + mainGrid.Children.Add(control); + } + } + } + else if (row.Kind == RowKind.Color) + { + var item = row.Items[0]; + var controls = item.BuildControls(0, 0); + + var panel = new StackPanel + { + Orientation = Orientation.Horizontal, + Spacing = 8, + HorizontalAlignment = HorizontalAlignment.Left + }; + + foreach (var control in controls) + { + if (control is TextBlock tb) + { + tb.FontSize = 11; + tb.VerticalAlignment = VerticalAlignment.Center; + } + panel.Children.Add(control); + } + + Grid.SetRow(panel, gridRow); + Grid.SetColumn(panel, 0); + Grid.SetColumnSpan(panel, 5); + mainGrid.Children.Add(panel); + } + else // Mixed + { + // Place items left-to-right into label/control pairs where possible, fall back to stacked panels + for (int i = 0; i < row.Items.Count && i < 2; i++) + { + var item = row.Items[i]; + var controls = item.BuildControls(0, 0); + var panel = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 6 }; + foreach (var c in controls) panel.Children.Add(c); + Grid.SetRow(panel, gridRow); + Grid.SetColumn(panel, i * 2); + Grid.SetColumnSpan(panel, 2); + mainGrid.Children.Add(panel); + } + } + + gridRow++; + } + + var headered = new HeaderedContentControl + { + Header = groupName, + Content = mainGrid, + Classes = { "compact-groupbox" } + }; + + headered.AttachedToVisualTree += (s, e) => + { + void UpdateLabelColumns() + { + try + { + double available = headered.Bounds.Width; + if (available <= 0) + { + available = preferredCap * 3; + } + if (available <= 0) available = preferredCap * 3; + + var target = Math.Min(measuredLabelWidth, Math.Min(preferredCap, available * 0.45)); + if (mainGrid.ColumnDefinitions.Count >= 3) + { + mainGrid.ColumnDefinitions[0].Width = new GridLength(target, GridUnitType.Pixel); + mainGrid.ColumnDefinitions[2].Width = new GridLength(target, GridUnitType.Pixel); + } + } + catch { } + } + + UpdateLabelColumns(); + headered.GetObservable(Control.BoundsProperty).Subscribe(_ => UpdateLabelColumns()); + }; + + return headered; + } +} diff --git a/MinishCapRandomizerUI/Elements/WrapperBase.cs b/MinishCapRandomizerUI.Avalonia/Elements/WrapperBase.cs similarity index 54% rename from MinishCapRandomizerUI/Elements/WrapperBase.cs rename to MinishCapRandomizerUI.Avalonia/Elements/WrapperBase.cs index 46f87967..e85afe5a 100644 --- a/MinishCapRandomizerUI/Elements/WrapperBase.cs +++ b/MinishCapRandomizerUI.Avalonia/Elements/WrapperBase.cs @@ -1,6 +1,6 @@ -using MinishCapRandomizerUI.DrawConstants; +using Avalonia.Controls; -namespace MinishCapRandomizerUI.Elements; +namespace MinishCapRandomizerUI.Avalonia.Elements; public abstract class WrapperBase { @@ -11,11 +11,12 @@ public abstract class WrapperBase protected WrapperBase(int elementWidth, int elementHeight, string settingGrouping, string page) { - ElementWidth = (int)(elementWidth*Constants.SpecialScaling); - ElementHeight = (int)(elementHeight*Constants.SpecialScaling); + ElementWidth = elementWidth; + ElementHeight = elementHeight; SettingGrouping = settingGrouping; Page = page; } - public abstract List GetControls(int initialX, int initialY); + public abstract IList BuildControls(int initialX, int initialY); } + diff --git a/MinishCapRandomizerUI.Avalonia/Github/Release.cs b/MinishCapRandomizerUI.Avalonia/Github/Release.cs new file mode 100644 index 00000000..e69de29b diff --git a/MinishCapRandomizerUI.Avalonia/MinishCapRandomizerUI.Avalonia.csproj b/MinishCapRandomizerUI.Avalonia/MinishCapRandomizerUI.Avalonia.csproj new file mode 100644 index 00000000..8d54fdf4 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/MinishCapRandomizerUI.Avalonia.csproj @@ -0,0 +1,62 @@ + + + WinExe + net8.0 + enable + enable + Resources/icon.ico + MinishCapRandomizerUI.Avalonia + MinishCapRandomizerUI.Avalonia + true + + + + win-x64;linux-x64 + true + true + $(EnableTrimming) + partial + false + true + true + embedded + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Patches\%(RecursiveDir)%(Filename)%(Extension) + PreserveNewest + + + + Language Raws\%(RecursiveDir)%(Filename)%(Extension) + PreserveNewest + + + diff --git a/MinishCapRandomizerUI.Avalonia/Program.cs b/MinishCapRandomizerUI.Avalonia/Program.cs new file mode 100644 index 00000000..83623bf0 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/Program.cs @@ -0,0 +1,16 @@ +using Avalonia; + +namespace MinishCapRandomizerUI.Avalonia; + +internal static class Program +{ + public static void Main(string[] args) + { + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + } + + public static AppBuilder BuildAvaloniaApp() => + AppBuilder.Configure() + .UsePlatformDetect() + .LogToTrace(); +} diff --git a/MinishCapRandomizerUI.Avalonia/Properties/ResourcesInfo.md b/MinishCapRandomizerUI.Avalonia/Properties/ResourcesInfo.md new file mode 100644 index 00000000..f17f84ad --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/Properties/ResourcesInfo.md @@ -0,0 +1,19 @@ +# Properties Folder (Parity Placeholder) + +WinForms used `Properties/Resources.resx` and Designer for strongly-typed resources. +Avalonia embeds resources directly via `AvaloniaResource` in the csproj. This placeholder documents parity. + +Potential future work: +- Add localization `.axaml` or `.resx` conversions +- Centralize resource URIs or theme assets. +namespace MinishCapRandomizerUI.Avalonia.Github; + +// Ported from WinForms version for parity (used for GitHub release deserialization if needed) +public class Release +{ + public string? Html_Url { get; set; } + public string? Tag_Name { get; set; } + public string? Name { get; set; } + public string? Body { get; set; } +} + diff --git a/MinishCapRandomizerUI/Resources/Presets/Cosmetics/Max Cosmetics_2.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Cosmetics/Max Cosmetics_2.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Cosmetics/Max Cosmetics_2.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Cosmetics/Max Cosmetics_2.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Cosmetics/Slow Heart Beeps_1.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Cosmetics/Slow Heart Beeps_1.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Cosmetics/Slow Heart Beeps_1.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Cosmetics/Slow Heart Beeps_1.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Cosmetics/Torture_3.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Cosmetics/Torture_3.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Cosmetics/Torture_3.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Cosmetics/Torture_3.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Mystery Cosmetics/Random Follower_1.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Cosmetics/Random Follower_1.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Mystery Cosmetics/Random Follower_1.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Cosmetics/Random Follower_1.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Mystery Cosmetics/Unweighted_2.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Cosmetics/Unweighted_2.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Mystery Cosmetics/Unweighted_2.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Cosmetics/Unweighted_2.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Mystery Settings/Expert_3.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Settings/Expert_3.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Mystery Settings/Expert_3.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Settings/Expert_3.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Mystery Settings/Friendly_1.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Settings/Friendly_1.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Mystery Settings/Friendly_1.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Settings/Friendly_1.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Mystery Settings/Grouped_2.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Settings/Grouped_2.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Mystery Settings/Grouped_2.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Settings/Grouped_2.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Mystery Settings/Unweighted_4.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Settings/Unweighted_4.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Mystery Settings/Unweighted_4.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Mystery Settings/Unweighted_4.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/3 Elements Fast_14.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/3 Elements Fast_14.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/3 Elements Fast_14.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/3 Elements Fast_14.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Advanced_3.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Advanced_3.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Advanced_3.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Advanced_3.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Beginner_1.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Beginner_1.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Beginner_1.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Beginner_1.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Expert_4.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Expert_4.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Expert_4.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Expert_4.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Figurine Hunt_12.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Figurine Hunt_12.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Figurine Hunt_12.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Figurine Hunt_12.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Firerod_9.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Firerod_9.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Firerod_9.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Firerod_9.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Funky Fusions_10.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Funky Fusions_10.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Funky Fusions_10.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Funky Fusions_10.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Intermediate_2.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Intermediate_2.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Intermediate_2.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Intermediate_2.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Max Random_5.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Max Random_5.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Max Random_5.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Max Random_5.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/No Logic Open World_8.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/No Logic Open World_8.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/No Logic Open World_8.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/No Logic Open World_8.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Regional Dungeon Items_13.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Regional Dungeon Items_13.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Regional Dungeon Items_13.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Regional Dungeon Items_13.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Tournament 4 Settings_7.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Tournament 4 Settings_7.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Tournament 4 Settings_7.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Tournament 4 Settings_7.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/Version 1.0 Tentative Race Settings_6.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Version 1.0 Tentative Race Settings_6.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/Version 1.0 Tentative Race Settings_6.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/Version 1.0 Tentative Race Settings_6.yaml diff --git a/MinishCapRandomizerUI/Resources/Presets/Settings/v0.1.0 Settings_11.yaml b/MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/v0.1.0 Settings_11.yaml similarity index 100% rename from MinishCapRandomizerUI/Resources/Presets/Settings/v0.1.0 Settings_11.yaml rename to MinishCapRandomizerUI.Avalonia/Resources/Presets/Settings/v0.1.0 Settings_11.yaml diff --git a/MinishCapRandomizerUI/Resources/Untitled110.png b/MinishCapRandomizerUI.Avalonia/Resources/Untitled110.png similarity index 100% rename from MinishCapRandomizerUI/Resources/Untitled110.png rename to MinishCapRandomizerUI.Avalonia/Resources/Untitled110.png diff --git a/MinishCapRandomizerUI/Resources/icon.ico b/MinishCapRandomizerUI.Avalonia/Resources/icon.ico similarity index 100% rename from MinishCapRandomizerUI/Resources/icon.ico rename to MinishCapRandomizerUI.Avalonia/Resources/icon.ico diff --git a/MinishCapRandomizerUI.Avalonia/UI/About/AboutWindow.axaml b/MinishCapRandomizerUI.Avalonia/UI/About/AboutWindow.axaml new file mode 100644 index 00000000..b3b7cfa5 --- /dev/null +++ b/MinishCapRandomizerUI.Avalonia/UI/About/AboutWindow.axaml @@ -0,0 +1,30 @@ + + +