mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 09:32:55 +08:00
Merge branch 'master' into fix-time-snapping-when-nearby-objects
This commit is contained in:
commit
4db01fc970
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
@ -10,7 +11,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -23,7 +23,6 @@ using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
|
||||
@ -35,8 +34,6 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
|
||||
private CatchDistanceSnapGrid distanceSnapGrid;
|
||||
|
||||
private readonly Bindable<TernaryState> distanceSnapToggle = new Bindable<TernaryState>();
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
private readonly BindableDouble timeRangeMultiplier = new BindableDouble(1)
|
||||
@ -81,6 +78,19 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
inputManager = GetContainingInputManager();
|
||||
}
|
||||
|
||||
protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after)
|
||||
{
|
||||
// osu!catch's distance snap implementation is limited, in that a custom spacing cannot be specified.
|
||||
// Therefore this functionality is not currently used.
|
||||
//
|
||||
// The implementation below is probably correct but should be checked if/when exposed via controls.
|
||||
|
||||
float expectedDistance = DurationToDistance(before, after.StartTime - before.GetEndTime());
|
||||
float actualDistance = Math.Abs(((CatchHitObject)before).EffectiveX - ((CatchHitObject)after).EffectiveX);
|
||||
|
||||
return actualDistance / expectedDistance;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -120,11 +130,6 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
new BananaShowerCompositionTool()
|
||||
};
|
||||
|
||||
protected override IEnumerable<TernaryButton> CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[]
|
||||
{
|
||||
new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler })
|
||||
});
|
||||
|
||||
public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
|
||||
{
|
||||
var result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType);
|
||||
@ -196,7 +201,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
|
||||
private void updateDistanceSnapGrid()
|
||||
{
|
||||
if (distanceSnapToggle.Value != TernaryState.True)
|
||||
if (DistanceSnapToggle.Value != TernaryState.True)
|
||||
{
|
||||
distanceSnapGrid.Hide();
|
||||
return;
|
||||
|
@ -44,6 +44,28 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
rectangularGridActive(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDistanceSnapMomentaryToggle()
|
||||
{
|
||||
AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1)));
|
||||
|
||||
AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType<OsuDistanceSnapGrid>().Any());
|
||||
AddStep("hold alt", () => InputManager.PressKey(Key.AltLeft));
|
||||
AddUntilStep("distance snap grid visible", () => this.ChildrenOfType<OsuDistanceSnapGrid>().Any());
|
||||
AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft));
|
||||
AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType<OsuDistanceSnapGrid>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGridSnapMomentaryToggle()
|
||||
{
|
||||
rectangularGridActive(false);
|
||||
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
||||
rectangularGridActive(true);
|
||||
AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft));
|
||||
rectangularGridActive(false);
|
||||
}
|
||||
|
||||
private void rectangularGridActive(bool active)
|
||||
{
|
||||
AddStep("choose placement tool", () => InputManager.Key(Key.Number2));
|
||||
@ -57,8 +79,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddAssert("placement blueprint at (0, 0)", () => Precision.AlmostEquals(Editor.ChildrenOfType<HitCirclePlacementBlueprint>().Single().HitObject.Position, new Vector2(0, 0)));
|
||||
else
|
||||
AddAssert("placement blueprint at (1, 1)", () => Precision.AlmostEquals(Editor.ChildrenOfType<HitCirclePlacementBlueprint>().Single().HitObject.Position, new Vector2(1, 1)));
|
||||
|
||||
AddStep("choose selection tool", () => InputManager.Key(Key.Number1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -14,8 +14,10 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -45,12 +47,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
new SpinnerCompositionTool()
|
||||
};
|
||||
|
||||
private readonly Bindable<TernaryState> distanceSnapToggle = new Bindable<TernaryState>();
|
||||
private readonly Bindable<TernaryState> rectangularGridSnapToggle = new Bindable<TernaryState>();
|
||||
|
||||
protected override IEnumerable<TernaryButton> CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[]
|
||||
{
|
||||
new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }),
|
||||
new TernaryButton(rectangularGridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Th })
|
||||
});
|
||||
|
||||
@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
placementObject = EditorBeatmap.PlacementObject.GetBoundCopy();
|
||||
placementObject.ValueChanged += _ => updateDistanceSnapGrid();
|
||||
distanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid();
|
||||
DistanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid();
|
||||
|
||||
// we may be entering the screen with a selection already active
|
||||
updateDistanceSnapGrid();
|
||||
@ -101,6 +101,14 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
private RectangularPositionSnapGrid rectangularPositionSnapGrid;
|
||||
|
||||
protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after)
|
||||
{
|
||||
float expectedDistance = DurationToDistance(before, after.StartTime - before.GetEndTime());
|
||||
float actualDistance = Vector2.Distance(((OsuHitObject)before).EndPosition, ((OsuHitObject)after).Position);
|
||||
|
||||
return actualDistance / expectedDistance;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -130,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
// We want to ensure that in this particular case, the time-snapping component of distance snap is still applied.
|
||||
// The easiest way to ensure this is to attempt application of distance snap after a nearby object is found, and copy over
|
||||
// the time value if the proposed positions are roughly the same.
|
||||
if (snapType.HasFlagFast(SnapType.Grids) && distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
|
||||
if (snapType.HasFlagFast(SnapType.Grids) && DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
|
||||
{
|
||||
(Vector2 distanceSnappedPosition, double distanceSnappedTime) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(snapResult.ScreenSpacePosition));
|
||||
if (Precision.AlmostEquals(distanceSnapGrid.ToScreenSpace(distanceSnappedPosition), snapResult.ScreenSpacePosition, 1))
|
||||
@ -144,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
if (snapType.HasFlagFast(SnapType.Grids))
|
||||
{
|
||||
if (distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
|
||||
if (DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
|
||||
{
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
|
||||
@ -213,7 +221,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
distanceSnapGridCache.Invalidate();
|
||||
distanceSnapGrid = null;
|
||||
|
||||
if (distanceSnapToggle.Value != TernaryState.True)
|
||||
if (DistanceSnapToggle.Value != TernaryState.True)
|
||||
return;
|
||||
|
||||
switch (BlueprintContainer.CurrentTool)
|
||||
@ -240,6 +248,42 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
handleToggleViaKey(e);
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(KeyUpEvent e)
|
||||
{
|
||||
handleToggleViaKey(e);
|
||||
base.OnKeyUp(e);
|
||||
}
|
||||
|
||||
protected override bool AdjustDistanceSpacing(GlobalAction action, float amount)
|
||||
{
|
||||
// To allow better visualisation, ensure that the spacing grid is visible before adjusting.
|
||||
DistanceSnapToggle.Value = TernaryState.True;
|
||||
|
||||
return base.AdjustDistanceSpacing(action, amount);
|
||||
}
|
||||
|
||||
private bool gridSnapMomentary;
|
||||
|
||||
private void handleToggleViaKey(KeyboardEvent key)
|
||||
{
|
||||
bool shiftPressed = key.ShiftPressed;
|
||||
|
||||
if (shiftPressed != gridSnapMomentary)
|
||||
{
|
||||
gridSnapMomentary = shiftPressed;
|
||||
rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False;
|
||||
}
|
||||
}
|
||||
|
||||
private DistanceSnapGrid createDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects)
|
||||
{
|
||||
if (BlueprintContainer.CurrentTool is SpinnerCompositionTool)
|
||||
|
@ -161,10 +161,11 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddStep("hold alt", () => InputManager.PressKey(Key.LAlt));
|
||||
|
||||
AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5));
|
||||
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
|
||||
|
||||
AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt));
|
||||
AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl));
|
||||
|
||||
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
|
||||
}
|
||||
|
||||
public class EditorBeatmapContainer : Container
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
@ -10,9 +13,11 @@ using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
@ -20,6 +25,7 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.OSD;
|
||||
using osu.Game.Overlays.Settings.Sections;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
@ -32,7 +38,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
private const float adjust_step = 0.1f;
|
||||
|
||||
public Bindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1.0)
|
||||
public BindableDouble DistanceSpacingMultiplier { get; } = new BindableDouble(1.0)
|
||||
{
|
||||
MinValue = 0.1,
|
||||
MaxValue = 6.0,
|
||||
@ -44,10 +50,15 @@ namespace osu.Game.Rulesets.Edit
|
||||
protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; }
|
||||
|
||||
private ExpandableSlider<double, SizeSlider<double>> distanceSpacingSlider;
|
||||
private ExpandableButton currentDistanceSpacingButton;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OnScreenDisplay onScreenDisplay { get; set; }
|
||||
|
||||
protected readonly Bindable<TernaryState> DistanceSnapToggle = new Bindable<TernaryState>();
|
||||
|
||||
private bool distanceSnapMomentary;
|
||||
|
||||
protected DistancedHitObjectComposer(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
@ -74,10 +85,27 @@ namespace osu.Game.Rulesets.Edit
|
||||
Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1,
|
||||
Child = new EditorToolboxGroup("snapping")
|
||||
{
|
||||
Child = distanceSpacingSlider = new ExpandableSlider<double, SizeSlider<double>>
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Current = { BindTarget = DistanceSpacingMultiplier },
|
||||
KeyboardStep = adjust_step,
|
||||
distanceSpacingSlider = new ExpandableSlider<double, SizeSlider<double>>
|
||||
{
|
||||
KeyboardStep = adjust_step,
|
||||
// Manual binding in LoadComplete to handle one-way event flow.
|
||||
Current = DistanceSpacingMultiplier.GetUnboundCopy(),
|
||||
},
|
||||
currentDistanceSpacingButton = new ExpandableButton
|
||||
{
|
||||
Action = () =>
|
||||
{
|
||||
(HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime();
|
||||
|
||||
Debug.Assert(objects != null);
|
||||
|
||||
DistanceSpacingMultiplier.Value = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after);
|
||||
DistanceSnapToggle.Value = TernaryState.True;
|
||||
},
|
||||
RelativeSizeAxes = Axes.X,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,6 +113,51 @@ namespace osu.Game.Rulesets.Edit
|
||||
});
|
||||
}
|
||||
|
||||
private (HitObject before, HitObject after)? getObjectsOnEitherSideOfCurrentTime()
|
||||
{
|
||||
HitObject lastBefore = Playfield.HitObjectContainer.AliveObjects.LastOrDefault(h => h.HitObject.StartTime <= EditorClock.CurrentTime)?.HitObject;
|
||||
|
||||
if (lastBefore == null)
|
||||
return null;
|
||||
|
||||
HitObject firstAfter = Playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime >= EditorClock.CurrentTime)?.HitObject;
|
||||
|
||||
if (firstAfter == null)
|
||||
return null;
|
||||
|
||||
if (lastBefore == firstAfter)
|
||||
return null;
|
||||
|
||||
return (lastBefore, firstAfter);
|
||||
}
|
||||
|
||||
protected abstract double ReadCurrentDistanceSnap(HitObject before, HitObject after);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
(HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime();
|
||||
|
||||
double currentSnap = objects == null
|
||||
? 0
|
||||
: ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after);
|
||||
|
||||
if (currentSnap > DistanceSpacingMultiplier.MinValue)
|
||||
{
|
||||
currentDistanceSpacingButton.Enabled.Value = currentDistanceSpacingButton.Expanded.Value
|
||||
&& !Precision.AlmostEquals(currentSnap, DistanceSpacingMultiplier.Value, DistanceSpacingMultiplier.Precision / 2);
|
||||
currentDistanceSpacingButton.ContractedLabelText = $"current {currentSnap:N2}x";
|
||||
currentDistanceSpacingButton.ExpandedLabelText = $"Use current ({currentSnap:N2}x)";
|
||||
}
|
||||
else
|
||||
{
|
||||
currentDistanceSpacingButton.Enabled.Value = false;
|
||||
currentDistanceSpacingButton.ContractedLabelText = string.Empty;
|
||||
currentDistanceSpacingButton.ExpandedLabelText = "Use current (unavailable)";
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -102,6 +175,45 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
EditorBeatmap.BeatmapInfo.DistanceSpacing = multiplier.NewValue;
|
||||
}, true);
|
||||
|
||||
// Manual binding to handle enabling distance spacing when the slider is interacted with.
|
||||
distanceSpacingSlider.Current.BindValueChanged(spacing =>
|
||||
{
|
||||
DistanceSpacingMultiplier.Value = spacing.NewValue;
|
||||
DistanceSnapToggle.Value = TernaryState.True;
|
||||
});
|
||||
DistanceSpacingMultiplier.BindValueChanged(spacing => distanceSpacingSlider.Current.Value = spacing.NewValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<TernaryButton> CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[]
|
||||
{
|
||||
new TernaryButton(DistanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler })
|
||||
});
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
handleToggleViaKey(e);
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(KeyUpEvent e)
|
||||
{
|
||||
handleToggleViaKey(e);
|
||||
base.OnKeyUp(e);
|
||||
}
|
||||
|
||||
private void handleToggleViaKey(KeyboardEvent key)
|
||||
{
|
||||
bool altPressed = key.AltPressed;
|
||||
|
||||
if (altPressed != distanceSnapMomentary)
|
||||
{
|
||||
distanceSnapMomentary = altPressed;
|
||||
DistanceSnapToggle.Value = DistanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False;
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +223,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
case GlobalAction.EditorIncreaseDistanceSpacing:
|
||||
case GlobalAction.EditorDecreaseDistanceSpacing:
|
||||
return adjustDistanceSpacing(e.Action, adjust_step);
|
||||
return AdjustDistanceSpacing(e.Action, adjust_step);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -127,13 +239,13 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
case GlobalAction.EditorIncreaseDistanceSpacing:
|
||||
case GlobalAction.EditorDecreaseDistanceSpacing:
|
||||
return adjustDistanceSpacing(e.Action, e.ScrollAmount * adjust_step);
|
||||
return AdjustDistanceSpacing(e.Action, e.ScrollAmount * adjust_step);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool adjustDistanceSpacing(GlobalAction action, float amount)
|
||||
protected virtual bool AdjustDistanceSpacing(GlobalAction action, float amount)
|
||||
{
|
||||
if (DistanceSpacingMultiplier.Disabled)
|
||||
return false;
|
||||
@ -143,6 +255,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
else if (action == GlobalAction.EditorDecreaseDistanceSpacing)
|
||||
DistanceSpacingMultiplier.Value -= amount;
|
||||
|
||||
DistanceSnapToggle.Value = TernaryState.True;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
101
osu.Game/Rulesets/Edit/ExpandableButton.cs
Normal file
101
osu.Game/Rulesets/Edit/ExpandableButton.cs
Normal file
@ -0,0 +1,101 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
internal class ExpandableButton : RoundedButton, IExpandable
|
||||
{
|
||||
private float actualHeight;
|
||||
|
||||
public override float Height
|
||||
{
|
||||
get => base.Height;
|
||||
set => base.Height = actualHeight = value;
|
||||
}
|
||||
|
||||
private LocalisableString contractedLabelText;
|
||||
|
||||
/// <summary>
|
||||
/// The label text to display when this button is in a contracted state.
|
||||
/// </summary>
|
||||
public LocalisableString ContractedLabelText
|
||||
{
|
||||
get => contractedLabelText;
|
||||
set
|
||||
{
|
||||
if (value == contractedLabelText)
|
||||
return;
|
||||
|
||||
contractedLabelText = value;
|
||||
|
||||
if (!Expanded.Value)
|
||||
Text = value;
|
||||
}
|
||||
}
|
||||
|
||||
private LocalisableString expandedLabelText;
|
||||
|
||||
/// <summary>
|
||||
/// The label text to display when this button is in an expanded state.
|
||||
/// </summary>
|
||||
public LocalisableString ExpandedLabelText
|
||||
{
|
||||
get => expandedLabelText;
|
||||
set
|
||||
{
|
||||
if (value == expandedLabelText)
|
||||
return;
|
||||
|
||||
expandedLabelText = value;
|
||||
|
||||
if (Expanded.Value)
|
||||
Text = value;
|
||||
}
|
||||
}
|
||||
|
||||
public BindableBool Expanded { get; } = new BindableBool();
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IExpandingContainer? expandingContainer { get; set; }
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
|
||||
{
|
||||
Expanded.Value = containerExpanded.NewValue;
|
||||
}, true);
|
||||
|
||||
Expanded.BindValueChanged(expanded =>
|
||||
{
|
||||
Text = expanded.NewValue ? expandedLabelText : contractedLabelText;
|
||||
|
||||
if (expanded.NewValue)
|
||||
{
|
||||
SpriteText.Anchor = Anchor.Centre;
|
||||
SpriteText.Origin = Anchor.Centre;
|
||||
SpriteText.Font = OsuFont.GetFont(weight: FontWeight.Bold);
|
||||
base.Height = actualHeight;
|
||||
Background.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
SpriteText.Anchor = Anchor.CentreLeft;
|
||||
SpriteText.Origin = Anchor.CentreLeft;
|
||||
SpriteText.Font = OsuFont.GetFont(weight: FontWeight.Regular);
|
||||
base.Height = actualHeight / 2;
|
||||
Background.Hide();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user