Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close object context menu on tool change #31081

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions osu.Game.Rulesets.Osu.Tests/Editor/TestSceneContextMenuClose.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.UI;
using osuTK.Input;

namespace osu.Game.Rulesets.Osu.Tests.Editor
{
public partial class TestSceneContextMenuClose : TestSceneOsuEditor
{
private ContextMenuContainer contextMenuContainer
=> Editor.ChildrenOfType<ContextMenuContainer>().First();

[Test]
public void TestToolChangeClosesMenu()
{
Playfield playfield = null!;

Check failure on line 23 in osu.Game.Rulesets.Osu.Tests/Editor/TestSceneContextMenuClose.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Value assigned is not used in any execution path in osu.Game.Rulesets.Osu.Tests\Editor\TestSceneContextMenuClose.cs on line 23

AddStep("select circle placement tool", () => InputManager.Key(Key.Number2));
AddStep("move mouse to top left of playfield", () =>
{
playfield = this.ChildrenOfType<Playfield>().Single();
var location = (3 * playfield.ScreenSpaceDrawQuad.TopLeft + playfield.ScreenSpaceDrawQuad.BottomRight) / 4;
InputManager.MoveMouseTo(location);
});
AddStep("place circle", () => InputManager.Click(MouseButton.Left));
AddStep("right click circle", () => InputManager.Click(MouseButton.Right));
AddUntilStep("context menu is visible", () => contextMenuContainer.ChildrenOfType<OsuContextMenu>().Single().State == MenuState.Open);
AddStep("select slider placement tool", () => InputManager.Key(Key.Number3));
AddUntilStep("context menu is not visible", () => contextMenuContainer.ChildrenOfType<OsuContextMenu>().Single().State == MenuState.Closed);
}
}
}
3 changes: 3 additions & 0 deletions osu.Game/Rulesets/Edit/HitObjectComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,9 @@ private void toolSelected(CompositionTool tool)

if (!(tool is SelectTool))
EditorBeatmap.SelectedHitObjects.Clear();

// Prevent object context menu from staying open.
GetContainingFocusManager()?.ChangeFocus(null);
Comment on lines +535 to +536
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is dodgy as all hell, and may be broken by future changes that allow multiple simultaneously focused drawables (hi @smoogipoo).

I think we need a proper "close the context menu that I opened" primitive in framework. Maybe something like (VERY UNTESTED, written just to demonstrate general idea):

diff --git a/osu.Framework/Graphics/Cursor/ContextMenuContainer.cs b/osu.Framework/Graphics/Cursor/ContextMenuContainer.cs
index 9ff908d69..0174322cf 100644
--- a/osu.Framework/Graphics/Cursor/ContextMenuContainer.cs
+++ b/osu.Framework/Graphics/Cursor/ContextMenuContainer.cs
@@ -76,6 +76,14 @@ protected override bool OnMouseDown(MouseDownEvent e)
 
                     menuTarget = target;
 
+                    menuTarget.CloseRequested += t =>
+                    {
+                        if (menuTarget != t)
+                            return;
+
+                        menu.Close();
+                    };
+
                     if (menuTarget == null || items.Length == 0)
                     {
                         if (menu.State == MenuState.Open)
diff --git a/osu.Framework/Graphics/Cursor/IHasContextMenu.cs b/osu.Framework/Graphics/Cursor/IHasContextMenu.cs
index 0a214a2f0..c3188e873 100644
--- a/osu.Framework/Graphics/Cursor/IHasContextMenu.cs
+++ b/osu.Framework/Graphics/Cursor/IHasContextMenu.cs
@@ -1,6 +1,7 @@
 // Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
 // See the LICENCE file in the repository root for full licence text.
 
+using System;
 using osu.Framework.Graphics.UserInterface;
 
 namespace osu.Framework.Graphics.Cursor
@@ -15,5 +16,9 @@ public interface IHasContextMenu : IDrawable
         /// <para>If null, this <see cref="Drawable"/> will not be picked as the menu target and other <see cref="Drawable"/>s underneath may become the menu target.</para>
         /// </remarks>
         MenuItem[]? ContextMenuItems { get; }
+
+        internal Action<IHasContextMenu>? CloseRequested { get; set; }
+
+        public void RequestClose() => CloseRequested?.Invoke(this);
     }
 }

}

#endregion
Expand Down
Loading