1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-14 15:57:24 +08:00

Add simple scale tool

This commit is contained in:
OliBomby 2024-05-25 18:31:19 +02:00
parent d55fa423e9
commit 6aa92bcc45
5 changed files with 167 additions and 2 deletions

View File

@ -101,7 +101,11 @@ namespace osu.Game.Rulesets.Osu.Edit
RightToolbox.AddRange(new EditorToolboxGroup[] RightToolbox.AddRange(new EditorToolboxGroup[]
{ {
new TransformToolboxGroup { RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }, new TransformToolboxGroup
{
RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler,
ScaleHandler = BlueprintContainer.SelectionHandler.ScaleHandler,
},
FreehandlSliderToolboxGroup FreehandlSliderToolboxGroup
} }
); );

View File

@ -0,0 +1,121 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Edit.Components.RadioButtons;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit
{
public partial class PreciseScalePopover : OsuPopover
{
private readonly SelectionScaleHandler scaleHandler;
private readonly Bindable<PreciseScaleInfo> scaleInfo = new Bindable<PreciseScaleInfo>(new PreciseScaleInfo(1, ScaleOrigin.PlayfieldCentre, true, true));
private SliderWithTextBoxInput<float> scaleInput = null!;
private EditorRadioButtonCollection scaleOrigin = null!;
private RadioButton selectionCentreButton = null!;
public PreciseScalePopover(SelectionScaleHandler scaleHandler)
{
this.scaleHandler = scaleHandler;
AllowableAnchors = new[] { Anchor.CentreLeft, Anchor.CentreRight };
}
[BackgroundDependencyLoader]
private void load()
{
Child = new FillFlowContainer
{
Width = 220,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(20),
Children = new Drawable[]
{
scaleInput = new SliderWithTextBoxInput<float>("Scale:")
{
Current = new BindableNumber<float>
{
MinValue = 0.5f,
MaxValue = 2,
Precision = 0.001f,
Value = 1,
Default = 1,
},
Instantaneous = true
},
scaleOrigin = new EditorRadioButtonCollection
{
RelativeSizeAxes = Axes.X,
Items = new[]
{
new RadioButton("Playfield centre",
() => scaleInfo.Value = scaleInfo.Value with { Origin = ScaleOrigin.PlayfieldCentre },
() => new SpriteIcon { Icon = FontAwesome.Regular.Square }),
selectionCentreButton = new RadioButton("Selection centre",
() => scaleInfo.Value = scaleInfo.Value with { Origin = ScaleOrigin.SelectionCentre },
() => new SpriteIcon { Icon = FontAwesome.Solid.VectorSquare })
}
}
}
};
selectionCentreButton.Selected.DisabledChanged += isDisabled =>
{
selectionCentreButton.TooltipText = isDisabled ? "Select more than one object to perform selection-based scaling." : string.Empty;
};
}
protected override void LoadComplete()
{
base.LoadComplete();
ScheduleAfterChildren(() => scaleInput.TakeFocus());
scaleInput.Current.BindValueChanged(scale => scaleInfo.Value = scaleInfo.Value with { Scale = scale.NewValue });
scaleOrigin.Items.First().Select();
scaleHandler.CanScaleX.BindValueChanged(e =>
{
selectionCentreButton.Selected.Disabled = !e.NewValue;
}, true);
scaleInfo.BindValueChanged(scale =>
{
var newScale = new Vector2(scale.NewValue.XAxis ? scale.NewValue.Scale : 1, scale.NewValue.YAxis ? scale.NewValue.Scale : 1);
scaleHandler.Update(newScale, scale.NewValue.Origin == ScaleOrigin.PlayfieldCentre ? OsuPlayfield.BASE_SIZE / 2 : null);
});
}
protected override void PopIn()
{
base.PopIn();
scaleHandler.Begin();
}
protected override void PopOut()
{
base.PopOut();
if (IsLoaded)
scaleHandler.Commit();
}
}
public enum ScaleOrigin
{
PlayfieldCentre,
SelectionCentre
}
public record PreciseScaleInfo(float Scale, ScaleOrigin Origin, bool XAxis, bool YAxis);
}

View File

@ -19,13 +19,20 @@ namespace osu.Game.Rulesets.Osu.Edit
public partial class TransformToolboxGroup : EditorToolboxGroup, IKeyBindingHandler<GlobalAction> public partial class TransformToolboxGroup : EditorToolboxGroup, IKeyBindingHandler<GlobalAction>
{ {
private readonly Bindable<bool> canRotate = new BindableBool(); private readonly Bindable<bool> canRotate = new BindableBool();
private readonly Bindable<bool> canScale = new BindableBool();
private EditorToolButton rotateButton = null!; private EditorToolButton rotateButton = null!;
private EditorToolButton scaleButton = null!;
private Bindable<bool> canRotatePlayfieldOrigin = null!; private Bindable<bool> canRotatePlayfieldOrigin = null!;
private Bindable<bool> canRotateSelectionOrigin = null!; private Bindable<bool> canRotateSelectionOrigin = null!;
private Bindable<bool> canScaleX = null!;
private Bindable<bool> canScaleY = null!;
private Bindable<bool> canScaleDiagonally = null!;
public SelectionRotationHandler RotationHandler { get; init; } = null!; public SelectionRotationHandler RotationHandler { get; init; } = null!;
public SelectionScaleHandler ScaleHandler { get; init; } = null!;
public TransformToolboxGroup() public TransformToolboxGroup()
: base("transform") : base("transform")
@ -45,7 +52,9 @@ namespace osu.Game.Rulesets.Osu.Edit
rotateButton = new EditorToolButton("Rotate", rotateButton = new EditorToolButton("Rotate",
() => new SpriteIcon { Icon = FontAwesome.Solid.Undo }, () => new SpriteIcon { Icon = FontAwesome.Solid.Undo },
() => new PreciseRotationPopover(RotationHandler)), () => new PreciseRotationPopover(RotationHandler)),
// TODO: scale scaleButton = new EditorToolButton("Scale",
() => new SpriteIcon { Icon = FontAwesome.Solid.ArrowsAlt },
() => new PreciseScalePopover(ScaleHandler))
} }
}; };
} }
@ -66,9 +75,25 @@ namespace osu.Game.Rulesets.Osu.Edit
canRotate.Value = RotationHandler.CanRotatePlayfieldOrigin.Value || RotationHandler.CanRotateSelectionOrigin.Value; canRotate.Value = RotationHandler.CanRotatePlayfieldOrigin.Value || RotationHandler.CanRotateSelectionOrigin.Value;
} }
// aggregate three values into canScale
canScaleX = ScaleHandler.CanScaleX.GetBoundCopy();
canScaleX.BindValueChanged(_ => updateCanScaleAggregate());
canScaleY = ScaleHandler.CanScaleY.GetBoundCopy();
canScaleY.BindValueChanged(_ => updateCanScaleAggregate());
canScaleDiagonally = ScaleHandler.CanScaleDiagonally.GetBoundCopy();
canScaleDiagonally.BindValueChanged(_ => updateCanScaleAggregate());
void updateCanScaleAggregate()
{
canScale.Value = ScaleHandler.CanScaleX.Value || ScaleHandler.CanScaleY.Value || ScaleHandler.CanScaleDiagonally.Value;
}
// bindings to `Enabled` on the buttons are decoupled on purpose // 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. // due to the weird `OsuButton` behaviour of resetting `Enabled` to `false` when `Action` is set.
canRotate.BindValueChanged(_ => rotateButton.Enabled.Value = canRotate.Value, true); canRotate.BindValueChanged(_ => rotateButton.Enabled.Value = canRotate.Value, true);
canScale.BindValueChanged(_ => scaleButton.Enabled.Value = canScale.Value, true);
} }
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e) public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
@ -82,6 +107,12 @@ namespace osu.Game.Rulesets.Osu.Edit
rotateButton.TriggerClick(); rotateButton.TriggerClick();
return true; return true;
} }
case GlobalAction.EditorToggleScaleControl:
{
scaleButton.TriggerClick();
return true;
}
} }
return false; return false;

View File

@ -142,6 +142,7 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelRight }, GlobalAction.EditorCyclePreviousBeatSnapDivisor), new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelRight }, GlobalAction.EditorCyclePreviousBeatSnapDivisor),
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelLeft }, GlobalAction.EditorCycleNextBeatSnapDivisor), new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelLeft }, GlobalAction.EditorCycleNextBeatSnapDivisor),
new KeyBinding(new[] { InputKey.Control, InputKey.R }, GlobalAction.EditorToggleRotateControl), new KeyBinding(new[] { InputKey.Control, InputKey.R }, GlobalAction.EditorToggleRotateControl),
new KeyBinding(new[] { InputKey.S }, GlobalAction.EditorToggleScaleControl),
}; };
private static IEnumerable<KeyBinding> inGameKeyBindings => new[] private static IEnumerable<KeyBinding> inGameKeyBindings => new[]
@ -411,6 +412,9 @@ namespace osu.Game.Input.Bindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))]
EditorToggleRotateControl, EditorToggleRotateControl,
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleScaleControl))]
EditorToggleScaleControl,
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseOffset))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseOffset))]
IncreaseOffset, IncreaseOffset,

View File

@ -369,6 +369,11 @@ namespace osu.Game.Localisation
/// </summary> /// </summary>
public static LocalisableString EditorToggleRotateControl => new TranslatableString(getKey(@"editor_toggle_rotate_control"), @"Toggle rotate control"); public static LocalisableString EditorToggleRotateControl => new TranslatableString(getKey(@"editor_toggle_rotate_control"), @"Toggle rotate control");
/// <summary>
/// "Toggle scale control"
/// </summary>
public static LocalisableString EditorToggleScaleControl => new TranslatableString(getKey(@"editor_toggle_scale_control"), @"Toggle scale control");
/// <summary> /// <summary>
/// "Increase mod speed" /// "Increase mod speed"
/// </summary> /// </summary>