diff --git a/Common/DereTore.Common/EnumerableExtensions.cs b/Common/DereTore.Common/EnumerableExtensions.cs index 4ac04d0..34d70ce 100644 --- a/Common/DereTore.Common/EnumerableExtensions.cs +++ b/Common/DereTore.Common/EnumerableExtensions.cs @@ -41,6 +41,19 @@ public static string BuildString(this IEnumerable enumerable, string separ return stringBuilder.ToString(); } + public static bool CountMoreThan(this IEnumerable enumerable, int n) { + var counter = 0; + using (var iter = enumerable.GetEnumerator()) { + while (iter.MoveNext()) { + ++counter; + if (counter > n) { + return true; + } + } + } + return false; + } + private static readonly string DefaultEnumerableStringSeparator = ", "; } diff --git a/StarlightDirector/StarlightDirector.Entities/Note.cs b/StarlightDirector/StarlightDirector.Entities/Note.cs index ab3f75e..8ff3deb 100644 --- a/StarlightDirector/StarlightDirector.Entities/Note.cs +++ b/StarlightDirector/StarlightDirector.Entities/Note.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Windows; using DereTore.Common; using Newtonsoft.Json; @@ -75,6 +76,7 @@ public Note PrevFlickOrSlideNote { _prevFlickOrSlideNote = value; UpdateFlickTypeStep2(); PrevFlickOrSlideNoteID = value?.ID ?? EntityID.Invalid; + DecideRenderingAsFlickOrSlide(); } } @@ -90,6 +92,7 @@ public Note NextFlickOrSlideNote { _nextFlickOrSlideNote = value; UpdateFlickTypeStep2(); NextFlickOrSlideNoteID = value?.ID ?? EntityID.Invalid; + DecideRenderingAsFlickOrSlide(); } } @@ -387,18 +390,27 @@ internal Note(int id, Bar bar) { internal void Reset() { if (NextFlickOrSlideNote != null) { - NextFlickOrSlideNote.PrevFlickOrSlideNote = null; + var next = NextFlickOrSlideNote; + next.PrevFlickOrSlideNote = null; + if (next.IsSlide && !next.IsSlideStart) { + next.Type = NoteType.TapOrFlick; + } } NextFlickOrSlideNote = null; if (PrevFlickOrSlideNote != null) { - PrevFlickOrSlideNote.NextFlickOrSlideNote = null; + var prev = PrevFlickOrSlideNote; + prev.NextFlickOrSlideNote = null; + if (prev.IsSlide && !prev.IsSlideEnd) { + prev.Type = NoteType.TapOrFlick; + } } PrevFlickOrSlideNote = null; if (HoldTarget != null) { - HoldTarget.HoldTarget = null; - if (HoldTarget != null) { - if (!HoldTarget.HasNextFlickOrSlide && !HoldTarget.HasPrevFlickOrSlide && HoldTarget.FlickType != NoteFlickType.Tap) { - HoldTarget.FlickType = NoteFlickType.Tap; + var hold = HoldTarget; + hold.HoldTarget = null; + if (hold != null) { + if (!hold.HasNextFlickOrSlide && !hold.HasPrevFlickOrSlide && hold.FlickType != NoteFlickType.Tap) { + hold.FlickType = NoteFlickType.Tap; } } } @@ -428,6 +440,12 @@ private static void OnTypeChanged(DependencyObject obj, DependencyPropertyChange note.IsSlide = note.IsSlideInternal(); note.UpdateFlickTypeStep2(); note.DecideRenderingAsFlickOrSlide(); + + if (note.IsFlick) { + note.UpdateAsFlickNote(); + } else if (note.IsSlide) { + note.UpdateAsSlideNote(); + } } private static void OnFlickTypeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { @@ -492,7 +510,7 @@ private void UpdateFlickTypeStep2() { private void DecideRenderingAsFlickOrSlide() { if (IsFlick || IsSlide) { if (IsSlide) { - ShouldBeRenderedAsSlide = !HasNextFlickOrSlide || !NextFlickOrSlideNote.IsFlick; + ShouldBeRenderedAsSlide = FlickType == NoteFlickType.Tap && (!HasNextFlickOrSlide || !NextFlickOrSlideNote.IsFlick); } else { ShouldBeRenderedAsSlide = false; } @@ -524,6 +542,20 @@ private void SetNextSyncTargetInternal(Note next) { IsSync = _prevSyncTarget != null || _nextSyncTarget != null; } + private void UpdateAsSlideNote() { + if (HasPrevFlickOrSlide && PrevFlickOrSlideNote.IsSlide) { + PrevFlickOrSlideNote.FlickType = NoteFlickType.Tap; + } + } + + private void UpdateAsFlickNote() { + if (HasPrevFlickOrSlide && PrevFlickOrSlideNote.IsSlide) { + var pos1 = (int)PrevFlickOrSlideNote.FinishPosition; + var pos2 = (int)FinishPosition; + PrevFlickOrSlideNote.FlickType = pos2 >= pos1 ? NoteFlickType.FlickRight : NoteFlickType.FlickLeft; + } + } + private Note _prevFlickOrSlideNote; private Note _nextFlickOrSlideNote; private Note _prevSyncTarget; diff --git a/StarlightDirector/StarlightDirector/UI/Controls/Primitives/ScoreBar.xaml.cs b/StarlightDirector/StarlightDirector/UI/Controls/Primitives/ScoreBar.xaml.cs index 55bb401..c093835 100644 --- a/StarlightDirector/StarlightDirector/UI/Controls/Primitives/ScoreBar.xaml.cs +++ b/StarlightDirector/StarlightDirector/UI/Controls/Primitives/ScoreBar.xaml.cs @@ -34,7 +34,7 @@ public ScoreBarHitTestInfo HitTest(Point pointRelativeToScoreBar) { row = (int)Math.Round((double)row / zoomMod) * zoomMod; var gridCrossingPosition = new Point(column * unitWidth, row * unitHeight); var distance = Point.Subtract(gridCrossingPosition, destPoint); - if (distance.Length > 2 * NoteRadius) { + if (distance.Length > HitTestRadiusScale * NoteRadius) { return new ScoreBarHitTestInfo(this, Bar, new Point(), column, row, false, false); } if (column < 0 || column > columnCount - 1) { @@ -133,5 +133,7 @@ private int GetBestFitZoomMod() { public static readonly int SignatureBase = ScoreSettings.DefaultGlobalGridPerSignature * ScoreSettings.DefaultGlobalSignature; public static readonly double ZoomFactor = 1.2; + private static readonly double HitTestRadiusScale = 1; + } } diff --git a/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.DependencyProperties.cs b/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.DependencyProperties.cs index 4275585..0fccab2 100644 --- a/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.DependencyProperties.cs +++ b/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.DependencyProperties.cs @@ -21,8 +21,13 @@ public TextBlock NoteInfoBlock { set { SetValue(NoteInfoBlockProperty, value); } } + public ContextMenu ExtraNotesContextMenu { + get { return (ContextMenu)GetValue(ExtraNotesContextMenuProperty); } + set { SetValue(ExtraNotesContextMenuProperty, value); } + } + public static readonly DependencyProperty EditModeProperty = DependencyProperty.Register(nameof(EditMode), typeof(EditMode), typeof(ScoreEditor), - new PropertyMetadata(EditMode.Select)); + new PropertyMetadata(EditMode.CreateRelations)); public static readonly DependencyProperty ProjectProperty = DependencyProperty.Register(nameof(Project), typeof(Project), typeof(ScoreEditor), new PropertyMetadata(null, OnProjectChanged)); @@ -30,6 +35,9 @@ public TextBlock NoteInfoBlock { public static readonly DependencyProperty NoteInfoBlockProperty = DependencyProperty.Register(nameof(NoteInfoBlock), typeof(TextBlock), typeof(ScoreEditor), new PropertyMetadata(null)); + public static readonly DependencyProperty ExtraNotesContextMenuProperty = DependencyProperty.Register(nameof(ExtraNotesContextMenu), typeof(ContextMenu), typeof(ScoreEditor), + new PropertyMetadata(null, OnExtraNotesContextMenuChanged)); + private static void OnProjectChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { var editor = (ScoreEditor)obj; var oldproject = (Project)e.OldValue; @@ -43,5 +51,10 @@ private static void OnProjectChanged(DependencyObject obj, DependencyPropertyCha CommandManager.InvalidateRequerySuggested(); } + private static void OnExtraNotesContextMenuChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { + var editor = (ScoreEditor)obj; + editor.SpecialNoteLayer.ContextMenu = (ContextMenu)e.NewValue; + } + } } diff --git a/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.EventHandlers.cs b/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.EventHandlers.cs index e7810c5..781df2a 100644 --- a/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.EventHandlers.cs +++ b/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.EventHandlers.cs @@ -6,6 +6,7 @@ using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; +using DereTore.Common; using StarlightDirector.Entities; using StarlightDirector.Entities.Extensions; using StarlightDirector.Extensions; @@ -75,14 +76,21 @@ private void ScoreNote_MouseDown(object sender, MouseButtonEventArgs e) { return; } var scoreNote = (ScoreNote)sender; - if (scoreNote.IsSelected) { - scoreNote.IsSelected = EditMode != EditMode.Select; + var isControlPressed = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl); + if (isControlPressed) { + scoreNote.IsSelected = !scoreNote.IsSelected; } else { - scoreNote.IsSelected = true; + var originalSelected = scoreNote.IsSelected; + var originalMoreThanOneSelectedNotes = GetSelectedScoreNotes().CountMoreThan(1); + UnselectAllScoreNotes(); + // !originalSelected || (originalSelected && originalAnySelectedNotes) + if (!originalSelected || originalMoreThanOneSelectedNotes) { + scoreNote.IsSelected = true; + } } - if (scoreNote.IsSelected && EditMode != EditMode.Select && EditMode != EditMode.Clear) { + if (scoreNote.IsSelected && EditMode != EditMode.ResetNote) { switch (EditMode) { - case EditMode.Relations: + case EditMode.CreateRelations: EditingLine.Stroke = LineLayer.RelationBrush; break; default: @@ -112,14 +120,11 @@ private void ScoreNote_MouseUp(object sender, MouseButtonEventArgs e) { Debug.Assert(DraggingEndNote != null, "DraggingEndNote != null"); if (DraggingStartNote != null && DraggingEndNote != null) { var mode = EditMode; - if (mode == EditMode.Select) { - return; - } var start = DraggingStartNote; var end = DraggingEndNote; var ns = start.Note; var ne = end.Note; - if (mode == EditMode.Clear) { + if (mode == EditMode.ResetNote) { ns.Reset(); LineLayer.NoteRelations.RemoveAll(start, NoteRelation.Hold); LineLayer.NoteRelations.RemoveAll(start, NoteRelation.FlickOrSlide); @@ -135,7 +140,7 @@ private void ScoreNote_MouseUp(object sender, MouseButtonEventArgs e) { MessageBox.Show(Application.Current.FindResource(App.ResourceKeys.NoteRelationAlreadyExistsPrompt), App.Title, MessageBoxButton.OK, MessageBoxImage.Exclamation); return; } - if (mode != EditMode.Relations) { + if (mode != EditMode.CreateRelations) { throw new ArgumentOutOfRangeException(nameof(mode)); } var first = ns < ne ? ns : ne; @@ -145,7 +150,7 @@ private void ScoreNote_MouseUp(object sender, MouseButtonEventArgs e) { Note.ConnectSync(ns, ne); LineLayer.NoteRelations.Add(start, end, NoteRelation.Sync); LineLayer.InvalidateVisual(); - } else if (ns.FinishPosition != ne.FinishPosition && (ns.Bar != ne.Bar || ns.IndexInGrid != ne.IndexInGrid) && (!ns.IsHoldStart && !ne.IsHoldStart) && !second.IsFlick) { + } else if (ns.FinishPosition != ne.FinishPosition && (ns.Bar != ne.Bar || ns.IndexInGrid != ne.IndexInGrid) && (!ns.IsHoldStart && !ne.IsHoldStart) && (first.IsSlide == second.IsSlide)) { // flick if (first.HasNextFlickOrSlide || second.HasPrevFlickOrSlide) { MessageBox.Show(Application.Current.FindResource(App.ResourceKeys.FlickRelationIsFullPrompt), App.Title, MessageBoxButton.OK, MessageBoxImage.Exclamation); @@ -185,24 +190,22 @@ private void ScoreNote_MouseDoubleClick(object sender, MouseButtonEventArgs e) { } var scoreNote = (ScoreNote)sender; var note = scoreNote.Note; - if (note.IsHoldEnd) { - if (note > note.HoldTarget) { - switch (note.FlickType) { - case NoteFlickType.Tap: - note.FlickType = NoteFlickType.FlickLeft; - Project.IsChanged = true; - break; - case NoteFlickType.FlickLeft: - note.FlickType = NoteFlickType.FlickRight; - Project.IsChanged = true; - break; - case NoteFlickType.FlickRight: - note.FlickType = NoteFlickType.Tap; - Project.IsChanged = true; - break; - default: - throw new ArgumentOutOfRangeException(nameof(note.FlickType)); - } + if (note.IsHoldEnd || note.IsSlideEnd) { + switch (note.FlickType) { + case NoteFlickType.Tap: + note.FlickType = NoteFlickType.FlickLeft; + Project.IsChanged = true; + break; + case NoteFlickType.FlickLeft: + note.FlickType = NoteFlickType.FlickRight; + Project.IsChanged = true; + break; + case NoteFlickType.FlickRight: + note.FlickType = NoteFlickType.Tap; + Project.IsChanged = true; + break; + default: + throw new ArgumentOutOfRangeException(nameof(note.FlickType)); } } e.Handled = true; @@ -292,15 +295,12 @@ private void ScoreEditor_OnMouseUp(object sender, MouseButtonEventArgs e) { var hitTestInfo = ((ScoreBar)element).HitTest(e.GetPosition(element)); LastHitTestInfo = hitTestInfo; } else { - ScoreBar s = null; - foreach (var scoreBar in ScoreBars) { - var top = Canvas.GetTop(scoreBar); - var bottom = top + scoreBar.ActualHeight; - if (top <= myPosition.Y && myPosition.Y < bottom) { - s = scoreBar; - break; - } - } + var s = (from scoreBar in ScoreBars + let top = Canvas.GetTop(scoreBar) + let bottom = top + scoreBar.ActualHeight + where top <= myPosition.Y && myPosition.Y < bottom + select scoreBar) + .FirstOrDefault(); if (s != null) { var hitTestInfo = s.HitTest(e.GetPosition(s)); LastHitTestInfo = hitTestInfo; diff --git a/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.xaml b/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.xaml index b6f4ec1..14028d2 100644 --- a/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.xaml +++ b/StarlightDirector/StarlightDirector/UI/Controls/ScoreEditor.xaml @@ -20,6 +20,7 @@ + @@ -29,6 +30,5 @@ - diff --git a/StarlightDirector/StarlightDirector/UI/EditMode.cs b/StarlightDirector/StarlightDirector/UI/EditMode.cs index 3a5f58a..6a25a54 100644 --- a/StarlightDirector/StarlightDirector/UI/EditMode.cs +++ b/StarlightDirector/StarlightDirector/UI/EditMode.cs @@ -1,9 +1,12 @@ -namespace StarlightDirector.UI { +using System.ComponentModel; + +namespace StarlightDirector.UI { public enum EditMode { - Select, - Relations, - Clear + [Description("Mode: Create relations")] + CreateRelations, + [Description("Mode: Reset note")] + ResetNote } } diff --git a/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.Commands.Edit.Mode.cs b/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.Commands.Edit.Mode.cs index ba3d0eb..e03f918 100644 --- a/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.Commands.Edit.Mode.cs +++ b/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.Commands.Edit.Mode.cs @@ -4,43 +4,23 @@ namespace StarlightDirector.UI.Windows { partial class MainWindow { - public static readonly ICommand CmdEditModeSelect = CommandHelper.RegisterCommand("Alt+1", "Alt+NumPad1"); - public static readonly ICommand CmdEditModeEditRelations = CommandHelper.RegisterCommand("Alt+2", "Alt+NumPad2"); - public static readonly ICommand CmdEditModeClear = CommandHelper.RegisterCommand("Alt+0", "Alt+NumPad0"); + public static readonly ICommand CmdEditModeCreateRelations = CommandHelper.RegisterCommand("Alt+1", "Alt+NumPad1"); + public static readonly ICommand CmdEditModeResetNote = CommandHelper.RegisterCommand("Alt+2", "Alt+NumPad2"); - private void CmdEditModeSelect_CanExecute(object sender, CanExecuteRoutedEventArgs e) { - e.CanExecute = Editor.EditMode != EditMode.Select; + private void CmdEditModeCreateRelations_CanExecute(object sender, CanExecuteRoutedEventArgs e) { + e.CanExecute = Editor.EditMode != EditMode.CreateRelations; } - private void CmdEditModeSelect_Executed(object sender, ExecutedRoutedEventArgs e) { - Debug.Print("Edit mode: select"); - Editor.EditMode = EditMode.Select; + private void CmdEditModeCreateRelations_Executed(object sender, ExecutedRoutedEventArgs e) { + Editor.EditMode = EditMode.CreateRelations; } - private void CmdEditModeEditRelations_CanExecute(object sender, CanExecuteRoutedEventArgs e) { - e.CanExecute = Editor.EditMode != EditMode.Relations; + private void CmdEditModeResetNote_CanExecute(object sender, CanExecuteRoutedEventArgs e) { + e.CanExecute = Editor.EditMode != EditMode.ResetNote; } - private void CmdEditModeEditRelations_Executed(object sender, ExecutedRoutedEventArgs e) { - if (Editor.EditMode == EditMode.Relations) { - Editor.EditMode = EditMode.Select; - return; - } - Editor.EditMode = EditMode.Relations; - Debug.Print("Edit mode: relations"); - } - - private void CmdEditModeClear_CanExecute(object sender, CanExecuteRoutedEventArgs e) { - e.CanExecute = Editor.EditMode != EditMode.Clear; - } - - private void CmdEditModeClear_Executed(object sender, ExecutedRoutedEventArgs e) { - if (Editor.EditMode == EditMode.Clear) { - Editor.EditMode = EditMode.Select; - return; - } - Editor.EditMode = EditMode.Clear; - Debug.Print("Edit mode: clear"); + private void CmdEditModeResetNote_Executed(object sender, ExecutedRoutedEventArgs e) { + Editor.EditMode = EditMode.ResetNote; } } diff --git a/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.Commands.Edit.Note.cs b/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.Commands.Edit.Note.cs index 4455480..dfc7fae 100644 --- a/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.Commands.Edit.Note.cs +++ b/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.Commands.Edit.Note.cs @@ -128,28 +128,29 @@ private void CmdEditNoteSetSlideTypeToFlick_Executed(object sender, ExecutedRout private void CmdEditNoteSetSlideTypeToSlide_CanExecute(object sender, CanExecuteRoutedEventArgs e) { var notes = Editor.GetSelectedScoreNotes(); var scoreNotes = notes as List ?? notes.ToList(); - if (scoreNotes.Count == 0) { - e.CanExecute = false; - return; - } - if (scoreNotes.Count == 1) { - var note = scoreNotes[0].Note; - if (note.HasPrevFlickOrSlide || note.HasNextFlickOrSlide) { + switch (scoreNotes.Count) { + case 0: e.CanExecute = false; return; - } + case 1: + var note = scoreNotes[0].Note; + if (note.HasPrevFlickOrSlide || note.HasNextFlickOrSlide) { + e.CanExecute = false; + return; + } + break; } Note groupPrevNote = null; scoreNotes.Sort((c1, c2) => Note.TimingThenPositionComparison(c1.Note, c2.Note)); foreach (var scoreNote in scoreNotes) { var note = scoreNote.Note; - if (!note.IsFlick && !note.IsSlide || note.IsHoldEnd) { + if ((!note.IsFlick && !note.IsSlide) || note.IsHoldEnd) { e.CanExecute = false; return; } var prevNote = note.PrevFlickOrSlideNote; - if (prevNote != null && prevNote != groupPrevNote) { + if (prevNote != null && prevNote != groupPrevNote && !prevNote.IsSlide) { e.CanExecute = false; return; } diff --git a/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.xaml b/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.xaml index 5e15f8a..6c772c9 100644 --- a/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.xaml +++ b/StarlightDirector/StarlightDirector/UI/Windows/MainWindow.xaml @@ -65,20 +65,12 @@ - - - - + + + - - - - - - @@ -237,13 +229,13 @@ MinHeight="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ScrollViewer}, Path=ViewportHeight, Mode=OneWay}" HorizontalAlignment="Center" ScrollViewer="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ScrollViewer}, Mode=OneWay}" NoteInfoBlock="{Binding ElementName=NoteInfo, Mode=OneTime}"> - + - +