Browse Source

Add RefreshBadge and SelectDataDirectoryDialog

pull/55/head
Ionite 1 year ago
parent
commit
f786172c5a
No known key found for this signature in database
  1. 40
      StabilityMatrix.Avalonia/App.axaml.cs
  2. 39
      StabilityMatrix.Avalonia/Controls/RefreshBadge.axaml
  3. 16
      StabilityMatrix.Avalonia/Controls/RefreshBadge.axaml.cs
  4. 7
      StabilityMatrix.Avalonia/DesignData/DesignData.cs
  5. 10
      StabilityMatrix.Avalonia/Styles/ThemeColors.cs
  6. 39
      StabilityMatrix.Avalonia/ViewModels/CheckpointFolder.cs
  7. 156
      StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectDataDirectoryViewModel.cs
  8. 58
      StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs
  9. 96
      StabilityMatrix.Avalonia/ViewModels/RefreshBadgeViewModel.cs
  10. 100
      StabilityMatrix.Avalonia/Views/Dialogs/SelectDataDirectoryDialog.axaml
  11. 17
      StabilityMatrix.Avalonia/Views/Dialogs/SelectDataDirectoryDialog.axaml.cs

40
StabilityMatrix.Avalonia/App.axaml.cs

@ -12,6 +12,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Platform.Storage;
using Avalonia.Styling;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -25,11 +26,13 @@ using Polly.Contrib.WaitAndRetry;
using Polly.Extensions.Http;
using Polly.Timeout;
using Refit;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.DesignData;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels;
using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Api;
using StabilityMatrix.Core.Converters.Json;
using StabilityMatrix.Core.Database;
@ -52,6 +55,9 @@ public partial class App : Application
[NotNull]
public static Visual? VisualRoot { get; private set; }
[NotNull]
public static IStorageProvider? StorageProvider { get; private set; }
public override void Initialize()
{
@ -63,7 +69,7 @@ public partial class App : Application
RequestedThemeVariant = ThemeVariant.Dark;
}
}
public override void OnFrameworkInitializationCompleted()
{
ConfigureServiceProvider();
@ -74,12 +80,14 @@ public partial class App : Application
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var mainWindow = Services.GetRequiredService<MainWindow>();
VisualRoot = mainWindow;
mainWindow.DataContext = mainViewModel;
mainWindow.NotificationService = notificationService;
VisualRoot = mainWindow;
StorageProvider = mainWindow.StorageProvider;
desktop.MainWindow = mainWindow;
desktop.Exit += OnExit;
Debug.WriteLine($"Shutdown mode: {desktop.ShutdownMode}");
}
base.OnFrameworkInitializationCompleted();
@ -112,28 +120,43 @@ public partial class App : Application
services.AddTransient<InstallerViewModel>();
services.AddTransient<OneClickInstallViewModel>();
services.AddTransient<SelectModelVersionViewModel>();
services.AddTransient<SelectDataDirectoryViewModel>();
// Other transients (usually sub view models)
services.AddTransient<CheckpointFolder>();
services.AddTransient<CheckpointFile>();
// Controls
services.AddTransient<RefreshBadgeViewModel>();
// Dialog factory
services.AddSingleton<ServiceManager<ViewModelBase>>(provider =>
new ServiceManager<ViewModelBase>()
.Register(provider.GetRequiredService<InstallerViewModel>)
.Register(provider.GetRequiredService<OneClickInstallViewModel>)
.Register(provider.GetRequiredService<SelectModelVersionViewModel>)
.Register(provider.GetRequiredService<SelectDataDirectoryViewModel>)
.Register(provider.GetRequiredService<CheckpointFolder>)
.Register(provider.GetRequiredService<CheckpointFile>));
.Register(provider.GetRequiredService<CheckpointFile>)
.Register(provider.GetRequiredService<RefreshBadgeViewModel>));
}
internal static void ConfigureViews(IServiceCollection services)
{
// Pages
services.AddTransient<CheckpointsPage>();
services.AddTransient<LaunchPageView>();
services.AddTransient<PackageManagerPage>();
services.AddTransient<SettingsPage>();
services.AddTransient<CheckpointBrowserPage>();
// Dialogs
services.AddTransient<SelectDataDirectoryDialog>();
// Controls
services.AddTransient<RefreshBadge>();
// Window
services.AddSingleton<MainWindow>();
}
@ -287,6 +310,15 @@ public partial class App : Application
return services;
}
public static void Shutdown()
{
if (Current is null) throw new InvalidOperationException("Current Application is not defined");
if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
lifetime.Shutdown();
}
}
private void OnExit(object? sender, ControlledApplicationLifetimeExitEventArgs args)
{
Debug.WriteLine("Start OnExit");

39
StabilityMatrix.Avalonia/Controls/RefreshBadge.axaml

@ -0,0 +1,39 @@
<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:vm="clr-namespace:StabilityMatrix.Avalonia.ViewModels"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
d:DataContext="{x:Static mocks:DesignData.RefreshBadgeViewModel}"
x:DataType="vm:RefreshBadgeViewModel"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="StabilityMatrix.Avalonia.Controls.RefreshBadge">
<Grid>
<TextBlock Text="Test"></TextBlock>
<Button
BorderThickness="0"
Command="{Binding RefreshCommand}"
FontSize="26"
Foreground="{Binding ColorBrush}"
Margin="4"
Padding="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEnabled="{Binding !IsWorking}">
<ui:SymbolIcon Symbol="{Binding Icon}"/>
</Button>
<controls:ProgressRing
FontSize="14"
Grid.Row="0"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEnabled="{Binding IsWorking}"
IsIndeterminate="True"
IsVisible="{Binding IsWorking}"
Width="20"
ToolTip.Tip="{Binding CurrentToolTip}" />
</Grid>
</controls:UserControlBase>

16
StabilityMatrix.Avalonia/Controls/RefreshBadge.axaml.cs

@ -0,0 +1,16 @@
using Avalonia.Markup.Xaml;
namespace StabilityMatrix.Avalonia.Controls;
public partial class RefreshBadge : UserControlBase
{
public RefreshBadge()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

7
StabilityMatrix.Avalonia/DesignData/DesignData.cs

@ -14,6 +14,7 @@ using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
@ -178,4 +179,10 @@ public static class DesignData
public static SelectModelVersionViewModel SelectModelVersionViewModel => Services.GetRequiredService<SelectModelVersionViewModel>();
public static OneClickInstallViewModel OneClickInstallViewModel => Services.GetRequiredService<OneClickInstallViewModel>();
public static InstallerViewModel InstallerViewModel => Services.GetRequiredService<InstallerViewModel>();
public static SelectDataDirectoryViewModel SelectDataDirectoryViewModel => Services.GetRequiredService<SelectDataDirectoryViewModel>();
public static RefreshBadgeViewModel RefreshBadgeViewModel => new()
{
State = ProgressState.Success
};
}

10
StabilityMatrix.Avalonia/Styles/ThemeColors.cs

@ -0,0 +1,10 @@
using Avalonia.Media;
namespace StabilityMatrix.Avalonia.Styles;
public static class ThemeColors
{
public static readonly SolidColorBrush ThemeGreen = SolidColorBrush.Parse("#4caf50");
public static readonly SolidColorBrush ThemeRed = SolidColorBrush.Parse("#f44336");
public static readonly SolidColorBrush ThemeYellow = SolidColorBrush.Parse("#ffeb3b");
}

39
StabilityMatrix.Avalonia/ViewModels/CheckpointFolder.cs

@ -6,12 +6,14 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using AvaloniaEdit.Utils;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models;
@ -162,6 +164,43 @@ public partial class CheckpointFolder : ViewModelBase
Process.Start("explorer.exe", path);
}
[RelayCommand]
private async Task CreateSubFolder()
{
Dispatcher.UIThread.VerifyAccess();
var textBox = new TextBox();
var dialog = new ContentDialog
{
Content = textBox,
DefaultButton = ContentDialogButton.Primary,
PrimaryButtonText = "OK",
IsPrimaryButtonEnabled = true,
};
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
var targetName = textBox.Text;
if (string.IsNullOrWhiteSpace(targetName)) return;
var subFolderPath = Path.Combine(DirectoryPath, targetName);
Directory.CreateDirectory(subFolderPath);
SubFolders.Add(new CheckpointFolder(settingsManager,
downloadService, modelFinder,
useCategoryVisibility: false)
{
Title = Path.GetFileName(subFolderPath),
DirectoryPath = subFolderPath,
FolderType = FolderType,
ParentFolder = this,
IsExpanded = false,
});
}
}
/// <summary>
/// Imports files to the folder. Reports progress to instance properties.
/// </summary>

156
StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectDataDirectoryViewModel.cs

@ -0,0 +1,156 @@
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using AsyncAwaitBestPractices;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using NLog;
using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Models.Settings;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;
[View(typeof(SelectDataDirectoryDialog))]
public partial class SelectDataDirectoryViewModel : ViewModelBase
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static string DefaultInstallLocation => Compat.AppDataHome;
private readonly ISettingsManager settingsManager;
private const string ValidExistingDirectoryText = "Valid existing data directory found";
private const string InvalidDirectoryText =
"Directory must be empty or have a valid settings.json file";
private const string NotEnoughFreeSpaceText = "Not enough free space on the selected drive";
[ObservableProperty] private string dataDirectory = DefaultInstallLocation;
[ObservableProperty] private bool isPortableMode;
[ObservableProperty] private string directoryStatusText = string.Empty;
[ObservableProperty] private bool isStatusBadgeVisible;
[ObservableProperty] private bool isDirectoryValid;
public RefreshBadgeViewModel ValidatorRefreshBadge { get; } = new()
{
State = ProgressState.Inactive,
SuccessToolTipText = ValidExistingDirectoryText,
FailToolTipText = InvalidDirectoryText
};
public bool HasOldData => settingsManager.GetOldInstalledPackages().Any();
public SelectDataDirectoryViewModel(ISettingsManager settingsManager)
{
this.settingsManager = settingsManager;
ValidatorRefreshBadge.RefreshFunc = ValidateDataDirectory;
}
public override void OnLoaded()
{
ValidatorRefreshBadge.RefreshCommand.ExecuteAsync(null).SafeFireAndForget();
}
// Revalidate on data directory change
partial void OnDataDirectoryChanged(string value)
{
ValidatorRefreshBadge.RefreshCommand.ExecuteAsync(null).SafeFireAndForget();
}
private async Task<bool> ValidateDataDirectory()
{
await using var delay = new MinimumDelay(100, 200);
// Doesn't exist, this is fine as a new install, hide badge
if (!Directory.Exists(DataDirectory))
{
IsStatusBadgeVisible = false;
IsDirectoryValid = true;
return true;
}
// Otherwise check that a settings.json exists
var settingsPath = Path.Combine(DataDirectory, "settings.json");
// settings.json exists: Try deserializing it
if (File.Exists(settingsPath))
{
try
{
var jsonText = await File.ReadAllTextAsync(settingsPath);
var _ = JsonSerializer.Deserialize<Settings>(jsonText, new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() }
});
// If successful, show existing badge
IsStatusBadgeVisible = true;
IsDirectoryValid = true;
DirectoryStatusText = ValidExistingDirectoryText;
return true;
}
catch (Exception e)
{
Logger.Info("Failed to deserialize settings.json: {Msg}", e.Message);
// If not, show error badge, and set directory to invalid to prevent continuing
IsStatusBadgeVisible = true;
IsDirectoryValid = false;
DirectoryStatusText = InvalidDirectoryText;
return false;
}
}
// No settings.json
// Check if the directory is %APPDATA%\StabilityMatrix: hide badge and set directory valid
if (DataDirectory == DefaultInstallLocation)
{
IsStatusBadgeVisible = false;
IsDirectoryValid = true;
return true;
}
// Check if the directory is empty: hide badge and set directory to valid
var isEmpty = !Directory.EnumerateFileSystemEntries(DataDirectory).Any();
if (isEmpty)
{
IsStatusBadgeVisible = false;
IsDirectoryValid = true;
return true;
}
// Not empty and not appdata: show error badge, and set directory to invalid
IsStatusBadgeVisible = true;
IsDirectoryValid = false;
DirectoryStatusText = InvalidDirectoryText;
return false;
}
private bool CanPickFolder => App.StorageProvider.CanPickFolder;
[RelayCommand(CanExecute = nameof(CanPickFolder))]
private async Task ShowFolderBrowserDialog()
{
var provider = App.StorageProvider;
var result = await provider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = "Select Data Folder",
AllowMultiple = false
});
if (result.Count != 1) return;
DataDirectory = result[0].Path.LocalPath;
}
partial void OnIsPortableModeChanged(bool value)
{
DataDirectory = value ? Compat.AppCurrentDir + "Data" : DefaultInstallLocation;
}
}

58
StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs

@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.Views;
@ -44,6 +47,8 @@ public partial class MainWindowViewModel : ViewModelBase
CurrentPage = Pages.FirstOrDefault();
SelectedCategory = Pages.FirstOrDefault();
EventManager.Instance.PageChangeRequested += OnPageChangeRequested;
await EnsureDataDirectory();
if (!settingsManager.Settings.InstalledPackages.Any())
{
@ -70,6 +75,59 @@ public partial class MainWindowViewModel : ViewModelBase
}
}
/// <summary>
/// Check if the data directory exists, if not, show the select data directory dialog.
/// </summary>
private async Task EnsureDataDirectory()
{
// Show dialog if not set
if (!settingsManager.TryFindLibrary())
{
await ShowSelectDataDirectoryDialog();
}
// Try to find library again, should be found now
if (!settingsManager.TryFindLibrary())
{
throw new Exception("Could not find library after setting path");
}
// Check if there are old packages, if so show migration dialog
// TODO: Migration dialog
}
private async Task ShowSelectDataDirectoryDialog()
{
var viewModel = dialogFactory.Get<SelectDataDirectoryViewModel>();
var dialog = new BetterContentDialog
{
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
Content = new SelectDataDirectoryDialog
{
DataContext = viewModel
}
};
var result = await dialog.ShowAsync();
if (result != ContentDialogResult.Primary)
{
App.Shutdown();
}
// 1. For portable mode, call settings.SetPortableMode()
if (viewModel.IsPortableMode)
{
settingsManager.SetPortableMode();
}
// 2. For custom path, call settings.SetLibraryPath(path)
else
{
settingsManager.SetLibraryPath(viewModel.DataDirectory);
}
}
private void OnPageChangeRequested(object? sender, Type e)
{
CurrentPage = Pages.FirstOrDefault(p => p.GetType() == e);

96
StabilityMatrix.Avalonia/ViewModels/RefreshBadgeViewModel.cs

@ -0,0 +1,96 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls;
using NLog;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Styles;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Models.Progress;
namespace StabilityMatrix.Avalonia.ViewModels;
[View(typeof(RefreshBadge))]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public partial class RefreshBadgeViewModel : ViewModelBase
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public string WorkingToolTipText { get; set; } = "Loading...";
public string SuccessToolTipText { get; set; } = "Success";
public string InactiveToolTipText { get; set; } = "";
public string FailToolTipText { get; set; } = "Failed";
public Symbol InactiveIcon { get; set; } = Symbol.Clear;
public Symbol SuccessIcon { get; set; } = Symbol.Checkmark;
public Symbol FailIcon { get; set; } = Symbol.AlertUrgent;
public IBrush SuccessColorBrush { get; set; } = ThemeColors.ThemeGreen;
public IBrush InactiveColorBrush { get; set; } = ThemeColors.ThemeYellow;
public IBrush FailColorBrush { get; set; } = ThemeColors.ThemeYellow;
public Func<Task<bool>>? RefreshFunc { get; set; }
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsWorking))]
[NotifyPropertyChangedFor(nameof(ColorBrush))]
[NotifyPropertyChangedFor(nameof(CurrentToolTip))]
[NotifyPropertyChangedFor(nameof(Icon))]
private ProgressState state;
public bool IsWorking => State == ProgressState.Working;
/*public ControlAppearance Appearance => State switch
{
ProgressState.Working => ControlAppearance.Info,
ProgressState.Success => ControlAppearance.Success,
ProgressState.Failed => ControlAppearance.Danger,
_ => ControlAppearance.Secondary
};*/
public IBrush ColorBrush => State switch
{
ProgressState.Success => SuccessColorBrush,
ProgressState.Inactive => InactiveColorBrush,
ProgressState.Failed => FailColorBrush,
_ => Brushes.Gray
};
public string CurrentToolTip => State switch
{
ProgressState.Working => WorkingToolTipText,
ProgressState.Success => SuccessToolTipText,
ProgressState.Inactive => InactiveToolTipText,
ProgressState.Failed => FailToolTipText,
_ => ""
};
public Symbol Icon => State switch
{
ProgressState.Success => SuccessIcon,
ProgressState.Failed => FailIcon,
_ => InactiveIcon
};
[RelayCommand]
private async Task Refresh()
{
Logger.Info("Running refresh command...");
if (RefreshFunc == null) return;
State = ProgressState.Working;
try
{
var result = await RefreshFunc.Invoke();
State = result ? ProgressState.Success : ProgressState.Failed;
}
catch (Exception ex)
{
State = ProgressState.Failed;
Logger.Error(ex, "Refresh command failed: {Ex}", ex.Message);
}
}
}

100
StabilityMatrix.Avalonia/Views/Dialogs/SelectDataDirectoryDialog.axaml

@ -0,0 +1,100 @@
<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:ui="using:FluentAvalonia.UI.Controls"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
d:DataContext="{x:Static mocks:DesignData.SelectDataDirectoryViewModel}"
x:DataType="dialogs:SelectDataDirectoryViewModel"
x:CompileBindings="True"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.SelectDataDirectoryDialog">
<Grid RowDefinitions="Auto,1*,1*"
Margin="16,0,16,16"
MinHeight="450"
MaxWidth="700" >
<StackPanel Grid.Row="0" Margin="0,0,0,16">
<ui:InfoBar
IsClosable="False"
IsOpen="True"
Margin="0,0,0,16"
IsVisible="{Binding HasOldData}"
Title="Welcome back! In this update, you can optionally choose a custom location to store all data. If you choose a new location, or opt to use Portable Mode, you'll be able to migrate your existing data on the next page."/>
<Label
Content="Data Directory"
FontSize="13"
Margin="0,16,0,0" />
<Grid ColumnDefinitions="*,Auto">
<TextBox
Height="36"
IsEnabled="{Binding !IsPortableMode}"
Margin="0,0,8,0"
Watermark="{Binding DefaultInstallLocation}"
Text="{Binding DataDirectory}"
VerticalAlignment="Stretch" />
<Button
Command="{Binding ShowFolderBrowserDialogCommand}"
Grid.Column="1"
Height="36"
HorizontalAlignment="Stretch"
IsEnabled="{Binding !IsPortableMode}">
<ui:SymbolIcon Symbol="OpenFolder" />
</Button>
</Grid>
<TextBlock
Text="This is where the model checkpoints, LORAs, web UIs, settings, etc. will be installed. If you were satisfied with the previous versions, you don't need to change anything here."
TextWrapping="Wrap"
Foreground="LightGray"
FontSize="12"
Margin="0,8,0,0" />
<CheckBox
Content="Portable Mode"
IsChecked="{Binding IsPortableMode, Mode=TwoWay}"
Margin="0,16,0,0" />
<ui:InfoBar
IsClosable="False"
IsOpen="True"
FontSize="13"
Margin="0,8,0,0"
Padding="16"
Title="In Portable Mode, all data and settings will be stored in the same directory as the application. You will be able to move the application with its 'Data' folder to a different location or computer." />
</StackPanel>
<!-- Indicator of existing or new data directory -->
<StackPanel
Grid.Row="1"
HorizontalAlignment="Center"
Margin="8,0,8,8"
Orientation="Horizontal"
IsVisible="{Binding IsStatusBadgeVisible}">
<controls:RefreshBadge DataContext="{Binding ValidatorRefreshBadge}" />
<TextBlock
FontSize="14"
Text="{Binding DirectoryStatusText}"
VerticalAlignment="Center" />
</StackPanel>
<!-- Appearance="Success"
Click="ContinueButton_OnClick" -->
<Button
Content="Continue"
FontSize="16"
Grid.Row="2"
HorizontalAlignment="Center"
IsEnabled="{Binding IsDirectoryValid}"
Padding="16,8" />
</Grid>
</controls:UserControlBase>

17
StabilityMatrix.Avalonia/Views/Dialogs/SelectDataDirectoryDialog.axaml.cs

@ -0,0 +1,17 @@
using Avalonia.Markup.Xaml;
using StabilityMatrix.Avalonia.Controls;
namespace StabilityMatrix.Avalonia.Views.Dialogs;
public partial class SelectDataDirectoryDialog : UserControlBase
{
public SelectDataDirectoryDialog()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
Loading…
Cancel
Save