From f6c125694c503c41d065746764e6931300bbfaa3 Mon Sep 17 00:00:00 2001 From: Hendrik Mennen Date: Wed, 15 Nov 2023 13:10:57 +0100 Subject: [PATCH] fix projectwatchinstance --- .../Views/Windows/AdvancedWindow.cs | 15 ++- .../Services/ProjectWatchInstance.cs | 5 +- .../ViewModels/ProjectExplorerViewModel.cs | 2 - src/OneWare.Shared/Models/IProjectManager.cs | 2 - .../ViewModels/FlexibleWindowViewModelBase.cs | 11 +- .../Models/FpgaModelBase.cs | 6 + .../UniversalFpgaProjectManager.cs | 105 ++++++++++-------- .../UniversalFpgaProjectCompileViewModel.cs | 63 +++++++++-- .../UniversalFpgaProjectCreatorViewModel.cs | 6 +- .../UniversalFpgaProjectCompileView.axaml | 2 +- 10 files changed, 149 insertions(+), 68 deletions(-) diff --git a/src/OneWare.Core/Views/Windows/AdvancedWindow.cs b/src/OneWare.Core/Views/Windows/AdvancedWindow.cs index 0ea573e5..734a0b58 100644 --- a/src/OneWare.Core/Views/Windows/AdvancedWindow.cs +++ b/src/OneWare.Core/Views/Windows/AdvancedWindow.cs @@ -1,8 +1,11 @@ -using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Runtime.InteropServices; using Avalonia; using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Media; +using OneWare.Shared.Controls; +using OneWare.Shared.ViewModels; namespace OneWare.Core.Views.Windows { @@ -87,5 +90,15 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } } + + protected override void OnClosing(WindowClosingEventArgs e) + { + if (Content is FlexibleWindow {DataContext: FlexibleWindowViewModelBase {IsDirty: true} vm} flexibleWindow) + { + e.Cancel = true; + vm.Close(flexibleWindow); + } + base.OnClosing(e); + } } } \ No newline at end of file diff --git a/src/OneWare.ProjectExplorer/Services/ProjectWatchInstance.cs b/src/OneWare.ProjectExplorer/Services/ProjectWatchInstance.cs index 0f3c0c78..f0153a51 100644 --- a/src/OneWare.ProjectExplorer/Services/ProjectWatchInstance.cs +++ b/src/OneWare.ProjectExplorer/Services/ProjectWatchInstance.cs @@ -109,11 +109,12 @@ private async Task ProcessAsync(string path, IReadOnlyCollection savable.LastSaveTime) + var lastWriteTime = File.GetLastWriteTime(savable.FullPath); + if (lastWriteTime > savable.LastSaveTime) await _projectExplorerService.ReloadAsync(entry); if (savable is IProjectFile { Root: IProjectRootWithFile rootWithFile } && - rootWithFile.ProjectFilePath == savable.FullPath) + rootWithFile.ProjectFilePath == savable.FullPath && lastWriteTime > rootWithFile.LastSaveTime) { await _projectExplorerService.ReloadAsync(rootWithFile); } diff --git a/src/OneWare.ProjectExplorer/ViewModels/ProjectExplorerViewModel.cs b/src/OneWare.ProjectExplorer/ViewModels/ProjectExplorerViewModel.cs index 4ab79962..e7a919ed 100644 --- a/src/OneWare.ProjectExplorer/ViewModels/ProjectExplorerViewModel.cs +++ b/src/OneWare.ProjectExplorer/ViewModels/ProjectExplorerViewModel.cs @@ -165,8 +165,6 @@ public void ConstructContextMenu(TopLevel topLevel) { if(reg.Invoke(SelectedItems) is {} items) menuItems.AddRange(items); } - - if (manager != null) menuItems.AddRange(manager.ConstructContextMenu(entry)); if (entry is IProjectRoot root) { diff --git a/src/OneWare.Shared/Models/IProjectManager.cs b/src/OneWare.Shared/Models/IProjectManager.cs index dc597b00..bd459372 100644 --- a/src/OneWare.Shared/Models/IProjectManager.cs +++ b/src/OneWare.Shared/Models/IProjectManager.cs @@ -5,6 +5,4 @@ public interface IProjectManager public Task LoadProjectAsync(string path); public Task SaveProjectAsync(IProjectRoot root); - - public IEnumerable ConstructContextMenu(IProjectEntry entry); } \ No newline at end of file diff --git a/src/OneWare.Shared/ViewModels/FlexibleWindowViewModelBase.cs b/src/OneWare.Shared/ViewModels/FlexibleWindowViewModelBase.cs index 9b5e5319..a36c9c01 100644 --- a/src/OneWare.Shared/ViewModels/FlexibleWindowViewModelBase.cs +++ b/src/OneWare.Shared/ViewModels/FlexibleWindowViewModelBase.cs @@ -6,8 +6,17 @@ namespace OneWare.Shared.ViewModels; public class FlexibleWindowViewModelBase : Document { - public void Close(FlexibleWindow window) + private bool _isDirty; + + public bool IsDirty + { + get => _isDirty; + set => SetProperty(ref _isDirty, value); + } + + public virtual void Close(FlexibleWindow window) { + IsDirty = false; window.Close(); } } \ No newline at end of file diff --git a/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs b/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs index b9410f63..40844e4f 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs @@ -61,6 +61,10 @@ public string SearchTextNodes get => _searchTextNodes; set => SetProperty(ref _searchTextNodes, value); } + + public event EventHandler NodeConnected; + + public event EventHandler NodeDisconnected; public FpgaModelBase() { @@ -115,6 +119,7 @@ private void Connect() SelectedNode.Connection = SelectedPin; ConnectCommand.NotifyCanExecuteChanged(); DisconnectCommand.NotifyCanExecuteChanged(); + NodeConnected?.Invoke(this, EventArgs.Empty); } private void Disconnect() @@ -124,6 +129,7 @@ private void Disconnect() SelectedNode!.Connection = null; ConnectCommand.NotifyCanExecuteChanged(); DisconnectCommand.NotifyCanExecuteChanged(); + NodeDisconnected?.Invoke(this, EventArgs.Empty); } protected void LoadFromJson(string path) diff --git a/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs b/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs index d892c813..e26806cf 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs @@ -17,12 +17,15 @@ public class UniversalFpgaProjectManager : IProjectManager private readonly IProjectExplorerService _projectExplorerService; private readonly IDockService _dockService; private readonly IWindowService _windowService; - - public UniversalFpgaProjectManager(IProjectExplorerService projectExplorerService, IDockService dockService, IWindowService windowService) + + public UniversalFpgaProjectManager(IProjectExplorerService projectExplorerService, IDockService dockService, + IWindowService windowService) { _projectExplorerService = projectExplorerService; _dockService = dockService; _windowService = windowService; + + _projectExplorerService.RegisterConstructContextMenu(ConstructContextMenu); } public async Task NewProjectDialogAsync() @@ -32,18 +35,18 @@ await _windowService.ShowDialogAsync(new UniversalFpgaProjectCreatorView() DataContext = ContainerLocator.Container.Resolve() }); } - + public async Task LoadProjectAsync(string path) { var root = await UniversalFpgaProjectParser.DeserializeAsync(path); if (root == null) return root; - + ProjectHelper.ImportEntries(root.FullPath, root); - + //Load Properties var top = root.Properties["TopEntity"]; - if (top != null && root.Search(top.ToString()) is {} entity) + if (top != null && root.Search(top.ToString()) is { } entity) { root.TopEntity = entity; } @@ -56,53 +59,61 @@ public async Task SaveProjectAsync(IProjectRoot root) return root is UniversalFpgaProjectRoot uFpga && await UniversalFpgaProjectParser.SerializeAsync(uFpga); } - public IEnumerable ConstructContextMenu(IProjectEntry entry) + public IEnumerable ConstructContextMenu(IList selected) { - switch (entry) + if (selected.Count == 1) { - case UniversalFpgaProjectRoot root: - yield return new MenuItemModel("Save") - { - Header = "Save", - Command = new AsyncRelayCommand(() => SaveProjectAsync(root)), - ImageIconObservable = Application.Current!.GetResourceObservable("VsImageLib.Save16XMd"), - }; - yield return new MenuItemModel("Reload") - { - Header = $"Reload", - Command = new AsyncRelayCommand(() => _projectExplorerService.ReloadAsync(root)), - ImageIconObservable = Application.Current!.GetResourceObservable("VsImageLib.RefreshGrey16X"), - }; - yield return new MenuItemModel("Edit") - { - Header = $"Edit {Path.GetFileName(root.ProjectFilePath)}", - Command = new AsyncRelayCommand(() => _dockService.OpenFileAsync(_projectExplorerService.GetTemporaryFile(root.ProjectFilePath))), - }; - break; - case IProjectFile { Root: UniversalFpgaProjectRoot universalFpgaProjectRoot } file: - if (universalFpgaProjectRoot.TopEntity == file) - { - yield return new MenuItemModel("Unset Top Entity") + switch (selected.First()) + { + case UniversalFpgaProjectRoot root: + yield return new MenuItemModel("Save") { - Header = $"Unset Top Entity", - Command = new RelayCommand(() => - { - universalFpgaProjectRoot.TopEntity = null; - }), + Header = "Save", + Command = new AsyncRelayCommand(() => SaveProjectAsync(root)), + ImageIconObservable = Application.Current!.GetResourceObservable("VsImageLib.Save16XMd"), }; - } - else - { - yield return new MenuItemModel("Set Top Entity") + yield return new MenuItemModel("Reload") { - Header = $"Set Top Entity", - Command = new RelayCommand(() => - { - universalFpgaProjectRoot.TopEntity = file; - }), + Header = $"Reload", + Command = new AsyncRelayCommand(() => _projectExplorerService.ReloadAsync(root)), + ImageIconObservable = Application.Current!.GetResourceObservable("VsImageLib.RefreshGrey16X"), }; - } - break; + yield return new MenuItemModel("Edit") + { + Header = $"Edit {Path.GetFileName(root.ProjectFilePath)}", + Command = new AsyncRelayCommand(() => + _dockService.OpenFileAsync(root.Search(root.ProjectFilePath) as IProjectFile + ?? _projectExplorerService.GetTemporaryFile(root.ProjectFilePath))), + }; + break; + case IProjectFile { Root: UniversalFpgaProjectRoot universalFpgaProjectRoot } file: + if (universalFpgaProjectRoot.TopEntity == file) + { + yield return new MenuItemModel("Unset Top Entity") + { + Header = $"Unset Top Entity", + Command = new RelayCommand(() => + { + universalFpgaProjectRoot.TopEntity = null; + _ = SaveProjectAsync(universalFpgaProjectRoot); + }), + }; + } + else + { + yield return new MenuItemModel("Set Top Entity") + { + Header = $"Set Top Entity", + Command = new RelayCommand(() => + { + universalFpgaProjectRoot.TopEntity = file; + _ = SaveProjectAsync(universalFpgaProjectRoot); + }), + }; + } + + break; + } } } } \ No newline at end of file diff --git a/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs b/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs index 502ac54c..0a3f09e5 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs @@ -1,20 +1,22 @@ using System.Collections.ObjectModel; -using AvaloniaEdit.Utils; -using CommunityToolkit.Mvvm.ComponentModel; +using System.Reactive.Linq; +using DynamicData.Binding; using OneWare.Shared.Controls; +using OneWare.Shared.Enums; using OneWare.Shared.Models; +using OneWare.Shared.Services; +using OneWare.Shared.ViewModels; using OneWare.UniversalFpgaProjectSystem.Models; using OneWare.UniversalFpgaProjectSystem.Services; -using OneWare.UniversalFpgaProjectSystem.Views; namespace OneWare.UniversalFpgaProjectSystem.ViewModels; -public class UniversalFpgaProjectCompileViewModel : ObservableObject +public class UniversalFpgaProjectCompileViewModel : FlexibleWindowViewModelBase { + private readonly IWindowService _windowService; private readonly UniversalFpgaProjectRoot _project; - public string Title => "Connect and Compile - " + _project.Header; - public List FpgaModels { get; } + public ObservableCollection FpgaModels { get; } = new(); private FpgaModelBase? _selectedFpga; public FpgaModelBase? SelectedFpga @@ -30,11 +32,30 @@ public bool HideExtensions set => SetProperty(ref _hideExtensions, value); } - public UniversalFpgaProjectCompileViewModel(FpgaService fpgaService, NodeProviderService nodeProviderService, UniversalFpgaProjectRoot project) + public UniversalFpgaProjectCompileViewModel(IWindowService windowService, FpgaService fpgaService, NodeProviderService nodeProviderService, UniversalFpgaProjectRoot project) { + _windowService = windowService; _project = project; - FpgaModels = fpgaService.GetFpgas().ToList(); + this.WhenValueChanged(x => x.IsDirty).Subscribe(x => + { + Title = $"Connect and Compile - {_project.Header}{(x ? "*" : "")}"; + }); + + foreach (var fpga in fpgaService.GetFpgas()) + { + Observable.FromEventPattern(fpga, nameof(fpga.NodeConnected)).Subscribe(_ => + { + IsDirty = true; + }); + + Observable.FromEventPattern(fpga, nameof(fpga.NodeDisconnected)).Subscribe(_ => + { + IsDirty = true; + }); + + FpgaModels.Add(fpga); + } SelectedFpga = FpgaModels.FirstOrDefault(); @@ -49,7 +70,31 @@ public UniversalFpgaProjectCompileViewModel(FpgaService fpgaService, NodeProvide } } - public async Task SaveAsync(FlexibleWindow window) + public override void Close(FlexibleWindow window) + { + if(!IsDirty) window.Close(); + _ = SafeQuitAsync(window); + } + + private async Task SafeQuitAsync(FlexibleWindow window) + { + var result = await _windowService.ShowYesNoCancelAsync("Warning", "Do you want to save changes?", MessageBoxIcon.Warning, window.Host); + + switch (result) + { + case MessageBoxStatus.Yes: + SaveAndClose(window); + break; + case MessageBoxStatus.No: + IsDirty = false; + break; + case MessageBoxStatus.Canceled: + return; + } + window.Close(); + } + + public void SaveAndClose(FlexibleWindow window) { CreatePcf(); } diff --git a/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCreatorViewModel.cs b/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCreatorViewModel.cs index a4507bab..981ff371 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCreatorViewModel.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCreatorViewModel.cs @@ -1,15 +1,15 @@ using System.Text.Json.Nodes; -using CommunityToolkit.Mvvm.ComponentModel; using OneWare.Settings; using OneWare.Settings.ViewModels; using OneWare.Settings.ViewModels.SettingTypes; using OneWare.Shared.Controls; using OneWare.Shared.Services; +using OneWare.Shared.ViewModels; using OneWare.UniversalFpgaProjectSystem.Models; namespace OneWare.UniversalFpgaProjectSystem.ViewModels; -public class UniversalFpgaProjectCreatorViewModel : ObservableObject +public class UniversalFpgaProjectCreatorViewModel : FlexibleWindowViewModelBase { public IPaths Paths { get; } private readonly IProjectExplorerService _projectExplorerService; @@ -92,7 +92,7 @@ public async Task SaveAsync(FlexibleWindow window) _projectExplorerService.ActiveProject = root; - window?.Close(); + Close(window); _ = _projectExplorerService.SaveLastProjectsFileAsync(); } diff --git a/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml b/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml index b1a83925..bfd71052 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml +++ b/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml @@ -105,7 +105,7 @@ -