Fix DirectoryOrCustomSelectControl

This commit is contained in:
Michael Bucari-Tovo
2025-11-06 13:47:51 -07:00
parent d4139861f3
commit bfee579719
4 changed files with 182 additions and 105 deletions

View File

@@ -6,29 +6,63 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="LibationAvalonia.Controls.DirectoryOrCustomSelectControl">
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto" Name="grid">
<controls:DirectorySelectControl
Grid.Column="1"
Grid.Row="0"
IsEnabled="{Binding KnownChecked}"
SelectedDirectory="{Binding SelectedDirectory, Mode=TwoWay}"
SubDirectory="{Binding $parent[1].SubDirectory}"
KnownDirectories="{Binding $parent[1].KnownDirectories}" />
<Grid
RowDefinitions="Auto,Auto,Auto"
ColumnDefinitions="Auto,*,Auto">
<RadioButton
Grid.Column="0"
Grid.Row="0"
IsChecked="{Binding KnownChecked, Mode=TwoWay}"/>
<RadioButton
Grid.RowSpan="2"
Name="rbKnown" />
<RadioButton
Grid.Column="0"
Grid.Row="1"
IsChecked="{Binding CustomChecked, Mode=TwoWay}"/>
<TextBlock
Grid.Column="1"
Grid.ColumnSpan="2"
VerticalAlignment="Center"
Margin="10,0"
IsEnabled="False"
IsVisible="{Binding #cmbKnownDirs.SelectedItem, Converter={x:Static ObjectConverters.IsNull}}"
Text="Select Known Directory:" />
<Grid Grid.Column="1" Grid.Row="1" ColumnDefinitions="*,Auto"
IsEnabled="{Binding CustomChecked}">
<TextBox Grid.Column="0" IsReadOnly="True" Text="{Binding CustomDir, Mode=TwoWay}" />
<Button Grid.Column="1" Content="..." Margin="5,0,0,0" Padding="10,0,10,0" Click="CustomDirBrowseBtn_Click" VerticalAlignment="Stretch" />
</Grid>
</Grid>
<controls:WheelComboBox
Grid.Column="1"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
Margin="0,0,0,3"
IsEnabled="{Binding #rbKnown.IsChecked}"
Name="cmbKnownDirs" />
<TextBox
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
IsReadOnly="True"
Margin="0,0,0,8"
Name="tboxKnownDirPath"
IsEnabled="{Binding #rbKnown.IsChecked}"
Text="{Binding #cmbKnownDirs.SelectedItem.Directory}" />
<RadioButton
Grid.Row="2"
Name="rbCustom" />
<TextBox
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Stretch"
Name="tboxCustomDirPath"
Margin="0,0,10,0"
VerticalAlignment="Center"
Text="{Binding $parent[1].Directory, Mode=OneWayToSource}"
IsEnabled="{Binding #rbCustom.IsChecked}"/>
<Button
Grid.Row="2"
Grid.Column="2"
Name="btnBrowse"
IsEnabled="{Binding #rbCustom.IsChecked}">
<TextBlock Text="..." />
</Button>
</Grid>
</UserControl>

View File

@@ -1,142 +1,184 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using Dinah.Core;
using LibationFileManager;
using ReactiveUI;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#nullable enable
namespace LibationAvalonia.Controls
{
public partial class DirectoryOrCustomSelectControl : UserControl
{
public static readonly StyledProperty<List<Configuration.KnownDirectories>> KnownDirectoriesProperty =
AvaloniaProperty.Register<DirectorySelectControl, List<Configuration.KnownDirectories>>(nameof(KnownDirectories), DirectorySelectControl.DefaultKnownDirectories);
public static readonly StyledProperty<IList<Configuration.KnownDirectories>?> KnownDirectoriesProperty =
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, IList<Configuration.KnownDirectories>?>(nameof(KnownDirectories), DefaultKnownDirectories);
public static readonly StyledProperty<string> SubDirectoryProperty =
AvaloniaProperty.Register<DirectorySelectControl, string>(nameof(SubDirectory));
public static readonly StyledProperty<string?> SubDirectoryProperty =
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, string?>(nameof(SubDirectory));
public static readonly StyledProperty<string> DirectoryProperty =
AvaloniaProperty.Register<DirectorySelectControl, string>(nameof(Directory));
public static readonly StyledProperty<string?> DirectoryProperty =
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, string?>(nameof(Directory));
public List<Configuration.KnownDirectories> KnownDirectories
public IList<Configuration.KnownDirectories>? KnownDirectories
{
get => GetValue(KnownDirectoriesProperty);
set => SetValue(KnownDirectoriesProperty, value);
}
public string Directory
public string? Directory
{
get => GetValue(DirectoryProperty);
set => SetValue(DirectoryProperty, value);
}
public string SubDirectory
public string? SubDirectory
{
get => GetValue(SubDirectoryProperty);
set => SetValue(SubDirectoryProperty, value);
}
private readonly DirectoryState directoryState = new();
public static IList<Configuration.KnownDirectories> DefaultKnownDirectories => [
Configuration.KnownDirectories.WinTemp,
Configuration.KnownDirectories.UserProfile,
Configuration.KnownDirectories.ApplicationData,
Configuration.KnownDirectories.AppDir,
Configuration.KnownDirectories.MyMusic,
Configuration.KnownDirectories.MyDocs,
Configuration.KnownDirectories.LibationFiles];
private readonly AvaloniaList<KnownDirectoryItem> _knownDirNames;
public DirectoryOrCustomSelectControl()
{
InitializeComponent();
grid.DataContext = directoryState;
directoryState.PropertyChanged += DirectoryState_PropertyChanged;
PropertyChanged += DirectoryOrCustomSelectControl_PropertyChanged;
_knownDirNames = new(GetKnownDirectories(DefaultKnownDirectories));
cmbKnownDirs.ItemsSource = _knownDirNames;
cmbKnownDirs.SelectionChanged += CmbKnownDirs_SelectionChanged;
btnBrowse.Click += Browse_Click;
}
private void DirectoryState_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void CmbKnownDirs_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (e.PropertyName is nameof(DirectoryState.SelectedDirectory) or nameof(DirectoryState.KnownChecked) &&
directoryState.KnownChecked &&
directoryState.SelectedDirectory is Configuration.KnownDirectories kdir &&
kdir is not Configuration.KnownDirectories.None)
if (cmbKnownDirs.SelectedItem is KnownDirectoryItem item && item.Directory is not null)
{
Directory = kdir is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute : Configuration.GetKnownDirectoryPath(kdir);
}
else if (e.PropertyName is nameof(DirectoryState.CustomDir) or nameof(DirectoryState.CustomChecked) &&
directoryState.CustomChecked &&
directoryState.CustomDir is not null)
{
Directory = directoryState.CustomDir;
Directory = item.Directory;
}
}
private class DirectoryState : ViewModels.ViewModelBase
{
private string _customDir;
private string _subDirectory;
private bool _knownChecked;
private bool _customChecked;
private Configuration.KnownDirectories? _selectedDirectory;
public string CustomDir { get => _customDir; set => this.RaiseAndSetIfChanged(ref _customDir, value); }
public string SubDirectory { get => _subDirectory; set => this.RaiseAndSetIfChanged(ref _subDirectory, value); }
public bool KnownChecked { get => _knownChecked; set => this.RaiseAndSetIfChanged(ref _knownChecked, value); }
public bool CustomChecked { get => _customChecked; set => this.RaiseAndSetIfChanged(ref _customChecked, value); }
private IEnumerable<KnownDirectoryItem> GetKnownDirectories(IEnumerable<Configuration.KnownDirectories> knownDirs)
=> knownDirs.Select(k => new KnownDirectoryItem(k, SubDirectory)).Where(k => k.Directory is not null);
public Configuration.KnownDirectories? SelectedDirectory { get => _selectedDirectory; set => this.RaiseAndSetIfChanged(ref _selectedDirectory, value); }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Property == SubDirectoryProperty)
{
foreach (var item in _knownDirNames)
{
item.SubDirectory = SubDirectory;
}
VerifyAndApplyDirectory(Directory);
}
else if (change.Property == KnownDirectoriesProperty)
{
var knownDirs = KnownDirectories?.Count > 0 ? KnownDirectories : DefaultKnownDirectories;
if (!_knownDirNames.Select(k => k.KnownDirectory).SequenceEqual(knownDirs))
{
_knownDirNames.Clear();
_knownDirNames.AddRange(GetKnownDirectories(knownDirs));
}
VerifyAndApplyDirectory(Directory);
}
else if (change.Property == DirectoryProperty)
{
VerifyAndApplyDirectory(Directory);
}
base.OnPropertyChanged(change);
}
private async void CustomDirBrowseBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
private void VerifyAndApplyDirectory(string? directory)
{
var options = new Avalonia.Platform.Storage.FolderPickerOpenOptions
if (string.IsNullOrWhiteSpace(Directory))
return;
bool dirIsKnown = false;
foreach (var item in _knownDirNames)
{
if (item.IsSamePathAs(directory))
{
rbKnown.IsChecked = true;
Directory = item.Directory;
cmbKnownDirs.SelectedItem = item;
dirIsKnown = true;
break;
}
}
if (!dirIsKnown)
{
tboxCustomDirPath.Text = directory;
rbCustom.IsChecked = true;
}
}
public async void Browse_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
if (VisualRoot is not Window window)
return;
var options = new FolderPickerOpenOptions
{
AllowMultiple = false
};
var selectedFolders = await (VisualRoot as Window).StorageProvider.OpenFolderPickerAsync(options);
directoryState.CustomDir = selectedFolders.SingleOrDefault()?.TryGetLocalPath() ?? directoryState.CustomDir;
var selectedFolders = await window.StorageProvider.OpenFolderPickerAsync(options);
Directory = selectedFolders.SingleOrDefault()?.TryGetLocalPath() ?? Directory;
}
private void DirectoryOrCustomSelectControl_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
private class KnownDirectoryItem : ReactiveObject
{
if (e.Property == DirectoryProperty)
public Configuration.KnownDirectories KnownDirectory { get; set; }
private string? _directory;
public string? Directory { get => _directory; private set => this.RaiseAndSetIfChanged(ref _directory, value); }
public string? Name { get; }
private string? _subDir;
public string? SubDirectory
{
var directory = Directory?.Trim() ?? "";
var noSubDir = RemoveSubDirectoryFromPath(directory);
var known = Configuration.GetKnownDirectory(noSubDir);
if (known == Configuration.KnownDirectories.None && noSubDir == Configuration.AppDir_Absolute)
known = Configuration.KnownDirectories.AppDir;
if (known is Configuration.KnownDirectories.None)
get => _subDir;
set
{
directoryState.CustomDir = directory;
directoryState.CustomChecked = true;
}
else
{
directoryState.SelectedDirectory = known;
directoryState.KnownChecked = true;
_subDir = value;
if (Configuration.GetKnownDirectoryPath(KnownDirectory) is string dir)
{
Directory = Path.Combine(dir, _subDir ?? "");
}
}
}
else if (e.Property == KnownDirectoriesProperty &&
KnownDirectories.Count > 0 &&
directoryState.SelectedDirectory is null or Configuration.KnownDirectories.None)
directoryState.SelectedDirectory = KnownDirectories[0];
}
private string RemoveSubDirectoryFromPath(string path)
{
if (string.IsNullOrWhiteSpace(SubDirectory))
return path;
public KnownDirectoryItem(Configuration.KnownDirectories known, string? subDir)
{
Name = known.GetDescription();
KnownDirectory = known;
SubDirectory = subDir;
}
path = path?.Trim() ?? "";
if (string.IsNullOrWhiteSpace(path))
return path;
public bool IsSamePathAs(string? otherPath)
{
if (string.IsNullOrWhiteSpace(otherPath) || string.IsNullOrWhiteSpace(Directory))
return false;
var bottomDir = System.IO.Path.GetFileName(path);
if (SubDirectory.EqualsInsensitive(bottomDir))
return System.IO.Path.GetDirectoryName(path);
try
{
var p1 = Path.GetFullPath(Directory);
var p2 = Path.GetFullPath(otherPath);
return p1.Equals(p2, System.StringComparison.OrdinalIgnoreCase);
}
catch { return false; }
}
return path;
public override string? ToString() => Name?.ToString();
}
}
}

View File

@@ -67,10 +67,11 @@ namespace LibationAvalonia.ViewModels.Settings
public List<Configuration.KnownDirectories> KnownDirectories { get; } = new()
{
Configuration.KnownDirectories.UserProfile,
Configuration.KnownDirectories.AppDir,
Configuration.KnownDirectories.MyDocs,
Configuration.KnownDirectories.LibationFiles,
Configuration.KnownDirectories.MyMusic,
Configuration.KnownDirectories.MyDocs,
Configuration.KnownDirectories.AppDir,
Configuration.KnownDirectories.UserProfile
};
public string BooksText { get; } = Configuration.GetDescription(nameof(Configuration.Books));

View File

@@ -10,8 +10,8 @@ namespace LibationFileManager
{
public partial class Configuration
{
public static string ProcessDirectory { get; } = Path.GetDirectoryName(Exe.FileLocationOnDisk)!;
public static string AppDir_Relative => $@".{Path.PathSeparator}{LIBATION_FILES_KEY}";
public static string ProcessDirectory { get; } = Path.GetDirectoryName(Environment.ProcessPath)!;
public static string AppDir_Relative => $@".{Path.DirectorySeparatorChar}{LIBATION_FILES_KEY}";
public static string AppDir_Absolute => Path.GetFullPath(Path.Combine(ProcessDirectory, LIBATION_FILES_KEY));
public static string MyDocs => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Libation"));
public static string MyMusic => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), "Libation"));