1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 20:07:25 +08:00

Merge pull request #26702 from honguyenminh/fix-rotate-editor-button-disabled

Fix rotate tool button in editor disabled when selecting 1 circle
This commit is contained in:
Dean Herbert 2024-04-02 14:31:43 +08:00 committed by GitHub
commit 51e9348d27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 98 additions and 18 deletions

View File

@ -24,14 +24,38 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
[Test]
public void TestHotkeyHandling()
{
AddStep("select single circle", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.OfType<HitCircle>().First()));
AddStep("deselect everything", () => EditorBeatmap.SelectedHitObjects.Clear());
AddStep("press rotate hotkey", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.R);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("no popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.Zero);
AddUntilStep("no popover present", getPopover, () => Is.Null);
AddStep("select single circle",
() => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.OfType<HitCircle>().First()));
AddStep("press rotate hotkey", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.R);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("popover present", getPopover, () => Is.Not.Null);
AddAssert("only playfield centre origin rotation available", () =>
{
var popover = getPopover();
var buttons = popover.ChildrenOfType<EditorRadioButton>();
return buttons.Any(btn => btn.Text == "Selection centre" && !btn.Enabled.Value)
&& buttons.Any(btn => btn.Text == "Playfield centre" && btn.Enabled.Value);
});
AddStep("press rotate hotkey", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.R);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("no popover present", getPopover, () => Is.Null);
AddStep("select first three objects", () =>
{
@ -44,14 +68,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
InputManager.Key(Key.R);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.EqualTo(1));
AddUntilStep("popover present", getPopover, () => Is.Not.Null);
AddAssert("both origin rotation available", () =>
{
var popover = getPopover();
var buttons = popover.ChildrenOfType<EditorRadioButton>();
return buttons.Any(btn => btn.Text == "Selection centre" && btn.Enabled.Value)
&& buttons.Any(btn => btn.Text == "Playfield centre" && btn.Enabled.Value);
});
AddStep("press rotate hotkey", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.R);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("no popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.Zero);
AddUntilStep("no popover present", getPopover, () => Is.Null);
PreciseRotationPopover? getPopover() => this.ChildrenOfType<PreciseRotationPopover>().SingleOrDefault();
}
[Test]

View File

@ -41,7 +41,8 @@ namespace osu.Game.Rulesets.Osu.Edit
private void updateState()
{
var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects);
CanRotate.Value = quad.Width > 0 || quad.Height > 0;
CanRotateSelectionOrigin.Value = quad.Width > 0 || quad.Height > 0;
CanRotatePlayfieldOrigin.Value = selectedMovableObjects.Any();
}
private OsuHitObject[]? objectsInRotation;

View File

@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Osu.Edit
private SliderWithTextBoxInput<float> angleInput = null!;
private EditorRadioButtonCollection rotationOrigin = null!;
private RadioButton selectionCentreButton = null!;
public PreciseRotationPopover(SelectionRotationHandler rotationHandler)
{
this.rotationHandler = rotationHandler;
@ -59,13 +61,17 @@ namespace osu.Game.Rulesets.Osu.Edit
new RadioButton("Playfield centre",
() => rotationInfo.Value = rotationInfo.Value with { Origin = RotationOrigin.PlayfieldCentre },
() => new SpriteIcon { Icon = FontAwesome.Regular.Square }),
new RadioButton("Selection centre",
selectionCentreButton = new RadioButton("Selection centre",
() => rotationInfo.Value = rotationInfo.Value with { Origin = RotationOrigin.SelectionCentre },
() => new SpriteIcon { Icon = FontAwesome.Solid.VectorSquare })
}
}
}
};
selectionCentreButton.Selected.DisabledChanged += isDisabled =>
{
selectionCentreButton.TooltipText = isDisabled ? "Select more than one object to perform selection-based rotation." : string.Empty;
};
}
protected override void LoadComplete()
@ -76,6 +82,11 @@ namespace osu.Game.Rulesets.Osu.Edit
angleInput.Current.BindValueChanged(angle => rotationInfo.Value = rotationInfo.Value with { Degrees = angle.NewValue });
rotationOrigin.Items.First().Select();
rotationHandler.CanRotateSelectionOrigin.BindValueChanged(e =>
{
selectionCentreButton.Selected.Disabled = !e.NewValue;
}, true);
rotationInfo.BindValueChanged(rotation =>
{
rotationHandler.Update(rotation.NewValue.Degrees, rotation.NewValue.Origin == RotationOrigin.PlayfieldCentre ? OsuPlayfield.BASE_SIZE / 2 : null);

View File

@ -22,6 +22,9 @@ namespace osu.Game.Rulesets.Osu.Edit
private EditorToolButton rotateButton = null!;
private Bindable<bool> canRotatePlayfieldOrigin = null!;
private Bindable<bool> canRotateSelectionOrigin = null!;
public SelectionRotationHandler RotationHandler { get; init; } = null!;
public TransformToolboxGroup()
@ -51,9 +54,20 @@ namespace osu.Game.Rulesets.Osu.Edit
{
base.LoadComplete();
// aggregate two values into canRotate
canRotatePlayfieldOrigin = RotationHandler.CanRotatePlayfieldOrigin.GetBoundCopy();
canRotatePlayfieldOrigin.BindValueChanged(_ => updateCanRotateAggregate());
canRotateSelectionOrigin = RotationHandler.CanRotateSelectionOrigin.GetBoundCopy();
canRotateSelectionOrigin.BindValueChanged(_ => updateCanRotateAggregate());
void updateCanRotateAggregate()
{
canRotate.Value = RotationHandler.CanRotatePlayfieldOrigin.Value || RotationHandler.CanRotateSelectionOrigin.Value;
}
// bindings to `Enabled` on the buttons are decoupled on purpose
// due to the weird `OsuButton` behaviour of resetting `Enabled` to `false` when `Action` is set.
canRotate.BindTo(RotationHandler.CanRotate);
canRotate.BindValueChanged(_ => rotateButton.Enabled.Value = canRotate.Value, true);
}

View File

@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Editing
{
this.getTargetContainer = getTargetContainer;
CanRotate.Value = true;
CanRotateSelectionOrigin.Value = true;
}
[CanBeNull]

View File

@ -41,7 +41,7 @@ namespace osu.Game.Overlays.SkinEditor
private void updateState()
{
CanRotate.Value = selectedItems.Count > 0;
CanRotateSelectionOrigin.Value = selectedItems.Count > 0;
}
private Drawable[]? objectsInRotation;

View File

@ -212,6 +212,14 @@ namespace osu.Game.Rulesets.Edit
.Select(t => new RadioButton(t.Name, () => toolSelected(t), t.CreateIcon))
.ToList();
foreach (var item in toolboxCollection.Items)
{
item.Selected.DisabledChanged += isDisabled =>
{
item.TooltipText = isDisabled ? "Add at least one timing point first!" : string.Empty;
};
}
TernaryStates = CreateTernaryButtons().ToArray();
togglesCollection.AddRange(TernaryStates.Select(b => new DrawableTernaryButton(b)));
@ -244,6 +252,14 @@ namespace osu.Game.Rulesets.Edit
if (!timing.NewValue)
setSelectTool();
});
EditorBeatmap.HasTiming.BindValueChanged(hasTiming =>
{
foreach (var item in toolboxCollection.Items)
{
item.Selected.Disabled = !hasTiming.NewValue;
}
}, true);
}
protected override void Update()

View File

@ -33,9 +33,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
private Drawable icon = null!;
[Resolved]
private EditorBeatmap? editorBeatmap { get; set; }
public EditorRadioButton(RadioButton button)
{
Button = button;
@ -76,8 +73,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
Selected?.Invoke(Button);
};
editorBeatmap?.HasTiming.BindValueChanged(hasTiming => Button.Selected.Disabled = !hasTiming.NewValue, true);
Button.Selected.BindDisabledChanged(disabled => Enabled.Value = !disabled, true);
updateSelectionState();
}
@ -99,6 +94,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
X = 40f
};
public LocalisableString TooltipText => Enabled.Value ? string.Empty : "Add at least one timing point first!";
public LocalisableString TooltipText => Button.TooltipText;
}
}

View File

@ -4,6 +4,7 @@
using System;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
namespace osu.Game.Screens.Edit.Components.RadioButtons
{
@ -11,6 +12,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
{
/// <summary>
/// Whether this <see cref="RadioButton"/> is selected.
/// Disable this bindable to disable the button.
/// </summary>
public readonly BindableBool Selected;
@ -50,5 +52,8 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
/// Deselects this <see cref="RadioButton"/>.
/// </summary>
public void Deselect() => Selected.Value = false;
// Tooltip text that will be shown when hovered over
public LocalisableString TooltipText { get; set; } = string.Empty;
}
}

View File

@ -174,7 +174,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void load()
{
if (rotationHandler != null)
canRotate.BindTo(rotationHandler.CanRotate);
canRotate.BindTo(rotationHandler.CanRotateSelectionOrigin);
canRotate.BindValueChanged(_ => recreate(), true);
}

View File

@ -13,9 +13,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
public partial class SelectionRotationHandler : Component
{
/// <summary>
/// Whether the rotation can currently be performed.
/// Whether rotation anchored by the selection origin can currently be performed.
/// </summary>
public Bindable<bool> CanRotate { get; private set; } = new BindableBool();
public Bindable<bool> CanRotateSelectionOrigin { get; private set; } = new BindableBool();
/// <summary>
/// Whether rotation anchored by the center of the playfield can currently be performed.
/// </summary>
public Bindable<bool> CanRotatePlayfieldOrigin { get; private set; } = new BindableBool();
/// <summary>
/// Performs a single, instant, atomic rotation operation.