diff --git a/OpenUtau.Core/Commands/ExpCommands.cs b/OpenUtau.Core/Commands/ExpCommands.cs
index 353a03b6c..55bf85e1b 100644
--- a/OpenUtau.Core/Commands/ExpCommands.cs
+++ b/OpenUtau.Core/Commands/ExpCommands.cs
@@ -193,6 +193,19 @@ public ResetPitchPointsCommand(UVoicePart part, UNote note) : base(part) {
public override void Unexecute() => Note.pitch = oldPitch;
}
+ public class SetPitchPointsCommand : PitchExpCommand {
+ UPitch oldPitch;
+ UPitch newPitch;
+ public SetPitchPointsCommand(UVoicePart part, UNote note, UPitch pitch) : base(part) {
+ Note = note;
+ oldPitch = note.pitch;
+ newPitch = pitch;
+ }
+ public override string ToString() => "Set pitch points";
+ public override void Execute() => Note.pitch = newPitch;
+ public override void Unexecute() => Note.pitch = oldPitch;
+ }
+
public class SetCurveCommand : ExpCommand {
readonly UProject project;
readonly string abbr;
diff --git a/OpenUtau/Controls/NotePropertiesControl.axaml b/OpenUtau/Controls/NotePropertiesControl.axaml
new file mode 100644
index 000000000..7fb31c017
--- /dev/null
+++ b/OpenUtau/Controls/NotePropertiesControl.axaml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OpenUtau/Controls/NotePropertiesControl.axaml.cs b/OpenUtau/Controls/NotePropertiesControl.axaml.cs
new file mode 100644
index 000000000..e2e177eac
--- /dev/null
+++ b/OpenUtau/Controls/NotePropertiesControl.axaml.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Reactive.Linq;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using OpenUtau.App.ViewModels;
+using OpenUtau.App.Views;
+using OpenUtau.Core;
+using OpenUtau.Core.Ustx;
+using ReactiveUI;
+
+namespace OpenUtau.App.Controls {
+ public partial class NotePropertiesControl : UserControl, ICmdSubscriber {
+ private readonly NotePropertiesViewModel ViewModel;
+
+ public NotePropertiesControl() {
+ InitializeComponent();
+ DataContext = ViewModel = new NotePropertiesViewModel();
+
+ DocManager.Inst.AddSubscriber(this);
+ }
+
+ private void LoadPart(UPart? part) {
+ ViewModel.LoadPart(part);
+ ExpressionsPanel.Children.Clear();
+ foreach (NotePropertyExpViewModel expVM in ViewModel.Expressions) {
+ var control = new NotePropertyExpression() { DataContext = expVM };
+ ExpressionsPanel.Children.Add(control);
+ }
+ }
+
+ void OnSavePortamentoPreset(object sender, RoutedEventArgs e) {
+ var dialog = new TypeInDialog() {
+ Title = ThemeManager.GetString("notedefaults.preset.namenew"),
+ onFinish = name => ViewModel.SavePortamentoPreset(name),
+ };
+ //dialog.ShowDialog(this);
+ }
+
+ void OnRemovePortamentoPreset(object sender, RoutedEventArgs e) {
+ ViewModel.RemoveAppliedPortamentoPreset();
+ }
+
+ void OnSaveVibratoPreset(object sender, RoutedEventArgs e) {
+ var dialog = new TypeInDialog() {
+ Title = ThemeManager.GetString("notedefaults.preset.namenew"),
+ onFinish = name => ViewModel.SaveVibratoPreset(name),
+ };
+ //dialog.ShowDialog(this);
+ }
+
+ void OnRemoveVibratoPreset(object sender, RoutedEventArgs e) {
+ ViewModel.RemoveAppliedVibratoPreset();
+ }
+
+ private void OnKeyDown(object? sender, KeyEventArgs e) {
+ switch (e.Key) {
+ case Key.Enter:
+ //OnFinish(sender, e);
+ e.Handled = true;
+ break;
+ case Key.Escape:
+ //OnCancel(sender, e);
+ e.Handled = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ public void OnNext(UCommand cmd, bool isUndo) {
+ if (cmd is LoadPartNotification loadPart) {
+ LoadPart(loadPart.part);
+ }
+ }
+ }
+}
diff --git a/OpenUtau/Controls/NotePropertyExpression.axaml b/OpenUtau/Controls/NotePropertyExpression.axaml
new file mode 100644
index 000000000..3ee189100
--- /dev/null
+++ b/OpenUtau/Controls/NotePropertyExpression.axaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
diff --git a/OpenUtau/Controls/NotePropertyExpression.axaml.cs b/OpenUtau/Controls/NotePropertyExpression.axaml.cs
new file mode 100644
index 000000000..c8a43707d
--- /dev/null
+++ b/OpenUtau/Controls/NotePropertyExpression.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using OpenUtau.App.ViewModels;
+
+namespace OpenUtau.App.Controls {
+ public partial class NotePropertyExpression : UserControl {
+ public NotePropertyExpression() {
+ InitializeComponent();
+ }
+
+ }
+}
diff --git a/OpenUtau/OpenUtau.csproj b/OpenUtau/OpenUtau.csproj
index 5e13bca0a..ca6c323e4 100644
--- a/OpenUtau/OpenUtau.csproj
+++ b/OpenUtau/OpenUtau.csproj
@@ -68,6 +68,12 @@
+
+ NotePropertyExpression.axaml
+
+
+ NotePropertiesControl.axaml
+
True
True
diff --git a/OpenUtau/Strings/Strings.axaml b/OpenUtau/Strings/Strings.axaml
index d3cbb8248..14f86bde9 100644
--- a/OpenUtau/Strings/Strings.axaml
+++ b/OpenUtau/Strings/Strings.axaml
@@ -139,7 +139,7 @@
Install Singer...
Install Singer (Advanced)...
Singers...
-
+
Lyric
Default Lyric
Portamento
@@ -164,6 +164,12 @@ Warning: this option removes custom presets.
Period
Shift
+ Apply
+ Cancel
+ Set
+ Set only on long notes
+ Enable
+
Alias
Color
Consonant
@@ -196,6 +202,7 @@ Warning: this option removes custom presets.
Remove Tone Suffix
Romaji to Hiragana
Note Defaults
+ Note Properties
Notes
Add tail "-"
Add tail "R"
diff --git a/OpenUtau/Strings/Strings.ja-JP.axaml b/OpenUtau/Strings/Strings.ja-JP.axaml
index ed011d774..aeb8feb0f 100644
--- a/OpenUtau/Strings/Strings.ja-JP.axaml
+++ b/OpenUtau/Strings/Strings.ja-JP.axaml
@@ -155,8 +155,8 @@
すべての値をリセットします。
警告: このオプションを実行すると、カスタムプリセットも削除されます。
ビブラート
- 最小値
- 最小値による自動ビブラート
+ 最短の音符長
+ 長い音符に自動でビブラートをかける
深さ
フェードイン
長さ
@@ -164,6 +164,12 @@
周期
位相
+ 適用
+ キャンセル
+ セット
+ 長い音符のみセットする
+ オン
+
エイリアス
子音部
@@ -196,6 +202,7 @@
音程のSuffixを削除
ローマ字→ひらがな
デフォルト値を設定
+ 音符のプロパティ
音符
末尾に"-"を追加
末尾に"R"を追加
diff --git a/OpenUtau/ViewModels/NotePropertiesViewModel.cs b/OpenUtau/ViewModels/NotePropertiesViewModel.cs
new file mode 100644
index 000000000..51884565d
--- /dev/null
+++ b/OpenUtau/ViewModels/NotePropertiesViewModel.cs
@@ -0,0 +1,350 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Linq;
+using Avalonia.Threading;
+using OpenUtau.Core;
+using OpenUtau.Core.Format;
+using OpenUtau.Core.Ustx;
+using OpenUtau.Core.Util;
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+using SharpCompress;
+
+namespace OpenUtau.App.ViewModels {
+ public class NotePropertiesViewModel : ViewModelBase, ICmdSubscriber {
+ [Reactive] public string Lyric { get; set; } = "a";
+ [Reactive] public int PortamentoLength { get; set; }
+ [Reactive] public int PortamentoStart { get; set; }
+ [Reactive] public bool VibratoEnable { get; set; }
+ [Reactive] public float VibratoLength { get; set; }
+ [Reactive] public float VibratoPeriod { get; set; }
+ [Reactive] public float VibratoDepth { get; set; }
+ [Reactive] public float VibratoIn { get; set; }
+ [Reactive] public float VibratoOut { get; set; }
+ [Reactive] public float VibratoShift { get; set; }
+ [Reactive] public float AutoVibratoNoteLength { get; set; }
+ [Reactive] public bool AutoVibratoToggle { get; set; }
+ [Reactive] public bool IsNoteSelected { get; set; } = false;
+
+ public List? PortamentoPresets { get; }
+ public NotePresets.PortamentoPreset? ApplyPortamentoPreset {
+ get => appliedPortamentoPreset;
+ set => this.RaiseAndSetIfChanged(ref appliedPortamentoPreset, value);
+ }
+ public List? VibratoPresets { get; }
+ public NotePresets.VibratoPreset? ApplyVibratoPreset {
+ get => appliedVibratoPreset;
+ set => this.RaiseAndSetIfChanged(ref appliedVibratoPreset, value);
+ }
+ private NotePresets.PortamentoPreset? appliedPortamentoPreset = NotePresets.Default.DefaultPortamento;
+ private NotePresets.VibratoPreset? appliedVibratoPreset = NotePresets.Default.DefaultVibrato;
+
+ private UVoicePart? part;
+ private HashSet selectedNotes = new HashSet();
+ public List Expressions = new List();
+
+ public NotePropertiesViewModel() {
+ PortamentoPresets = NotePresets.Default.PortamentoPresets;
+ VibratoPresets = NotePresets.Default.VibratoPresets;
+
+ this.WhenAnyValue(vm => vm.ApplyPortamentoPreset)
+ .WhereNotNull()
+ .Subscribe(portamentoPreset => {
+ if (portamentoPreset != null) {
+ PortamentoLength = portamentoPreset.PortamentoLength;
+ PortamentoStart = portamentoPreset.PortamentoStart;
+ }
+ });
+ this.WhenAnyValue(vm => vm.ApplyVibratoPreset)
+ .WhereNotNull()
+ .Subscribe(vibratoPreset => {
+ if (vibratoPreset != null) {
+ VibratoLength = Math.Max(0, Math.Min(100, vibratoPreset.VibratoLength));
+ VibratoPeriod = Math.Max(5, Math.Min(500, vibratoPreset.VibratoPeriod));
+ VibratoDepth = Math.Max(5, Math.Min(200, vibratoPreset.VibratoDepth));
+ VibratoIn = Math.Max(0, Math.Min(100, vibratoPreset.VibratoIn));
+ VibratoOut = Math.Max(0, Math.Min(100, vibratoPreset.VibratoOut));
+ VibratoShift = Math.Max(0, Math.Min(100, vibratoPreset.VibratoShift));
+ }
+ });
+
+ MessageBus.Current.Listen()
+ .Subscribe(e => {
+ selectedNotes.Clear();
+ selectedNotes.UnionWith(e.selectedNotes);
+ selectedNotes.UnionWith(e.tempSelectedNotes);
+ OnSelectNotes();
+ });
+
+ DocManager.Inst.AddSubscriber(this);
+ }
+
+ private void OnSelectNotes() {
+ PortamentoLength = NotePresets.Default.DefaultPortamento.PortamentoLength;
+ PortamentoStart = NotePresets.Default.DefaultPortamento.PortamentoStart;
+ AutoVibratoNoteLength = NotePresets.Default.AutoVibratoNoteDuration;
+ AutoVibratoToggle = NotePresets.Default.AutoVibratoToggle;
+
+ if (selectedNotes.Count > 0) {
+ IsNoteSelected = true;
+ var note = selectedNotes.First();
+
+ Lyric = note.lyric;
+ VibratoEnable = note.vibrato.length == 0 ? false : true;
+ VibratoLength = note.vibrato.length == 0 ? NotePresets.Default.DefaultVibrato.VibratoLength : note.vibrato.length;
+ VibratoPeriod = note.vibrato.period;
+ VibratoDepth = note.vibrato.depth;
+ VibratoIn = note.vibrato.@in;
+ VibratoOut = note.vibrato.@out;
+ VibratoShift = note.vibrato.shift;
+ } else {
+ IsNoteSelected = false;
+ Lyric = NotePresets.Default.DefaultLyric;
+ VibratoLength = NotePresets.Default.DefaultVibrato.VibratoLength;
+ VibratoPeriod = NotePresets.Default.DefaultVibrato.VibratoPeriod;
+ VibratoDepth = NotePresets.Default.DefaultVibrato.VibratoDepth;
+ VibratoIn = NotePresets.Default.DefaultVibrato.VibratoIn;
+ VibratoOut = NotePresets.Default.DefaultVibrato.VibratoOut;
+ VibratoShift = NotePresets.Default.DefaultVibrato.VibratoShift;
+ }
+ AttachExpressions();
+ }
+
+ public void LoadPart(UPart? part) {
+ Expressions.Clear();
+ if (part != null && part is UVoicePart) {
+ this.part = part as UVoicePart;
+
+ foreach (KeyValuePair pair in DocManager.Inst.Project.expressions) {
+ if (pair.Value.type != UExpressionType.Curve) {
+ var viewModel = new NotePropertyExpViewModel(pair.Value);
+ if (pair.Value.abbr == Ustx.CLR) {
+ var track = DocManager.Inst.Project.tracks[part.trackNo];
+ if (track.VoiceColorExp != null && track.VoiceColorExp.options.Length > 0) {
+ track.VoiceColorExp.options.ForEach(opt => viewModel.Options.Add(opt));
+ }
+ }
+ Expressions.Add(viewModel);
+ }
+ }
+ AttachExpressions();
+ } else {
+ this.part = null;
+ }
+ }
+
+ private void AttachExpressions() {
+ if (Expressions.Count > 0) {
+ if (selectedNotes.Count > 0) {
+ var note = selectedNotes.First();
+
+ foreach (NotePropertyExpViewModel exp in Expressions) {
+ exp.IsNoteSelected = true;
+ var phonemeExpression = note.phonemeExpressions.FirstOrDefault(e => e.abbr == exp.abbr);
+ if (phonemeExpression != null) {
+ if (exp.IsNumerical) {
+ exp.Value = phonemeExpression.value;
+ } else if (exp.IsOptions) {
+ exp.SelectedOption = (int)phonemeExpression.value;
+ }
+ } else {
+ if (exp.IsNumerical) {
+ exp.Value = exp.defaultValue;
+ } else if (exp.IsOptions) {
+ exp.SelectedOption = (int)exp.defaultValue;
+ }
+ }
+ }
+ } else {
+ foreach (NotePropertyExpViewModel exp in Expressions) {
+ exp.IsNoteSelected = false;
+ if (exp.IsNumerical) {
+ exp.Value = exp.defaultValue;
+ } else if (exp.IsOptions) {
+ exp.SelectedOption = (int)exp.defaultValue;
+ }
+ }
+ }
+ }
+ }
+
+ // presets
+ public void SavePortamentoPreset(string name) {
+ if (string.IsNullOrEmpty(name)) {
+ return;
+ }
+ NotePresets.Default.PortamentoPresets.Add(new NotePresets.PortamentoPreset(name, PortamentoLength, PortamentoStart));
+ NotePresets.Save();
+ }
+ public void RemoveAppliedPortamentoPreset() {
+ if (appliedPortamentoPreset == null) {
+ return;
+ }
+ NotePresets.Default.PortamentoPresets.Remove(appliedPortamentoPreset);
+ NotePresets.Save();
+ }
+ public void SaveVibratoPreset(string name) {
+ if (string.IsNullOrEmpty(name)) {
+ return;
+ }
+ NotePresets.Default.VibratoPresets.Add(new NotePresets.VibratoPreset(name, VibratoLength, VibratoPeriod, VibratoDepth, VibratoIn, VibratoOut, VibratoShift));
+ NotePresets.Save();
+ }
+ public void RemoveAppliedVibratoPreset() {
+ if (appliedVibratoPreset == null) {
+ return;
+ }
+ NotePresets.Default.VibratoPresets.Remove(appliedVibratoPreset);
+ NotePresets.Save();
+ }
+
+ #region ICmdSubscriber
+ public void OnNext(UCommand cmd, bool isUndo) {
+ var note = selectedNotes.FirstOrDefault();
+ if (note == null) { return; }
+
+ if (cmd is ChangeNoteLyricCommand changeNoteLyricCommand) {
+ if (changeNoteLyricCommand.Notes.Contains(note)) {
+ Lyric = note.lyric;
+ }
+ } else if (cmd is VibratoLengthCommand vibratoLengthCommand) {
+ if (vibratoLengthCommand.Notes.Contains(note)) {
+ if (note.vibrato.length == 0) {
+ VibratoEnable = false;
+ VibratoLength = NotePresets.Default.DefaultVibrato.VibratoLength;
+ } else {
+ VibratoEnable = true;
+ VibratoLength = note.vibrato.length;
+ }
+ }
+ } else if (cmd is VibratoFadeInCommand vibratoFadeInCommand) {
+ if (vibratoFadeInCommand.Notes.Contains(note)) {
+ VibratoIn = note.vibrato.@in;
+ }
+ } else if (cmd is VibratoFadeOutCommand vibratoFadeOutCommand) {
+ if (vibratoFadeOutCommand.Notes.Contains(note)) {
+ VibratoOut = note.vibrato.@out;
+ }
+ } else if (cmd is VibratoDepthCommand vibratoDepthCommand) {
+ if (vibratoDepthCommand.Notes.Contains(note)) {
+ VibratoDepth = note.vibrato.depth;
+ }
+ } else if (cmd is VibratoPeriodCommand vibratoPeriodCommand) {
+ if (vibratoPeriodCommand.Notes.Contains(note)) {
+ VibratoPeriod = note.vibrato.period;
+ }
+ } else if (cmd is VibratoShiftCommand vibratoShiftCommand) {
+ if (vibratoShiftCommand.Notes.Contains(note)) {
+ VibratoShift = note.vibrato.shift;
+ }
+ } else if (cmd is PitchExpCommand pitchExpCommand) {
+ //
+ } else if (cmd is SetPhonemeExpressionCommand || cmd is ResetExpressionsCommand) {
+ AttachExpressions();
+ }
+ }
+ #endregion
+
+ /*public void Finish() {
+ if (notesViewModel.Part != null) {
+ UVoicePart part = notesViewModel.Part;
+ List selectedNotes = notesViewModel.Selection.ToList();
+
+ DocManager.Inst.StartUndoGroup();
+
+ if (SetLyric) {
+ foreach (UNote note in selectedNotes) {
+ if (note.lyric != Lyric) {
+ DocManager.Inst.ExecuteCmd(new ChangeNoteLyricCommand(part, note, Lyric));
+ }
+ }
+ }
+ if (SetPortamento) {
+ foreach (UNote note in selectedNotes) {
+ var pitch = new UPitch();
+ pitch.AddPoint(new PitchPoint(PortamentoStart, 0));
+ pitch.AddPoint(new PitchPoint(PortamentoStart + PortamentoLength, 0));
+ DocManager.Inst.ExecuteCmd(new SetPitchPointsCommand(part, note, pitch));
+ }
+ }
+ if (SetVibrato) {
+ foreach (UNote note in selectedNotes) {
+ if(VibratoEnable && VibratoLength != 0) {
+ if (!AutoVibratoToggle || (AutoVibratoToggle && note.duration >= AutoVibratoNoteLength)) {
+ DocManager.Inst.ExecuteCmd(new VibratoLengthCommand(part, note, VibratoLength));
+ DocManager.Inst.ExecuteCmd(new VibratoFadeInCommand(part, note, VibratoIn));
+ DocManager.Inst.ExecuteCmd(new VibratoFadeOutCommand(part, note, VibratoOut));
+ DocManager.Inst.ExecuteCmd(new VibratoDepthCommand(part, note, VibratoDepth));
+ DocManager.Inst.ExecuteCmd(new VibratoPeriodCommand(part, note, VibratoPeriod));
+ DocManager.Inst.ExecuteCmd(new VibratoShiftCommand(part, note, VibratoShift));
+ } else {
+ DocManager.Inst.ExecuteCmd(new VibratoLengthCommand(part, note, 0));
+ }
+ } else if (note.vibrato.length != 0) {
+ DocManager.Inst.ExecuteCmd(new VibratoLengthCommand(part, note, 0));
+ }
+ }
+ }
+ foreach (NotePropertyExpViewModel expVM in Expressions) {
+ if (expVM.Set) {
+ float value;
+ if (expVM.IsNumerical) {
+ value = expVM.Value;
+ } else if (expVM.IsOptions) {
+ value = expVM.SelectedOption;
+ } else {
+ continue;
+ }
+ var track = notesViewModel.Project.tracks[notesViewModel.Part.trackNo];
+ foreach (UNote note in selectedNotes) {
+ foreach (UPhoneme phoneme in notesViewModel.Part.phonemes) {
+ if (phoneme.Parent == note) {
+ DocManager.Inst.ExecuteCmd(new SetPhonemeExpressionCommand(notesViewModel.Project, track, notesViewModel.Part, phoneme, expVM.abbr, value));
+ }
+ }
+ }
+ }
+ }
+
+ DocManager.Inst.EndUndoGroup();
+ }
+ }*/
+ }
+
+ public class NotePropertyExpViewModel : ViewModelBase {
+ public string Name { get; set; }
+ public bool IsNumerical { get; set; } = false;
+ public bool IsOptions { get; set; } = false;
+ public float Min { get; set; }
+ public float Max { get; set; }
+ public ObservableCollection Options { get; set; } = new ObservableCollection();
+ public string abbr;
+ public float defaultValue;
+
+ [Reactive] public bool IsNoteSelected { get; set; } = false;
+ [Reactive] public float Value { get; set; }
+ [Reactive] public int SelectedOption { get; set; }
+
+ public NotePropertyExpViewModel(UExpressionDescriptor descriptor) {
+ Name = descriptor.name;
+ defaultValue = descriptor.defaultValue;
+ abbr = descriptor.abbr;
+ if (descriptor.type == UExpressionType.Numerical) {
+ IsNumerical = true;
+ Max = descriptor.max;
+ Min = descriptor.min;
+ Value = defaultValue;
+ } else if (descriptor.type == UExpressionType.Options) {
+ IsOptions = true;
+ descriptor.options.ForEach(opt => Options.Add(opt));
+ SelectedOption = (int)defaultValue;
+ }
+ }
+ public override string ToString() {
+ return Name;
+ }
+ }
+}
diff --git a/OpenUtau/Views/PianoRollWindow.axaml b/OpenUtau/Views/PianoRollWindow.axaml
index 8cf080134..7005bd42b 100644
--- a/OpenUtau/Views/PianoRollWindow.axaml
+++ b/OpenUtau/Views/PianoRollWindow.axaml
@@ -30,6 +30,7 @@
+
+ ToolTip.Tip="{DynamicResource pianoroll.toggle.noteparams}">
@@ -446,6 +447,11 @@
IsHitTestVisible="False" VerticalAlignment="Bottom" Margin="10,5"
IsVisible="{Binding NotesViewModel.PrimaryKeyNotSupported}"
Text="{DynamicResource tip.exps.notsupported}"/>
+
+
+
+
diff --git a/OpenUtau/Views/PianoRollWindow.axaml.cs b/OpenUtau/Views/PianoRollWindow.axaml.cs
index 1390f3870..bddf56099 100644
--- a/OpenUtau/Views/PianoRollWindow.axaml.cs
+++ b/OpenUtau/Views/PianoRollWindow.axaml.cs
@@ -350,7 +350,6 @@ private void NotesCanvasLeftPointerPressed(Control control, PointerPoint point,
private void NotesCanvasRightPointerPressed(Control control, PointerPoint point, PointerPressedEventArgs args) {
var selectedNotes = ViewModel.NotesViewModel.Selection.ToList();
- ViewModel.NotesViewModel.DeselectNotes();
if (ViewModel.NotesViewModel.DrawPitchTool) {
editState = new ResetPitchState(control, ViewModel, this);
return;
@@ -406,42 +405,47 @@ private void NotesCanvasRightPointerPressed(Control control, PointerPoint point,
}
if (ViewModel.NotesViewModel.CursorTool || ViewModel.NotesViewModel.PenTool) {
var hitInfo = ViewModel.NotesViewModel.HitTest.HitTestNote(point.Position);
- if (hitInfo.hitBody) {
- // if note in question was already in selection before clearing
- if (selectedNotes.Contains(hitInfo.note)) {
+ var vibHitInfo = ViewModel.NotesViewModel.HitTest.HitTestVibrato(point.Position);
+ if ((hitInfo.hitBody && hitInfo.note != null) || vibHitInfo.hit) {
+ if (hitInfo.note != null && !selectedNotes.Contains(hitInfo.note)) {
+ ViewModel.NotesViewModel.DeselectNotes();
ViewModel.NotesViewModel.SelectNote(hitInfo.note, false);
}
- }
- if (ViewModel.NotesViewModel.Selection.Count > 0) {
- ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
- Header = ThemeManager.GetString("context.note.copy"),
- Command = ViewModel.NoteCopyCommand,
- CommandParameter = hitInfo,
- });
- ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
- Header = ThemeManager.GetString("context.note.delete"),
- Command = ViewModel.NoteDeleteCommand,
- CommandParameter = hitInfo,
- });
- ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
- Header = ThemeManager.GetString("pianoroll.menu.notes"),
- Items = ViewModel.NoteBatchEdits.ToArray(),
- });
- ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
- Header = ThemeManager.GetString("pianoroll.menu.lyrics"),
- Items = ViewModel.LyricBatchEdits.ToArray(),
- });
- ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
- Header = ThemeManager.GetString("pianoroll.menu.lyrics.edit"),
- Command = lyricsDialogCommand,
- });
- ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
- Header = ThemeManager.GetString("pianoroll.menu.notedefaults"),
- Command = noteDefaultsCommand,
- });
- shouldOpenNotesContextMenu = true;
+ if (ViewModel.NotesViewModel.Selection.Count > 0) {
+ ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
+ Header = ThemeManager.GetString("context.note.copy"),
+ Command = ViewModel.NoteCopyCommand,
+ CommandParameter = hitInfo,
+ });
+ ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
+ Header = ThemeManager.GetString("context.note.delete"),
+ Command = ViewModel.NoteDeleteCommand,
+ CommandParameter = hitInfo,
+ });
+ ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
+ Header = ThemeManager.GetString("pianoroll.menu.notes"),
+ Items = ViewModel.NoteBatchEdits.ToArray(),
+ });
+ ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
+ Header = ThemeManager.GetString("pianoroll.menu.lyrics"),
+ Items = ViewModel.LyricBatchEdits.ToArray(),
+ });
+ ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
+ Header = ThemeManager.GetString("pianoroll.menu.lyrics.edit"),
+ Command = lyricsDialogCommand,
+ });
+ ViewModel.NotesContextMenuItems.Add(new MenuItemViewModel() {
+ Header = ThemeManager.GetString("pianoroll.menu.notedefaults"),
+ Command = noteDefaultsCommand,
+ });
+ shouldOpenNotesContextMenu = true;
+ return;
+ }
+ } else {
+ ViewModel.NotesViewModel.DeselectNotes();
}
} else if (ViewModel.NotesViewModel.EraserTool || ViewModel.NotesViewModel.PenPlusTool) {
+ ViewModel.NotesViewModel.DeselectNotes();
editState = new NoteEraseEditState(control, ViewModel, this, MouseButton.Right);
Cursor = ViewConstants.cursorNo;
}