From 1081b4286820159afd98b5481834b7eb3e63e6e8 Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Fri, 22 Nov 2024 18:58:09 -0600 Subject: [PATCH 01/64] snapshot of file picker storage api --- .../ViewModels/FileOpenPickerViewModel.cs | 12 ++++ .../ViewModels/FileSavePickerViewModel.cs | 12 ++++ .../ViewModels/FolderPickerViewModel.cs | 12 ++++ .../Controls/ViewModels/PickerViewModel.cs | 55 +++++++++++++++++++ .../Controls/Views/FileOpenPicker.axaml | 42 ++++++++++++++ .../Controls/Views/FileOpenPicker.axaml.cs | 44 +++++++++++++++ 6 files changed, 177 insertions(+) create mode 100644 src/Consolonia.Core/Controls/ViewModels/FileOpenPickerViewModel.cs create mode 100644 src/Consolonia.Core/Controls/ViewModels/FileSavePickerViewModel.cs create mode 100644 src/Consolonia.Core/Controls/ViewModels/FolderPickerViewModel.cs create mode 100644 src/Consolonia.Core/Controls/ViewModels/PickerViewModel.cs create mode 100644 src/Consolonia.Core/Controls/Views/FileOpenPicker.axaml create mode 100644 src/Consolonia.Core/Controls/Views/FileOpenPicker.axaml.cs diff --git a/src/Consolonia.Core/Controls/ViewModels/FileOpenPickerViewModel.cs b/src/Consolonia.Core/Controls/ViewModels/FileOpenPickerViewModel.cs new file mode 100644 index 00000000..ca5cc011 --- /dev/null +++ b/src/Consolonia.Core/Controls/ViewModels/FileOpenPickerViewModel.cs @@ -0,0 +1,12 @@ +using Avalonia.Platform.Storage; + +namespace Consolonia.Core.Controls.ViewModels +{ + public class FileOpenPickerViewModel : PickerViewModel + { + public FileOpenPickerViewModel(FilePickerOpenOptions options) + : base(options) + { + } + } +} diff --git a/src/Consolonia.Core/Controls/ViewModels/FileSavePickerViewModel.cs b/src/Consolonia.Core/Controls/ViewModels/FileSavePickerViewModel.cs new file mode 100644 index 00000000..97205051 --- /dev/null +++ b/src/Consolonia.Core/Controls/ViewModels/FileSavePickerViewModel.cs @@ -0,0 +1,12 @@ +using Avalonia.Platform.Storage; + +namespace Consolonia.Core.Controls.ViewModels +{ + public class FileSavePickerViewModel : PickerViewModel + { + public FileSavePickerViewModel(FilePickerSaveOptions options) + : base(options) + { + } + } +} diff --git a/src/Consolonia.Core/Controls/ViewModels/FolderPickerViewModel.cs b/src/Consolonia.Core/Controls/ViewModels/FolderPickerViewModel.cs new file mode 100644 index 00000000..f6618541 --- /dev/null +++ b/src/Consolonia.Core/Controls/ViewModels/FolderPickerViewModel.cs @@ -0,0 +1,12 @@ +using Avalonia.Platform.Storage; + +namespace Consolonia.Core.Controls.ViewModels +{ + public class FolderPickerViewModel : PickerViewModel + { + public FolderPickerViewModel(FolderPickerOpenOptions options) + : base(options) + { + } + } +} diff --git a/src/Consolonia.Core/Controls/ViewModels/PickerViewModel.cs b/src/Consolonia.Core/Controls/ViewModels/PickerViewModel.cs new file mode 100644 index 00000000..e7230190 --- /dev/null +++ b/src/Consolonia.Core/Controls/ViewModels/PickerViewModel.cs @@ -0,0 +1,55 @@ +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using Avalonia.Platform.Storage; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Consolonia.Core.Controls.ViewModels +{ + + public partial class PickerViewModel : ObservableObject + where TPickerOptions : PickerOptions + { + public PickerViewModel(TPickerOptions options) + { + Options = options; + PropertyChanged += PickerViewModel_PropertyChanged; + CurrentFolder = options.SuggestedStartLocation; + } + + + [ObservableProperty] + private TPickerOptions _options; + + [ObservableProperty] + private IStorageFolder _currentFolder; + + [ObservableProperty] + private ObservableCollection _items = new ObservableCollection(); + + [ObservableProperty] + private IStorageItem _selectedItem; + + private async void PickerViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(CurrentFolder): + Items.Clear(); + + await foreach (var item in CurrentFolder.GetItemsAsync()) + { + if (item is IStorageFolder) + this.Items.Add(item); + } + + await foreach (var item in CurrentFolder.GetItemsAsync()) + { + if (item is IStorageItem) + this.Items.Add(item); + } + break; + } + } + } +} diff --git a/src/Consolonia.Core/Controls/Views/FileOpenPicker.axaml b/src/Consolonia.Core/Controls/Views/FileOpenPicker.axaml new file mode 100644 index 00000000..5376d677 --- /dev/null +++ b/src/Consolonia.Core/Controls/Views/FileOpenPicker.axaml @@ -0,0 +1,42 @@ + + + r + 📁 + + + + + + + 📁 + + + + + + 📰 + + + + + + + Selected: + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs new file mode 100644 index 00000000..7d5141a8 --- /dev/null +++ b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Avalonia.Media; +using Avalonia.Platform.Storage; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Consolonia.Gallery.Gallery.GalleryViews +{ + public partial class GalleryStorage : UserControl + { + public GalleryStorage() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + this.DataContext = new GalleryStorageViewModel(); + } + + private async void OnOpenFile(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + IClassicDesktopStyleApplicationLifetime lifetime = App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime; + if (lifetime != null) + { + var storageProvider = lifetime.MainWindow.StorageProvider; + if (storageProvider.CanOpen) + { + var results = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions() + { + Title = "Open File", + FileTypeFilter = new List() + { + new FilePickerFileType("Text") + { + Patterns = new List() { "*.txt" } + } + }, + }); + if (results.Any()) + { + + } + } + } + } + + private void OnOpenFolder(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { } + private void OnSaveFile(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + } + } + + public partial class GalleryStorageViewModel : ObservableObject + { + [ObservableProperty] + private IStorageFile _file; + + [ObservableProperty] + private IStorageFolder _folder; + } +} \ No newline at end of file diff --git a/src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml b/src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml index 06c80b17..fc6c7d52 100644 --- a/src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml +++ b/src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml @@ -1,7 +1,7 @@ - - - DialogWrap.axaml - - - diff --git a/src/Consolonia.Themes.TurboVision/Templates/Controls/DialogWindow.axaml b/src/Consolonia.Themes.TurboVision/Templates/Controls/DialogWindow.axaml index 0627a67c..a288dcc5 100644 --- a/src/Consolonia.Themes.TurboVision/Templates/Controls/DialogWindow.axaml +++ b/src/Consolonia.Themes.TurboVision/Templates/Controls/DialogWindow.axaml @@ -1,6 +1,6 @@ diff --git a/src/Consolonia.Themes.TurboVision/Templates/Controls/Window.axaml b/src/Consolonia.Themes.TurboVision/Templates/Controls/Window.axaml index 041ca03d..98995b5e 100644 --- a/src/Consolonia.Themes.TurboVision/Templates/Controls/Window.axaml +++ b/src/Consolonia.Themes.TurboVision/Templates/Controls/Window.axaml @@ -1,5 +1,5 @@ From 1bf295003084072db529d8be3a7e1dc3f80c52ca Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Sat, 23 Nov 2024 18:18:45 -0600 Subject: [PATCH 12/64] file type filtering --- .../Controls/FileOpenPicker.axaml | 15 ++++++--- .../Controls/FileOpenPicker.axaml.cs | 31 ++++++++++++++++--- .../Controls/FileSavePicker.axaml.cs | 28 ++++++++++++++++- .../Controls/FolderOpenPicker.axaml.cs | 7 ++++- .../Controls/PickerViewModel.cs | 9 +++--- .../GalleryViews/GalleryStorage.axaml.cs | 13 ++++++++ 6 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/Consolonia.Core/Controls/FileOpenPicker.axaml b/src/Consolonia.Core/Controls/FileOpenPicker.axaml index 69bfd5fc..1f2819a9 100644 --- a/src/Consolonia.Core/Controls/FileOpenPicker.axaml +++ b/src/Consolonia.Core/Controls/FileOpenPicker.axaml @@ -29,10 +29,17 @@ - - - - + + + + + + + + + + + - - + + + - - + Files: + + + + + + + - - + Folders: + + + + + + + \ No newline at end of file diff --git a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs index 4eb60fb6..120d5dcc 100644 --- a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs +++ b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs @@ -1,21 +1,24 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Reactive.Joins; -using System.Reactive.Linq; -using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Avalonia.Media; using Avalonia.Platform.Storage; using CommunityToolkit.Mvvm.ComponentModel; using Consolonia.Core.Infrastructure; -using Microsoft.VisualBasic.FileIO; namespace Consolonia.Gallery.Gallery.GalleryViews { + public partial class GalleryStorageViewModel : ObservableObject + { + [ObservableProperty] + private IReadOnlyList _files; + + [ObservableProperty] + private IReadOnlyList _folders; + } + public partial class GalleryStorage : UserControl { public GalleryStorage() @@ -119,13 +122,5 @@ private async void OnSaveFile(object sender, Avalonia.Interactivity.RoutedEventA } } } - - public partial class GalleryStorageViewModel : ObservableObject - { - [ObservableProperty] - private IReadOnlyList _files; - - [ObservableProperty] - private IReadOnlyList _folders; - } + } \ No newline at end of file From 439a91ee69f6d275d97087b0b147b9d0dac12408 Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Sat, 23 Nov 2024 21:01:59 -0600 Subject: [PATCH 14/64] clean up warningsc --- src/Consolonia.Core/Consolonia.Core.csproj | 11 +++++++---- src/Consolonia.Core/Controls/DialogWindow.cs | 18 +++++++++--------- .../Controls/FileOpenPicker.axaml.cs | 6 +++--- .../Controls/FileSavePicker.axaml.cs | 13 +++++++------ .../Controls/FolderOpenPicker.axaml.cs | 6 +++--- .../Controls/PickerViewModel.cs | 6 +++--- .../Infrastructure/ConsoleWindow.cs | 4 ++-- .../ConsoloniaStorageProvider.cs | 8 ++++---- .../InputLessDefaultNetConsole.cs | 4 ++-- .../Infrastructure/SystemStorageFile.cs | 6 +++--- .../Infrastructure/SystemStorageFolder.cs | 6 +++--- src/Consolonia.NUnit/Consolonia.NUnit.csproj | 4 ++++ src/Consolonia.NUnit/ConsoloniaAppTestBase.cs | 2 +- src/Consolonia.NUnit/UnitTestConsole.cs | 4 ++-- 14 files changed, 53 insertions(+), 45 deletions(-) diff --git a/src/Consolonia.Core/Consolonia.Core.csproj b/src/Consolonia.Core/Consolonia.Core.csproj index 6c9ff3b5..dbd62c78 100644 --- a/src/Consolonia.Core/Consolonia.Core.csproj +++ b/src/Consolonia.Core/Consolonia.Core.csproj @@ -1,10 +1,13 @@ + + CA2007; + - - - - + + + + diff --git a/src/Consolonia.Core/Controls/DialogWindow.cs b/src/Consolonia.Core/Controls/DialogWindow.cs index 36cc9077..1c9fe181 100644 --- a/src/Consolonia.Core/Controls/DialogWindow.cs +++ b/src/Consolonia.Core/Controls/DialogWindow.cs @@ -32,13 +32,13 @@ public class DialogWindow : UserControl public static readonly StyledProperty CanResizeProperty = AvaloniaProperty.Register(nameof(CanResize), true); - public static readonly StyledProperty IconProperty = - AvaloniaProperty.Register(nameof(Icon)); + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon)); private Size _contentSize; private ContentPresenter _partContentPresenter; - private TaskCompletionSource _taskCompletionSource; + private TaskCompletionSource _taskCompletionSource; static DialogWindow() @@ -86,7 +86,7 @@ public bool CanResize /// /// Gets or sets the icon of the window. /// - public string? Icon + public string Icon { get => GetValue(IconProperty); set => SetValue(IconProperty, value); @@ -158,7 +158,7 @@ private void Window_SizeChanged(object sender, SizeChangedEventArgs e) } // ReSharper disable once VirtualMemberNeverOverridden.Global overriden in other packages, why resharper suggests this? - public virtual void CloseDialog(object? result = null) + public virtual void CloseDialog(object result = null) { DialogHost dialogHost = GetDialogHost(this); dialogHost.PopInternal(this); @@ -174,9 +174,9 @@ public async Task ShowDialogAsync(Control parent) if (_taskCompletionSource != null) throw new NotImplementedException(); - _taskCompletionSource = new TaskCompletionSource(); + _taskCompletionSource = new TaskCompletionSource(); ShowDialogInternal(parent); - await _taskCompletionSource.Task.ConfigureAwait(false); + await _taskCompletionSource.Task; } public async Task ShowDialogAsync(Control parent) @@ -184,9 +184,9 @@ public async Task ShowDialogAsync(Control parent) if (_taskCompletionSource != null) throw new NotImplementedException(); - _taskCompletionSource = new TaskCompletionSource(); + _taskCompletionSource = new TaskCompletionSource(); ShowDialogInternal(parent); - var result = await _taskCompletionSource.Task.ConfigureAwait(false); + var result = await _taskCompletionSource.Task; return (T)result; } diff --git a/src/Consolonia.Core/Controls/FileOpenPicker.axaml.cs b/src/Consolonia.Core/Controls/FileOpenPicker.axaml.cs index cc7a9e4f..6cae68e0 100644 --- a/src/Consolonia.Core/Controls/FileOpenPicker.axaml.cs +++ b/src/Consolonia.Core/Controls/FileOpenPicker.axaml.cs @@ -57,7 +57,7 @@ private void InitializeComponent() AvaloniaXamlLoader.Load(this); } - private void OnDoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) + private void OnDoubleTapped(object sender, Avalonia.Input.TappedEventArgs e) { var listbox = (ListBox)sender; if (listbox.SelectedItem is SystemStorageFolder folder) @@ -73,13 +73,13 @@ private void OnDoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) } } - private void OnOK(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnOK(object sender, Avalonia.Interactivity.RoutedEventArgs e) { var model = (FileOpenPickerViewModel)this.DataContext; this.CloseDialog(new IStorageFile[] { model.SelectedFile }); } - private void OnCancel(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnCancel(object sender, Avalonia.Interactivity.RoutedEventArgs e) { this.CloseDialog(null); } diff --git a/src/Consolonia.Core/Controls/FileSavePicker.axaml.cs b/src/Consolonia.Core/Controls/FileSavePicker.axaml.cs index 5c9f61d3..02b81b56 100644 --- a/src/Consolonia.Core/Controls/FileSavePicker.axaml.cs +++ b/src/Consolonia.Core/Controls/FileSavePicker.axaml.cs @@ -13,14 +13,15 @@ public partial class FileSavePickerViewModel : PickerViewModel ((FileSavePickerViewModel)DataContext).Options; - private void OnDoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) + private void OnDoubleTapped(object sender, Avalonia.Input.TappedEventArgs e) { var listbox = (ListBox)sender; if (listbox.SelectedItem is SystemStorageFolder folder) @@ -76,13 +77,13 @@ private void OnDoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) } } - private void OnOK(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnOK(object sender, Avalonia.Interactivity.RoutedEventArgs e) { var model = (FileSavePickerViewModel)this.DataContext; this.CloseDialog(new IStorageFile[] { (IStorageFile)model.SelectedItem }); } - private void OnCancel(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnCancel(object sender, Avalonia.Interactivity.RoutedEventArgs e) { this.CloseDialog(null); } diff --git a/src/Consolonia.Core/Controls/FolderOpenPicker.axaml.cs b/src/Consolonia.Core/Controls/FolderOpenPicker.axaml.cs index c29ed6f2..33f6e753 100644 --- a/src/Consolonia.Core/Controls/FolderOpenPicker.axaml.cs +++ b/src/Consolonia.Core/Controls/FolderOpenPicker.axaml.cs @@ -39,7 +39,7 @@ private void InitializeComponent() public FolderPickerOpenOptions Options => ((FolderPickerViewModel)DataContext).Options; - private void OnDoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) + private void OnDoubleTapped(object sender, Avalonia.Input.TappedEventArgs e) { var listbox = (ListBox)sender; if (listbox.SelectedItem is SystemStorageFolder folder) @@ -55,13 +55,13 @@ private void OnDoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) } } - private void OnOK(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnOK(object sender, Avalonia.Interactivity.RoutedEventArgs e) { var model = (FileSavePickerViewModel)this.DataContext; this.CloseDialog(new IStorageFolder[] { model.SelectedFolder }); } - private void OnCancel(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnCancel(object sender, Avalonia.Interactivity.RoutedEventArgs e) { this.CloseDialog(null); } diff --git a/src/Consolonia.Core/Controls/PickerViewModel.cs b/src/Consolonia.Core/Controls/PickerViewModel.cs index 6a63d82b..80fccff5 100644 --- a/src/Consolonia.Core/Controls/PickerViewModel.cs +++ b/src/Consolonia.Core/Controls/PickerViewModel.cs @@ -11,7 +11,7 @@ namespace Consolonia.Core.Controls public abstract partial class PickerViewModel : ObservableObject where TPickerOptions : PickerOptions { - public PickerViewModel(TPickerOptions options) + protected PickerViewModel(TPickerOptions options) { Options = options; CurrentFolderPath = options.SuggestedStartLocation?.Path.LocalPath; @@ -38,9 +38,9 @@ public PickerViewModel(TPickerOptions options) [NotifyPropertyChangedFor(nameof(SelectedFolder))] private IStorageItem _selectedItem; - public IStorageFile SelectedFile => _selectedItem as IStorageFile; + public IStorageFile SelectedFile => SelectedItem as IStorageFile; - public IStorageFolder SelectedFolder => _selectedItem as IStorageFolder; + public IStorageFolder SelectedFolder => SelectedItem as IStorageFolder; private async void PickerViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { diff --git a/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs b/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs index c689cc7b..e8729947 100644 --- a/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs +++ b/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs @@ -317,7 +317,7 @@ private void OnConsoleOnResized() try { // Wait for the delay period, this task will be canceled if another refresh comes in. - await Task.Delay(_resizeDelay, _resizeCancellationTokenSource.Token).ConfigureAwait(false); + await Task.Delay(_resizeDelay, _resizeCancellationTokenSource.Token); // dispatch to the UI thread Dispatcher.UIThread.Post(() => @@ -362,7 +362,7 @@ await Dispatcher.UIThread.InvokeAsync(() => #pragma warning restore CS0618 // Type or member is obsolete Input!(rawInputEventArgs); handled = rawInputEventArgs.Handled; - }, DispatcherPriority.Input).GetTask().ConfigureAwait(true); + }, DispatcherPriority.Input); if (!handled && !char.IsControl(keyChar)) Dispatcher.UIThread.Post(() => diff --git a/src/Consolonia.Core/Infrastructure/ConsoloniaStorageProvider.cs b/src/Consolonia.Core/Infrastructure/ConsoloniaStorageProvider.cs index aeebfa1b..92758248 100644 --- a/src/Consolonia.Core/Infrastructure/ConsoloniaStorageProvider.cs +++ b/src/Consolonia.Core/Infrastructure/ConsoloniaStorageProvider.cs @@ -18,7 +18,7 @@ public class ConsoloniaStorageProvider : IStorageProvider public bool CanPickFolder => true; - public async Task OpenFileBookmarkAsync(string bookmark) + public Task OpenFileBookmarkAsync(string bookmark) { throw new NotImplementedException(); } @@ -28,7 +28,7 @@ public async Task> OpenFilePickerAsync(FilePickerOpe var mainWindow = ((IClassicDesktopStyleApplicationLifetime)Application.Current.ApplicationLifetime).MainWindow; var picker = new FileOpenPicker(options); - var results = await picker.ShowDialogAsync(mainWindow).ConfigureAwait(false); + var results = await picker.ShowDialogAsync(mainWindow); return results; } @@ -42,7 +42,7 @@ public async Task> OpenFolderPickerAsync(FolderPic var mainWindow = ((IClassicDesktopStyleApplicationLifetime)Application.Current.ApplicationLifetime).MainWindow; var picker = new FolderOpenPicker(options); - var results = await picker.ShowDialogAsync(mainWindow).ConfigureAwait(false); + var results = await picker.ShowDialogAsync(mainWindow); return results; } @@ -51,7 +51,7 @@ public async Task SaveFilePickerAsync(FilePickerSaveOptions option var mainWindow = ((IClassicDesktopStyleApplicationLifetime)Application.Current.ApplicationLifetime).MainWindow; var picker = new FileSavePicker(options); - var results = await picker.ShowDialogAsync(mainWindow).ConfigureAwait(false); + var results = await picker.ShowDialogAsync(mainWindow); return results; } diff --git a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs index d23a5d3d..96260749 100644 --- a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs +++ b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs @@ -203,10 +203,10 @@ protected void StartSizeCheckTimerAsync(uint slowInterval = 1500) { Task pauseTask = PauseTask; if (pauseTask != null) - await pauseTask.ConfigureAwait(false); + await pauseTask; int timeout = (int)(CheckActualizeTheSize() ? 1 : slowInterval); - await Task.Delay(timeout).ConfigureAwait(false); + await Task.Delay(timeout); } }); } diff --git a/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs b/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs index f0fc10c1..e91bbcd2 100644 --- a/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs +++ b/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs @@ -5,7 +5,7 @@ namespace Consolonia.Core.Infrastructure { - public class SystemStorageFile : IStorageFile + public sealed class SystemStorageFile : IStorageFile { private FileInfo _fileInfo; @@ -62,13 +62,13 @@ public Task MoveAsync(IStorageFolder destination) public async Task OpenReadAsync() { - await Task.CompletedTask.ConfigureAwait(false); + await Task.CompletedTask; return _fileInfo.OpenRead(); } public async Task OpenWriteAsync() { - await Task.CompletedTask.ConfigureAwait(false); + await Task.CompletedTask; return _fileInfo.OpenWrite(); } diff --git a/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs b/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs index 3109eb84..f963d42f 100644 --- a/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs +++ b/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs @@ -8,7 +8,7 @@ namespace Consolonia.Core.Infrastructure { [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] - public class SystemStorageFolder : IStorageFolder + public sealed class SystemStorageFolder : IStorageFolder { private DirectoryInfo _directoryInfo; private bool _isParent; @@ -37,7 +37,7 @@ public SystemStorageFolder(DirectoryInfo directoryInfo, bool isParent = false) public async Task CreateFileAsync(string name) { - await Task.CompletedTask.ConfigureAwait(false); + await Task.CompletedTask; var path = System.IO.Path.Combine(_directoryInfo.FullName, name); using (var stream = File.Create(path)) @@ -70,7 +70,7 @@ public Task GetBasicPropertiesAsync() public async IAsyncEnumerable GetItemsAsync() { - await Task.CompletedTask.ConfigureAwait(false); + await Task.CompletedTask; if (_directoryInfo.Exists) { diff --git a/src/Consolonia.NUnit/Consolonia.NUnit.csproj b/src/Consolonia.NUnit/Consolonia.NUnit.csproj index dae1791f..9f8ac3f0 100644 --- a/src/Consolonia.NUnit/Consolonia.NUnit.csproj +++ b/src/Consolonia.NUnit/Consolonia.NUnit.csproj @@ -6,6 +6,10 @@ true + + CA2007; + + diff --git a/src/Consolonia.NUnit/ConsoloniaAppTestBase.cs b/src/Consolonia.NUnit/ConsoloniaAppTestBase.cs index 9bb49037..2cc76d4b 100644 --- a/src/Consolonia.NUnit/ConsoloniaAppTestBase.cs +++ b/src/Consolonia.NUnit/ConsoloniaAppTestBase.cs @@ -70,7 +70,7 @@ await Task.Run(async () => { Window mainWindow = _lifetime?.MainWindow; return mainWindow != null; - }).GetTask().ConfigureAwait(false); + }); if (windowFound) return; } diff --git a/src/Consolonia.NUnit/UnitTestConsole.cs b/src/Consolonia.NUnit/UnitTestConsole.cs index a74040ac..baf7b37d 100644 --- a/src/Consolonia.NUnit/UnitTestConsole.cs +++ b/src/Consolonia.NUnit/UnitTestConsole.cs @@ -94,9 +94,9 @@ public async Task StringInput(string input) // todo: check why Yield is not enough: https://github.com/jinek/Consolonia/runs/7055199426?check_suite_focus=true const ulong interval = 50; KeyEvent?.Invoke(key, c, RawInputModifiers.None, true, timestamp); - await Task.Delay((int)interval).ConfigureAwait(false); + await Task.Delay((int)interval); KeyEvent?.Invoke(key, c, RawInputModifiers.None, false, timestamp + interval); - await Task.Delay((int)interval).ConfigureAwait(false); + await Task.Delay((int)interval); } await WaitRendered().ConfigureAwait(true); From 26a5987b6a2d6f9ccaec4034081a775207f254c6 Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Sun, 24 Nov 2024 16:20:58 -0600 Subject: [PATCH 15/64] multiple select works focus works escape works --- src/Consolonia.Core/Consolonia.Core.csproj | 6 ++ .../Controls/FileOpenPicker.axaml | 27 +++--- .../Controls/FileOpenPicker.axaml.cs | 79 +++++++++--------- .../Controls/FileOpenPickerViewModel.cs | 51 ++++++++++++ .../Controls/FileSavePicker.axaml | 8 +- .../Controls/FileSavePicker.axaml.cs | 54 ++---------- .../Controls/FileSavePickerViewModel.cs | 45 ++++++++++ .../Controls/FolderOpenPicker.axaml.cs | 69 --------------- ...derOpenPicker.axaml => FolderPicker.axaml} | 19 ++--- .../Controls/FolderPicker.axaml.cs | 83 +++++++++++++++++++ .../Controls/FolderPickerViewModel.cs | 30 +++++++ ...kerViewModel.cs => PickerViewModelBase.cs} | 12 +-- .../ConsoloniaStorageProvider.cs | 6 +- .../Gallery/GalleryViews/GalleryStorage.axaml | 67 +++++++++------ .../GalleryViews/GalleryStorage.axaml.cs | 42 +++++++--- 15 files changed, 364 insertions(+), 234 deletions(-) create mode 100644 src/Consolonia.Core/Controls/FileOpenPickerViewModel.cs create mode 100644 src/Consolonia.Core/Controls/FileSavePickerViewModel.cs delete mode 100644 src/Consolonia.Core/Controls/FolderOpenPicker.axaml.cs rename src/Consolonia.Core/Controls/{FolderOpenPicker.axaml => FolderPicker.axaml} (70%) create mode 100644 src/Consolonia.Core/Controls/FolderPicker.axaml.cs create mode 100644 src/Consolonia.Core/Controls/FolderPickerViewModel.cs rename src/Consolonia.Core/Controls/{PickerViewModel.cs => PickerViewModelBase.cs} (81%) diff --git a/src/Consolonia.Core/Consolonia.Core.csproj b/src/Consolonia.Core/Consolonia.Core.csproj index dbd62c78..9e65a4cf 100644 --- a/src/Consolonia.Core/Consolonia.Core.csproj +++ b/src/Consolonia.Core/Consolonia.Core.csproj @@ -21,4 +21,10 @@ + + + FolderPicker.axaml + + + diff --git a/src/Consolonia.Core/Controls/FileOpenPicker.axaml b/src/Consolonia.Core/Controls/FileOpenPicker.axaml index 1f2819a9..dff70e1b 100644 --- a/src/Consolonia.Core/Controls/FileOpenPicker.axaml +++ b/src/Consolonia.Core/Controls/FileOpenPicker.axaml @@ -6,14 +6,15 @@ VerticalAlignment="Center" x:DataType="controls:FileOpenPickerViewModel" x:Class="Consolonia.Core.Controls.FileOpenPicker" - Icon="📰" + Icon="📰" CancelOnEscape="True" Title="{Binding Options.Title}"> - + @@ -29,20 +30,16 @@ - - - - - - - - - - - + + + + + + + - - - - - - Files: - - - - - - - - - - Folders: - - - - - - - - + + + + + + + + + + + + + + Files + + + + + + + + + + + + + + + + Folders + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs index 120d5dcc..74c6fa7f 100644 --- a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs +++ b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; @@ -26,6 +27,8 @@ public GalleryStorage() InitializeComponent(); } + public GalleryStorageViewModel ViewModel => (GalleryStorageViewModel)DataContext; + private void InitializeComponent() { AvaloniaXamlLoader.Load(this); @@ -33,6 +36,15 @@ private void InitializeComponent() } private async void OnOpenFile(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + await OpenFiles("Open file", allowMultiple: false); + } + private async void OnOpenMultipleFiles(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + await OpenFiles("Open files", allowMultiple: true); + } + + private async Task OpenFiles(string title, bool allowMultiple) { IClassicDesktopStyleApplicationLifetime lifetime = App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime; if (lifetime != null) @@ -42,7 +54,8 @@ private async void OnOpenFile(object sender, Avalonia.Interactivity.RoutedEventA { var files = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions() { - Title = "Open File", + Title = title, + AllowMultiple = allowMultiple, SuggestedStartLocation = new SystemStorageFolder(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments))), FileTypeFilter = new List() { @@ -65,13 +78,22 @@ private async void OnOpenFile(object sender, Avalonia.Interactivity.RoutedEventA }, }); - var model = (GalleryStorageViewModel)this.DataContext; - model.Files = files; + ViewModel.Files = files; } } } private async void OnOpenFolder(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + await OpenFolders(title: "Selecct a folder", allowMultiple: false); + } + + private async void OnOpenMultipleFolders(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + await OpenFolders(title: "Select folder(s)", allowMultiple: true); + } + + private async Task OpenFolders(string title, bool allowMultiple) { IClassicDesktopStyleApplicationLifetime lifetime = App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime; if (lifetime != null) @@ -81,14 +103,15 @@ private async void OnOpenFolder(object sender, Avalonia.Interactivity.RoutedEven { var folders = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions() { - Title = "Select a folder", - AllowMultiple = false + Title = title, + SuggestedStartLocation = new SystemStorageFolder(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments))), + AllowMultiple = allowMultiple }); - var model = (GalleryStorageViewModel)this.DataContext; - model.Folders = folders; + ViewModel.Folders = folders; } } } + private async void OnSaveFile(object sender, Avalonia.Interactivity.RoutedEventArgs e) { IClassicDesktopStyleApplicationLifetime lifetime = App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime; @@ -116,11 +139,10 @@ private async void OnSaveFile(object sender, Avalonia.Interactivity.RoutedEventA }, }); - var model = (GalleryStorageViewModel)this.DataContext; - model.Files = new List() { file }; + ViewModel.Files = new List() { file }; } } } } - + } \ No newline at end of file From bd1821ef4234febe9b5f7887ef2021f38248b064 Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Sun, 24 Nov 2024 16:29:08 -0600 Subject: [PATCH 16/64] update storagefile and storage folder display attributes --- src/Consolonia.Core/Infrastructure/SystemStorageFile.cs | 2 ++ src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs b/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs index e91bbcd2..c3bb2b02 100644 --- a/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs +++ b/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs @@ -1,10 +1,12 @@ using System; +using System.Diagnostics; using System.IO; using System.Threading.Tasks; using Avalonia.Platform.Storage; namespace Consolonia.Core.Infrastructure { + [DebuggerDisplay("{Name}")] public sealed class SystemStorageFile : IStorageFile { private FileInfo _fileInfo; diff --git a/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs b/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs index f963d42f..801755fa 100644 --- a/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs +++ b/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs @@ -2,12 +2,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Avalonia.Platform.Storage; namespace Consolonia.Core.Infrastructure { - [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] + [DebuggerDisplay("{Name}")] public sealed class SystemStorageFolder : IStorageFolder { private DirectoryInfo _directoryInfo; @@ -106,10 +107,5 @@ public Task SaveBookmarkAsync() { throw new NotImplementedException(); } - - private string GetDebuggerDisplay() - { - return _directoryInfo.FullName; - } } } From d256979580d2ef94496efbc252756d7e361db22a Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Sun, 24 Nov 2024 16:30:00 -0600 Subject: [PATCH 17/64] update display attributes --- src/Consolonia.Core/Infrastructure/SystemStorageFile.cs | 2 +- src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs b/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs index c3bb2b02..ae9ea54a 100644 --- a/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs +++ b/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs @@ -6,7 +6,7 @@ namespace Consolonia.Core.Infrastructure { - [DebuggerDisplay("{Name}")] + [DebuggerDisplay("File: {Name}")] public sealed class SystemStorageFile : IStorageFile { private FileInfo _fileInfo; diff --git a/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs b/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs index 801755fa..e9040b37 100644 --- a/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs +++ b/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs @@ -8,7 +8,7 @@ namespace Consolonia.Core.Infrastructure { - [DebuggerDisplay("{Name}")] + [DebuggerDisplay("Folder: {Name}")] public sealed class SystemStorageFolder : IStorageFolder { private DirectoryInfo _directoryInfo; From 8d8bacba7313cd4a94ddfdca8541717f3b7498b4 Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Sun, 24 Nov 2024 17:43:21 -0600 Subject: [PATCH 18/64] add unit tests for storage --- .../Controls/PickerViewModelBase.cs | 3 + .../Infrastructure/SystemStorageFile.cs | 5 +- .../Infrastructure/SystemStorageFolder.cs | 7 +- .../Consolonia.Core.Tests/StorageTests.cs | 153 ++++++++++++++++++ 4 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 src/Tests/Consolonia.Core.Tests/StorageTests.cs diff --git a/src/Consolonia.Core/Controls/PickerViewModelBase.cs b/src/Consolonia.Core/Controls/PickerViewModelBase.cs index 7bf3620b..69116422 100644 --- a/src/Consolonia.Core/Controls/PickerViewModelBase.cs +++ b/src/Consolonia.Core/Controls/PickerViewModelBase.cs @@ -60,6 +60,9 @@ private async Task LoadCurrentFolder() Items.Clear(); if (CurrentFolder != null) { + + this.Items.Add(new SystemStorageFolder(new DirectoryInfo(Path.Combine(CurrentFolder.Path.LocalPath, "..")), true)); + await foreach (var item in CurrentFolder.GetItemsAsync()) { if (this.FilterItem(item)) diff --git a/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs b/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs index ae9ea54a..142d8fbf 100644 --- a/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs +++ b/src/Consolonia.Core/Infrastructure/SystemStorageFile.cs @@ -58,8 +58,9 @@ public Task GetParentAsync() public Task MoveAsync(IStorageFolder destination) { var path = destination.Path.LocalPath; - _fileInfo.MoveTo(path); - return Task.FromResult((IStorageItem)this); + var targetPath = System.IO.Path.Combine(path, _fileInfo.Name); + _fileInfo.MoveTo(targetPath); + return Task.FromResult((IStorageItem)new SystemStorageFile(targetPath)); } public async Task OpenReadAsync() diff --git a/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs b/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs index e9040b37..e9868c65 100644 --- a/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs +++ b/src/Consolonia.Core/Infrastructure/SystemStorageFolder.cs @@ -75,8 +75,6 @@ public async IAsyncEnumerable GetItemsAsync() if (_directoryInfo.Exists) { - yield return new SystemStorageFolder(_directoryInfo.Parent, isParent: true); - foreach (var folder in _directoryInfo.GetDirectories()) { yield return new SystemStorageFolder(folder); @@ -99,8 +97,9 @@ public Task GetParentAsync() public Task MoveAsync(IStorageFolder destination) { - _directoryInfo.MoveTo(destination.Path.LocalPath); - return Task.FromResult((IStorageItem)new SystemStorageFolder(new DirectoryInfo(destination.Path.LocalPath))); + var targetPath = System.IO.Path.Combine(destination.Path.LocalPath, _directoryInfo.Name); + _directoryInfo.MoveTo(targetPath); + return Task.FromResult((IStorageItem)new SystemStorageFolder(targetPath)); } public Task SaveBookmarkAsync() diff --git a/src/Tests/Consolonia.Core.Tests/StorageTests.cs b/src/Tests/Consolonia.Core.Tests/StorageTests.cs new file mode 100644 index 00000000..f60cc5d6 --- /dev/null +++ b/src/Tests/Consolonia.Core.Tests/StorageTests.cs @@ -0,0 +1,153 @@ +#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task +using System; +using System.IO; +using System.Threading.Tasks; +using Avalonia.Platform.Storage; +using Consolonia.Core.Infrastructure; +using NUnit.Framework; + +namespace Consolonia.Core.Tests +{ + [TestFixture] + public class StorageTests + { + + [Test] + public void DefaultAttributes() + { + var storageProvider = new ConsoloniaStorageProvider(); + Assert.True(storageProvider.CanOpen); + Assert.True(storageProvider.CanSave); + Assert.True(storageProvider.CanPickFolder); + } + + [Test] + public async Task TestFileSemantics() + { + var storageProvider = new ConsoloniaStorageProvider(); + var tempFile = Path.GetTempFileName(); + var file = await storageProvider.TryGetFileFromPathAsync(new Uri($"file://{tempFile}")); + Assert.IsNotNull(file); + Assert.AreEqual(tempFile, file.Path.LocalPath); + + using (var stream = await file.OpenWriteAsync()) + { + using (var streamWriter = new StreamWriter(stream)) + { + streamWriter.Write("Hello world"); + } + } + Assert.True(File.Exists(tempFile)); + Assert.True(File.Exists(file.Path.LocalPath)); + var props = await file.GetBasicPropertiesAsync(); + Assert.AreEqual((DateTimeOffset)File.GetCreationTime(tempFile), props.DateCreated); + Assert.AreEqual((DateTimeOffset)File.GetLastWriteTime(tempFile), props.DateModified); + Assert.AreEqual(new FileInfo(tempFile).Length, (long)props.Size); + + using (var stream = await file.OpenReadAsync()) + { + using (var streamReader = new StreamReader(stream)) + { + var text = await streamReader.ReadToEndAsync(); + Assert.AreEqual("Hello world", text); + } + } + + props = await file.GetBasicPropertiesAsync(); + Assert.AreEqual((DateTimeOffset)File.GetCreationTime(tempFile), props.DateCreated); + Assert.AreEqual((DateTimeOffset)File.GetLastWriteTime(tempFile), props.DateModified); + Assert.AreEqual(new FileInfo(tempFile).Length, (long)props.Size); + + var parentFolder = await file.GetParentAsync(); + Assert.IsNotNull(parentFolder); + Assert.AreEqual(Path.GetDirectoryName(tempFile), parentFolder.Path.LocalPath); + + var subPath = Path.Combine(Path.GetDirectoryName(tempFile), nameof(TestFileSemantics)); + if (Directory.Exists(subPath)) + Directory.Delete(subPath); + + var subFolder = await storageProvider.TryGetFolderFromPathAsync(new Uri($"file://{subPath}")); + Assert.IsNull(subFolder); + + subFolder = await parentFolder.CreateFolderAsync(nameof(TestFileSemantics)); + Assert.IsNotNull(subFolder); + Assert.IsTrue(new DirectoryInfo(subFolder.Path.LocalPath).Exists); + + var newFile = await file.MoveAsync(subFolder); + Assert.True(File.Exists(newFile.Path.LocalPath)); + Assert.False(File.Exists(tempFile)); + Assert.AreEqual(new Uri($"file://{parentFolder.Path.LocalPath}/{nameof(TestFileSemantics)}/{Path.GetFileName(tempFile)}"), newFile.Path); + await newFile.DeleteAsync(); + Assert.False(File.Exists(newFile.Path.LocalPath)); + + await subFolder.DeleteAsync(); + Assert.IsFalse(new DirectoryInfo(subFolder.Path.LocalPath).Exists); + } + + [Test] + public async Task TestWellKnownFolder() + { + var storageProvider = new ConsoloniaStorageProvider(); + var folder = await storageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Pictures); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), folder.Path.LocalPath); + + folder = await storageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), folder.Path.LocalPath); + + folder = await storageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Music); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), folder.Path.LocalPath); + + folder = await storageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Videos); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.MyVideos), folder.Path.LocalPath); + + folder = await storageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Downloads); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), folder.Path.LocalPath); + + folder = await storageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Desktop); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), folder.Path.LocalPath); + } + + + [Test] + public async Task TestFolderSemantics() + { + var storageProvider = new ConsoloniaStorageProvider(); + var tempPath = Environment.GetEnvironmentVariable("TEMP"); + var tempFolder = await storageProvider.TryGetFolderFromPathAsync(new Uri($"file://{tempPath}")); + var testPath = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), nameof(TestFolderSemantics)); + var testFolder = await tempFolder.CreateFolderAsync(nameof(TestFolderSemantics)); + + Assert.IsNotNull(testFolder); + Assert.AreEqual(testPath, testFolder.Path.LocalPath); + Assert.IsFalse(testFolder.CanBookmark); + Assert.AreEqual(nameof(TestFolderSemantics), testFolder.Name); + var file = await testFolder.CreateFileAsync($"{nameof(TestFolderSemantics)}.txt"); + Assert.IsTrue(File.Exists(file.Path.LocalPath)); + + var props = await testFolder.GetBasicPropertiesAsync(); + Assert.AreEqual((DateTimeOffset)Directory.GetCreationTime(testPath), props.DateCreated); + Assert.AreEqual((DateTimeOffset)Directory.GetLastWriteTime(testPath), props.DateModified); + + await file.DeleteAsync(); + Assert.IsFalse(File.Exists(file.Path.LocalPath)); + + var subFolder = await testFolder.CreateFolderAsync("sub"); + Assert.IsTrue(Directory.Exists(subFolder.Path.LocalPath)); + + file = await subFolder.CreateFileAsync($"{nameof(TestFolderSemantics)}.txt"); + Assert.IsTrue(File.Exists(file.Path.LocalPath)); + + await foreach (var item in subFolder.GetItemsAsync()) + { + Assert.AreEqual(file.Path.LocalPath, item.Path.LocalPath); + } + await file.DeleteAsync(); + + await subFolder.DeleteAsync(); + Assert.IsFalse(Directory.Exists(subFolder.Path.LocalPath)); + + await testFolder.DeleteAsync(); + Assert.IsFalse(Directory.Exists(testPath)); + } + } +} \ No newline at end of file From 4ab158372ac2ea9be1f18617bb9354f4f4f8e986 Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Sun, 24 Nov 2024 21:17:39 -0600 Subject: [PATCH 19/64] cleanup sample --- .../GalleryViews/GalleryStorage.axaml.cs | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs index 74c6fa7f..38d1ae09 100644 --- a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs +++ b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryStorage.axaml.cs @@ -59,22 +59,10 @@ private async Task OpenFiles(string title, bool allowMultiple) SuggestedStartLocation = new SystemStorageFolder(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments))), FileTypeFilter = new List() { - new FilePickerFileType("All files") - { - Patterns = new List() { "*" } - }, - new FilePickerFileType("Text") - { - Patterns = new List() { "*.txt" } - }, - new FilePickerFileType("Comma Delimited Files") - { - Patterns = new List() { "*.csv" } - }, - new FilePickerFileType("PDF") - { - Patterns = new List() { "*.pdf" } - } + new FilePickerFileType("All files") { Patterns = new[] { "*" } }, + new FilePickerFileType("Text") { Patterns = new[] { "*.txt" } }, + new FilePickerFileType("Comma Delimited Files") { Patterns = new[] { "*.csv" } }, + new FilePickerFileType("PDF") { Patterns = new[] { "*.pdf" } } }, }); @@ -128,14 +116,9 @@ private async void OnSaveFile(object sender, Avalonia.Interactivity.RoutedEventA SuggestedFileName = "NewFile.txt", FileTypeChoices = new List() { - new FilePickerFileType("Text") - { - Patterns = new List() { "*.txt" } - }, - new FilePickerFileType("Comma delimited values") - { - Patterns = new List() { "*.csv" } - }, + new FilePickerFileType("Text") { Patterns = new[] { "*.txt" } }, + new FilePickerFileType("Comma Delimited Files") { Patterns = new[] { "*.csv" } }, + new FilePickerFileType("PDF") { Patterns = new[] { "*.pdf" } } }, }); From 7c3f6e1cf123c92cfa67b82ef93f404c877e20b1 Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Sun, 24 Nov 2024 21:48:18 -0600 Subject: [PATCH 20/64] lint pass --- .../Controls/FileOpenPicker.axaml | 6 ++--- .../Controls/FileOpenPickerViewModel.cs | 5 ++-- .../Controls/FileSavePickerViewModel.cs | 23 ++++++++----------- .../Controls/FolderPicker.axaml | 2 +- .../Controls/FolderPicker.axaml.cs | 1 - .../Controls/PickerViewModelBase.cs | 16 ++++++------- .../Infrastructure/Extensions.cs | 12 ---------- .../Infrastructure/SystemStorageFolder.cs | 2 +- .../Consolonia.Core.Tests/StorageTests.cs | 6 ++--- 9 files changed, 27 insertions(+), 46 deletions(-) delete mode 100644 src/Consolonia.Core/Infrastructure/Extensions.cs diff --git a/src/Consolonia.Core/Controls/FileOpenPicker.axaml b/src/Consolonia.Core/Controls/FileOpenPicker.axaml index dff70e1b..4718f47f 100644 --- a/src/Consolonia.Core/Controls/FileOpenPicker.axaml +++ b/src/Consolonia.Core/Controls/FileOpenPicker.axaml @@ -17,12 +17,12 @@ SelectionChanged="ListBox_SelectionChanged" DoubleTapped="OnDoubleTapped"> - + - + @@ -38,7 +38,7 @@ -