// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Game.Configuration; using osuTK; namespace osu.Game.Screens.Play.HUD { /// /// A flowing display of all gameplay keys. Individual keys can be added using implementations. /// public abstract partial class KeyCounterDisplay : CompositeDrawable { /// /// Whether the key counter should be visible regardless of the configuration value. /// This is true by default, but can be changed. /// public Bindable AlwaysVisible { get; } = new Bindable(true); /// /// The s contained in this . /// public IEnumerable Counters => KeyFlow; protected abstract FillFlowContainer KeyFlow { get; } /// /// Whether the actions reported by all s within this should be counted. /// public Bindable IsCounting { get; } = new BindableBool(true); protected readonly Bindable ConfigVisibility = new Bindable(); protected abstract void UpdateVisibility(); private Receptor? receptor; public void SetReceptor(Receptor receptor) { if (this.receptor != null) throw new InvalidOperationException("Cannot set a new receptor when one is already active"); this.receptor = receptor; } /// /// Add a to this display. /// public void Add(InputTrigger trigger) { var keyCounter = CreateCounter(trigger); KeyFlow.Add(keyCounter); IsCounting.BindTo(keyCounter.IsCounting); } /// /// Add a range of to this display. /// public void AddRange(IEnumerable triggers) => triggers.ForEach(Add); protected abstract KeyCounter CreateCounter(InputTrigger trigger); [BackgroundDependencyLoader] private void load(OsuConfigManager config) { config.BindWith(OsuSetting.KeyOverlay, ConfigVisibility); } protected override void LoadComplete() { base.LoadComplete(); AlwaysVisible.BindValueChanged(_ => UpdateVisibility()); ConfigVisibility.BindValueChanged(_ => UpdateVisibility(), true); } public override bool HandleNonPositionalInput => receptor == null; public override bool HandlePositionalInput => receptor == null; public partial class Receptor : Drawable { protected readonly KeyCounterDisplay Target; public Receptor(KeyCounterDisplay target) { RelativeSizeAxes = Axes.Both; Depth = float.MinValue; Target = target; } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; protected override bool Handle(UIEvent e) { switch (e) { case KeyDownEvent: case KeyUpEvent: case MouseDownEvent: case MouseUpEvent: return Target.InternalChildren.Any(c => c.TriggerEvent(e)); } return base.Handle(e); } } } }