diff --git a/osu.Game/Overlays/SkinEditor/SkinComponentToolbox.cs b/osu.Game/Overlays/SkinEditor/SkinComponentToolbox.cs index cd9f7cc935..d0707abec6 100644 --- a/osu.Game/Overlays/SkinEditor/SkinComponentToolbox.cs +++ b/osu.Game/Overlays/SkinEditor/SkinComponentToolbox.cs @@ -2,12 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Logging; -using osu.Game.Graphics; +using osu.Framework.Threading; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; @@ -64,7 +65,8 @@ namespace osu.Game.Overlays.SkinEditor fill.Add(new ToolboxComponentButton(instance, target) { - RequestPlacement = t => RequestPlacement?.Invoke(t) + RequestPlacement = t => RequestPlacement?.Invoke(t), + Expanding = contractOtherButtons, }); } catch (DependencyNotRegisteredException) @@ -78,15 +80,29 @@ namespace osu.Game.Overlays.SkinEditor } } + private void contractOtherButtons(ToolboxComponentButton obj) + { + foreach (var b in fill.OfType()) + { + if (b == obj) + continue; + + b.Contract(); + } + } + public partial class ToolboxComponentButton : OsuButton { public Action? RequestPlacement; + public Action? Expanding; private readonly Drawable component; private readonly CompositeDrawable? dependencySource; private Container innerContainer = null!; + private ScheduledDelegate? expandContractAction; + private const float contracted_size = 60; private const float expanded_size = 120; @@ -101,20 +117,45 @@ namespace osu.Game.Overlays.SkinEditor Height = contracted_size; } + private const double animation_duration = 500; + protected override bool OnHover(HoverEvent e) { - this.Delay(300).ResizeHeightTo(expanded_size, 500, Easing.OutQuint); + expandContractAction?.Cancel(); + expandContractAction = Scheduler.AddDelayed(() => + { + this.ResizeHeightTo(expanded_size, animation_duration, Easing.OutQuint); + Expanding?.Invoke(this); + }, 100); + return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - this.ResizeHeightTo(contracted_size, 500, Easing.OutQuint); + + expandContractAction?.Cancel(); + // If no other component is selected for too long, force a contract. + // Otherwise we will generally contract when Contract() is called from outside. + expandContractAction = Scheduler.AddDelayed(Contract, 1000); + } + + public void Contract() + { + // Cheap debouncing to avoid stacking animations. + // The only place this is nulled is at the end of this method. + if (expandContractAction == null) + return; + + this.ResizeHeightTo(contracted_size, animation_duration, Easing.OutQuint); + + expandContractAction?.Cancel(); + expandContractAction = null; } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, OsuColour colours) + private void load(OverlayColourProvider colourProvider) { BackgroundColour = colourProvider.Background3;