diff --git a/OpenUtau/ViewModels/NotesViewModelHitTest.cs b/OpenUtau/ViewModels/NotesViewModelHitTest.cs index b222569e1..65cda3465 100644 --- a/OpenUtau/ViewModels/NotesViewModelHitTest.cs +++ b/OpenUtau/ViewModels/NotesViewModelHitTest.cs @@ -12,6 +12,7 @@ public struct NoteHitInfo { public UPhoneme phoneme; public bool hitBody; public bool hitResizeArea; + public bool hitResizeAreaFromStart; public bool hitX; } @@ -72,12 +73,17 @@ public NoteHitInfo HitTestNote(Point point) { result.note = note; result.hitX = true; var tone = viewModel.PointToTone(point); - if (tone == note.tone) { - result.hitBody = true; - double x = viewModel.TickToneToPoint(note.End, tone).X; - result.hitResizeArea = point.X <= x && point.X > x - ViewConstants.ResizeMargin; - break; + if (tone != note.tone) { + continue; } + result.hitBody = true; + double x1 = viewModel.TickToneToPoint(note.position, note.tone).X; + double x2 = viewModel.TickToneToPoint(note.End, tone).X; + var hitLeftResizeArea = point.X >= x1 && point.X < x1 + ViewConstants.ResizeMargin; + var hitRightResizeArea = point.X <= x2 && point.X > x2 - ViewConstants.ResizeMargin; + result.hitResizeAreaFromStart = hitLeftResizeArea && !hitRightResizeArea; // prefer resizing from end + result.hitResizeArea = hitLeftResizeArea || hitRightResizeArea; // hit either of the areas + break; } return result; } diff --git a/OpenUtau/Views/NoteEditStates.cs b/OpenUtau/Views/NoteEditStates.cs index b61d1e9ca..4b3b6f710 100644 --- a/OpenUtau/Views/NoteEditStates.cs +++ b/OpenUtau/Views/NoteEditStates.cs @@ -279,22 +279,35 @@ public override void End(IPointer pointer, Point point) { class NoteResizeEditState : NoteEditState { public readonly UNote note; - public readonly UNote? nextNote; - public readonly bool resizeNext; + public readonly UNote? neighborNote; + public readonly bool resizeNeighbor; + public readonly bool fromStart; public NoteResizeEditState( Control control, PianoRollViewModel vm, IValueTip valueTip, UNote note, - bool resizeNext) : base(control, vm, valueTip) { + bool resizeNeighbor, + bool fromStart = false) : base(control, vm, valueTip) { this.note = note; var notesVm = vm.NotesViewModel; if (!notesVm.Selection.Contains(note)) { notesVm.DeselectNotes(); } - this.resizeNext = notesVm.Selection.Count == 0 && - resizeNext && note.Next != null && note.End == note.Next.position; - nextNote = note.Next; + if (fromStart) { + this.resizeNeighbor = notesVm.Selection.Count == 0 + && resizeNeighbor + && note.Prev != null + && note.position == note.Prev.End; + neighborNote = note.Prev; + } else { + this.resizeNeighbor = notesVm.Selection.Count == 0 + && resizeNeighbor + && note.Next != null + && note.End == note.Next.position; + neighborNote = note.Next; + } + this.fromStart = fromStart; } public override void Update(IPointer pointer, Point point) { var project = DocManager.Inst.Project; @@ -304,11 +317,16 @@ public override void Update(IPointer pointer, Point point) { return; } int snapUnit = project.resolution * 4 / notesVm.SnapDiv; - int newEnd = notesVm.PointToTick(point); + int newTick = notesVm.PointToTick(point); if (notesVm.IsSnapOn) { - newEnd = (int)Math.Floor((double)newEnd / snapUnit) * snapUnit + snapUnit; + newTick = this.fromStart + ? (int)Math.Floor((double)newTick / snapUnit) * snapUnit + : (int)Math.Floor((double)newTick / snapUnit) * snapUnit + snapUnit; } - int deltaDuration = newEnd - note.End; + + int deltaDuration = this.fromStart + ? note.position - newTick + : newTick - note.End; int minNoteTicks = notesVm.IsSnapOn ? snapUnit : 15; if (deltaDuration < 0) { int maxNegDelta = note.duration - minNoteTicks; @@ -320,8 +338,8 @@ public override void Update(IPointer pointer, Point point) { } deltaDuration = Math.Max(deltaDuration, -maxNegDelta); } - if (resizeNext && nextNote != null) { - var maxDelta = Math.Max(0, nextNote.duration - minNoteTicks); + if (resizeNeighbor && neighborNote != null) { + var maxDelta = Math.Max(0, neighborNote.duration - minNoteTicks); deltaDuration = Math.Min(deltaDuration, maxDelta); } if (deltaDuration == 0) { @@ -329,14 +347,22 @@ public override void Update(IPointer pointer, Point point) { return; } if (notesVm.Selection.Count == 0) { - if (resizeNext && nextNote != null) { - DocManager.Inst.ExecuteCmd(new MoveNoteCommand(part, nextNote, deltaDuration, 0)); - DocManager.Inst.ExecuteCmd(new ResizeNoteCommand(part, nextNote, -deltaDuration)); + if (resizeNeighbor && neighborNote != null) { + if (!fromStart) { + DocManager.Inst.ExecuteCmd(new MoveNoteCommand(part, neighborNote, deltaDuration, 0)); + } + DocManager.Inst.ExecuteCmd(new ResizeNoteCommand(part, neighborNote, -deltaDuration)); + } + if (fromStart) { + DocManager.Inst.ExecuteCmd(new MoveNoteCommand(part, note, -deltaDuration, 0)); } DocManager.Inst.ExecuteCmd(new ResizeNoteCommand(part, note, deltaDuration)); valueTip.UpdateValueTip(note.duration.ToString()); return; } + if (fromStart) { + DocManager.Inst.ExecuteCmd(new MoveNoteCommand(part, notesVm.Selection.ToList(), -deltaDuration, 0)); + } DocManager.Inst.ExecuteCmd(new ResizeNoteCommand(part, notesVm.Selection.ToList(), deltaDuration)); valueTip.UpdateValueTip(note.duration.ToString()); } diff --git a/OpenUtau/Views/PianoRollWindow.axaml.cs b/OpenUtau/Views/PianoRollWindow.axaml.cs index 316573553..e60133ed6 100644 --- a/OpenUtau/Views/PianoRollWindow.axaml.cs +++ b/OpenUtau/Views/PianoRollWindow.axaml.cs @@ -364,7 +364,8 @@ private void NotesCanvasLeftPointerPressed(Control control, PointerPoint point, if (noteHitInfo.hitResizeArea) { editState = new NoteResizeEditState( control, ViewModel, this, noteHitInfo.note, - args.KeyModifiers == KeyModifiers.Alt); + args.KeyModifiers == KeyModifiers.Alt, + fromStart: noteHitInfo.hitResizeAreaFromStart); Cursor = ViewConstants.cursorSizeWE; } else if (args.KeyModifiers == cmdKey) { ViewModel.NotesViewModel.ToggleSelectNote(noteHitInfo.note);