From b33efe0a3ca26265c9345212e2d9c0ed66938053 Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sat, 26 Oct 2024 18:57:27 -0400 Subject: [PATCH 1/9] Fix clipboard brush mode not updating contents --- .../Behaviors/BatchMode/BatchModeBehavior.cs | 15 +++++++++------ .../Behaviors/BatchMode/BatchModeSubmode.cs | 9 ++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs index c78f5252..9af278db 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs @@ -8,7 +8,6 @@ using System.Windows.Input; using Caliburn.Micro; using Microsoft.Xaml.Behaviors; -using Microsoft.Xaml.Behaviors.Core; using OngekiFumenEditor.Base; using OngekiFumenEditor.Kernel.KeyBinding; using OngekiFumenEditor.Modules.FumenObjectPropertyBrowser; @@ -19,7 +18,6 @@ using OngekiFumenEditor.Properties; using OngekiFumenEditor.UI.KeyBinding.Input; using OngekiFumenEditor.Utils; -using Xceed.Wpf.Toolkit.Core.Input; using EventTrigger = Microsoft.Xaml.Behaviors.EventTrigger; using TriggerAction = Microsoft.Xaml.Behaviors.TriggerAction; using TriggerBase = Microsoft.Xaml.Behaviors.TriggerBase; @@ -28,7 +26,7 @@ namespace OngekiFumenEditor.Modules.FumenVisualEditor.Behaviors.BatchMode; public class BatchModeBehavior : Behavior { - private static readonly ImmutableDictionary CommandDefinitions = + private static readonly ImmutableDictionary CommandDefinitions = new Dictionary { [KeyBindingDefinitions.KBD_Batch_ModeWallLeft] = typeof(BatchModeInputWallLeft), @@ -46,7 +44,7 @@ public class BatchModeBehavior : Behavior [KeyBindingDefinitions.KBD_Batch_ModeFilterLanes] = typeof(BatchModeFilterLanes), [KeyBindingDefinitions.KBD_Batch_ModeFilterDockableObjects] = typeof(BatchModeFilterDockableObjects), [KeyBindingDefinitions.KBD_Batch_ModeFilterFloatingObjects] = typeof(BatchModeFilterFloatingObjects), - }.ToImmutableDictionary(); + }.ToImmutableDictionary(kv => kv.Key, kv => (BatchModeSubmode)Activator.CreateInstance(kv.Value)); private static readonly ImmutableDictionary> ClickTriggers = new Dictionary>() @@ -92,7 +90,7 @@ protected override void OnAttached() foreach (var mod in new[] { ModifierKeys.None, ModifierKeys.Shift }) { // It's useful to hold down shift as we place multiple lanes, so bind everything to Shift+ as well. var newTrigger = new KeyTrigger() { Key = key.Key, Modifiers = mod }; - newTrigger.Actions.Add(new ChangePropertyAction() { TargetObject = this, PropertyName = nameof(CurrentSubmode), Value = Activator.CreateInstance(submodeType) }); + newTrigger.Actions.Add(new LambdaTriggerAction(_ => ApplySubmode(submodeType))); triggerCollection.Add(newTrigger); NewKeyTriggers.Add(newTrigger); } @@ -147,6 +145,11 @@ protected override void OnDetaching() } } + private void ApplySubmode(BatchModeSubmode submode) + { + CurrentSubmode = submode; + } + #region Mouse handling private bool lastLeftClickWasAltClick = false; @@ -447,7 +450,7 @@ private static void ConsumeAlt(object sender, KeyEventArgs e) #region Dependency property - public static readonly DependencyProperty CurrentSubmodeProperty = DependencyProperty.RegisterAttached(nameof(CurrentSubmode), typeof(BatchModeSubmode), typeof(BatchModeBehavior), new PropertyMetadata(Activator.CreateInstance(CommandDefinitions[KeyBindingDefinitions.KBD_Batch_ModeClipboard]))); + public static readonly DependencyProperty CurrentSubmodeProperty = DependencyProperty.RegisterAttached(nameof(CurrentSubmode), typeof(BatchModeSubmode), typeof(BatchModeBehavior), new(CommandDefinitions[KeyBindingDefinitions.KBD_Batch_ModeClipboard])); #endregion diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeSubmode.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeSubmode.cs index 024ec7e1..bde3a49e 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeSubmode.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeSubmode.cs @@ -45,18 +45,17 @@ public abstract class BatchModeInputSubmode : BatchModeSubmode public class BatchModeInputClipboard : BatchModeInputSubmode { - private IReadOnlyCollection ClipboardContents; + private IFumenEditorClipboard Clipboard; + public BatchModeInputClipboard() { - ClipboardContents = IoC.Get() - .CurrentCopiedObjects.OfType() - .Select(obj => (OngekiTimelineObjectBase)obj.CopyNew()).ToList(); + Clipboard = IoC.Get(); } public override string DisplayName => Resources.Clipboard; public override IEnumerable GenerateObject() { - return ClipboardContents.Select(obj => (OngekiTimelineObjectBase)obj.CopyNew()); + return Clipboard.CurrentCopiedObjects.Select(obj => (OngekiTimelineObjectBase)obj.CopyNew()); } } From 5c37b31c0c61a59d693c2510c65c3344f53f87dc Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sat, 26 Oct 2024 19:13:43 -0400 Subject: [PATCH 2/9] Prevent placement of same objects at same position --- .../Behaviors/BatchMode/BatchModeBehavior.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs index 9af278db..03881af4 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs @@ -326,6 +326,24 @@ private void PerformBrush(BatchModeInputSubmode submode) return; } + // TODO Support multiple objects in clipboard + var ongekiObject = ongekiObjects.Single(); + editor!.MoveObjectTo(ongekiObject, editor.CurrentCursorPosition!.Value); + if (ctrl && submode.ModifyObjectCtrl is { } modCtrl) + modCtrl.Function?.Invoke(ongekiObject); + if (shift && submode.ModifyObjectShift is { } modShift) + modShift.Function?.Invoke(ongekiObject); + + // Find existing objects of the same type at the same position. + // TODO Separate this check into a function somehow + if (editor.Fumen.GetAllDisplayableObjects().FirstOrDefault(o => + o is OngekiTimelineObjectBase obj && obj.GetType().IsInstanceOfType(ongekiObject) && obj.TGrid == ongekiObject.TGrid && + (obj is not OngekiMovableObjectBase oObj || + (ongekiObject is OngekiMovableObjectBase movObj && movObj.XGrid == oObj.XGrid))) is { } existingObj) { + editor.NotifyObjectClicked((OngekiObjectBase)existingObj); + return; + } + var objectName = CurrentSubmode.DisplayName; editor.UndoRedoManager.ExecuteAction(new LambdaUndoAction(Resources.BatchModeAddObject.Format(objectName), Redo, Undo)); @@ -333,15 +351,6 @@ private void PerformBrush(BatchModeInputSubmode submode) void Redo() { - // TODO Support multiple objects in clipboard - var ongekiObject = ongekiObjects.Single(); - - if (ctrl && submode.ModifyObjectCtrl is { } modCtrl) - modCtrl.Function?.Invoke(ongekiObject); - if (shift && submode.ModifyObjectShift is { } modShift) - modShift.Function?.Invoke(ongekiObject); - - editor!.MoveObjectTo(ongekiObject, editor.CurrentCursorPosition!.Value); editor.Fumen.AddObject(ongekiObject); editor.InteractiveManager.GetInteractive(ongekiObject).OnMoveCanvas(ongekiObject, editor.CurrentCursorPosition.Value, editor); From d7ac8931ddfedb999efb960b5c9e236448373f93 Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sat, 26 Oct 2024 19:46:50 -0400 Subject: [PATCH 3/9] Add generic clash detection to OngekiObjects --- OngekiFumenEditor/Base/OngekiMovableObjectBase.cs | 10 ++++++++++ OngekiFumenEditor/Base/OngekiObjects/Bell.cs | 13 +++++++++---- OngekiFumenEditor/Base/OngekiObjects/Bullet.cs | 9 +++++++++ OngekiFumenEditor/Base/OngekiObjects/Hold.cs | 5 +++++ OngekiFumenEditor/Base/OngekiObjects/Tap.cs | 11 +++++++++++ OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs | 5 +++++ .../Behaviors/BatchMode/BatchModeBehavior.cs | 8 ++------ 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/OngekiFumenEditor/Base/OngekiMovableObjectBase.cs b/OngekiFumenEditor/Base/OngekiMovableObjectBase.cs index 8c49f2f5..8fd09139 100644 --- a/OngekiFumenEditor/Base/OngekiMovableObjectBase.cs +++ b/OngekiFumenEditor/Base/OngekiMovableObjectBase.cs @@ -19,6 +19,11 @@ public virtual XGrid XGrid } } + public bool Clashes(OngekiMovableObjectBase other) + { + return base.Clashes(other) && other.XGrid == XGrid; + } + public override string ToString() => $"{base.ToString()} {XGrid}"; public override void Copy(OngekiObjectBase fromObj) @@ -36,5 +41,10 @@ public override void Dispose() base.Dispose(); XGrid = default; } + + public override bool Clashes(OngekiTimelineObjectBase other) + { + return base.Clashes(other) && other is OngekiMovableObjectBase mov && mov.XGrid == XGrid; + } } } diff --git a/OngekiFumenEditor/Base/OngekiObjects/Bell.cs b/OngekiFumenEditor/Base/OngekiObjects/Bell.cs index c0a61a10..e95aa993 100644 --- a/OngekiFumenEditor/Base/OngekiObjects/Bell.cs +++ b/OngekiFumenEditor/Base/OngekiObjects/Bell.cs @@ -1,9 +1,9 @@ using OngekiFumenEditor.Base.Attributes; using OngekiFumenEditor.Base.OngekiObjects.BulletPalleteEnums; -using System.Collections.Generic; +using System.Collections.Generic; using static OngekiFumenEditor.Base.OngekiObjects.BulletPallete; -namespace OngekiFumenEditor.Base.OngekiObjects +namespace OngekiFumenEditor.Base.OngekiObjects { public class Bell : OngekiMovableObjectBase, IBulletPalleteReferencable { @@ -117,5 +117,10 @@ public override void Copy(OngekiObjectBase fromObj) ReferenceBulletPallete = from.ReferenceBulletPallete; } - } -} + + public override bool Clashes(OngekiTimelineObjectBase other) + { + return other is Bell otherBell && base.Clashes(other) && otherBell.ReferenceBulletPallete == ReferenceBulletPallete; + } + } +} diff --git a/OngekiFumenEditor/Base/OngekiObjects/Bullet.cs b/OngekiFumenEditor/Base/OngekiObjects/Bullet.cs index ce34af3b..4ebfa698 100644 --- a/OngekiFumenEditor/Base/OngekiObjects/Bullet.cs +++ b/OngekiFumenEditor/Base/OngekiObjects/Bullet.cs @@ -134,5 +134,14 @@ public override void Copy(OngekiObjectBase fromObj) ReferenceBulletPallete = from.ReferenceBulletPallete; BulletDamageTypeValue = from.BulletDamageTypeValue; } + + public override bool Clashes(OngekiTimelineObjectBase other) + { + if (other is Bullet bullet) { + return base.Clashes(bullet) && bullet.ReferenceBulletPallete == ReferenceBulletPallete; + } + + return false; + } } } diff --git a/OngekiFumenEditor/Base/OngekiObjects/Hold.cs b/OngekiFumenEditor/Base/OngekiObjects/Hold.cs index f80b76b6..68ffa0a6 100644 --- a/OngekiFumenEditor/Base/OngekiObjects/Hold.cs +++ b/OngekiFumenEditor/Base/OngekiObjects/Hold.cs @@ -92,5 +92,10 @@ public override IEnumerable GetDisplayableObjects() yield return this; yield return HoldEnd; } + + public override bool Clashes(OngekiTimelineObjectBase other) + { + return other is OngekiMovableObjectBase movable and (Tap or Hold) && base.Clashes(movable); + } } } diff --git a/OngekiFumenEditor/Base/OngekiObjects/Tap.cs b/OngekiFumenEditor/Base/OngekiObjects/Tap.cs index 92d74bcb..b9dc6960 100644 --- a/OngekiFumenEditor/Base/OngekiObjects/Tap.cs +++ b/OngekiFumenEditor/Base/OngekiObjects/Tap.cs @@ -67,5 +67,16 @@ public override void Copy(OngekiObjectBase fromObj) IsCritical = from.IsCritical; ReferenceLaneStart = from.ReferenceLaneStart; } + + public override bool Clashes(OngekiTimelineObjectBase other) + { + if (other is OngekiMovableObjectBase movable) { + if (other.TGrid == TGrid && movable.XGrid == XGrid && movable is Tap or Hold or HoldEnd) { + return true; + } + } + + return false; + } } } diff --git a/OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs b/OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs index 81d07866..1daddcb2 100644 --- a/OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs +++ b/OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs @@ -52,6 +52,11 @@ public override void Copy(OngekiObjectBase fromObj) TGrid = timelineObject.TGrid; } + public virtual bool Clashes(OngekiTimelineObjectBase other) + { + return GetType().IsInstanceOfType(other) && other.TGrid == TGrid; + } + public override string ToString() => $"{base.ToString()} {TGrid}"; public virtual void Dispose() diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs index 03881af4..57ec1574 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs @@ -335,12 +335,8 @@ private void PerformBrush(BatchModeInputSubmode submode) modShift.Function?.Invoke(ongekiObject); // Find existing objects of the same type at the same position. - // TODO Separate this check into a function somehow - if (editor.Fumen.GetAllDisplayableObjects().FirstOrDefault(o => - o is OngekiTimelineObjectBase obj && obj.GetType().IsInstanceOfType(ongekiObject) && obj.TGrid == ongekiObject.TGrid && - (obj is not OngekiMovableObjectBase oObj || - (ongekiObject is OngekiMovableObjectBase movObj && movObj.XGrid == oObj.XGrid))) is { } existingObj) { - editor.NotifyObjectClicked((OngekiObjectBase)existingObj); + if (editor.Fumen.GetAllDisplayableObjects().FirstOrDefault(o => o is OngekiTimelineObjectBase timelineObj && timelineObj.Clashes(ongekiObject)) is { } clash) { + editor.NotifyObjectClicked((OngekiObjectBase)clash); return; } From 6c86e51e1d32450911b75a29e7e71659e61a06d8 Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sat, 26 Oct 2024 21:20:04 -0400 Subject: [PATCH 4/9] Perform conflict check in Editor --- .../Base/OngekiMovableObjectBase.cs | 10 -------- OngekiFumenEditor/Base/OngekiObjects/Bell.cs | 5 ---- .../Base/OngekiObjects/Bullet.cs | 9 ------- OngekiFumenEditor/Base/OngekiObjects/Hold.cs | 5 ---- OngekiFumenEditor/Base/OngekiObjects/Tap.cs | 11 --------- .../Base/OngekiTimelineObjectBase.cs | 5 ---- .../Behaviors/BatchMode/BatchModeBehavior.cs | 4 ++-- .../BulletBell/BulletDrawingTarget.cs | 4 ++++ ...lEditorViewModel.UserInteractionActions.cs | 24 +++++++++++++++++++ 9 files changed, 30 insertions(+), 47 deletions(-) diff --git a/OngekiFumenEditor/Base/OngekiMovableObjectBase.cs b/OngekiFumenEditor/Base/OngekiMovableObjectBase.cs index 8fd09139..8c49f2f5 100644 --- a/OngekiFumenEditor/Base/OngekiMovableObjectBase.cs +++ b/OngekiFumenEditor/Base/OngekiMovableObjectBase.cs @@ -19,11 +19,6 @@ public virtual XGrid XGrid } } - public bool Clashes(OngekiMovableObjectBase other) - { - return base.Clashes(other) && other.XGrid == XGrid; - } - public override string ToString() => $"{base.ToString()} {XGrid}"; public override void Copy(OngekiObjectBase fromObj) @@ -41,10 +36,5 @@ public override void Dispose() base.Dispose(); XGrid = default; } - - public override bool Clashes(OngekiTimelineObjectBase other) - { - return base.Clashes(other) && other is OngekiMovableObjectBase mov && mov.XGrid == XGrid; - } } } diff --git a/OngekiFumenEditor/Base/OngekiObjects/Bell.cs b/OngekiFumenEditor/Base/OngekiObjects/Bell.cs index e95aa993..6a59be5f 100644 --- a/OngekiFumenEditor/Base/OngekiObjects/Bell.cs +++ b/OngekiFumenEditor/Base/OngekiObjects/Bell.cs @@ -117,10 +117,5 @@ public override void Copy(OngekiObjectBase fromObj) ReferenceBulletPallete = from.ReferenceBulletPallete; } - - public override bool Clashes(OngekiTimelineObjectBase other) - { - return other is Bell otherBell && base.Clashes(other) && otherBell.ReferenceBulletPallete == ReferenceBulletPallete; - } } } diff --git a/OngekiFumenEditor/Base/OngekiObjects/Bullet.cs b/OngekiFumenEditor/Base/OngekiObjects/Bullet.cs index 4ebfa698..ce34af3b 100644 --- a/OngekiFumenEditor/Base/OngekiObjects/Bullet.cs +++ b/OngekiFumenEditor/Base/OngekiObjects/Bullet.cs @@ -134,14 +134,5 @@ public override void Copy(OngekiObjectBase fromObj) ReferenceBulletPallete = from.ReferenceBulletPallete; BulletDamageTypeValue = from.BulletDamageTypeValue; } - - public override bool Clashes(OngekiTimelineObjectBase other) - { - if (other is Bullet bullet) { - return base.Clashes(bullet) && bullet.ReferenceBulletPallete == ReferenceBulletPallete; - } - - return false; - } } } diff --git a/OngekiFumenEditor/Base/OngekiObjects/Hold.cs b/OngekiFumenEditor/Base/OngekiObjects/Hold.cs index 68ffa0a6..f80b76b6 100644 --- a/OngekiFumenEditor/Base/OngekiObjects/Hold.cs +++ b/OngekiFumenEditor/Base/OngekiObjects/Hold.cs @@ -92,10 +92,5 @@ public override IEnumerable GetDisplayableObjects() yield return this; yield return HoldEnd; } - - public override bool Clashes(OngekiTimelineObjectBase other) - { - return other is OngekiMovableObjectBase movable and (Tap or Hold) && base.Clashes(movable); - } } } diff --git a/OngekiFumenEditor/Base/OngekiObjects/Tap.cs b/OngekiFumenEditor/Base/OngekiObjects/Tap.cs index b9dc6960..92d74bcb 100644 --- a/OngekiFumenEditor/Base/OngekiObjects/Tap.cs +++ b/OngekiFumenEditor/Base/OngekiObjects/Tap.cs @@ -67,16 +67,5 @@ public override void Copy(OngekiObjectBase fromObj) IsCritical = from.IsCritical; ReferenceLaneStart = from.ReferenceLaneStart; } - - public override bool Clashes(OngekiTimelineObjectBase other) - { - if (other is OngekiMovableObjectBase movable) { - if (other.TGrid == TGrid && movable.XGrid == XGrid && movable is Tap or Hold or HoldEnd) { - return true; - } - } - - return false; - } } } diff --git a/OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs b/OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs index 1daddcb2..81d07866 100644 --- a/OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs +++ b/OngekiFumenEditor/Base/OngekiTimelineObjectBase.cs @@ -52,11 +52,6 @@ public override void Copy(OngekiObjectBase fromObj) TGrid = timelineObject.TGrid; } - public virtual bool Clashes(OngekiTimelineObjectBase other) - { - return GetType().IsInstanceOfType(other) && other.TGrid == TGrid; - } - public override string ToString() => $"{base.ToString()} {TGrid}"; public virtual void Dispose() diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs index 57ec1574..663357a9 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs @@ -335,8 +335,8 @@ private void PerformBrush(BatchModeInputSubmode submode) modShift.Function?.Invoke(ongekiObject); // Find existing objects of the same type at the same position. - if (editor.Fumen.GetAllDisplayableObjects().FirstOrDefault(o => o is OngekiTimelineObjectBase timelineObj && timelineObj.Clashes(ongekiObject)) is { } clash) { - editor.NotifyObjectClicked((OngekiObjectBase)clash); + if (editor.GetConflictingObject(ongekiObject) is { } clash) { + editor.NotifyObjectClicked(clash); return; } diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/TargetImpl/OngekiObjects/BulletBell/BulletDrawingTarget.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/TargetImpl/OngekiObjects/BulletBell/BulletDrawingTarget.cs index 3fb22627..3f1243f0 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/TargetImpl/OngekiObjects/BulletBell/BulletDrawingTarget.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/TargetImpl/OngekiObjects/BulletBell/BulletDrawingTarget.cs @@ -100,6 +100,10 @@ void SetTexture(BulletDamageType k1, BulletType k2, string rPath, string key, Ve public override void DrawVisibleObject_DesignMode(IFumenEditorDrawingContext target, Bullet obj, Vector2 pos, float rotate) { + if (obj.ReferenceBulletPallete is null) { + Log.LogWarn($"Bullet {obj.Id} has null reference bullet pallete"); + return; + } var damageType = obj.BulletDamageTypeValue; var bulletType = obj.ReferenceBulletPallete.TypeValue; diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs index b33d3093..43351b8c 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs @@ -1778,6 +1778,30 @@ public void ToastNotify(string message) public void MoveObjectTo(OngekiObjectBase obj, Point point) => InteractiveManager.GetInteractive(obj).OnMoveCanvas(obj, point, this); + public OngekiTimelineObjectBase? GetConflictingObject(OngekiTimelineObjectBase obj) + { + return (OngekiTimelineObjectBase)Fumen.GetAllDisplayableObjects().FirstOrDefault(x => + { + if (x is not OngekiTimelineObjectBase tX) return false; + + // Check coordinates are the same + if (tX.TGrid != obj.TGrid) return false; + if (obj is OngekiMovableObjectBase movable) { + var mX = (OngekiMovableObjectBase)x; + if (movable.XGrid != mX.XGrid) return false; + } + + return obj switch + { + Tap => x is Tap or Hold or HoldEnd, + Hold => x is Hold or Tap, + HoldEnd => x is Tap, + IBulletPalleteReferencable bullet => x is IBulletPalleteReferencable bX && bX.ReferenceBulletPallete == bullet.ReferenceBulletPallete, + _ => obj.GetType().IsInstanceOfType(x) + }; + }); + } + #endregion } } \ No newline at end of file From d7e883e97ecee23467842acc766626ef16f4cd6b Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sun, 27 Oct 2024 03:32:59 -0400 Subject: [PATCH 5/9] Refactor SelectionArea Sometimes crashes while clicking and dragging, see PR. --- .../Behaviors/BatchMode/BatchModeBehavior.cs | 155 +++++------------- .../Editors/DrawSelectingRangeHelper.cs | 74 ++++++--- ...lEditorViewModel.UserInteractionActions.cs | 133 +++------------ .../ViewModels/SelectionArea.cs | 148 +++++++++++++++++ 4 files changed, 259 insertions(+), 251 deletions(-) create mode 100644 OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/SelectionArea.cs diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs index 663357a9..9503ffe0 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs @@ -166,9 +166,7 @@ private void MouseMove(MouseEventArgs args) if (Mouse.RightButton == MouseButtonState.Pressed) { // In normal mode, right mouse is not used to drag, so we manually do that here - editor.SelectionVisibility = Visibility.Visible; - editor.SelectionCurrentCursorPosition = editor.CurrentCursorPosition?.ToSystemNumericsVector2() - ?? args.GetPosition(null).ToSystemNumericsVector2(); + editor.SelectionArea = new SelectionArea(editor, SelectionAreaKind.Delete); } } @@ -182,32 +180,36 @@ private void MouseDown(MouseButtonEventArgs args) if (args.ChangedButton == MouseButton.Left) { // In all sub-modes, Alt forces normal mouse behavior if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { - editor.SelectRegionType = SelectRegionType.Select; lastLeftClickWasAltClick = true; - editor.SelectionStartPosition = cursor.ToSystemNumericsVector2(); + + editor.SelectionArea = new SelectionArea(editor, SelectionAreaKind.Select); if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) { - editor.SelectRegionType = SelectRegionType.SelectFiltered; - editor.SelectionVisibility = Visibility.Visible; + editor.SelectionArea.FilterFunc = GetFilterFunc(); } return; } if (CurrentSubmode is BatchModeFilterSubmode) { // If it's a FilterSubmode, dragging without Alt will apply the filter. - editor.SelectionStartPosition = cursor.ToSystemNumericsVector2(); - editor.SelectRegionType = SelectRegionType.SelectFiltered; + editor.SelectionArea = new SelectionArea(editor, SelectionAreaKind.Select) + { + FilterFunc = GetFilterFunc() + }; return; } args.Handled = true; } else if (args.ChangedButton == MouseButton.Right) { - editor.SelectionStartPosition = cursor.ToSystemNumericsVector2(); + editor.SelectionArea = new SelectionArea(editor, SelectionAreaKind.Delete); + if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { lastRightClickWasAltClick = true; - editor.SelectRegionType = SelectRegionType.Delete; + if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) { + editor.SelectionArea.FilterFunc = GetFilterFunc(); + } } else { - editor.SelectRegionType = SelectRegionType.DeleteFiltered; + editor.SelectionArea.FilterFunc = GetFilterFunc(); } args.Handled = true; @@ -222,43 +224,23 @@ private void MouseUp(MouseButtonEventArgs args) if (args.ChangedButton == MouseButton.Left) { if (editor.IsRangeSelecting) { - // Selection mode - Func filterFunc = null; - if (editor.SelectRegionType == SelectRegionType.SelectFiltered && CurrentSubmode is BatchModeFilterSubmode filterSubmode) { - filterFunc = filterSubmode.FilterFunction; - } else if (editor.SelectRegionType == SelectRegionType.SelectFiltered && CurrentSubmode is BatchModeSingleInputSubmode singleInputSubmode) { - filterFunc = o => o.GetType() == singleInputSubmode.ObjectType || o.GetType().IsSubclassOf(singleInputSubmode.ObjectType); - } - - if (filterFunc != null) { - editor.SelectionVisibility = Visibility.Hidden; - PerformFilterSelect(filterFunc); - args.Handled = true; - } - - editor.SelectRegionType = SelectRegionType.Select; - // otherwise fall through to FumenVisualEditorViewModel click handler + // Let VisualEditorViewModel code handle it + lastLeftClickWasAltClick = false; + return; } if (!lastLeftClickWasAltClick) { // Can hold alt while releasing to "cancel" the left click - if ((Keyboard.Modifiers & ModifierKeys.Alt) == 0) { - if (CurrentSubmode is BatchModeInputSubmode inputSubmode) { - if (inputSubmode is BatchModeSingleInputSubmode singleInputSubmode - && editor.GetHits().Where(kv => - kv.Value.Contains(editor.CurrentCursorPosition!.Value) && - singleInputSubmode.ObjectType.IsInstanceOfType(kv.Key)).Select(kv => kv.Key) - .FirstOrDefault() is { } hit) { - editor.NotifyObjectClicked(hit); - } - else { - PerformBrush(inputSubmode); - } - } - else if (CurrentSubmode is BatchModeFilterSubmode filterSubmode && editor.IsRangeSelecting) { - editor.SelectionVisibility = Visibility.Hidden; - PerformFilterSelect(filterSubmode.FilterFunction); - } + if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { + args.Handled = true; + return; + } + + if (CurrentSubmode is BatchModeInputSubmode inputSubmode) { + PerformBrush(inputSubmode); + } + else if (CurrentSubmode is BatchModeFilterSubmode filterSubmode && editor.IsRangeSelecting) { + editor.ConsumeSelectionArea(); } args.Handled = true; } @@ -268,12 +250,7 @@ private void MouseUp(MouseButtonEventArgs args) } else if (args.ChangedButton == MouseButton.Right) { if (!lastRightClickWasAltClick) { if (editor.IsRangeSelecting) { - // Drag filter delete - if (CurrentSubmode is BatchModeSingleInputSubmode typeSubmode) { - PerformRemoveGroup(typeSubmode); - } else if (CurrentSubmode is BatchModeFilterSubmode filterSubmode) { - PerformRemoveGroup(filterSubmode); - } + editor.ConsumeSelectionArea(); } else { // Delete single @@ -281,26 +258,35 @@ private void MouseUp(MouseButtonEventArgs args) PerformRemove(typeSubmode); } } - - editor.SelectionVisibility = Visibility.Hidden; } else { lastRightClickWasAltClick = false; if (editor.IsRangeSelecting) { - PerformRemoveGroup(null, Resources.Objects); + editor.ConsumeSelectionArea(); } } if (editor.IsRangeSelecting) { - // End selection - editor.SelectRegionType = SelectRegionType.Select; - editor.SelectionVisibility = Visibility.Hidden; + editor.SelectionArea!.ApplyRangeAction(); + editor.SelectionArea = null; } args.Handled = true; } } + private Func? GetFilterFunc() + { + if (CurrentSubmode is BatchModeFilterSubmode filterSubmode) { + return filterSubmode.FilterFunction; + } + if (CurrentSubmode is BatchModeSingleInputSubmode singleInputSubmode) { + return obj => singleInputSubmode.ObjectType.IsInstanceOfType(obj); + } + + return null; + } + #endregion private void PerformBrush(BatchModeInputSubmode submode) @@ -365,23 +351,6 @@ void Undo() } } - // TODO Refactor into `SelectionBox` class or something - private void PerformFilterSelect(Func filterFunction) - { - var editor = (FumenVisualEditorViewModel)AssociatedObject.DataContext; - var hits = editor.GetRangeObjects().Where(filterFunction).ToList(); - - if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) { - editor.ClearSelection(); - } - - foreach (var hit in hits) { - if (hit is OngekiMovableObjectBase selectable) - selectable.IsSelected = true; - } - IoC.Get().RefreshSelected(editor); - } - private void PerformRemove(BatchModeSingleInputSubmode submode) { var editor = (FumenVisualEditorViewModel)AssociatedObject.DataContext; @@ -408,44 +377,6 @@ void Undo() } } - private void PerformRemoveGroup(BatchModeSingleInputSubmode submode) - { - PerformRemoveGroup(obj => obj.GetType() == submode.ObjectType, submode.DisplayName); - } - - private void PerformRemoveGroup(BatchModeFilterSubmode filter) - { - PerformRemoveGroup(filter.FilterFunction, filter.DisplayName); - } - - private void PerformRemoveGroup(Func? filterFunc, string displayName) - { - var editor = (FumenVisualEditorViewModel)AssociatedObject.DataContext; - var hits = editor.GetRangeObjects().Where(filterFunc ?? (_ => true)).ToArray(); - - if (hits.Length == 0) { - return; - } - - editor.UndoRedoManager.ExecuteAction(new LambdaUndoAction( - Resources.BatchModeDeleteRangeOfObjectType.Format(displayName, hits.Length), Redo, Undo)); - - return; - - void Redo() - { - foreach (var hit in hits) { - editor.RemoveObject(hit); - } - } - - void Undo() - { - editor.Fumen.AddObjects(hits); - } - - } - private static void ConsumeAlt(object sender, KeyEventArgs e) { if (e.Key == Key.System && e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt) { diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/Editors/DrawSelectingRangeHelper.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/Editors/DrawSelectingRangeHelper.cs index d9adf60a..02d047de 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/Editors/DrawSelectingRangeHelper.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/Editors/DrawSelectingRangeHelper.cs @@ -5,19 +5,23 @@ using System.Collections.Generic; using System.Numerics; using OngekiFumenEditor.Modules.FumenVisualEditor.ViewModels; +using OngekiFumenEditor.Utils; using static OngekiFumenEditor.Kernel.Graphics.ILineDrawing; namespace OngekiFumenEditor.Modules.FumenVisualEditor.Graphics.Drawing.Editors { public class DrawSelectingRangeHelper { - private Dictionary RegionColors = new() + private record RangeColors(Vector4 LineColor, Vector4 RectColor) { - [SelectRegionType.Select] = (new(1, 0, 1, 1), new(1, 1, 1, 0.15f)), - [SelectRegionType.SelectFiltered] = (new(1, 0, 1, 1), new(0.8f, 0.0f, 0.8f, 0.15f)), - [SelectRegionType.Delete] = (new(1, 0.1f, 0.1f, 1), new(1, 0.1f, 0.1f, 0.15f)), - [SelectRegionType.DeleteFiltered] = (new(1, 0.1f, 0.1f, 1), new(0.8f, 0.8f, 0, 0.15f)) - }; + public readonly Vector4 LineColor = LineColor; + public readonly Vector4 RectColor = RectColor; + } + + private RangeColors SelectAll = new(new(1, 0, 1, 1), new(1, 1, 1, 0.15f)); + private RangeColors SelectFiltered = new(new(1, 0, 1, 1), new(0.8f, 0.0f, 0.8f, 0.15f)); + private RangeColors DeleteAll = new(new(1, 0.1f, 0.1f, 1), new(1, 0.1f, 0.1f, 0.15f)); + private RangeColors DeleteFiltered = new(new(1, 0.1f, 0.1f, 1), new(0.8f, 0.8f, 0, 0.15f)); private ISimpleLineDrawing lineDrawing; private IPolygonDrawing polygonDrawing; @@ -36,39 +40,57 @@ public DrawSelectingRangeHelper() public void Draw(IFumenEditorDrawingContext target) { - if (target.Editor.SelectionVisibility != System.Windows.Visibility.Visible) + if (target.Editor.SelectionArea is not { } selectionArea || selectionArea.Rect is { Height: 0, Width: 0 }) return; - var (lineColor, fillColor) = RegionColors[target.Editor.SelectRegionType]; + RangeColors colors; + if (selectionArea.SelectionAreaKind == SelectionAreaKind.Select) { + if (selectionArea.FilterFunc is not null) { + colors = SelectFiltered; + } + else { + colors = SelectAll; + } + } else if (selectionArea.SelectionAreaKind == SelectionAreaKind.Delete) { + if (selectionArea.FilterFunc is not null) { + colors = DeleteFiltered; + } + else { + colors = DeleteAll; + } + } + else { + colors = SelectAll; + } - var topY = Math.Max(target.Editor.SelectionCurrentCursorPosition.Y, target.Editor.SelectionStartPosition.Y); - var buttomY = Math.Min(target.Editor.SelectionCurrentCursorPosition.Y, target.Editor.SelectionStartPosition.Y); - var rightX = Math.Max(target.Editor.SelectionCurrentCursorPosition.X, target.Editor.SelectionStartPosition.X); - var leftX = Math.Min(target.Editor.SelectionCurrentCursorPosition.X, target.Editor.SelectionStartPosition.X); + var topY = (float)selectionArea.Rect.Top; + var buttomY = (float)selectionArea.Rect.Bottom; + var rightX = (float)selectionArea.Rect.Right; + var leftX = (float)selectionArea.Rect.Left; var centerX = (leftX + rightX) / 2; var centerY = (topY + buttomY) / 2; polygonDrawing.Begin(target, PrimitiveType.TriangleStrip); { - polygonDrawing.PostPoint(new(leftX, buttomY), fillColor); - polygonDrawing.PostPoint(new(centerX, centerY), fillColor); - polygonDrawing.PostPoint(new(leftX, topY), fillColor); - polygonDrawing.PostPoint(new(centerX, centerY), fillColor); - polygonDrawing.PostPoint(new(rightX, topY), fillColor); - polygonDrawing.PostPoint(new(centerX, centerY), fillColor); - polygonDrawing.PostPoint(new(rightX, buttomY), fillColor); - polygonDrawing.PostPoint(new(centerX, centerY), fillColor); - polygonDrawing.PostPoint(new(leftX, buttomY), fillColor); + polygonDrawing.PostPoint(new(leftX, buttomY), colors.RectColor); + polygonDrawing.PostPoint(new(centerX, centerY), colors.RectColor); + polygonDrawing.PostPoint(new(leftX, topY), colors.RectColor); + polygonDrawing.PostPoint(new(centerX, centerY), colors.RectColor); + polygonDrawing.PostPoint(new(rightX, topY), colors.RectColor); + polygonDrawing.PostPoint(new(centerX, centerY), colors.RectColor); + polygonDrawing.PostPoint(new(rightX, buttomY), colors.RectColor); + polygonDrawing.PostPoint(new(centerX, centerY), colors.RectColor); + polygonDrawing.PostPoint(new(leftX, buttomY), colors.RectColor); } polygonDrawing.End(); lineDrawing.Begin(target, 1); { - lineDrawing.PostPoint(new(leftX, buttomY), lineColor, dash); - lineDrawing.PostPoint(new(leftX, topY), lineColor, dash); - lineDrawing.PostPoint(new(rightX, topY), lineColor, dash); - lineDrawing.PostPoint(new(rightX, buttomY), lineColor, dash); - lineDrawing.PostPoint(new(leftX, buttomY), lineColor, dash); + lineDrawing.PostPoint(new(leftX, buttomY), colors.LineColor, dash); + lineDrawing.PostPoint(new(leftX, topY), colors.LineColor, dash); + lineDrawing.PostPoint(new(rightX, topY), colors.LineColor, dash); + lineDrawing.PostPoint(new(rightX, buttomY), colors.LineColor, dash); + lineDrawing.PostPoint(new(leftX, buttomY), colors.LineColor, dash); } lineDrawing.End(); } diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs index 43351b8c..fc79e473 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs @@ -86,41 +86,25 @@ private bool IsUserRequestHideEditorObject #region Selection - private Visibility selectionVisibility = Visibility.Collapsed; - public Visibility SelectionVisibility + private SelectionArea? selectionArea; + public SelectionArea? SelectionArea { - get => selectionVisibility; - set => Set(ref selectionVisibility, value); + get => selectionArea; + set => Set(ref selectionArea, value); } - private Vector2 selectionStartPosition; - public Vector2 SelectionStartPosition - { - get => selectionStartPosition; - set => Set(ref selectionStartPosition, value); - } + public bool IsRangeSelecting => SelectionArea is not null; + public bool IsPreventMutualExclusionSelecting { get; set; } - private Vector2 selectionCurrentCursorPosition; - public Vector2 SelectionCurrentCursorPosition + public void ConsumeSelectionArea() { - get => selectionCurrentCursorPosition; - set - { - Set(ref selectionCurrentCursorPosition, value); - RecalculateSelectionRect(); - } - } + if (SelectionArea is null) + return; - private Rect selectionRect; - public Rect SelectionRect - { - get => selectionRect; - set => Set(ref selectionRect, value); + SelectionArea.ApplyRangeAction(); + SelectionArea = null; } - public bool IsRangeSelecting => SelectionVisibility == Visibility.Visible; - public bool IsPreventMutualExclusionSelecting { get; set; } - public void ClearSelection() { foreach (var obj in SelectObjects) @@ -417,7 +401,6 @@ private void SelectLaneObjects(ConnectableStartObject start) private Dictionary cacheObjectAudioTime = new(); private OngekiObjectBase mouseDownHitObject; private Point? mouseDownHitObjectPosition; - private Point mouseSelectRangeStartPosition; /// /// 表示指针是否出拖动出滚动范围 /// @@ -480,61 +463,6 @@ public void MenuItemAction_RecoverySelectedObjectToAudioTime(ActionExecutionCont #endregion - private void SelectRangeObjects() - { - if (!IsDesignMode) - { - ToastNotify(Resources.EditorMustBeDesignMode); - return; - } - - var selectObjects = GetRangeObjects().ToArray(); - - if (selectObjects.Length == 1) - NotifyObjectClicked(selectObjects.FirstOrDefault()); - else - { - foreach (var o in selectObjects.OfType()) - o.IsSelected = true; - IoC.Get().RefreshSelected(this); - } - } - - public IEnumerable GetRangeObjects() - { - var topY = Math.Max(SelectionCurrentCursorPosition.Y, SelectionStartPosition.Y); - var buttomY = Math.Min(SelectionCurrentCursorPosition.Y, SelectionStartPosition.Y); - var rightX = Math.Max(SelectionCurrentCursorPosition.X, SelectionStartPosition.X); - var leftX = Math.Min(SelectionCurrentCursorPosition.X, SelectionStartPosition.X); - - var minTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(buttomY, this); - var maxTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(topY, this); - var minXGrid = XGridCalculator.ConvertXToXGrid(leftX, this); - var maxXGrid = XGridCalculator.ConvertXToXGrid(rightX, this); - - return Fumen.GetAllDisplayableObjects() - .OfType() - .Distinct() - .Where(Check); - - bool Check(OngekiObjectBase obj) - { - if (obj is ITimelineObject timelineObject) - { - if (timelineObject.TGrid > maxTGrid || timelineObject.TGrid < minTGrid) - return false; - } - - if (obj is IHorizonPositionObject horizonPositionObject) - { - if (horizonPositionObject.XGrid > maxXGrid || horizonPositionObject.XGrid < minXGrid) - return false; - } - - return true; - } - } - public void OnFocusableChanged(ActionExecutionContext e) { Log.LogInfo($"OnFocusableChanged {e.EventArgs}"); @@ -1057,9 +985,10 @@ public void OnMouseUp(ActionExecutionContext e) { if (arg.ChangedButton == MouseButton.Left) { - if (IsRangeSelecting && SelectionCurrentCursorPosition != SelectionStartPosition) + if (SelectionArea is not null && !SelectionArea.IsClick()) { - SelectRangeObjects(); + SelectionArea.ApplyRangeAction(); + SelectionArea = null; } else { @@ -1108,7 +1037,7 @@ public void OnMouseUp(ActionExecutionContext e) } isSelectRangeDragging = false; - SelectionVisibility = Visibility.Collapsed; + SelectionArea = null; currentDraggingActionId = int.MaxValue; } @@ -1178,23 +1107,19 @@ public void OnMouseDown(ActionExecutionContext e) mouseDownHitObject = null; mouseDownNextHitObject = null; mouseDownHitObjectPosition = default; - mouseSelectRangeStartPosition = position; dragOutBound = false; if (hitOngekiObject is null) { TryCancelAllObjectSelecting(); + Log.LogInfo($"SelectionArea ${CurrentCursorPosition}"); + SelectionArea = new(this, SelectionAreaKind.Select); - //enable show selection - - SelectionStartPosition = new Vector2((float)position.X, (float)position.Y); - SelectionCurrentCursorPosition = SelectionStartPosition; - SelectionVisibility = Visibility.Visible; } else { //这里如果已经有物件选择了就判断是否还有其他物件可以选择 - SelectionVisibility = Visibility.Collapsed; + SelectionArea = null; mouseDownHitObject = hitOngekiObject; mouseDownHitObjectPosition = position; @@ -1284,10 +1209,9 @@ public async void OnMouseMove(Point pos) //Log.LogInfo($"diffY: {diffY:F2} ScrollViewerVerticalOffset: {ScrollViewerVerticalOffset:F2}"); } - if (IsRangeSelecting) + if (SelectionArea is not null) { var rp = 1 - pos.Y / ViewHeight; - var srp = 1 - mouseSelectRangeStartPosition.Y / ViewHeight; var offsetY = 0d; //const double dragDist = 0.7; @@ -1318,7 +1242,7 @@ public async void OnMouseMove(Point pos) //拉框 var p = pos; p.Y = Math.Min(TotalDurationHeight, Math.Max(0, Rect.MaxY - p.Y + offsetY)); - SelectionCurrentCursorPosition = new Vector2((float)p.X, (float)p.Y); + SelectionArea.EndPoint = p; } if (Mouse.LeftButton == MouseButtonState.Pressed) @@ -1335,7 +1259,6 @@ public async void OnMouseMove(Point pos) }); var rp = 1 - pos.Y / ViewHeight; - var srp = 1 - mouseSelectRangeStartPosition.Y / ViewHeight; var offsetY = 0d; //const double dragDist = 0.7; @@ -1484,22 +1407,6 @@ public OngekiObjectBase NotifyObjectClicked(OngekiObjectBase obj, OngekiObjectBa #endregion - private void RecalculateSelectionRect() - { - var sx = Math.Min(SelectionStartPosition.X, SelectionCurrentCursorPosition.X); - var sy = Math.Min(SelectionStartPosition.Y, SelectionCurrentCursorPosition.Y); - - var ex = Math.Max(SelectionStartPosition.X, SelectionCurrentCursorPosition.X); - var ey = Math.Max(SelectionStartPosition.Y, SelectionCurrentCursorPosition.Y); - - var width = Math.Abs(sx - ex); - var height = Math.Abs(sy - ey); - - SelectionRect = new Rect(sx, sy, width, height); - - //Log.LogDebug($"SelectionRect = {SelectionRect}"); - } - private void UpdateCurrentCursorPosition(Point pos) { var contentObject = IoC.Get().SubRightMainContentViewModel; diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/SelectionArea.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/SelectionArea.cs new file mode 100644 index 00000000..b3bf4a93 --- /dev/null +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/SelectionArea.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Forms.Design; +using Caliburn.Micro; +using Gemini.Framework.Services; +using OngekiFumenEditor.Base; +using OngekiFumenEditor.Modules.FumenObjectPropertyBrowser; +using OngekiFumenEditor.Properties; +using OngekiFumenEditor.Utils; + +namespace OngekiFumenEditor.Modules.FumenVisualEditor.ViewModels; + +public class SelectionArea : PropertyChangedBase +{ + public readonly SelectionAreaKind SelectionAreaKind; + + private FumenVisualEditorViewModel editor; + + private Func? filterFunc; + public Func? FilterFunc + { + get => filterFunc; + set => Set(ref filterFunc, value); + } + + private Point startPoint; + public Point StartPoint + { + get => startPoint; + set + { + Set(ref startPoint, value); + Rect = new Rect(startPoint, endPoint); + Log.LogInfo(Rect.ToString()); + } + } + + private Point endPoint; + + public Point EndPoint + { + get => endPoint; + set + { + Set(ref endPoint, value); + Rect = new Rect(startPoint, endPoint); + } + } + + private Rect rect; + public Rect Rect + { + get => rect; + set => Set(ref rect, value); + } + + public SelectionArea(FumenVisualEditorViewModel editor, SelectionAreaKind selectionAreaKind, Func? filterFunc = null) + { + this.editor = editor; + + SelectionAreaKind = selectionAreaKind; + StartPoint = editor.CurrentCursorPosition ?? throw new InvalidOperationException("No cursor position"); + EndPoint = StartPoint; + Rect = new Rect(this.startPoint, this.endPoint); + FilterFunc = filterFunc; + } + + public bool IsClick() + { + return Rect.Size.Width * Rect.Size.Height < 5; + } + + public IEnumerable GetRangeObjects(bool applyFilter = true) + { + var minTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(Rect.Bottom, editor); + var maxTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(Rect.Top, editor); + var minXGrid = XGridCalculator.ConvertXToXGrid(Rect.Left, editor); + var maxXGrid = XGridCalculator.ConvertXToXGrid(Rect.Right, editor); + + return editor.Fumen.GetAllDisplayableObjects() + .OfType() + .Distinct() + .Where(Check); + + bool Check(OngekiObjectBase obj) + { + if (obj is ITimelineObject timelineObject) + { + if (timelineObject.TGrid > maxTGrid || timelineObject.TGrid < minTGrid) + return false; + } + + if (obj is IHorizonPositionObject horizonPositionObject) + { + if (horizonPositionObject.XGrid > maxXGrid || horizonPositionObject.XGrid < minXGrid) + return false; + } + + if (applyFilter && (FilterFunc?.Invoke(obj) ?? false)) { + return false; + } + + return true; + } + } + + public void ApplyRangeAction() + { + if (!editor.IsDesignMode) + { + editor.ToastNotify(Resources.EditorMustBeDesignMode); + return; + } + + SelectionAreaKind.SelectAction(editor, GetRangeObjects()); + } +} + +public class SelectionAreaKind +{ + public static readonly SelectionAreaKind Select = new SelectionAreaKind((editor, objs) => + { + objs = objs.ToArray(); + + if (objs.Count() == 1) + editor.NotifyObjectClicked(objs.Single()); + else { + foreach (var o in objs.OfType()) + o.IsSelected = true; + IoC.Get().RefreshSelected(editor); + } + }); + + public static readonly SelectionAreaKind Delete = new SelectionAreaKind((editor, objs) => + { + foreach (var o in objs) + editor.Fumen.RemoveObject(o); + IoC.Get().RefreshSelected(editor); + }); + + public readonly Action> SelectAction; + private SelectionAreaKind(Action> selectAction) + { + SelectAction = selectAction; + } +} \ No newline at end of file From 2b03b5f466df044e5ed3a1fb18ac052128245279 Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sun, 27 Oct 2024 15:36:16 -0400 Subject: [PATCH 6/9] Finish SelectionArea refactor --- .../Behaviors/BatchMode/BatchModeBehavior.cs | 38 ++++----------- .../Editors/DrawSelectingRangeHelper.cs | 2 +- ...lEditorViewModel.UserInteractionActions.cs | 46 +++++++++++------- .../ViewModels/FumenVisualEditorViewModel.cs | 2 + .../ViewModels/SelectionArea.cs | 48 ++++++++++++++----- 5 files changed, 74 insertions(+), 62 deletions(-) diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs index 9503ffe0..a16d0de4 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs @@ -163,11 +163,6 @@ private void MouseMove(MouseEventArgs args) return; if (AssociatedObject.DataContext is not FumenVisualEditorViewModel editor) return; - - if (Mouse.RightButton == MouseButtonState.Pressed) { - // In normal mode, right mouse is not used to drag, so we manually do that here - editor.SelectionArea = new SelectionArea(editor, SelectionAreaKind.Delete); - } } private void MouseDown(MouseButtonEventArgs args) @@ -175,32 +170,23 @@ private void MouseDown(MouseButtonEventArgs args) if (AssociatedObject.DataContext is not FumenVisualEditorViewModel editor) return; - var cursor = editor.CurrentCursorPosition!.Value; - if (args.ChangedButton == MouseButton.Left) { // In all sub-modes, Alt forces normal mouse behavior if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { lastLeftClickWasAltClick = true; - - editor.SelectionArea = new SelectionArea(editor, SelectionAreaKind.Select); - if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) { - editor.SelectionArea.FilterFunc = GetFilterFunc(); - } return; } if (CurrentSubmode is BatchModeFilterSubmode) { // If it's a FilterSubmode, dragging without Alt will apply the filter. - editor.SelectionArea = new SelectionArea(editor, SelectionAreaKind.Select) - { - FilterFunc = GetFilterFunc() - }; + editor.InitializeSelectionArea(SelectionAreaKind.Select); + editor.SelectionArea.FilterFunc = GetFilterFunc(); return; } args.Handled = true; } else if (args.ChangedButton == MouseButton.Right) { - editor.SelectionArea = new SelectionArea(editor, SelectionAreaKind.Delete); + editor.InitializeSelectionArea(SelectionAreaKind.Delete); if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { lastRightClickWasAltClick = true; @@ -238,37 +224,29 @@ private void MouseUp(MouseButtonEventArgs args) if (CurrentSubmode is BatchModeInputSubmode inputSubmode) { PerformBrush(inputSubmode); + args.Handled = true; } - else if (CurrentSubmode is BatchModeFilterSubmode filterSubmode && editor.IsRangeSelecting) { - editor.ConsumeSelectionArea(); - } - args.Handled = true; } else { lastLeftClickWasAltClick = false; } } else if (args.ChangedButton == MouseButton.Right) { if (!lastRightClickWasAltClick) { - if (editor.IsRangeSelecting) { - editor.ConsumeSelectionArea(); - } - else { + if (!editor.IsRangeSelecting) { // Delete single if (CurrentSubmode is BatchModeSingleInputSubmode typeSubmode) { PerformRemove(typeSubmode); } + args.Handled = true; + return; } } else { lastRightClickWasAltClick = false; - if (editor.IsRangeSelecting) { - editor.ConsumeSelectionArea(); - } } if (editor.IsRangeSelecting) { - editor.SelectionArea!.ApplyRangeAction(); - editor.SelectionArea = null; + editor.ConsumeSelectionArea(); } args.Handled = true; diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/Editors/DrawSelectingRangeHelper.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/Editors/DrawSelectingRangeHelper.cs index 02d047de..0adbb058 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/Editors/DrawSelectingRangeHelper.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Graphics/Drawing/Editors/DrawSelectingRangeHelper.cs @@ -40,7 +40,7 @@ public DrawSelectingRangeHelper() public void Draw(IFumenEditorDrawingContext target) { - if (target.Editor.SelectionArea is not { } selectionArea || selectionArea.Rect is { Height: 0, Width: 0 }) + if (target.Editor.SelectionArea is not { } selectionArea || selectionArea.Rect is { Height: 0, Width: 0 } || !selectionArea.IsActive) return; RangeColors colors; diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs index fc79e473..cf6094db 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs @@ -86,23 +86,18 @@ private bool IsUserRequestHideEditorObject #region Selection - private SelectionArea? selectionArea; - public SelectionArea? SelectionArea - { - get => selectionArea; - set => Set(ref selectionArea, value); - } + public readonly SelectionArea SelectionArea; - public bool IsRangeSelecting => SelectionArea is not null; + public bool IsRangeSelecting => SelectionArea.IsActive; public bool IsPreventMutualExclusionSelecting { get; set; } public void ConsumeSelectionArea() { - if (SelectionArea is null) + if (!SelectionArea.IsActive) return; SelectionArea.ApplyRangeAction(); - SelectionArea = null; + SelectionArea.IsActive = false; } public void ClearSelection() @@ -985,10 +980,9 @@ public void OnMouseUp(ActionExecutionContext e) { if (arg.ChangedButton == MouseButton.Left) { - if (SelectionArea is not null && !SelectionArea.IsClick()) + if (SelectionArea.IsActive && !SelectionArea.IsClick()) { - SelectionArea.ApplyRangeAction(); - SelectionArea = null; + ConsumeSelectionArea(); } else { @@ -1037,8 +1031,8 @@ public void OnMouseUp(ActionExecutionContext e) } isSelectRangeDragging = false; - SelectionArea = null; currentDraggingActionId = int.MaxValue; + SelectionArea.IsActive = false; } if (arg.ChangedButton == MouseButton.Middle) @@ -1113,13 +1107,15 @@ public void OnMouseDown(ActionExecutionContext e) { TryCancelAllObjectSelecting(); Log.LogInfo($"SelectionArea ${CurrentCursorPosition}"); - SelectionArea = new(this, SelectionAreaKind.Select); + if (!SelectionArea.IsActive) { + InitializeSelectionArea(SelectionAreaKind.Select); + } } else { //这里如果已经有物件选择了就判断是否还有其他物件可以选择 - SelectionArea = null; + SelectionArea.IsActive = false; mouseDownHitObject = hitOngekiObject; mouseDownHitObjectPosition = position; @@ -1168,6 +1164,20 @@ public void OnMouseDown(ActionExecutionContext e) (e.View as FrameworkElement)?.Focus(); } + public void InitializeSelectionArea(SelectionAreaKind kind, Point? position = null) + { + var cursor = position ?? CurrentCursorPosition!.Value; + + if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) { + TryCancelAllObjectSelecting(); + } + + SelectionArea.SelectionAreaKind = kind; + SelectionArea.IsActive = true; + SelectionArea.StartPoint = cursor; + SelectionArea.EndPoint = cursor; + } + public void OnMouseMove(ActionExecutionContext e) { if (e.EventArgs is not MouseEventArgs args || args.Handled) @@ -1209,7 +1219,7 @@ public async void OnMouseMove(Point pos) //Log.LogInfo($"diffY: {diffY:F2} ScrollViewerVerticalOffset: {ScrollViewerVerticalOffset:F2}"); } - if (SelectionArea is not null) + if (SelectionArea.IsActive) { var rp = 1 - pos.Y / ViewHeight; var offsetY = 0d; @@ -1685,9 +1695,9 @@ public void ToastNotify(string message) public void MoveObjectTo(OngekiObjectBase obj, Point point) => InteractiveManager.GetInteractive(obj).OnMoveCanvas(obj, point, this); - public OngekiTimelineObjectBase? GetConflictingObject(OngekiTimelineObjectBase obj) + public OngekiObjectBase? GetConflictingObject(OngekiTimelineObjectBase obj) { - return (OngekiTimelineObjectBase)Fumen.GetAllDisplayableObjects().FirstOrDefault(x => + return (OngekiObjectBase)Fumen.GetAllDisplayableObjects().FirstOrDefault(x => { if (x is not OngekiTimelineObjectBase tX) return false; diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs index bdd8d280..6f938311 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs @@ -181,6 +181,8 @@ public FumenVisualEditorViewModel() : base() EditorGlobalSetting.Default.PropertyChanged += OnSettingPropertyChanged; DisplayName = default; + + SelectionArea = new(this); } #region Document New/Save/Load diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/SelectionArea.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/SelectionArea.cs index b3bf4a93..b5add758 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/SelectionArea.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/SelectionArea.cs @@ -7,6 +7,7 @@ using Gemini.Framework.Services; using OngekiFumenEditor.Base; using OngekiFumenEditor.Modules.FumenObjectPropertyBrowser; +using OngekiFumenEditor.Modules.FumenVisualEditor.Base; using OngekiFumenEditor.Properties; using OngekiFumenEditor.Utils; @@ -14,7 +15,7 @@ namespace OngekiFumenEditor.Modules.FumenVisualEditor.ViewModels; public class SelectionArea : PropertyChangedBase { - public readonly SelectionAreaKind SelectionAreaKind; + public SelectionAreaKind SelectionAreaKind; private FumenVisualEditorViewModel editor; @@ -56,15 +57,18 @@ public Rect Rect set => Set(ref rect, value); } - public SelectionArea(FumenVisualEditorViewModel editor, SelectionAreaKind selectionAreaKind, Func? filterFunc = null) + private bool isActive = true; + public bool IsActive { - this.editor = editor; + get => isActive; + set => Set(ref isActive, value); + } - SelectionAreaKind = selectionAreaKind; - StartPoint = editor.CurrentCursorPosition ?? throw new InvalidOperationException("No cursor position"); - EndPoint = StartPoint; - Rect = new Rect(this.startPoint, this.endPoint); - FilterFunc = filterFunc; + public SelectionArea(FumenVisualEditorViewModel editor) + { + this.editor = editor; + SelectionAreaKind = SelectionAreaKind.Select; + IsActive = false; } public bool IsClick() @@ -74,8 +78,8 @@ public bool IsClick() public IEnumerable GetRangeObjects(bool applyFilter = true) { - var minTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(Rect.Bottom, editor); - var maxTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(Rect.Top, editor); + var minTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(Rect.Top, editor); + var maxTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(Rect.Bottom, editor); var minXGrid = XGridCalculator.ConvertXToXGrid(Rect.Left, editor); var maxXGrid = XGridCalculator.ConvertXToXGrid(Rect.Right, editor); @@ -98,7 +102,7 @@ bool Check(OngekiObjectBase obj) return false; } - if (applyFilter && (FilterFunc?.Invoke(obj) ?? false)) { + if (applyFilter && (!FilterFunc?.Invoke(obj) ?? false)) { return false; } @@ -135,9 +139,27 @@ public class SelectionAreaKind public static readonly SelectionAreaKind Delete = new SelectionAreaKind((editor, objs) => { - foreach (var o in objs) - editor.Fumen.RemoveObject(o); + objs = objs.ToArray(); + if (!objs.Any()) + return; + IoC.Get().RefreshSelected(editor); + + editor.UndoRedoManager.ExecuteAction(new LambdaUndoAction(Resources.DeleteObjects, Redo, Undo)); + + return; + + void Redo() + { + foreach (var o in objs) + editor.Fumen.RemoveObject(o); + } + + void Undo() + { + foreach (var o in objs) + editor.Fumen.AddObject(o); + } }); public readonly Action> SelectAction; From 555f884915380ab995d30e17193189708125a5d7 Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sun, 27 Oct 2024 15:40:44 -0400 Subject: [PATCH 7/9] Fix filter func not going away --- .../FumenVisualEditorViewModel.UserInteractionActions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs index cf6094db..edef0c06 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs @@ -1174,6 +1174,7 @@ public void InitializeSelectionArea(SelectionAreaKind kind, Point? position = nu SelectionArea.SelectionAreaKind = kind; SelectionArea.IsActive = true; + SelectionArea.FilterFunc = null; SelectionArea.StartPoint = cursor; SelectionArea.EndPoint = cursor; } From 612160e3229723bcf503dad2ea4e0d7ed5e430c7 Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sun, 27 Oct 2024 15:45:47 -0400 Subject: [PATCH 8/9] Fix Ctrl+Alt filter behavior --- .../Behaviors/BatchMode/BatchModeBehavior.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs index a16d0de4..f75fa58b 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs @@ -171,9 +171,17 @@ private void MouseDown(MouseButtonEventArgs args) return; if (args.ChangedButton == MouseButton.Left) { - // In all sub-modes, Alt forces normal mouse behavior if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { + // Holding alt forces Normal Mode click behavior. lastLeftClickWasAltClick = true; + + if (CurrentSubmode is BatchModeSingleInputSubmode single && + Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) { + // Ctrl+Alt will filter for single objects + editor.InitializeSelectionArea(SelectionAreaKind.Select); + editor.SelectionArea.FilterFunc = o => single.ObjectType.IsInstanceOfType(o); + } + return; } From 8000550f66aa08ae4a7ea0f151f287afb0748bf4 Mon Sep 17 00:00:00 2001 From: Zach Day Date: Sun, 27 Oct 2024 21:10:32 -0400 Subject: [PATCH 9/9] Code cleanup --- .../Behaviors/BatchMode/BatchModeBehavior.cs | 53 +++++++------------ .../ViewModels/FumenVisualEditorViewModel.cs | 2 - 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs index f75fa58b..f18f845d 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Behaviors/BatchMode/BatchModeBehavior.cs @@ -50,8 +50,7 @@ public class BatchModeBehavior : Behavior new Dictionary>() { ["PreviewMouseDown"] = b => new LambdaTriggerAction(o => { b.MouseDown((MouseButtonEventArgs)o); }), - ["PreviewMouseUp"] = b => new LambdaTriggerAction(o => { b.MouseUp((MouseButtonEventArgs)o); }), - ["MouseMove"] = b => new LambdaTriggerAction(o => { b.MouseMove((MouseEventArgs)o);}) + ["PreviewMouseUp"] = b => new LambdaTriggerAction(o => { b.MouseUp((MouseButtonEventArgs)o); }) }.ToImmutableDictionary(); public BatchModeSubmode CurrentSubmode @@ -64,11 +63,8 @@ public BatchModeSubmode CurrentSubmode private readonly List OldKeyTriggers = new(); private readonly List NewKeyTriggers = new(); - private readonly IFumenEditorClipboard Clipboard; - public BatchModeBehavior() { - Clipboard = IoC.Get(); } protected override void OnAttached() @@ -76,24 +72,27 @@ protected override void OnAttached() if (AssociatedObject.DataContext is not FumenVisualEditorViewModel editor) return; - var triggerCollection = Interaction.GetTriggers(AssociatedObject); + var editorTriggers = Interaction.GetTriggers(AssociatedObject); // Create brush key triggers on the FumenVisualEditorView. - // Temporarily delete existing ones that clash with brush keys. - foreach (var (key, submodeType) in CommandDefinitions) { - var existingTriggers = triggerCollection.Where(t => - t is ActionMessageKeyBinding am && am.Definition.Key == key.Key && - am.Definition.Modifiers == key.Modifiers); + // Temporarily delete existing ones that clash with brush keys. + var allCommands = CommandDefinitions.SelectMany + , ((Key, ModifierKeys), BatchModeSubmode)>(kv => + [ + ((kv.Key.Key, kv.Key.Modifiers), kv.Value), + ((kv.Key.Key, kv.Key.Modifiers | ModifierKeys.Shift), kv.Value) + ]); + + foreach (var ((key, modifiers), submodeType) in allCommands) { + var existingTriggers = editorTriggers.Where(t => + t is ActionMessageKeyBinding am && am.Definition.Key == key && am.Definition.Modifiers == modifiers); OldKeyTriggers.AddRange(existingTriggers); - OldKeyTriggers.ForEach(t => triggerCollection.Remove(t)); - - foreach (var mod in new[] { ModifierKeys.None, ModifierKeys.Shift }) { - // It's useful to hold down shift as we place multiple lanes, so bind everything to Shift+ as well. - var newTrigger = new KeyTrigger() { Key = key.Key, Modifiers = mod }; - newTrigger.Actions.Add(new LambdaTriggerAction(_ => ApplySubmode(submodeType))); - triggerCollection.Add(newTrigger); - NewKeyTriggers.Add(newTrigger); - } + OldKeyTriggers.ForEach(t => editorTriggers.Remove(t)); + + var newTrigger = new KeyTrigger() { Key = key, Modifiers = modifiers }; + newTrigger.Actions.Add(new LambdaTriggerAction(_ => ApplySubmode(submodeType))); + editorTriggers.Add(newTrigger); + NewKeyTriggers.Add(newTrigger); } // Add mouse click events directly on the GlView. @@ -139,10 +138,6 @@ protected override void OnDetaching() GeneratedClickTriggerActions.Clear(); AssociatedObject.KeyDown -= ConsumeAlt; - - if (AssociatedObject.DataContext is FumenVisualEditorViewModel edtior) { - edtior.SelectRegionType = SelectRegionType.Select; - } } private void ApplySubmode(BatchModeSubmode submode) @@ -155,16 +150,6 @@ private void ApplySubmode(BatchModeSubmode submode) private bool lastLeftClickWasAltClick = false; private bool lastRightClickWasAltClick = false; - private void MouseMove(MouseEventArgs args) - { - if (Mouse.LeftButton != MouseButtonState.Pressed && Mouse.RightButton != MouseButtonState.Pressed) - return; - if (CurrentSubmode is null) - return; - if (AssociatedObject.DataContext is not FumenVisualEditorViewModel editor) - return; - } - private void MouseDown(MouseButtonEventArgs args) { if (AssociatedObject.DataContext is not FumenVisualEditorViewModel editor) diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs index 6f938311..e0dd294b 100644 --- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs +++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs @@ -172,8 +172,6 @@ public bool IsBatchMode public EditorSetting Setting { get; } = new EditorSetting(); - public SelectRegionType SelectRegionType = SelectRegionType.Select; - public FumenVisualEditorViewModel() : base() { //replace owned impl