diff --git a/OpenUtau.Core/Commands/Notifications.cs b/OpenUtau.Core/Commands/Notifications.cs index 128b468d5..dbdd77b09 100644 --- a/OpenUtau.Core/Commands/Notifications.cs +++ b/OpenUtau.Core/Commands/Notifications.cs @@ -26,6 +26,24 @@ public ErrorMessageNotification(string message, Exception e) { public override string ToString() => $"Error message: {message} {e}"; } + public class LoadingNotification : UNotification { + public readonly Type window; + public readonly bool startLoading; + public readonly string loadObject; + public LoadingNotification(Type window, bool startLoading, string loadObject) { + this.window = window; + this.startLoading = startLoading; + this.loadObject = loadObject; + } + public override string ToString() { + if (startLoading) { + return $"Start loading {loadObject}"; + } else { + return $"Finish loading {loadObject}"; + } + } + } + public class LoadPartNotification : UNotification { public readonly int tick; public LoadPartNotification(UPart part, UProject project, int tick) { diff --git a/OpenUtau/ViewModels/MainWindowViewModel.cs b/OpenUtau/ViewModels/MainWindowViewModel.cs index 07d50d625..4e19271bd 100644 --- a/OpenUtau/ViewModels/MainWindowViewModel.cs +++ b/OpenUtau/ViewModels/MainWindowViewModel.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Avalonia.Threading; using DynamicData.Binding; +using OpenUtau.App.Views; using OpenUtau.Core; using OpenUtau.Core.Ustx; using ReactiveUI; @@ -59,8 +60,7 @@ public MainWindowViewModel() { try { OpenProject(new[] { file }); } catch (Exception e) { - DocManager.Inst.ExecuteCmd(new ErrorMessageNotification( - "failed to open recent.", e)); + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification("failed to open recent.", e)); } }); OpenTemplateCommand = ReactiveCommand.Create(file => { @@ -69,8 +69,7 @@ public MainWindowViewModel() { DocManager.Inst.Project.Saved = false; DocManager.Inst.Project.FilePath = string.Empty; } catch (Exception e) { - DocManager.Inst.ExecuteCmd(new ErrorMessageNotification( - "failed to open template.", e)); + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification("failed to open template.", e)); } }); PartDeleteCommand = ReactiveCommand.Create(part => { @@ -109,8 +108,7 @@ public void NewProject() { DocManager.Inst.Project.FilePath = string.Empty; return; } catch (Exception e) { - DocManager.Inst.ExecuteCmd(new ErrorMessageNotification( - "failed to load default template.", e)); + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification("failed to load default template.", e)); } } DocManager.Inst.ExecuteCmd(new LoadProjectNotification(Core.Format.Ustx.Create())); @@ -120,9 +118,14 @@ public void OpenProject(string[] files) { if (files == null) { return; } - Core.Format.Formats.LoadProject(files); - DocManager.Inst.ExecuteCmd(new VoiceColorRemappingNotification(-1, true)); - this.RaisePropertyChanged(nameof(Title)); + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), true, "project")); + try { + Core.Format.Formats.LoadProject(files); + DocManager.Inst.ExecuteCmd(new VoiceColorRemappingNotification(-1, true)); + this.RaisePropertyChanged(nameof(Title)); + } finally { + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), false, "project")); + } } public void SaveProject(string file = "") { diff --git a/OpenUtau/ViewModels/PianoRollViewModel.cs b/OpenUtau/ViewModels/PianoRollViewModel.cs index 4993c2102..0456049a7 100644 --- a/OpenUtau/ViewModels/PianoRollViewModel.cs +++ b/OpenUtau/ViewModels/PianoRollViewModel.cs @@ -5,6 +5,7 @@ using Avalonia.Input; using Avalonia.Threading; using DynamicData.Binding; +using OpenUtau.App.Views; using OpenUtau.Classic; using OpenUtau.Core; using OpenUtau.Core.Editing; @@ -120,18 +121,25 @@ public PianoRollViewModel() { if (NotesViewModel.Part == null || NotesViewModel.Part.notes.Count == 0) { return; } - var part = NotesViewModel.Part; - UNote? first; - UNote? last; - if (NotesViewModel.Selection.IsEmpty) { - first = part.notes.First(); - last = part.notes.Last(); - } else { - first = NotesViewModel.Selection.FirstOrDefault(); - last = NotesViewModel.Selection.LastOrDefault(); + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(PianoRollWindow), true, "legacy plugin")); + try { + var part = NotesViewModel.Part; + UNote? first; + UNote? last; + if (NotesViewModel.Selection.IsEmpty) { + first = part.notes.First(); + last = part.notes.Last(); + } else { + first = NotesViewModel.Selection.FirstOrDefault(); + last = NotesViewModel.Selection.LastOrDefault(); + } + var runner = PluginRunner.from(PathManager.Inst, DocManager.Inst); + runner.Execute(NotesViewModel.Project, part, first, last, plugin); + } catch (Exception e) { + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification(e)); + } finally { + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(PianoRollWindow), false, "legacy plugin")); } - var runner = PluginRunner.from(PathManager.Inst, DocManager.Inst); - runner.Execute(NotesViewModel.Project, part, first, last, plugin); }); LoadLegacyPlugins(); @@ -139,8 +147,8 @@ public PianoRollViewModel() { if (NotesViewModel.Part != null) { try{ edit.Run(NotesViewModel.Project, NotesViewModel.Part, NotesViewModel.Selection.ToList(), DocManager.Inst); - }catch(System.Exception e){ - DocManager.Inst.ExecuteCmd(new ErrorMessageNotification("Failed to run editing macro",e)); + } catch (Exception e) { + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification("Failed to run editing macro", e)); } } }); diff --git a/OpenUtau/ViewModels/SingersViewModel.cs b/OpenUtau/ViewModels/SingersViewModel.cs index 847dd795d..c069e73ae 100644 --- a/OpenUtau/ViewModels/SingersViewModel.cs +++ b/OpenUtau/ViewModels/SingersViewModel.cs @@ -10,6 +10,7 @@ using DynamicData.Binding; using NAudio.Wave; using NWaves.Signals; +using OpenUtau.App.Views; using OpenUtau.Classic; using OpenUtau.Core; using OpenUtau.Core.Ustx; @@ -56,17 +57,36 @@ public SingersViewModel() { this.WhenAnyValue(vm => vm.Singer) .WhereNotNull() .Subscribe(singer => { - singer.EnsureLoaded(); - Avatar = LoadAvatar(singer); - Otos.Clear(); - Otos.AddRange(singer.Otos); - DisplayedOtos.Clear(); - DisplayedOtos.AddRange(singer.Otos); - Info = $"Author: {singer.Author}\nVoice: {singer.Voice}\nWeb: {singer.Web}\nVersion: {singer.Version}\n{singer.OtherInfo}\n\n{string.Join("\n", singer.Errors)}"; - HasWebsite = !string.IsNullOrEmpty(singer.Web); - LoadSubbanks(); - DocManager.Inst.ExecuteCmd(new OtoChangedNotification()); - this.RaisePropertyChanged(nameof(IsClassic)); + if (MessageBox.LoadingIsActive()) { + try { + AttachSinger(); + } catch (Exception e) { + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification(e)); + } + } else { + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(SingersDialog), true, "singer")); + try { + AttachSinger(); + } catch (Exception e) { + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification(e)); + } finally { + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(SingersDialog), false, "singer")); + } + } + void AttachSinger() { + singer.EnsureLoaded(); + Avatar = LoadAvatar(singer); + Otos.Clear(); + Otos.AddRange(singer.Otos); + DisplayedOtos.Clear(); + DisplayedOtos.AddRange(singer.Otos); + Info = $"Author: {singer.Author}\nVoice: {singer.Voice}\nWeb: {singer.Web}\nVersion: {singer.Version}\n{singer.OtherInfo}\n\n{string.Join("\n", singer.Errors)}"; + HasWebsite = !string.IsNullOrEmpty(singer.Web); + LoadSubbanks(); + DocManager.Inst.ExecuteCmd(new OtoChangedNotification()); + this.RaisePropertyChanged(nameof(IsClassic)); + this.RaisePropertyChanged(nameof(UseSearchAlias)); + } }); this.WhenAnyValue(vm => vm.SearchAlias) .Subscribe(alias => { @@ -195,15 +215,22 @@ public void Refresh() { if (Singer == null) { return; } - var singerId = Singer.Id; - SingerManager.Inst.SearchAllSingers(); - this.RaisePropertyChanged(nameof(Singers)); - if (SingerManager.Inst.Singers.TryGetValue(singerId, out var singer)) { - Singer = singer; - } else { - Singer = Singers.FirstOrDefault(); + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(SingersDialog), true, "singer")); + try { + var singerId = Singer.Id; + SingerManager.Inst.SearchAllSingers(); + this.RaisePropertyChanged(nameof(Singers)); + if (SingerManager.Inst.Singers.TryGetValue(singerId, out var singer)) { + Singer = singer; + } else { + Singer = Singers.FirstOrDefault(); + } + DocManager.Inst.ExecuteCmd(new SingersRefreshedNotification()); + } catch (Exception e) { + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification(e)); + } finally { + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(SingersDialog), false, "singer")); } - DocManager.Inst.ExecuteCmd(new SingersRefreshedNotification()); } Bitmap? LoadAvatar(USinger singer) { diff --git a/OpenUtau/Views/MainWindow.axaml.cs b/OpenUtau/Views/MainWindow.axaml.cs index 4c0edc842..f9a843a14 100644 --- a/OpenUtau/Views/MainWindow.axaml.cs +++ b/OpenUtau/Views/MainWindow.axaml.cs @@ -16,6 +16,7 @@ using OpenUtau.App.ViewModels; using OpenUtau.Classic; using OpenUtau.Core; +using OpenUtau.Core.DiffSinger; using OpenUtau.Core.Format; using OpenUtau.Core.Ustx; using OpenUtau.Core.Util; @@ -23,8 +24,6 @@ using Serilog; using Point = Avalonia.Point; -using OpenUtau.Core.DiffSinger; - namespace OpenUtau.App.Views { public partial class MainWindow : Window, ICmdSubscriber { private readonly KeyModifiers cmdKey = @@ -475,25 +474,33 @@ public void OpenSingersWindow() { if (lifetime == null) { return; } - var dialog = lifetime.Windows.FirstOrDefault(w => w is SingersDialog); - if (dialog == null) { - USinger? singer = null; - if (viewModel.TracksViewModel.SelectedParts.Count > 0) { - singer = viewModel.TracksViewModel.Tracks[viewModel.TracksViewModel.SelectedParts.First().trackNo].Singer; - } - if (singer == null && viewModel.TracksViewModel.Tracks.Count > 0) { - singer = viewModel.TracksViewModel.Tracks.First().Singer; + + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), true, "singers window")); + try { + var dialog = lifetime.Windows.FirstOrDefault(w => w is SingersDialog); + if (dialog == null) { + USinger? singer = null; + if (viewModel.TracksViewModel.SelectedParts.Count > 0) { + singer = viewModel.TracksViewModel.Tracks[viewModel.TracksViewModel.SelectedParts.First().trackNo].Singer; + } + if (singer == null && viewModel.TracksViewModel.Tracks.Count > 0) { + singer = viewModel.TracksViewModel.Tracks.First().Singer; + } + var vm = new SingersViewModel(); + if (singer != null) { + vm.Singer = singer; + } + dialog = new SingersDialog() { DataContext = vm }; + dialog.Show(); } - var vm = new SingersViewModel(); - if (singer != null) { - vm.Singer = singer; + dialog.Activate(); + if (dialog.Position.Y < 0) { + dialog.Position = dialog.Position.WithY(0); } - dialog = new SingersDialog() { DataContext = vm }; - dialog.Show(); - } - dialog.Activate(); - if (dialog.Position.Y < 0) { - dialog.Position = dialog.Position.WithY(0); + } catch (Exception e) { + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification(e)); + } finally { + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), false, "singers window")); } } @@ -973,10 +980,12 @@ public void PartsCanvasDoubleTapped(object sender, TappedEventArgs args) { var control = canvas.InputHitTest(args.GetPosition(canvas)); if (control is PartControl partControl && partControl.part is UVoicePart) { if (pianoRollWindow == null) { + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), true, "pianoroll window")); pianoRollWindow = new PianoRollWindow() { MainWindow = this, }; pianoRollWindow.ViewModel.PlaybackViewModel = viewModel.PlaybackViewModel; + DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), false, "pianoroll window")); } // Workaround for new window losing focus. openPianoRollWindow = true; @@ -1161,6 +1170,12 @@ private async Task AskIfSaveAndContinue() { public void OnNext(UCommand cmd, bool isUndo) { if (cmd is ErrorMessageNotification notif) { MessageBox.ShowError(this, notif.message, notif.e); + } else if (cmd is LoadingNotification loadingNotif && loadingNotif.window == typeof(MainWindow)) { + if (loadingNotif.startLoading) { + MessageBox.ShowLoading(this); + } else { + MessageBox.CloseLoading(); + } } else if (cmd is VoiceColorRemappingNotification voicecolorNotif) { if(voicecolorNotif.TrackNo < 0 || DocManager.Inst.Project.tracks.Count <= voicecolorNotif.TrackNo) { ValidateTracksVoiceColor(); diff --git a/OpenUtau/Views/MessageBox.axaml.cs b/OpenUtau/Views/MessageBox.axaml.cs index 1fd29eceb..ee5f8f21b 100644 --- a/OpenUtau/Views/MessageBox.axaml.cs +++ b/OpenUtau/Views/MessageBox.axaml.cs @@ -10,6 +10,8 @@ public partial class MessageBox : Window { public enum MessageBoxButtons { Ok, OkCancel, YesNo, YesNoCancel, OkCopy } public enum MessageBoxResult { Ok, Cancel, Yes, No } + private static MessageBox? loadingDialog; + public MessageBox() { InitializeComponent(); } @@ -102,5 +104,23 @@ public static MessageBox ShowModal(Window parent, string text, string title) { msgbox.ShowDialog(parent); return msgbox; } + + public static void ShowLoading(Window parent) { + loadingDialog = new MessageBox() { + Title = "Loading" + }; + loadingDialog.Text.Text = "Please wait..."; + loadingDialog.Show(parent); + } + + public static void CloseLoading() { + if (loadingDialog != null) { + loadingDialog.Close(); + } + } + + public static bool LoadingIsActive() { + return loadingDialog != null && loadingDialog.IsActive; + } } } diff --git a/OpenUtau/Views/PianoRollWindow.axaml.cs b/OpenUtau/Views/PianoRollWindow.axaml.cs index e60133ed6..3448cc665 100644 --- a/OpenUtau/Views/PianoRollWindow.axaml.cs +++ b/OpenUtau/Views/PianoRollWindow.axaml.cs @@ -21,7 +21,7 @@ interface IValueTip { void UpdateValueTip(string text); } - public partial class PianoRollWindow : Window, IValueTip { + public partial class PianoRollWindow : Window, IValueTip, ICmdSubscriber { public MainWindow? MainWindow { get; set; } public readonly PianoRollViewModel ViewModel; @@ -58,6 +58,8 @@ public PianoRollWindow() { noteDefaultsCommand = ReactiveCommand.Create(() => { EditNoteDefaults(); }); + + DocManager.Inst.AddSubscriber(this); } public void WindowDeactivated(object sender, EventArgs args) { @@ -1376,5 +1378,15 @@ public void AttachExpressions() { exps[DocManager.Inst.Project.expSecondary].SelectExp(); exps[DocManager.Inst.Project.expPrimary].SelectExp(); } + + public void OnNext(UCommand cmd, bool isUndo) { + if (cmd is LoadingNotification loadingNotif && loadingNotif.window == typeof(PianoRollWindow)) { + if (loadingNotif.startLoading) { + MessageBox.ShowLoading(this); + } else { + MessageBox.CloseLoading(); + } + } + } } } diff --git a/OpenUtau/Views/SingersDialog.axaml.cs b/OpenUtau/Views/SingersDialog.axaml.cs index f52a78362..900630ba0 100644 --- a/OpenUtau/Views/SingersDialog.axaml.cs +++ b/OpenUtau/Views/SingersDialog.axaml.cs @@ -421,7 +421,13 @@ void OnKeyDown(object sender, KeyEventArgs args) { #region ICmdSubscriber public void OnNext(UCommand cmd, bool isUndo) { - if (cmd is OtoChangedNotification otoChanged) { + if (cmd is LoadingNotification loadingNotif && loadingNotif.window == typeof(SingersDialog)) { + if (loadingNotif.startLoading) { + MessageBox.ShowLoading(this); + } else { + MessageBox.CloseLoading(); + } + } else if (cmd is OtoChangedNotification otoChanged) { var viewModel = DataContext as SingersViewModel; if (viewModel == null) { return;