A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System.Collections.Generic;
5using System.Collections.Immutable;
6using osu.Framework.Input.StateChanges.Events;
7using osu.Framework.Input.States;
8
9namespace osu.Framework.Input.StateChanges
10{
11 /// <summary>
12 /// An abstract base class of an <see cref="IInput"/> which denotes a list of button state changes (pressed or released).
13 /// </summary>
14 /// <typeparam name="TButton">The type of button.</typeparam>
15 public abstract class ButtonInput<TButton> : IInput
16 where TButton : struct
17 {
18 public ImmutableArray<ButtonInputEntry<TButton>> Entries;
19
20 protected ButtonInput(IEnumerable<ButtonInputEntry<TButton>> entries)
21 {
22 Entries = entries.ToImmutableArray();
23 }
24
25 /// <summary>
26 /// Creates a <see cref="ButtonInput{TButton}"/> with a single <typeparamref name="TButton"/> state.
27 /// </summary>
28 /// <param name="button">The <typeparamref name="TButton"/> to add.</param>
29 /// <param name="isPressed">The state of <paramref name="button"/>.</param>
30 protected ButtonInput(TButton button, bool isPressed)
31 {
32 Entries = ImmutableArray.Create(new ButtonInputEntry<TButton>(button, isPressed));
33 }
34
35 /// <summary>
36 /// Creates a <see cref="ButtonInput{TButton}"/> from the difference of two <see cref="ButtonStates{TButton}"/>.
37 /// </summary>
38 /// <remarks>
39 /// Buttons that are pressed in <paramref name="previous"/> and not pressed in <paramref name="current"/> will be listed as <see cref="ButtonStateChangeKind.Released"/>.
40 /// Buttons that are not pressed in <paramref name="previous"/> and pressed in <paramref name="current"/> will be listed as <see cref="ButtonStateChangeKind.Pressed"/>.
41 /// </remarks>
42 /// <param name="current">The newer <see cref="ButtonStates{TButton}"/>.</param>
43 /// <param name="previous">The older <see cref="ButtonStates{TButton}"/>.</param>
44 protected ButtonInput(ButtonStates<TButton> current, ButtonStates<TButton> previous)
45 {
46 var difference = (current ?? new ButtonStates<TButton>()).EnumerateDifference(previous ?? new ButtonStates<TButton>());
47
48 var builder = ImmutableArray.CreateBuilder<ButtonInputEntry<TButton>>(difference.Released.Length + difference.Pressed.Length);
49
50 foreach (var button in difference.Released)
51 builder.Add(new ButtonInputEntry<TButton>(button, false));
52 foreach (var button in difference.Pressed)
53 builder.Add(new ButtonInputEntry<TButton>(button, true));
54
55 Entries = builder.MoveToImmutable();
56 }
57
58 /// <summary>
59 /// Retrieves the <see cref="ButtonStates{TButton}"/> from an <see cref="InputState"/>.
60 /// </summary>
61 protected abstract ButtonStates<TButton> GetButtonStates(InputState state);
62
63 /// <summary>
64 /// Create a <typeparamref name="TButton"/> state change event.
65 /// </summary>
66 /// <param name="state">The <see cref="InputState"/> which changed.</param>
67 /// <param name="button">The <typeparamref name="TButton"/> that changed.</param>
68 /// <param name="kind">The type of change that occurred on <paramref name="button"/>.</param>
69 protected virtual ButtonStateChangeEvent<TButton> CreateEvent(InputState state, TButton button, ButtonStateChangeKind kind) => new ButtonStateChangeEvent<TButton>(state, this, button, kind);
70
71 public virtual void Apply(InputState state, IInputStateChangeHandler handler)
72 {
73 if (Entries.Length == 0)
74 return;
75
76 var buttonStates = GetButtonStates(state);
77
78 foreach (var entry in Entries)
79 {
80 if (buttonStates.SetPressed(entry.Button, entry.IsPressed))
81 {
82 var buttonStateChange = CreateEvent(state, entry.Button, entry.IsPressed ? ButtonStateChangeKind.Pressed : ButtonStateChangeKind.Released);
83 handler.HandleInputStateChange(buttonStateChange);
84 }
85 }
86 }
87 }
88}