// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Osu.UI.Cursor { public partial class OsuCursorContainer : GameplayCursorContainer, IKeyBindingHandler { public new OsuCursor ActiveCursor => (OsuCursor)base.ActiveCursor; protected override Drawable CreateCursor() => new OsuCursor(); protected override Container Content => fadeContainer; private readonly Container fadeContainer; private readonly Bindable showTrail = new Bindable(true); private readonly SkinnableDrawable cursorTrail; private readonly CursorRippleVisualiser rippleVisualiser; public OsuCursorContainer() { InternalChild = fadeContainer = new Container { RelativeSizeAxes = Axes.Both, Children = new CompositeDrawable[] { cursorTrail = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling), rippleVisualiser = new CursorRippleVisualiser(), new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling), } }; } [BackgroundDependencyLoader(true)] private void load(OsuRulesetConfigManager rulesetConfig) { rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorTrail, showTrail); } protected override void LoadComplete() { base.LoadComplete(); showTrail.BindValueChanged(v => cursorTrail.FadeTo(v.NewValue ? 1 : 0, 200), true); ActiveCursor.CursorScale.BindValueChanged(e => { var newScale = new Vector2(e.NewValue); rippleVisualiser.CursorScale = newScale; cursorTrail.Scale = newScale; }, true); } private int downCount; private void updateExpandedState() { if (downCount > 0) ActiveCursor.Expand(); else ActiveCursor.Contract(); } protected override void Update() { base.Update(); if (cursorTrail.Drawable is CursorTrail trail) trail.NewPartScale = ActiveCursor.CurrentExpandedScale; } public bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) { case OsuAction.LeftButton: case OsuAction.RightButton: downCount++; updateExpandedState(); break; } return false; } public void OnReleased(KeyBindingReleaseEvent e) { switch (e.Action) { case OsuAction.LeftButton: case OsuAction.RightButton: // Todo: Math.Max() is required as a temporary measure to address https://github.com/ppy/osu-framework/issues/2576 downCount = Math.Max(0, downCount - 1); if (downCount == 0) updateExpandedState(); break; } } public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. protected override void PopIn() { fadeContainer.FadeTo(1, 300, Easing.OutQuint); ActiveCursor.ScaleTo(1f, 400, Easing.OutQuint); } protected override void PopOut() { fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint); } private partial class DefaultCursorTrail : CursorTrail { [BackgroundDependencyLoader] private void load(TextureStore textures) { Texture = textures.Get(@"Cursor/cursortrail"); Scale = new Vector2(1 / Texture.ScaleAdjust); } } } }