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;
5using osu.Framework.Extensions.TypeExtensions;
6using osu.Framework.Utils;
7
8namespace osu.Framework.Bindables
9{
10 /// <summary>
11 /// An interface which can be bound to other <see cref="IBindable"/>s in order to watch for (and react to) <see cref="ICanBeDisabled.Disabled">Disabled</see> changes.
12 /// </summary>
13 public interface IBindable : ICanBeDisabled, IHasDefaultValue, IUnbindable, IHasDescription
14 {
15 /// <summary>
16 /// Binds ourselves to another bindable such that we receive any value limitations of the bindable we bind width.
17 /// </summary>
18 /// <param name="them">The foreign bindable. This should always be the most permanent end of the bind (ie. a ConfigManager)</param>
19 void BindTo(IBindable them);
20
21 /// <summary>
22 /// An alias of <see cref="BindTo"/> provided for use in object initializer scenarios.
23 /// Passes the provided value as the foreign (more permanent) bindable.
24 /// </summary>
25 sealed IBindable BindTarget
26 {
27 set => BindTo(value);
28 }
29
30 /// <summary>
31 /// Retrieve a new bindable instance weakly bound to the configuration backing.
32 /// If you are further binding to events of a bindable retrieved using this method, ensure to hold
33 /// a local reference.
34 /// </summary>
35 /// <returns>A weakly bound copy of the specified bindable.</returns>
36 /// <exception cref="InvalidOperationException">Thrown when attempting to instantiate a copy bindable that's not matching the original's type.</exception>
37 IBindable GetBoundCopy();
38
39 /// <summary>
40 /// Creates a new instance of this <see cref="IBindable"/> for use in <see cref="GetBoundCopy"/>.
41 /// The returned instance must have match the most derived type of the bindable class this method is implemented on.
42 /// </summary>
43 protected IBindable CreateInstance();
44
45 /// <summary>
46 /// Helper method which implements <see cref="GetBoundCopy"/> for use in final classes.
47 /// </summary>
48 /// <param name="source">The source <see cref="IBindable"/>.</param>
49 /// <typeparam name="T">The bindable type.</typeparam>
50 /// <returns>The bound copy.</returns>
51 protected static T GetBoundCopyImplementation<T>(T source)
52 where T : IBindable
53 {
54 var copy = source.CreateInstance();
55
56 if (copy.GetType() != source.GetType())
57 {
58 ThrowHelper.ThrowInvalidOperationException($"Attempted to create a copy of {source.GetType().ReadableName()}, but the returned instance type was {copy.GetType().ReadableName()}. "
59 + $"Override {source.GetType().ReadableName()}.{nameof(CreateInstance)}() for {nameof(GetBoundCopy)}() to function properly.");
60 }
61
62 copy.BindTo(source);
63 return (T)copy;
64 }
65 }
66
67 /// <summary>
68 /// An interface which can be bound to other <see cref="IBindable{T}"/>s in order to watch for (and react to) <see cref="ICanBeDisabled.Disabled">Disabled</see> and <see cref="IBindable{T}.Value">Value</see> changes.
69 /// </summary>
70 /// <typeparam name="T">The type of value encapsulated by this <see cref="IBindable{T}"/>.</typeparam>
71 public interface IBindable<T> : ICanBeDisabled, IHasDefaultValue, IUnbindable, IHasDescription
72 {
73 /// <summary>
74 /// An event which is raised when <see cref="Value"/> has changed.
75 /// </summary>
76 event Action<ValueChangedEvent<T>> ValueChanged;
77
78 /// <summary>
79 /// The current value of this bindable.
80 /// </summary>
81 T Value { get; }
82
83 /// <summary>
84 /// The default value of this bindable. Used when querying <see cref="IHasDefaultValue.IsDefault">IsDefault</see>.
85 /// </summary>
86 T Default { get; }
87
88 /// <summary>
89 /// Binds ourselves to another bindable such that we receive any values and value limitations of the bindable we bind width.
90 /// </summary>
91 /// <param name="them">The foreign bindable. This should always be the most permanent end of the bind (ie. a ConfigManager)</param>
92 void BindTo(IBindable<T> them);
93
94 /// <summary>
95 /// An alias of <see cref="BindTo"/> provided for use in object initializer scenarios.
96 /// Passes the provided value as the foreign (more permanent) bindable.
97 /// </summary>
98 IBindable<T> BindTarget
99 {
100 set => BindTo(value);
101 }
102
103 /// <summary>
104 /// Bind an action to <see cref="ValueChanged"/> with the option of running the bound action once immediately.
105 /// </summary>
106 /// <param name="onChange">The action to perform when <see cref="Value"/> changes.</param>
107 /// <param name="runOnceImmediately">Whether the action provided in <paramref name="onChange"/> should be run once immediately.</param>
108 void BindValueChanged(Action<ValueChangedEvent<T>> onChange, bool runOnceImmediately = false);
109
110 /// <inheritdoc cref="IBindable.GetBoundCopy"/>
111 IBindable<T> GetBoundCopy();
112 }
113}