From 209544dd5a0d9de21421bae4060828a312ce922b Mon Sep 17 00:00:00 2001 From: Yuto Terada Date: Tue, 14 Apr 2026 13:27:11 +0900 Subject: [PATCH 01/21] chore: upgrade Avalonia to 12.0 and related dependencies --- Directory.Packages.props | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5fb8b89ec..ef64cce96 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,10 +5,10 @@ - - - - + + + + @@ -22,10 +22,10 @@ - - - - + + + + @@ -34,14 +34,14 @@ - + - - + + @@ -66,9 +66,9 @@ - - - + + + @@ -77,9 +77,9 @@ - - - + + + From ca17ea6d7fd80afb4a8faed213f526bb429666b3 Mon Sep 17 00:00:00 2001 From: Yuto Terada Date: Tue, 14 Apr 2026 13:31:48 +0900 Subject: [PATCH 02/21] refactor: replace Avalonia.Diagnostics with AvaloniaUI.DiagnosticsSupport and use WithDeveloperTools() --- Directory.Packages.props | 2 +- src/Beutl.ExceptionHandler/Beutl.ExceptionHandler.csproj | 3 ++- src/Beutl.ExceptionHandler/Program.cs | 3 ++- src/Beutl.PackageTools.UI/Beutl.PackageTools.UI.csproj | 8 +++++--- src/Beutl.PackageTools.UI/Program.cs | 3 ++- src/Beutl.WaitingDialog/Beutl.WaitingDialog.csproj | 5 +++-- src/Beutl.WaitingDialog/MainWindow.axaml.cs | 3 --- src/Beutl.WaitingDialog/Program.cs | 3 ++- src/Beutl/Beutl.csproj | 2 +- src/Beutl/Pages/SettingsDialog.axaml.cs | 3 --- src/Beutl/Program.cs | 1 + src/Beutl/Views/MacWindow.axaml.cs | 4 ---- src/Beutl/Views/MainWindow.axaml.cs | 3 --- tests/KeySplineEditor/KeySplineEditor.csproj | 2 +- tests/KeySplineEditor/MainWindow.axaml.cs | 3 --- tests/KeySplineEditor/Program.cs | 3 ++- tests/TextFormattingPlayground/MainWindow.axaml.cs | 3 --- tests/TextFormattingPlayground/Program.cs | 3 ++- .../TextFormattingPlayground.csproj | 2 +- tests/XamlPreview/Program.cs | 3 ++- tests/XamlPreview/XamlPreview.csproj | 6 +++--- 21 files changed, 30 insertions(+), 38 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ef64cce96..755f56653 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,7 +9,7 @@ - + diff --git a/src/Beutl.ExceptionHandler/Beutl.ExceptionHandler.csproj b/src/Beutl.ExceptionHandler/Beutl.ExceptionHandler.csproj index 37eefb9a2..8bc3f8989 100644 --- a/src/Beutl.ExceptionHandler/Beutl.ExceptionHandler.csproj +++ b/src/Beutl.ExceptionHandler/Beutl.ExceptionHandler.csproj @@ -16,7 +16,8 @@ - + diff --git a/src/Beutl.ExceptionHandler/Program.cs b/src/Beutl.ExceptionHandler/Program.cs index 173808706..4041a18bf 100644 --- a/src/Beutl.ExceptionHandler/Program.cs +++ b/src/Beutl.ExceptionHandler/Program.cs @@ -15,5 +15,6 @@ public static void Main(string[] args) => BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .LogToTrace(); + .LogToTrace() + .WithDeveloperTools(); } diff --git a/src/Beutl.PackageTools.UI/Beutl.PackageTools.UI.csproj b/src/Beutl.PackageTools.UI/Beutl.PackageTools.UI.csproj index 1cf2bc303..325459fc1 100644 --- a/src/Beutl.PackageTools.UI/Beutl.PackageTools.UI.csproj +++ b/src/Beutl.PackageTools.UI/Beutl.PackageTools.UI.csproj @@ -23,7 +23,8 @@ - + @@ -43,7 +44,8 @@ - @@ -56,4 +58,4 @@ - + \ No newline at end of file diff --git a/src/Beutl.PackageTools.UI/Program.cs b/src/Beutl.PackageTools.UI/Program.cs index 60666ba14..79ae458fe 100644 --- a/src/Beutl.PackageTools.UI/Program.cs +++ b/src/Beutl.PackageTools.UI/Program.cs @@ -49,5 +49,6 @@ public static AppBuilder BuildAvaloniaApp() { DefaultFamilyName = Media.FontManager.Instance.DefaultTypeface.FontFamily.Name }) - .LogToTrace(); + .LogToTrace() + .WithDeveloperTools(); } diff --git a/src/Beutl.WaitingDialog/Beutl.WaitingDialog.csproj b/src/Beutl.WaitingDialog/Beutl.WaitingDialog.csproj index 9d1cd03e9..d4522edd0 100644 --- a/src/Beutl.WaitingDialog/Beutl.WaitingDialog.csproj +++ b/src/Beutl.WaitingDialog/Beutl.WaitingDialog.csproj @@ -19,10 +19,11 @@ - + - + \ No newline at end of file diff --git a/src/Beutl.WaitingDialog/MainWindow.axaml.cs b/src/Beutl.WaitingDialog/MainWindow.axaml.cs index b9ded6131..0f2246b37 100644 --- a/src/Beutl.WaitingDialog/MainWindow.axaml.cs +++ b/src/Beutl.WaitingDialog/MainWindow.axaml.cs @@ -25,9 +25,6 @@ public MainWindow() InitializeComponent(); ShowAsDialog = true; Topmost = true; -#if DEBUG - this.AttachDevTools(); -#endif var titleOption = new Option("--title"); var subtitleOption = new Option("--subtitle"); var iconOption = new Option("--icon"); diff --git a/src/Beutl.WaitingDialog/Program.cs b/src/Beutl.WaitingDialog/Program.cs index 063337545..42ccc4e81 100644 --- a/src/Beutl.WaitingDialog/Program.cs +++ b/src/Beutl.WaitingDialog/Program.cs @@ -18,5 +18,6 @@ public static void Main(string[] args) public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .LogToTrace(); + .LogToTrace() + .WithDeveloperTools(); } diff --git a/src/Beutl/Beutl.csproj b/src/Beutl/Beutl.csproj index 1e0637bb2..9af6ed161 100644 --- a/src/Beutl/Beutl.csproj +++ b/src/Beutl/Beutl.csproj @@ -49,7 +49,7 @@ - + diff --git a/src/Beutl/Pages/SettingsDialog.axaml.cs b/src/Beutl/Pages/SettingsDialog.axaml.cs index 2331c9818..f4835e2c1 100644 --- a/src/Beutl/Pages/SettingsDialog.axaml.cs +++ b/src/Beutl/Pages/SettingsDialog.axaml.cs @@ -44,9 +44,6 @@ public SettingsDialog() nav.BackRequested += Nav_BackRequested; nav.SelectedItem = selected; -#if DEBUG - this.AttachDevTools(); -#endif } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) diff --git a/src/Beutl/Program.cs b/src/Beutl/Program.cs index d70ea32cf..3e52f7317 100644 --- a/src/Beutl/Program.cs +++ b/src/Beutl/Program.cs @@ -83,6 +83,7 @@ public static AppBuilder BuildAvaloniaApp() DefaultFamilyName = Media.FontManager.Instance.DefaultTypeface.FontFamily.Name }) .AfterSetup(_ => Telemetry.CompressLogFiles()) + .WithDeveloperTools() #if DEBUG .LogToTrace(); #else diff --git a/src/Beutl/Views/MacWindow.axaml.cs b/src/Beutl/Views/MacWindow.axaml.cs index 810247cc2..c97a75e7b 100644 --- a/src/Beutl/Views/MacWindow.axaml.cs +++ b/src/Beutl/Views/MacWindow.axaml.cs @@ -40,10 +40,6 @@ public MacWindow() var rect = new PixelRect(pos.Value.X, pos.Value.Y, size.Value.Width, size.Value.Height); SetRect(rect); } - -#if DEBUG - this.AttachDevTools(); -#endif } private void SetRect(PixelRect rect) diff --git a/src/Beutl/Views/MainWindow.axaml.cs b/src/Beutl/Views/MainWindow.axaml.cs index 2b491320e..7afd7f94c 100644 --- a/src/Beutl/Views/MainWindow.axaml.cs +++ b/src/Beutl/Views/MainWindow.axaml.cs @@ -29,9 +29,6 @@ public MainWindow() } TitleBar.Height = 40; -#if DEBUG - this.AttachDevTools(); -#endif } private void SetRect(PixelRect rect) diff --git a/tests/KeySplineEditor/KeySplineEditor.csproj b/tests/KeySplineEditor/KeySplineEditor.csproj index 1c0051b9f..b78d901b7 100644 --- a/tests/KeySplineEditor/KeySplineEditor.csproj +++ b/tests/KeySplineEditor/KeySplineEditor.csproj @@ -9,6 +9,6 @@ - + diff --git a/tests/KeySplineEditor/MainWindow.axaml.cs b/tests/KeySplineEditor/MainWindow.axaml.cs index dc4539b9e..ccd9f5ada 100644 --- a/tests/KeySplineEditor/MainWindow.axaml.cs +++ b/tests/KeySplineEditor/MainWindow.axaml.cs @@ -30,9 +30,6 @@ public MainWindow() _keySplineDrawing = new KeySplineDrawing(panel, _keySpline); panel.Children.Insert(0, _keySplineDrawing); -#if DEBUG - this.AttachDevTools(); -#endif } protected override Size ArrangeOverride(Size finalSize) diff --git a/tests/KeySplineEditor/Program.cs b/tests/KeySplineEditor/Program.cs index 2b6b86d80..315a13289 100644 --- a/tests/KeySplineEditor/Program.cs +++ b/tests/KeySplineEditor/Program.cs @@ -15,5 +15,6 @@ public static void Main(string[] args) => BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .LogToTrace(); + .LogToTrace() + .WithDeveloperTools(); } diff --git a/tests/TextFormattingPlayground/MainWindow.axaml.cs b/tests/TextFormattingPlayground/MainWindow.axaml.cs index cd9b2e727..bc5b6f662 100644 --- a/tests/TextFormattingPlayground/MainWindow.axaml.cs +++ b/tests/TextFormattingPlayground/MainWindow.axaml.cs @@ -27,9 +27,6 @@ public MainWindow() _node = new DrawableRenderNode(_resource); textBox.GetObservable(TextBox.TextProperty).Subscribe(TextChanged); -#if DEBUG - this.AttachDevTools(); -#endif } private void TextChanged(string? obj) diff --git a/tests/TextFormattingPlayground/Program.cs b/tests/TextFormattingPlayground/Program.cs index 1de52a64a..5ef7df2d3 100644 --- a/tests/TextFormattingPlayground/Program.cs +++ b/tests/TextFormattingPlayground/Program.cs @@ -15,5 +15,6 @@ public static void Main(string[] args) => BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .LogToTrace(); + .LogToTrace() + .WithDeveloperTools(); } diff --git a/tests/TextFormattingPlayground/TextFormattingPlayground.csproj b/tests/TextFormattingPlayground/TextFormattingPlayground.csproj index 4211c2304..6f9f1d916 100644 --- a/tests/TextFormattingPlayground/TextFormattingPlayground.csproj +++ b/tests/TextFormattingPlayground/TextFormattingPlayground.csproj @@ -13,6 +13,6 @@ - + diff --git a/tests/XamlPreview/Program.cs b/tests/XamlPreview/Program.cs index 840918de7..d668f0b28 100644 --- a/tests/XamlPreview/Program.cs +++ b/tests/XamlPreview/Program.cs @@ -15,5 +15,6 @@ public static void Main(string[] args) => BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .LogToTrace(); + .LogToTrace() + .WithDeveloperTools(); } diff --git a/tests/XamlPreview/XamlPreview.csproj b/tests/XamlPreview/XamlPreview.csproj index 746bf00d7..3acbea013 100644 --- a/tests/XamlPreview/XamlPreview.csproj +++ b/tests/XamlPreview/XamlPreview.csproj @@ -16,12 +16,12 @@ - - + - + \ No newline at end of file From 8d7ebdcaea287de2af23b8912305a40c88e8b538 Mon Sep 17 00:00:00 2001 From: Yuto Terada Date: Tue, 14 Apr 2026 14:35:41 +0900 Subject: [PATCH 03/21] refactor: remove DirectoryViewTest project and related files --- Beutl.slnx | 1 - src/Beutl.Controls/DirectoryTreeView.cs | 905 ------------------ tests/DirectoryViewTest/App.axaml | 7 - tests/DirectoryViewTest/App.axaml.cs | 23 - .../DirectoryViewTest.csproj | 15 - tests/DirectoryViewTest/MainWindow.axaml | 9 - tests/DirectoryViewTest/MainWindow.axaml.cs | 24 - tests/DirectoryViewTest/Program.cs | 19 - .../Properties/launchSettings.json | 8 - 9 files changed, 1011 deletions(-) delete mode 100644 src/Beutl.Controls/DirectoryTreeView.cs delete mode 100644 tests/DirectoryViewTest/App.axaml delete mode 100644 tests/DirectoryViewTest/App.axaml.cs delete mode 100644 tests/DirectoryViewTest/DirectoryViewTest.csproj delete mode 100644 tests/DirectoryViewTest/MainWindow.axaml delete mode 100644 tests/DirectoryViewTest/MainWindow.axaml.cs delete mode 100644 tests/DirectoryViewTest/Program.cs delete mode 100644 tests/DirectoryViewTest/Properties/launchSettings.json diff --git a/Beutl.slnx b/Beutl.slnx index 7bf6d0fbc..31c8227d1 100644 --- a/Beutl.slnx +++ b/Beutl.slnx @@ -30,7 +30,6 @@ - diff --git a/src/Beutl.Controls/DirectoryTreeView.cs b/src/Beutl.Controls/DirectoryTreeView.cs deleted file mode 100644 index 99127a659..000000000 --- a/src/Beutl.Controls/DirectoryTreeView.cs +++ /dev/null @@ -1,905 +0,0 @@ -using System.Diagnostics; -using Avalonia; -using Avalonia.Collections; -using Avalonia.Controls; -using Avalonia.Controls.Primitives; -using Avalonia.Input; -using Avalonia.Input.Platform; -using Avalonia.Interactivity; -using Avalonia.LogicalTree; -using Avalonia.Platform.Storage; -using Avalonia.Threading; - -using Beutl.Language; - -using FluentAvalonia.UI.Controls; - -namespace Beutl.Controls; - -public sealed class DirectoryTreeView : TreeView -{ - private readonly FileSystemWatcher _watcher; - private readonly AvaloniaList _items = []; - private readonly DirectoryInfo _directoryInfo; - private readonly MenuItem _open; - private readonly MenuItem _copy; - private readonly MenuItem _remove; - private readonly MenuItem _rename; - private readonly MenuItem _addfolder; - private readonly List _menuItem; - private readonly Func _contextFactory; - - public DirectoryTreeView(FileSystemWatcher watcher, Func contextFactory = null) - { - _watcher = watcher; - _directoryInfo = new DirectoryInfo(watcher.Path); - _contextFactory = contextFactory; - ItemsSource = _items; - InitSubDirectory(); - - _watcher.Renamed += Watcher_Renamed; - _watcher.Deleted += Watcher_Deleted; - _watcher.Created += Watcher_Created; - - AddHandler(DragDrop.DropEvent, OnDrop); - AddHandler(DragDrop.DragOverEvent, OnDragOver); - DragDrop.SetAllowDrop(this, true); - - _open = new MenuItem - { - Header = Strings.Open, - Icon = new SymbolIcon - { - Symbol = Symbol.Open, - FontSize = 20, - } - }; - _copy = new MenuItem - { - Header = Strings.Copy, - Icon = new SymbolIcon - { - Symbol = Symbol.Copy, - FontSize = 20, - } - }; - _remove = new MenuItem - { - Header = Strings.Remove, - Icon = new SymbolIcon - { - Symbol = Symbol.Delete, - FontSize = 20, - } - }; - _rename = new MenuItem - { - Header = Strings.Rename, - Icon = new SymbolIcon - { - Symbol = Symbol.Rename, - FontSize = 20, - } - }; - _addfolder = new MenuItem - { - Header = Strings.NewFolder, - Icon = new SymbolIcon - { - Symbol = Symbol.Folder, - FontSize = 20, - } - }; - - _open.Click += Open; - _copy.Click += Copy; - _remove.Click += Remove; - _rename.Click += Rename; - _addfolder.Click += AddDirectory; - - _menuItem = - [ - _open, - new MenuItem - { - Header = Strings.CreateNew, - Items = - { - _addfolder, - }, - }, - _copy, - _remove, - _rename, - new Separator() - ]; - - //foreach (var (asm, menus) in PluginManager.Default.FileMenus) - //{ - // foreach (var menu in menus) - // { - // var menuItem = new MenuItem - // { - // Header = menu.Name, - // DataContext = menu, - // }; - - // menuItem.Click += PluginFileMenu_Click; - - // _menuItem.Add(menuItem); - // } - //} - - ContextMenu = new ContextMenu - { - ItemsSource = _menuItem - }; - - ContextMenu.Opening += ContextMenu_ContextMenuOpening; - } - - //private void PluginFileMenu_Click(object sender, RoutedEventArgs e) - //{ - // if (SelectedItem is FileTreeItem fileTree - // && sender is MenuItem menuItem - // && menuItem.DataContext is FileMenu fileMenu) - // { - // fileMenu.MainWindow = VisualRoot; - // fileMenu.Execute(fileTree.Info.FullName); - // } - //} - - private void ContextMenu_ContextMenuOpening(object sender, System.ComponentModel.CancelEventArgs e) - { - _remove.IsEnabled = CanRemove(); - _open.IsEnabled = CanOpen(); - - //if (SelectedItem is FileTreeItem fileTree) - //{ - // foreach (var (menu, model) in _menuItem.OfType() - // .Where(i => i.DataContext is FileMenu) - // .Select(i => (Menu: i, Model: (FileMenu)i.DataContext!))) - // { - // menu.IsVisible = model.IsMatch(fileTree.Info.FullName); - // } - //} - //else - //{ - // foreach (var menu in _menuItem.OfType() - // .Where(i => i.DataContext is FileMenu)) - // { - // menu.IsVisible = false; - // } - //} - } - - protected override Type StyleKeyOverride => typeof(TreeView); - - private bool CanOpen() - { - return SelectedItem is DirectoryTreeItem or FileTreeItem; - } - - private void Open(object sender, RoutedEventArgs e) - { - if (SelectedItem is DirectoryTreeItem directoryTree) - { - directoryTree.IsExpanded = true; - } - else if (SelectedItem is FileTreeItem fileTree) - { - Process.Start(new ProcessStartInfo(fileTree.Info.FullName) - { - UseShellExecute = true, - }); - } - } - - private async void Copy(object sender, RoutedEventArgs e) - { - if (TopLevel.GetTopLevel(this) is { Clipboard: IClipboard clipboard, StorageProvider: IStorageProvider storageProvider }) - { - if (SelectedItem is DirectoryTreeItem directoryTree) - { - await clipboard.SetTextAsync(directoryTree.Info.FullName); - } - else if (SelectedItem is FileTreeItem fileTree) - { - var data = new DataTransfer(); - data.Add(DataTransferItem.CreateFile(await storageProvider.TryGetFileFromPathAsync(fileTree.Info.FullName))); - - await clipboard.SetDataAsync(data); - } - } - } - - private bool CanRemove() - { - return SelectedItem is DirectoryTreeItem or FileTreeItem; - } - - private async void Remove(object sender, RoutedEventArgs e) - { - if (SelectedItem is DirectoryTreeItem directory) - { - var dialog = new ContentDialog - { - Content = MessageStrings.ConfirmDeleteDirectory, - PrimaryButtonText = Strings.OK, - CloseButtonText = Strings.Cancel, - DefaultButton = ContentDialogButton.Primary, - IsSecondaryButtonEnabled = false, - }; - - if (await dialog.ShowAsync() == ContentDialogResult.Primary) - { - directory.Info.Delete(true); - } - } - else if (SelectedItem is FileTreeItem file) - { - var dialog = new ContentDialog - { - Content = MessageStrings.ConfirmDeleteFile, - PrimaryButtonText = Strings.OK, - CloseButtonText = Strings.Cancel, - DefaultButton = ContentDialogButton.Primary, - IsSecondaryButtonEnabled = false, - }; - - if (await dialog.ShowAsync() == ContentDialogResult.Primary) - { - file.Info.Delete(); - } - } - } - - private void Rename(object sender, RoutedEventArgs e) - { - if (SelectedItem is DirectoryTreeItem directory) - { - directory.StartRename(); - } - else if (SelectedItem is FileTreeItem file) - { - file.StartRename(); - } - } - - private void AddDirectory(object sender, RoutedEventArgs e) - { - string baseDir = _directoryInfo.FullName; - if (SelectedItem is DirectoryTreeItem directoryTree) - { - baseDir = directoryTree.Info.FullName; - } - else if (SelectedItem is FileTreeItem fileTree && fileTree.Info.DirectoryName != null) - { - baseDir = fileTree.Info.DirectoryName; - } - - int count = 0; - string str = Strings.NewFolder; - string defaultName = str; - - while (Directory.Exists(Path.Combine(baseDir, defaultName))) - { - count++; - defaultName = $"{str}{count}"; - } - - Directory.CreateDirectory(Path.Combine(baseDir, defaultName)); - } - - private void Watcher_Created(object sender, FileSystemEventArgs e) - { - Dispatcher.UIThread.InvokeAsync(() => - { - string parent = Path.GetDirectoryName(e.FullPath); - - if (parent == _directoryInfo.FullName) - { - if (Directory.Exists(e.FullPath)) - { - var di = new DirectoryInfo(e.FullPath); - _items.Add(new DirectoryTreeItem(di, _watcher, _contextFactory) - { - DataContext = _contextFactory?.Invoke(e.FullPath) - }); - } - else - { - _items.Add(new FileTreeItem(new FileInfo(e.FullPath)) - { - DataContext = _contextFactory?.Invoke(e.FullPath) - }); - } - } - - Sort(); - }); - } - - private void Watcher_Deleted(object sender, FileSystemEventArgs e) - { - Dispatcher.UIThread.InvokeAsync(() => - { - string parent = Path.GetDirectoryName(e.FullPath); - string filename = Path.GetFileName(e.Name); - - if (parent == _directoryInfo.FullName) - { - TreeViewItem item = _items.FirstOrDefault(i => i.Header is string str && str == filename); - if (item != null) - { - _items.Remove(item); - } - } - }); - } - - private void Watcher_Renamed(object sender, RenamedEventArgs e) - { - Dispatcher.UIThread.InvokeAsync(() => - { - string parent = Path.GetDirectoryName(e.FullPath); - string oldFilename = Path.GetFileName(e.OldName); - string newFilename = Path.GetFileName(e.Name); - - if (parent == _directoryInfo.FullName) - { - TreeViewItem item = _items.FirstOrDefault(i => i.Header is string str && str == oldFilename); - if (item is DirectoryTreeItem dir) - { - dir.Info = new DirectoryInfo(e.FullPath); - } - - if (item is FileTreeItem file) - { - file.Info = new FileInfo(e.FullPath); - } - - item.DataContext = _contextFactory?.Invoke(e.FullPath); - } - - Sort(); - }); - } - - private void OnDragOver(object sender, DragEventArgs e) - { - if (e.DataTransfer.Contains(DataFormat.File)) - { - e.DragEffects = DragDropEffects.Copy; - } - } - - private void OnDrop(object sender, DragEventArgs e) - { - if (e.DataTransfer.Contains(DataFormat.File) && e.Source is ILogical logical) - { - e.DragEffects = DragDropEffects.Copy; - - TreeViewItem treeViewItem = logical.FindLogicalAncestorOfType(); - string baseDir = _directoryInfo.FullName; - - if (treeViewItem is DirectoryTreeItem directoryTreeItem) - baseDir = directoryTreeItem.Info.FullName; - else if (treeViewItem is FileTreeItem fileTree && fileTree.Info.DirectoryName != null) - baseDir = fileTree.Info.DirectoryName; - - foreach (IStorageItem src in e.DataTransfer.TryGetFiles() ?? []) - { - if (src is IStorageFile - && src.TryGetLocalPath() is string localPath) - { - string dst = Path.Combine(baseDir, Path.GetFileName(localPath)); - if (!File.Exists(dst)) - { - File.Copy(localPath, dst); - } - } - } - } - } - - //サブフォルダツリー追加 - private void InitSubDirectory() - { - //すべてのサブフォルダを追加 - foreach (DirectoryInfo item in _directoryInfo.GetDirectories()) - { - if (!item.Attributes.HasAnyFlag(FileAttributes.Hidden | FileAttributes.System)) - { - _items.Add(new DirectoryTreeItem(item, _watcher, _contextFactory) - { - DataContext = _contextFactory?.Invoke(item.FullName) - }); - } - } - - // 全てのファイル追加 - foreach (FileInfo item in _directoryInfo.GetFiles()) - { - if (!item.Attributes.HasAnyFlag(FileAttributes.Hidden | FileAttributes.System)) - { - _items.Add(new FileTreeItem(item) - { - DataContext = _contextFactory?.Invoke(item.FullName) - }); - } - } - } - - public void Sort() - { - static string Func(TreeViewItem item) - { - if (item.Header is string header) - { - return header; - } - else if (item.Header is TextBlock tb) - { - return tb.Text; - } - else - { - return item.Header.ToString(); - } - } - - FileTreeItem[] fileArray = [.. _items.OfType().OrderBy(Func)]; - DirectoryTreeItem[] dirArray = [.. _items.OfType().OrderBy(Func)]; - _items.Clear(); - _items.AddRange(dirArray); - _items.AddRange(fileArray); - - foreach (DirectoryTreeItem item in dirArray) - { - item.Sort(); - } - } -} - -public sealed class FileTreeItem : TreeViewItem -{ - private FileInfo _info; - // 名前を変更中 - private bool _isRenaming; - - public FileTreeItem(FileInfo info) - { - _info = info; - Header = Info.Name; - DoubleTapped += FileTreeItem_DoubleTapped; - } - - public FileInfo Info - { - get => _info; - set - { - _info = value; - Header = _info.Name; - } - } - - protected override Type StyleKeyOverride => typeof(TreeViewItem); - - public void Refresh() - { - Info.Refresh(); - Header = Info.Name; - } - - public void StartRename() - { - if (!_isRenaming) - { - _isRenaming = true; - - TextBox tb; - Header = tb = new TextBox - { - Text = Info.Name - }; - - tb.SelectAll(); - tb.AddHandler(KeyUpEvent, TextBox_KeyUp, RoutingStrategies.Tunnel); - tb.TemplateApplied += TextBox_TemplateApplied; - tb.LostFocus += TextBox_LostFocus; - } - } - - private void TextBox_LostFocus(object sender, RoutedEventArgs e) - { - EndRename(); - } - - private void TextBox_TemplateApplied(object sender, TemplateAppliedEventArgs e) - { - ((TextBox)sender).Focus(); - } - - private void TextBox_KeyUp(object sender, KeyEventArgs e) - { - if (sender is TextBox tb) - { - switch (e.Key) - { - case Key.Enter: - EndRename(); - break; - case Key.Escape: - tb.Text = Info.Name; - EndRename(); - break; - default: - break; - } - } - } - - public async void EndRename() - { - if (_isRenaming && Header is TextBox tb) - { - _isRenaming = false; - string old = Info.FullName; - string @new = Path.Combine(Info.DirectoryName, tb.Text); - if (File.Exists(@new)) - { - string content = MessageStrings.RenameConflict; - content = string.Format(content, Info.Name, tb.Text); - var dialog = new ContentDialog() - { - CloseButtonText = Strings.Close, - Content = content, - DefaultButton = ContentDialogButton.None, - IsPrimaryButtonEnabled = false, - IsSecondaryButtonEnabled = false, - }; - - await dialog.ShowAsync(); - } - else if (string.Compare(old, @new, StringComparison.OrdinalIgnoreCase) != 0) - { - File.Move(old, @new); - _info = new FileInfo(@new); - } - - - tb.RemoveHandler(KeyUpEvent, TextBox_KeyUp); - tb.TemplateApplied -= TextBox_TemplateApplied; - tb.LostFocus -= TextBox_LostFocus; - Header = _info.Name; - } - } - - protected override async void OnPointerPressed(PointerPressedEventArgs e) - { - base.OnPointerPressed(e); - if (TopLevel.GetTopLevel(this) is not { StorageProvider: IStorageProvider storageProvider }) - return; - - if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) - { - - TreeView parent = this.FindLogicalAncestorOfType(); - parent.SelectedItem = this; - Refresh(); - - var data = new DataTransfer(); - data.Add(DataTransferItem.CreateFile(await storageProvider.TryGetFileFromPathAsync(Info.FullName))); - - // ドラッグ開始 - await DragDrop.DoDragDropAsync(e, data, DragDropEffects.Copy).ConfigureAwait(false); - } - } - - private void FileTreeItem_DoubleTapped(object sender, RoutedEventArgs e) - { - Refresh(); - Process.Start(new ProcessStartInfo(Info.FullName) - { - UseShellExecute = true, - }); - } -} - -public sealed class DirectoryTreeItem : TreeViewItem -{ - private readonly AvaloniaList _items = []; - private readonly FileSystemWatcher _watcher; - private readonly Func _contextFactory; - // //サブフォルダを作成済みかどうか - private bool _isAdd; - private DirectoryInfo _info; - // 名前を変更中 - private bool _isRenaming; - - public DirectoryTreeItem(DirectoryInfo info, FileSystemWatcher watcher, Func contextFactory = null) - { - _info = info; - Header = info.Name; - ItemsSource = _items; - _watcher = watcher; - _contextFactory = contextFactory; - if (info.EnumerateFileSystemInfos().Any()) - { - _items.Add(new TreeViewItem()); - } - - this.GetObservable(IsExpandedProperty).Subscribe(v => - { - if (!_isAdd && v) - { - InitSubDirectory(); - } - }); - } - - public DirectoryInfo Info - { - get => _info; - set - { - _info = value; - Header = _info.Name; - } - } - - protected override Type StyleKeyOverride => typeof(TreeViewItem); - - //サブフォルダツリー追加 - private void InitSubDirectory() - { - Refresh(); - _items.Clear(); - // すべてのサブフォルダを追加 - foreach (DirectoryInfo item in Info.GetDirectories()) - { - if (!item.Attributes.HasAnyFlag(FileAttributes.Hidden | FileAttributes.System)) - { - _items.Add(new DirectoryTreeItem(item, _watcher, _contextFactory) - { - DataContext = _contextFactory?.Invoke(item.FullName) - }); - } - } - - // 全てのファイル追加 - foreach (FileInfo item in Info.GetFiles()) - { - if (!item.Attributes.HasAnyFlag(FileAttributes.Hidden | FileAttributes.System)) - { - _items.Add(new FileTreeItem(item) - { - DataContext = _contextFactory?.Invoke(item.FullName) - }); - } - } - - _isAdd = true; - } - - public void Sort() - { - static string Func(TreeViewItem item) - { - if (item.Header is string header) - { - return header; - } - else if (item.Header is TextBlock tb) - { - return tb.Text; - } - else - { - return item.Header?.ToString(); - } - } - - if (!_isAdd) - return; - - FileTreeItem[] fileArray = [.. _items.OfType().OrderBy(Func)]; - DirectoryTreeItem[] dirArray = [.. _items.OfType().OrderBy(Func)]; - _items.Clear(); - _items.AddRange(dirArray); - _items.AddRange(fileArray); - - foreach (DirectoryTreeItem item in dirArray) - { - item.Sort(); - } - } - - public void Refresh() - { - Info.Refresh(); - Header = Info.Name; - } - - public void StartRename() - { - if (!_isRenaming) - { - _isRenaming = true; - - TextBox tb; - Header = tb = new TextBox - { - Text = Info.Name - }; - - tb.SelectAll(); - tb.AddHandler(KeyUpEvent, TextBox_KeyUp, RoutingStrategies.Tunnel); - tb.TemplateApplied += TextBox_TemplateApplied; - tb.LostFocus += TextBox_LostFocus; - } - } - - private void TextBox_LostFocus(object sender, RoutedEventArgs e) - { - EndRename(); - } - - private void TextBox_TemplateApplied(object sender, TemplateAppliedEventArgs e) - { - ((TextBox)sender).Focus(); - } - - private void TextBox_KeyUp(object sender, KeyEventArgs e) - { - if (sender is TextBox tb) - { - switch (e.Key) - { - case Key.Enter: - EndRename(); - break; - case Key.Escape: - tb.Text = Info.Name; - EndRename(); - break; - default: - break; - } - } - } - - public async void EndRename() - { - if (_isRenaming && Header is TextBox tb) - { - _isRenaming = false; - string old = Info.FullName; - string @new = Path.Combine(Info.Parent.FullName, tb.Text); - if (Directory.Exists(@new)) - { - string content = MessageStrings.RenameConflict; - content = string.Format(content, Info.Name, tb.Text); - var dialog = new ContentDialog() - { - CloseButtonText = Strings.Close, - Content = content, - DefaultButton = ContentDialogButton.None, - IsPrimaryButtonEnabled = false, - IsSecondaryButtonEnabled = false, - }; - - await dialog.ShowAsync(); - } - else if (string.Compare(old, @new, StringComparison.OrdinalIgnoreCase) != 0) - { - Directory.Move(old, @new); - _info = new DirectoryInfo(@new); - } - - - tb.RemoveHandler(KeyUpEvent, TextBox_KeyUp); - tb.TemplateApplied -= TextBox_TemplateApplied; - tb.LostFocus -= TextBox_LostFocus; - Header = _info.Name; - } - } - - protected override void OnAttachedToLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs e) - { - base.OnAttachedToLogicalTree(e); - - _watcher.Renamed += Watcher_Renamed; - _watcher.Deleted += Watcher_Deleted; - _watcher.Created += Watcher_Created; - } - - protected override void OnDetachedFromLogicalTree(Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs e) - { - base.OnDetachedFromLogicalTree(e); - - _watcher.Renamed -= Watcher_Renamed; - _watcher.Deleted -= Watcher_Deleted; - _watcher.Created -= Watcher_Created; - } - - private void Watcher_Created(object sender, FileSystemEventArgs e) - { - Dispatcher.UIThread.InvokeAsync(() => - { - Refresh(); - string parent = Path.GetDirectoryName(e.FullPath); - - if (parent == Info.FullName) - { - if (Directory.Exists(e.FullPath)) - { - var di = new DirectoryInfo(e.FullPath); - _items.Add(new DirectoryTreeItem(di, _watcher, _contextFactory) - { - DataContext = _contextFactory?.Invoke(e.FullPath) - }); - } - else - { - _items.Add(new FileTreeItem(new FileInfo(e.FullPath)) - { - DataContext = _contextFactory?.Invoke(e.FullPath) - }); - } - - Sort(); - } - }); - } - - private void Watcher_Deleted(object sender, FileSystemEventArgs e) - { - Dispatcher.UIThread.InvokeAsync(() => - { - Refresh(); - string parent = Path.GetDirectoryName(e.FullPath); - string filename = Path.GetFileName(e.Name); - - if (parent == Info.FullName) - { - TreeViewItem item = _items.FirstOrDefault(i => i.Header is string str && str == filename); - if (item != null) - { - _items.Remove(item); - } - } - }); - } - - private void Watcher_Renamed(object sender, RenamedEventArgs e) - { - Dispatcher.UIThread.InvokeAsync(() => - { - Refresh(); - string parent = Path.GetDirectoryName(e.FullPath); - string oldFilename = Path.GetFileName(e.OldName); - string newFilename = Path.GetFileName(e.Name); - - if (parent == Info.FullName) - { - TreeViewItem item = _items.FirstOrDefault(i => i.Header is string str && str == oldFilename); - if (item is DirectoryTreeItem dir) - { - dir.Info = new DirectoryInfo(e.FullPath); - } - - if (item is FileTreeItem file) - { - file.Info = new FileInfo(e.FullPath); - } - - item.DataContext = _contextFactory?.Invoke(e.FullPath); - } - - Sort(); - }); - } -} diff --git a/tests/DirectoryViewTest/App.axaml b/tests/DirectoryViewTest/App.axaml deleted file mode 100644 index 9bfa0a2c1..000000000 --- a/tests/DirectoryViewTest/App.axaml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/tests/DirectoryViewTest/App.axaml.cs b/tests/DirectoryViewTest/App.axaml.cs deleted file mode 100644 index b6a4eea7c..000000000 --- a/tests/DirectoryViewTest/App.axaml.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; - -namespace DirectoryViewTest; - -public 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(); - } -} diff --git a/tests/DirectoryViewTest/DirectoryViewTest.csproj b/tests/DirectoryViewTest/DirectoryViewTest.csproj deleted file mode 100644 index 7266be03d..000000000 --- a/tests/DirectoryViewTest/DirectoryViewTest.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - WinExe - - - - - - - - - - - - diff --git a/tests/DirectoryViewTest/MainWindow.axaml b/tests/DirectoryViewTest/MainWindow.axaml deleted file mode 100644 index bbf3d5c0c..000000000 --- a/tests/DirectoryViewTest/MainWindow.axaml +++ /dev/null @@ -1,9 +0,0 @@ - - Welcome to Avalonia! - diff --git a/tests/DirectoryViewTest/MainWindow.axaml.cs b/tests/DirectoryViewTest/MainWindow.axaml.cs deleted file mode 100644 index 5c6b33204..000000000 --- a/tests/DirectoryViewTest/MainWindow.axaml.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Markup.Xaml; - -using Beutl.Controls; - -namespace DirectoryViewTest; - -public partial class MainWindow : Window -{ - public MainWindow() - { - InitializeComponent(); - Content = new DirectoryTreeView(new FileSystemWatcher("D:\\source") - { - EnableRaisingEvents = true, - IncludeSubdirectories = true - }); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } -} diff --git a/tests/DirectoryViewTest/Program.cs b/tests/DirectoryViewTest/Program.cs deleted file mode 100644 index 2692f531e..000000000 --- a/tests/DirectoryViewTest/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Avalonia; - -namespace DirectoryViewTest; - -internal class Program -{ - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - [STAThread] - public static void Main(string[] args) => BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); - - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UsePlatformDetect() - .LogToTrace(); -} diff --git a/tests/DirectoryViewTest/Properties/launchSettings.json b/tests/DirectoryViewTest/Properties/launchSettings.json deleted file mode 100644 index 33504c948..000000000 --- a/tests/DirectoryViewTest/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "WSL": { - "commandName": "WSL2", - "distributionName": "" - } - } -} \ No newline at end of file From b25957637a9fa8ed328223dfa47b6169baaaea2f Mon Sep 17 00:00:00 2001 From: Yuto Terada Date: Tue, 14 Apr 2026 14:36:27 +0900 Subject: [PATCH 04/21] refactor: remove custom BcTabItem and BcTabView controls --- src/Beutl.Controls/BcTabItem/BcTabItem.cs | 110 ----- .../BcTabItem/BcTabItem.events.cs | 29 -- .../BcTabItem/BcTabItem.properties.cs | 45 -- src/Beutl.Controls/BcTabView/BcTabView.cs | 178 -------- .../BcTabView/BcTabView.events.cs | 15 - .../BcTabView/BcTabView.properties.cs | 129 ------ .../Behaviors/ItemDragBehavior.cs | 267 ------------ .../Generators/BcTabItemContainerGenerator.cs | 126 ------ src/Beutl.Controls/Styles.axaml | 2 - src/Beutl.Controls/Styling/BcTabItem.axaml | 232 ---------- src/Beutl.Controls/Styling/BcTabView.axaml | 411 ------------------ .../Styling/Dock/DockFluent.axaml | 2 +- 12 files changed, 1 insertion(+), 1545 deletions(-) delete mode 100644 src/Beutl.Controls/BcTabItem/BcTabItem.cs delete mode 100644 src/Beutl.Controls/BcTabItem/BcTabItem.events.cs delete mode 100644 src/Beutl.Controls/BcTabItem/BcTabItem.properties.cs delete mode 100644 src/Beutl.Controls/BcTabView/BcTabView.cs delete mode 100644 src/Beutl.Controls/BcTabView/BcTabView.events.cs delete mode 100644 src/Beutl.Controls/BcTabView/BcTabView.properties.cs delete mode 100644 src/Beutl.Controls/Behaviors/ItemDragBehavior.cs delete mode 100644 src/Beutl.Controls/Generators/BcTabItemContainerGenerator.cs delete mode 100644 src/Beutl.Controls/Styling/BcTabItem.axaml delete mode 100644 src/Beutl.Controls/Styling/BcTabView.axaml diff --git a/src/Beutl.Controls/BcTabItem/BcTabItem.cs b/src/Beutl.Controls/BcTabItem/BcTabItem.cs deleted file mode 100644 index 63ce91374..000000000 --- a/src/Beutl.Controls/BcTabItem/BcTabItem.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Metadata; -using Avalonia.Controls.Primitives; -using Avalonia.Interactivity; -using Avalonia.Layout; - -using Beutl.Controls.Behaviors; -using Beutl.Controls.Extensions; - -namespace Beutl.Controls; - -[PseudoClasses(":dragging", ":lockdrag")] -public partial class BcTabItem : TabItem -{ - private readonly ItemDragBehavior _dragBehavior; - private Button _closeButton; - - public BcTabItem() - { - Closing += OnClosing; - _dragBehavior = new ItemDragBehavior(); - _dragBehavior.Attach(this); - } - - static BcTabItem() - { - TabStripPlacementProperty.Changed.AddClassHandler((x, _) => x.OnTabStripPlacementChanged()); - CanBeDraggedProperty.Changed.AddClassHandler((x, e) => x.OnCanDraggablePropertyChanged(x, e)); - IsSelectedProperty.Changed.AddClassHandler((x, _) => UpdatePseudoClass(x)); - IsClosableProperty.Changed.Subscribe(e => - { - if (e.Sender is BcTabItem a && a._closeButton != null) - { - a._closeButton.IsVisible = a.IsClosable; - } - }); - } - - private void OnTabStripPlacementChanged() - { - _dragBehavior.Orientation = TabStripPlacement is Dock.Top or Dock.Bottom - ? Orientation.Horizontal - : Orientation.Vertical; - } - - private static void UpdatePseudoClass(BcTabItem item) - { - if (!item.IsSelected) - { - item.PseudoClasses.Remove(":dragging"); - } - } - - internal bool CloseCore() - { - if (Parent is TabControl x) - { - try - { - x.CloseTab(this); - return true; - } - catch - { - return false; - } - } - return false; - } - - public bool Close() - { - RaiseEvent(new RoutedEventArgs(ClosingEvent)); - return CloseCore(); - } - - protected void OnCanDraggablePropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) - { - if (CanBeDragged) - { - PseudoClasses.Add(":lockdrag"); - } - else - { - PseudoClasses.Remove(":lockdrag"); - } - } - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - - _closeButton = e.NameScope.Find