diff --git a/OpenUtau/Controls/PianoRoll.axaml b/OpenUtau/Controls/PianoRoll.axaml index fdc205387..9504a2b70 100644 --- a/OpenUtau/Controls/PianoRoll.axaml +++ b/OpenUtau/Controls/PianoRoll.axaml @@ -22,7 +22,7 @@ - + @@ -378,48 +378,45 @@ - - + + - - + + - - + + - - + + - + - - + + - + @@ -603,10 +600,10 @@ - - + + - + - + + + + diff --git a/OpenUtau/Controls/PianoRoll.axaml.cs b/OpenUtau/Controls/PianoRoll.axaml.cs index 1d5a660fe..1ef17beeb 100644 --- a/OpenUtau/Controls/PianoRoll.axaml.cs +++ b/OpenUtau/Controls/PianoRoll.axaml.cs @@ -397,16 +397,19 @@ void SetPenToolIcon() { penTool.Classes.Remove("penTool"); penTool.Classes.Remove("penPlusTool"); penTool.Classes.Add(ViewModel.EditTool.PenToolVariation == 1 ? "penPlusTool" : "penTool"); + ToolTip.SetTip(penTool, ViewModel.EditTool.PenToolVariation == 1 ? ViewModel.PenPlusToolTip : ViewModel.PenToolTip); } void SetDrawPitchToolIcon() { drawPitchTool.Classes.Remove("drawPitchTool"); drawPitchTool.Classes.Remove("overwritePitchTool"); drawPitchTool.Classes.Add(ViewModel.EditTool.DrawPitchToolVariation == 1 ? "overwritePitchTool" : "drawPitchTool"); + ToolTip.SetTip(drawPitchTool, ViewModel.EditTool.DrawPitchToolVariation == 1 ? ViewModel.OverwritePitchToolTip : ViewModel.DrawPitchToolTip); } void SetDrawLinePitchToolIcon() { drawLinePitchTool.Classes.Remove("drawLinePitchTool"); drawLinePitchTool.Classes.Remove("overwriteLinePitchTool"); drawLinePitchTool.Classes.Add(ViewModel.EditTool.DrawLinePitchToolVariation == 1 ? "overwriteLinePitchTool" : "drawLinePitchTool"); + ToolTip.SetTip(drawLinePitchTool, ViewModel.EditTool.DrawLinePitchToolVariation == 1 ? ViewModel.OverwriteLinePitchToolTip : ViewModel.DrawLinePitchToolTip); } void SearchNote() { @@ -601,6 +604,9 @@ public void KeyboardPointerMoved(object sender, PointerEventArgs args) { var element = (TrackBackground)sender; keyboardPlayState.Update(args.Pointer, args.GetPosition(element)); } + Cursor = null; + ViewModel.SetStatusBarText("Keyboard"); + args.Handled = true; } public void KeyboardPointerReleased(object sender, PointerReleasedEventArgs args) { @@ -657,6 +663,9 @@ public void TimelinePointerMoved(object sender, PointerEventArgs args) { int tick = left + ViewModel.NotesViewModel.Part?.position ?? 0; ViewModel.PlaybackViewModel?.MovePlayPos(tick); } + Cursor = null; + ViewModel.SetStatusBarText("Timeline"); + args.Handled = true; } public void TimelinePointerReleased(object sender, PointerReleasedEventArgs args) { @@ -937,6 +946,7 @@ private void NotesCanvasRightPointerPressed(Control control, PointerPoint point, public void NotesCanvasPointerMoved(object sender, PointerEventArgs args) { var control = (Control)sender; var point = args.GetCurrentPoint(control); + ViewModel.SetStatusBarText("NotesCanvas"); args.Handled = true; if (ValueTipCanvas != null) { valueTipPointerPosition = args.GetCurrentPoint(ValueTipCanvas!).Position; @@ -1114,6 +1124,7 @@ public void ExpCanvasPointerPressed(object sender, PointerPressedEventArgs args) public void ExpCanvasPointerMoved(object sender, PointerEventArgs args) { var control = (Control)sender; var point = args.GetCurrentPoint(control); + ViewModel.SetStatusBarText("ExpCanvas"); args.Handled = true; if (ValueTipCanvas != null) { valueTipPointerPosition = args.GetCurrentPoint(ValueTipCanvas!).Position; @@ -1238,6 +1249,7 @@ public async void PhonemeCanvasPointerPressed(object sender, PointerPressedEvent } public void PhonemeCanvasPointerMoved(object sender, PointerEventArgs args) { + ViewModel.SetStatusBarText("PhonemeCanvas"); args.Handled = true; if (ViewModel?.NotesViewModel?.Part == null) { return; @@ -1285,6 +1297,7 @@ public void PhonemeCanvasPointerReleased(object sender, PointerReleasedEventArgs public void BackgroundPointerMoved(object sender, PointerEventArgs args) { Cursor = null; + ViewModel.SetStatusBarText("Background"); args.Handled = true; } diff --git a/OpenUtau/Strings/Strings.axaml b/OpenUtau/Strings/Strings.axaml index 0be61d251..a48d9be71 100644 --- a/OpenUtau/Strings/Strings.axaml +++ b/OpenUtau/Strings/Strings.axaml @@ -382,6 +382,13 @@ Warning: this option removes custom presets. Enable Phonemizer Follows Parent Phonemizer + + Click to move playhead + Click to play sound + Double click a phoneme to edit + Scroll to zoom in/out horizontally + , + Drag to edit timing and envelope Alias Color @@ -514,40 +521,6 @@ Warning: this option removes custom presets. View Vibrato (U) View Waveform (W) View Expressions (L) - Line Draw Pitch Tool (5) - Left click to draw (draw straight line) - Right click to reset - Hold Ctrl to select - Hold Alt to smoothen - Draw Pitch Tool (4) - Left click to draw - Right click to reset - Hold Ctrl to select - Hold Alt to smoothen - Eraser Tool (3) - Knife Tool (5) - Overwrite Pitch Tool (4) - Left click to draw (overwrites vibrato or mod+) - Right click to reset - Hold Ctrl to select - Hold Alt to smoothen - Overwrite Line Pitch Tool (5) - Left click to draw - (draw straight line overwrites the vibrato or mod+) - Hold Ctrl to draw sine curve - Hold Alt + Shift to tune S-curve - Hold Alt to draw S-curve - Right click to reset - Hold Ctrl to select - Hold Alt to smoothen - Pen Plus Tool (Ctrl + 2) - Left click to draw - Right click to delete - Hold Ctrl to select - Pen Tool (2) - Left click to draw - Hold Ctrl to select - Selection Tool (1) Attack time delta Overlap Preutter @@ -781,6 +754,35 @@ General Space: Play ◀ Scroll here to zoom horizontally ▶ Scroll here to zoom vertically ▶ + + Draw Line Pitch Tool + Draw Pitch Tool + Eraser Tool + Knife Tool + Overwrite Line Pitch Tool + Overwrite Pitch Tool + Pen Tool + Pen Plus Tool + Selection Tool + Hold Alt to smoothen + Hold Ctrl to select + Click to delete note + Drag to create note + Drag to draw + Drag to draw straight line + Drag to draw straight line (overwrites vibrato or mod+) + Drag to draw (overwrites vibrato or mod+) + Drag to reset + Drag to select + Click to set expression + Click to split note + Right click to delete note + Right click to deselect + Right drag to reset + Right click to reset + Hold Shift + Ctrl to draw straight line + Hold Shift to draw horizontal line + Hold Shift to set same expressions Duplicate Track Duplicate Track Settings diff --git a/OpenUtau/ViewModels/PianoRollViewModel.cs b/OpenUtau/ViewModels/PianoRollViewModel.cs index 7864e86ab..8e9fe9b4f 100644 --- a/OpenUtau/ViewModels/PianoRollViewModel.cs +++ b/OpenUtau/ViewModels/PianoRollViewModel.cs @@ -82,6 +82,18 @@ public bool ShowPhonemizerTags { [Reactive] public int PenToolIndex { get; set; } = Preferences.Default.EditTool.PenToolVariation; [Reactive] public int DrawPitchToolIndex { get; set; } = Preferences.Default.EditTool.DrawPitchToolVariation; [Reactive] public int DrawLinePitchToolIndex { get; set; } = Preferences.Default.EditTool.DrawLinePitchToolVariation; + public string SelectionToolTip => GetOperationHint(["tools.selection", "tools.tips.leftdragselect", "tools.tips.rightdeselect"], "\n "); + public string PenToolTip => GetOperationHint(["tools.pen", "tools.tips.leftdragcreate", "tools.tips.rightdeselect", "tools.tips.ctrlselect"], "\n "); + public string PenPlusToolTip => GetOperationHint(["tools.penplus", "tools.tips.leftdragcreate", "tools.tips.rightdelete", "tools.tips.ctrlselect"], "\n "); + public string EraserToolTip => GetOperationHint(["tools.eraser", "tools.tips.leftdelete", "tools.tips.rightdeselect", "tools.tips.ctrlselect"], "\n "); + public string DrawPitchToolTip => GetOperationHint(["tools.drawpitch", "tools.tips.leftdragdraw", "tools.tips.rightdragreset", "tools.tips.altsmoothen", "tools.tips.ctrlselect"], "\n "); + public string OverwritePitchToolTip => GetOperationHint(["tools.overwritepitch", "tools.tips.leftdragdrawoverwrite", "tools.tips.rightdragreset", "tools.tips.altsmoothen", "tools.tips.ctrlselect"], "\n "); + public string DrawLinePitchToolTip => GetOperationHint(["tools.drawlinepitch", "tools.tips.leftdragdrawline", "tools.tips.rightdragreset", "tools.tips.altsmoothen", "tools.tips.ctrlselect"], "\n "); + public string OverwriteLinePitchToolTip => GetOperationHint(["tools.overwritelinepitch", "tools.tips.leftdragdrawlineoverwrite", "tools.tips.rightdragreset", "tools.tips.altsmoothen", "tools.tips.ctrlselect"], "\n "); + public string KnifeToolTip => GetOperationHint(["tools.knife", "tools.tips.leftsplit", "tools.tips.rightdeselect", "tools.tips.ctrlselect"], "\n "); + public string CurveSelectionToolTip => GetOperationHint(["tools.selection", "tools.tips.leftdragselect", "tools.tips.rightdeselect"], "\n "); + public string CurvePenToolTip => GetOperationHint(["tools.pen", "tools.tips.leftdragdraw", "tools.tips.rightdragreset", "tools.tips.shifthorizontal", "tools.tips.shiftctrlline"], "\n "); + public string CurveEraserToolTip => GetOperationHint(["tools.eraser", "tools.tips.leftdragreset", "tools.tips.rightdeselect"], "\n "); public ObservableCollectionExtended LegacyPlugins { get; private set; } = new ObservableCollectionExtended(); @@ -98,6 +110,7 @@ public bool ShowPhonemizerTags { public Dictionary LegacyPluginShortcuts { get; private set; } = new Dictionary(); + [Reactive] public string StatusBarText { get; set; } = string.Empty; [Reactive] public double Progress { get; set; } [Reactive] public bool CanUndo { get; set; } = false; [Reactive] public bool CanRedo { get; set; } = false; @@ -219,6 +232,92 @@ public PianoRollViewModel() { DocManager.Inst.AddSubscriber(this); } + public void SetStatusBarText(string pointer) { + string separator = ThemeManager.GetString("operation.separator"); + switch (pointer) { + case "Keyboard": + StatusBarText = GetOperationHint(["operation.clickplaysound"], separator); + break; + case "Timeline": + StatusBarText = GetOperationHint(["operation.clickplayhead", "operation.scroolzoom"], separator); + break; + case "NotesCanvas": + switch (EditTool.CurrentTool) { + case EditTools.CursorTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragselect", "tools.tips.rightdeselect"], separator); + break; + case EditTools.PenTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragcreate", "tools.tips.rightdeselect", "tools.tips.ctrlselect"], separator); + break; + case EditTools.PenPlusTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragcreate", "tools.tips.rightdelete", "tools.tips.ctrlselect"], separator); + break; + case EditTools.EraserTool: + StatusBarText = GetOperationHint(["tools.tips.leftdelete", "tools.tips.rightdeselect", "tools.tips.ctrlselect"], separator); + break; + case EditTools.DrawPitchTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragdraw", "tools.tips.rightdragreset", "tools.tips.altsmoothen", "tools.tips.ctrlselect"], separator); + break; + case EditTools.OverwritePitchTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragdrawoverwrite", "tools.tips.rightdragreset", "tools.tips.altsmoothen", "tools.tips.ctrlselect"], separator); + break; + case EditTools.DrawLinePitchTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragdrawline", "tools.tips.rightdragreset", "tools.tips.altsmoothen", "tools.tips.ctrlselect"], separator); + break; + case EditTools.OverwriteLinePitchTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragdrawlineoverwrite", "tools.tips.rightdragreset", "tools.tips.altsmoothen", "tools.tips.ctrlselect"], separator); + break; + case EditTools.KnifeTool: + StatusBarText = GetOperationHint(["tools.tips.leftsplit", "tools.tips.rightdeselect", "tools.tips.ctrlselect"], separator); + break; + default: + break; + } + break; + case "PhonemeCanvas": + StatusBarText = GetOperationHint(["operation.doubleeditphoneme", "operation.timingenvelope"], separator); + break; + case "ExpCanvas": + var vm = NotesViewModel; + if (vm.Project == null + || vm.Part == null + || vm.Project.tracks.Count <= vm.Part.trackNo + || !vm.Project.tracks[vm.Part.trackNo].TryGetExpDescriptor(vm.Project, vm.PrimaryKey, out var exp)) { + StatusBarText = string.Empty; + break; + } + if (exp.type == UExpressionType.Curve) { + switch (CurveViewModel.CurveTool) { + case CurveTools.CurveSelectTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragselect", "tools.tips.rightdeselect"], separator); + break; + case CurveTools.CurvePenTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragdraw", "tools.tips.rightdragreset", "tools.tips.shifthorizontal", "tools.tips.shiftctrlline"], separator); + break; + case CurveTools.CurveEraserTool: + StatusBarText = GetOperationHint(["tools.tips.leftdragreset", "tools.tips.rightdeselect"], separator); + break; + default: + break; + } + } else { + StatusBarText = GetOperationHint(["tools.tips.leftexp", "tools.tips.rightreset", "tools.tips.shiftsameexp"], separator); + } + break; + case "Background": + default: + StatusBarText = string.Empty; + break; + } + } + private string GetOperationHint(string[] keys, string separator) { + var strings = new List(); + foreach (string key in keys) { + strings.Add(ThemeManager.GetString(key)); + } + return string.Join(separator, strings); + } + private void SetUndoState() { CanUndo = DocManager.Inst.GetUndoState(out string? undoNameKey); if (!string.IsNullOrWhiteSpace(undoNameKey)) {