Ionite
9 months ago
9 changed files with 292 additions and 0 deletions
@ -0,0 +1,65 @@
|
||||
<Styles |
||||
xmlns="https://github.com/avaloniaui" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
xmlns:controls="using:StabilityMatrix.Avalonia.Controls" |
||||
xmlns:system="using:System" |
||||
xmlns:treeFileExplorer="clr-namespace:StabilityMatrix.Avalonia.Models.TreeFileExplorer" |
||||
xmlns:mock="clr-namespace:StabilityMatrix.Avalonia.DesignData" |
||||
xmlns:sg="clr-namespace:SpacedGridControl.Avalonia;assembly=SpacedGridControl.Avalonia" |
||||
xmlns:fluentIcons="clr-namespace:FluentIcons.Avalonia.Fluent;assembly=FluentIcons.Avalonia.Fluent"> |
||||
<Design.PreviewWith> |
||||
<StackPanel Spacing="16"> |
||||
<Panel Height="300" Margin="4"> |
||||
<controls:TreeFileExplorer |
||||
RootPath="{x:Static mock:DesignData.CurrentDirectory}" /> |
||||
</Panel> |
||||
|
||||
<Panel Height="300" Margin="4"> |
||||
<controls:TreeFileExplorer |
||||
IndexFiles="False" |
||||
CanSelectFiles="False" |
||||
RootPath="{x:Static mock:DesignData.CurrentDirectory}" /> |
||||
</Panel> |
||||
</StackPanel> |
||||
</Design.PreviewWith> |
||||
|
||||
<Style Selector="controls|TreeFileExplorer"> |
||||
<!-- Set Defaults --> |
||||
<Setter Property="Template"> |
||||
<ControlTemplate> |
||||
<Grid> |
||||
<TreeView |
||||
ScrollViewer.VerticalScrollBarVisibility="Auto" |
||||
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=RootItem.Children}"> |
||||
<TreeView.DataTemplates> |
||||
<DataTemplate |
||||
DataType="treeFileExplorer:TreeFileExplorerFile"> |
||||
<sg:SpacedGrid ColumnDefinitions="Auto,*" RowSpacing="0" ColumnSpacing="4"> |
||||
<fluentIcons:SymbolIcon |
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" |
||||
Symbol="Document" /> |
||||
<TextBlock |
||||
Grid.Column="1" |
||||
Text="{Binding Path.Name}" /> |
||||
</sg:SpacedGrid> |
||||
</DataTemplate> |
||||
<TreeDataTemplate |
||||
DataType="treeFileExplorer:TreeFileExplorerDirectory" |
||||
ItemsSource="{Binding Children}"> |
||||
<sg:SpacedGrid ColumnDefinitions="Auto,*" RowSpacing="0" ColumnSpacing="4"> |
||||
<fluentIcons:SymbolIcon |
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" |
||||
IsFilled="True" |
||||
Symbol="Folder" /> |
||||
<TextBlock |
||||
Grid.Column="1" |
||||
Text="{Binding Path.Name}" /> |
||||
</sg:SpacedGrid> |
||||
</TreeDataTemplate> |
||||
</TreeView.DataTemplates> |
||||
</TreeView> |
||||
</Grid> |
||||
</ControlTemplate> |
||||
</Setter> |
||||
</Style> |
||||
</Styles> |
@ -0,0 +1,135 @@
|
||||
using Avalonia; |
||||
using Avalonia.Controls.Primitives; |
||||
using StabilityMatrix.Avalonia.Models.TreeFileExplorer; |
||||
using StabilityMatrix.Core.Models.FileInterfaces; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Controls; |
||||
|
||||
public class TreeFileExplorer : TemplatedControl |
||||
{ |
||||
public static readonly StyledProperty<TreeFileExplorerDirectory?> RootItemProperty = |
||||
AvaloniaProperty.Register<TreeFileExplorer, TreeFileExplorerDirectory?>("RootItem"); |
||||
|
||||
public TreeFileExplorerDirectory? RootItem |
||||
{ |
||||
get => GetValue(RootItemProperty); |
||||
set => SetValue(RootItemProperty, value); |
||||
} |
||||
|
||||
public static readonly StyledProperty<string?> RootPathProperty = AvaloniaProperty.Register< |
||||
TreeFileExplorer, |
||||
string? |
||||
>("RootPath"); |
||||
|
||||
public string? RootPath |
||||
{ |
||||
get => GetValue(RootPathProperty); |
||||
set => SetValue(RootPathProperty, value); |
||||
} |
||||
|
||||
public static readonly StyledProperty<IPathObject?> SelectedPathProperty = AvaloniaProperty.Register< |
||||
TreeFileExplorer, |
||||
IPathObject? |
||||
>("SelectedPath"); |
||||
|
||||
public IPathObject? SelectedPath |
||||
{ |
||||
get => GetValue(SelectedPathProperty); |
||||
set => SetValue(SelectedPathProperty, value); |
||||
} |
||||
|
||||
public static readonly StyledProperty<bool> CanSelectFilesProperty = AvaloniaProperty.Register< |
||||
TreeFileExplorer, |
||||
bool |
||||
>("CanSelectFiles", true); |
||||
|
||||
public bool CanSelectFiles |
||||
{ |
||||
get => GetValue(CanSelectFilesProperty); |
||||
set => SetValue(CanSelectFilesProperty, value); |
||||
} |
||||
|
||||
public static readonly StyledProperty<bool> CanSelectFoldersProperty = AvaloniaProperty.Register< |
||||
TreeFileExplorer, |
||||
bool |
||||
>("CanSelectFolders", true); |
||||
|
||||
public bool CanSelectFolders |
||||
{ |
||||
get => GetValue(CanSelectFoldersProperty); |
||||
set => SetValue(CanSelectFoldersProperty, value); |
||||
} |
||||
|
||||
public static readonly StyledProperty<bool> IndexFilesProperty = AvaloniaProperty.Register< |
||||
TreeFileExplorer, |
||||
bool |
||||
>("IndexFiles", true); |
||||
|
||||
public bool IndexFiles |
||||
{ |
||||
get => GetValue(IndexFilesProperty); |
||||
set => SetValue(IndexFilesProperty, value); |
||||
} |
||||
|
||||
public static readonly StyledProperty<bool> IndexFoldersProperty = AvaloniaProperty.Register< |
||||
TreeFileExplorer, |
||||
bool |
||||
>("IndexFolders", true); |
||||
|
||||
public bool IndexFolders |
||||
{ |
||||
get => GetValue(IndexFoldersProperty); |
||||
set => SetValue(IndexFoldersProperty, value); |
||||
} |
||||
|
||||
private TreeFileExplorerOptions GetOptions() |
||||
{ |
||||
var options = TreeFileExplorerOptions.None; |
||||
|
||||
if (CanSelectFiles) |
||||
{ |
||||
options |= TreeFileExplorerOptions.CanSelectFiles; |
||||
} |
||||
if (CanSelectFolders) |
||||
{ |
||||
options |= TreeFileExplorerOptions.CanSelectFolders; |
||||
} |
||||
if (IndexFiles) |
||||
{ |
||||
options |= TreeFileExplorerOptions.IndexFiles; |
||||
} |
||||
if (IndexFolders) |
||||
{ |
||||
options |= TreeFileExplorerOptions.IndexFolders; |
||||
} |
||||
|
||||
return options; |
||||
} |
||||
|
||||
/// <inheritdoc /> |
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) |
||||
{ |
||||
base.OnApplyTemplate(e); |
||||
|
||||
if (RootItem is null) |
||||
{ |
||||
RootItem = RootPath is null |
||||
? null |
||||
: new TreeFileExplorerDirectory(new DirectoryPath(RootPath), GetOptions()); |
||||
} |
||||
} |
||||
|
||||
/// <inheritdoc /> |
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) |
||||
{ |
||||
base.OnPropertyChanged(change); |
||||
|
||||
if (change.Property == RootPathProperty) |
||||
{ |
||||
var path = change.GetNewValue<string?>(); |
||||
RootItem = path is null |
||||
? null |
||||
: new TreeFileExplorerDirectory(new DirectoryPath(path), GetOptions()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,50 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using StabilityMatrix.Core.Models.FileInterfaces; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Models.TreeFileExplorer; |
||||
|
||||
public class TreeFileExplorerDirectory(IPathObject path, TreeFileExplorerOptions options) |
||||
: TreeFileExplorerItem(path, options) |
||||
{ |
||||
public IEnumerable<TreeFileExplorerItem> Children => |
||||
GetChildren(Path, Options) |
||||
.OrderByDescending(item => item.Path is DirectoryPath) |
||||
.ThenBy(item => item.Path.Name); |
||||
|
||||
private static IEnumerable<TreeFileExplorerItem> GetChildren( |
||||
IPathObject pathObject, |
||||
TreeFileExplorerOptions options |
||||
) |
||||
{ |
||||
return pathObject switch |
||||
{ |
||||
FilePath => Enumerable.Empty<TreeFileExplorerItem>(), |
||||
DirectoryPath directoryPath => GetChildren(directoryPath, options), |
||||
_ => throw new NotSupportedException() |
||||
}; |
||||
} |
||||
|
||||
private static IEnumerable<TreeFileExplorerItem> GetChildren( |
||||
DirectoryPath directoryPath, |
||||
TreeFileExplorerOptions options |
||||
) |
||||
{ |
||||
if (options.HasFlag(TreeFileExplorerOptions.IndexFiles)) |
||||
{ |
||||
foreach (var file in directoryPath.EnumerateFiles()) |
||||
{ |
||||
yield return new TreeFileExplorerFile(file, options); |
||||
} |
||||
} |
||||
|
||||
if (options.HasFlag(TreeFileExplorerOptions.IndexFolders)) |
||||
{ |
||||
foreach (var directory in directoryPath.EnumerateDirectories()) |
||||
{ |
||||
yield return new TreeFileExplorerDirectory(directory, options); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
using StabilityMatrix.Core.Models.FileInterfaces; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Models.TreeFileExplorer; |
||||
|
||||
public class TreeFileExplorerFile(IPathObject path, TreeFileExplorerOptions options) |
||||
: TreeFileExplorerItem(path, options); |
@ -0,0 +1,10 @@
|
||||
using StabilityMatrix.Core.Models.FileInterfaces; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Models.TreeFileExplorer; |
||||
|
||||
public class TreeFileExplorerItem(IPathObject path, TreeFileExplorerOptions options) |
||||
{ |
||||
public IPathObject Path { get; } = path; |
||||
|
||||
public TreeFileExplorerOptions Options { get; } = options; |
||||
} |
@ -0,0 +1,15 @@
|
||||
using System; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Models.TreeFileExplorer; |
||||
|
||||
[Flags] |
||||
public enum TreeFileExplorerOptions |
||||
{ |
||||
None = 0, |
||||
|
||||
IndexFiles = 1 << 5, |
||||
IndexFolders = 1 << 6, |
||||
|
||||
CanSelectFiles = 1 << 10, |
||||
CanSelectFolders = 1 << 11, |
||||
} |
Loading…
Reference in new issue