mirror of
https://github.com/ppy/osu
synced 2025-03-19 09:34:49 +00:00
Move individual mask selection logic out of MaskSelection
This commit is contained in:
parent
b6b8c51657
commit
4d71f2084c
@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(SelectionBox),
|
||||
typeof(MaskSelection),
|
||||
typeof(HitObjectComposer),
|
||||
typeof(OsuHitObjectComposer),
|
||||
typeof(HitObjectMaskLayer),
|
||||
|
@ -248,11 +248,11 @@ namespace osu.Game.Rulesets.Edit
|
||||
public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SelectionBox"/> which outlines <see cref="DrawableHitObject"/>s
|
||||
/// Creates a <see cref="MaskSelection"/> which outlines <see cref="DrawableHitObject"/>s
|
||||
/// and handles hitobject pattern adjustments.
|
||||
/// </summary>
|
||||
/// <param name="maskContainer">The <see cref="HitObjectMask"/> container.</param>
|
||||
public virtual SelectionBox CreateSelectionBox(MaskContainer maskContainer) => new SelectionBox(maskContainer);
|
||||
public virtual MaskSelection CreateSelectionBox(MaskContainer maskContainer) => new MaskSelection(maskContainer);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
@ -24,6 +25,13 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
public event Action<HitObjectMask> Deselected;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this <see cref="HitObjectMask"/> has rqeuested selection.
|
||||
/// Will fire even if already selected.
|
||||
/// Does not actually perform selection.
|
||||
/// </summary>
|
||||
public event Action<HitObjectMask> SelectionRequested;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DrawableHitObject"/> which this <see cref="HitObjectMask"/> applies to.
|
||||
/// </summary>
|
||||
@ -31,7 +39,8 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
protected override bool ShouldBeAlive => HitObject.IsAlive || State == Visibility.Visible;
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
public override bool HandleMouseInput => HitObject.IsPresent;
|
||||
|
||||
public override bool HandleMouseInput => ShouldBeAlive;
|
||||
|
||||
public HitObjectMask(DrawableHitObject hitObject)
|
||||
{
|
||||
@ -55,6 +64,12 @@ namespace osu.Game.Rulesets.Edit
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
SelectionRequested?.Invoke(this);
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deselects this <see cref="HitObjectMask"/>, causing it to become invisible.
|
||||
/// </summary>
|
||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
{
|
||||
Masking = true,
|
||||
BorderColour = Color4.White,
|
||||
BorderThickness = SelectionBox.BORDER_RADIUS,
|
||||
BorderThickness = MaskSelection.BORDER_RADIUS,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
private readonly HitObjectComposer composer;
|
||||
|
||||
private MaskContainer maskContainer;
|
||||
private SelectionBox selectionBox;
|
||||
private MaskSelection maskSelection;
|
||||
|
||||
public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer)
|
||||
{
|
||||
@ -33,16 +33,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
{
|
||||
maskContainer = new MaskContainer();
|
||||
|
||||
selectionBox = composer.CreateSelectionBox(maskContainer);
|
||||
maskSelection = composer.CreateSelectionBox(maskContainer);
|
||||
|
||||
dragLayer.DragEnd += () => selectionBox.UpdateVisibility();
|
||||
var dragLayer = new DragLayer(maskContainer.Select);
|
||||
dragLayer.DragEnd += () => maskSelection.UpdateVisibility();
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
dragLayer,
|
||||
maskSelection,
|
||||
maskContainer,
|
||||
selectionBox,
|
||||
dragLayer.CreateProxy()
|
||||
};
|
||||
|
||||
@ -50,6 +50,12 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
addMask(obj);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
maskContainer.DeselectAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a mask for a <see cref="DrawableHitObject"/> which adds movement support.
|
||||
/// </summary>
|
||||
@ -75,11 +81,5 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
|
||||
maskContainer.Remove(mask);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
selectionBox.DeselectAll();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,17 +23,25 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
/// </summary>
|
||||
public event Action<HitObjectMask> MaskDeselected;
|
||||
|
||||
public event Action<HitObjectMask> MaskSelectionRequested;
|
||||
|
||||
/// <summary>
|
||||
/// All the <see cref="HitObjectMask"/>s with <see cref="IsAlive"/> == true.
|
||||
/// </summary>
|
||||
public IEnumerable<HitObjectMask> AliveMasks => AliveInternalChildren.Cast<HitObjectMask>();
|
||||
|
||||
public MaskContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
public override void Add(HitObjectMask drawable)
|
||||
{
|
||||
base.Add(drawable);
|
||||
|
||||
drawable.Selected += onMaskSelected;
|
||||
drawable.Deselected += onMaskDeselected;
|
||||
drawable.SelectionRequested += onSelectionRequested;
|
||||
}
|
||||
|
||||
public override bool Remove(HitObjectMask drawable)
|
||||
@ -44,6 +52,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
{
|
||||
drawable.Selected -= onMaskSelected;
|
||||
drawable.Deselected -= onMaskDeselected;
|
||||
drawable.SelectionRequested -= onSelectionRequested;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -59,13 +68,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
{
|
||||
if (mask.IsPresent && rect.Contains(mask.SelectionPoint))
|
||||
mask.Select();
|
||||
else
|
||||
mask.Deselect();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deselects all selected <see cref="HitObjectMask"/>s.
|
||||
/// </summary>
|
||||
public void DeselectAll() => AliveMasks.ToList().ForEach(m => m.Deselect());
|
||||
|
||||
private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask);
|
||||
private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask);
|
||||
private void onSelectionRequested(HitObjectMask mask) => MaskSelectionRequested?.Invoke(mask);
|
||||
|
||||
protected override int Compare(Drawable x, Drawable y)
|
||||
{
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
@ -19,19 +18,19 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
/// <summary>
|
||||
/// A box which surrounds <see cref="HitObjectMask"/>s and provides interactive handles, context menus etc.
|
||||
/// </summary>
|
||||
public class SelectionBox : CompositeDrawable
|
||||
public class MaskSelection : CompositeDrawable
|
||||
{
|
||||
public const float BORDER_RADIUS = 2;
|
||||
|
||||
private readonly MaskContainer maskContainer;
|
||||
|
||||
private readonly SortedList<HitObjectMask> selectedMasks;
|
||||
private IEnumerable<HitObjectMask> selectableMasks => maskContainer.AliveMasks;
|
||||
|
||||
private Drawable outline;
|
||||
|
||||
public SelectionBox(MaskContainer maskContainer)
|
||||
public MaskSelection(MaskContainer maskContainer)
|
||||
{
|
||||
// todo: remove this
|
||||
this.maskContainer = maskContainer;
|
||||
|
||||
selectedMasks = new SortedList<HitObjectMask>(maskContainer.Compare);
|
||||
@ -42,6 +41,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
|
||||
maskContainer.MaskSelected += onSelected;
|
||||
maskContainer.MaskDeselected += onDeselected;
|
||||
maskContainer.MaskSelectionRequested += onSelectionRequested;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -63,42 +63,23 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
|
||||
#region User Input Handling
|
||||
|
||||
/// <summary>
|
||||
/// Handle input on currently selectable or already selected masks.
|
||||
/// Keep in mind that selectedMasks may contain masks for non-current objects, which we still want to handle input while selected.
|
||||
/// </summary>
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => selectableMasks.Reverse().Concat(selectedMasks).Any(m => m.ReceiveMouseInputAt(screenSpacePos));
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => handleInput(state);
|
||||
|
||||
protected override bool OnDragStart(InputState state) => handleInput(state);
|
||||
|
||||
protected override bool OnDragEnd(InputState state) => true;
|
||||
|
||||
private bool handleInput(InputState state)
|
||||
{
|
||||
// If masks are overlapping, make sure we don't change the selection if the overlapped portion is pressed
|
||||
if (selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)))
|
||||
return true;
|
||||
|
||||
DeselectAll();
|
||||
selectableMasks.Reverse().First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)).Select();
|
||||
if (!selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)))
|
||||
return false;
|
||||
|
||||
UpdateVisibility();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
// If there's only mask, this isn't going to change anything, so we can save on doing some processing here
|
||||
if (selectedMasks.Count == 1)
|
||||
return true;
|
||||
|
||||
var toSelect = selectedMasks.Reverse().First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position));
|
||||
|
||||
DeselectAll();
|
||||
toSelect.Select();
|
||||
|
||||
UpdateVisibility();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(InputState state) => true;
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
{
|
||||
// Todo: Various forms of snapping
|
||||
@ -116,8 +97,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnDragEnd(InputState state) => true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Selection Handling
|
||||
@ -133,15 +112,32 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deselects all selected <see cref="HitObjectMask"/>s.
|
||||
/// </summary>
|
||||
public void DeselectAll() => selectedMasks.ToList().ForEach(m => m.Deselect());
|
||||
private void onSelectionRequested(HitObjectMask mask)
|
||||
{
|
||||
if (GetContainingInputManager().CurrentState.Keyboard.ControlPressed)
|
||||
{
|
||||
if (mask.State == Visibility.Visible)
|
||||
// we don't want this deselection to affect input for this frame.
|
||||
Schedule(() => mask.Deselect());
|
||||
else
|
||||
mask.Select();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mask.State == Visibility.Visible)
|
||||
return;
|
||||
|
||||
maskContainer.DeselectAll();
|
||||
mask.Select();
|
||||
}
|
||||
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Updates whether this <see cref="SelectionBox"/> is visible.
|
||||
/// Updates whether this <see cref="MaskSelection"/> is visible.
|
||||
/// </summary>
|
||||
internal void UpdateVisibility()
|
||||
{
|
||||
@ -183,6 +179,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
|
||||
maskContainer.MaskSelected -= onSelected;
|
||||
maskContainer.MaskDeselected -= onDeselected;
|
||||
maskContainer.MaskSelectionRequested -= onSelectionRequested;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user