diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs new file mode 100644 index 0000000000..e3dae9c27e --- /dev/null +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Configuration; +using osuTK; + +namespace osu.Game.Tests.Visual.Settings +{ + [TestFixture] + public class TestSceneSettingsSource : OsuTestScene + { + public TestSceneSettingsSource() + { + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(20), + Width = 0.5f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(50), + ChildrenEnumerable = new TestTargetClass().CreateSettingsControls() + }, + }; + } + + private class TestTargetClass + { + [SettingSource("Sample bool", "Clicking this changes a setting")] + public BindableBool TickBindable { get; } = new BindableBool(); + + [SettingSource("Sample float", "Change something for a mod")] + public BindableFloat SliderBindable { get; } = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + Default = 5, + Value = 7 + }; + + [SettingSource("Sample enum", "Change something for a mod")] + public Bindable EnumBindable { get; } = new Bindable + { + Default = TestEnum.Value1, + Value = TestEnum.Value2 + }; + + [SettingSource("Sample string", "Change something for a mod")] + public Bindable StringBindable { get; } = new Bindable(); + } + + private enum TestEnum + { + Value1, + Value2 + } + } +} diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs new file mode 100644 index 0000000000..056fa8bcc0 --- /dev/null +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -0,0 +1,110 @@ +// 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.Reflection; +using JetBrains.Annotations; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Configuration +{ + /// + /// An attribute to mark a bindable as being exposed to the user via settings controls. + /// Can be used in conjunction with to automatically create UI controls. + /// + [MeansImplicitUse] + [AttributeUsage(AttributeTargets.Property)] + public class SettingSourceAttribute : Attribute + { + public string Label { get; } + + public string Description { get; } + + public SettingSourceAttribute(string label, string description = null) + { + Label = label ?? string.Empty; + Description = description ?? string.Empty; + } + } + + public static class SettingSourceExtensions + { + public static IEnumerable CreateSettingsControls(this object obj) + { + foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) + { + var attr = property.GetCustomAttribute(true); + + if (attr == null) + continue; + + var prop = property.GetValue(obj); + + switch (prop) + { + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case Bindable bBool: + yield return new SettingsCheckbox + { + LabelText = attr.Label, + Bindable = bBool + }; + + break; + + case Bindable bString: + yield return new SettingsTextBox + { + LabelText = attr.Label, + Bindable = bString + }; + + break; + + case IBindable bindable: + var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]); + var dropdown = (Drawable)Activator.CreateInstance(dropdownType); + + dropdown.GetType().GetProperty(nameof(IHasCurrentValue.Current))?.SetValue(dropdown, obj); + + yield return dropdown; + + break; + + default: + throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({prop})"); + } + } + } + } +}