1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-05 22:34:48 +08:00

Implement new settings item component

This commit is contained in:
Salman Alshamrani
2025-12-18 04:09:02 -05:00
Unverified
parent 729cd722bd
commit 78043fa782
4 changed files with 391 additions and 0 deletions
@@ -0,0 +1,176 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterfaceV2;
namespace osu.Game.Overlays.Settings
{
public sealed partial class SettingsItemV2 : CompositeDrawable, ISettingsItem, IConditionalFilterable
{
private readonly IFormControl control;
private readonly SettingsRevertToDefaultButton revertButton;
private readonly BindableBool controlDefault = new BindableBool(true);
private readonly BindableBool controlEnabled = new BindableBool(true);
/// <summary>
/// Whether a revert-to-default button should be displayed.
/// </summary>
public bool ShowDefaultRevertButton { get; init; } = true;
/// <summary>
/// A note to display underneath the setting.
/// </summary>
public readonly Bindable<SettingsNote.Data?> Note = new Bindable<SettingsNote.Data?>();
public SettingsItemV2(IFormControl control)
{
this.control = control;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS_RIGHT },
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
revertButton = new SettingsRevertToDefaultButton
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Action = ApplyDefault,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = (Drawable)control,
}
}
},
new SettingsNote
{
RelativeSizeAxes = Axes.X,
Current = { BindTarget = Note },
},
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
controlDefault.Value = control.IsValueDefault;
controlEnabled.Value = !control.IsDisabled;
controlDefault.BindValueChanged(_ => updateDefaultState());
controlEnabled.BindValueChanged(_ => updateDefaultState(), true);
FinishTransforms(true);
}
private void updateDefaultState()
{
bool showRevertButton = !controlDefault.Value && controlEnabled.Value && ShowDefaultRevertButton;
if (showRevertButton)
revertButton.Show();
else
revertButton.Hide();
}
protected override void Update()
{
base.Update();
controlDefault.Value = control.IsValueDefault;
controlEnabled.Value = !control.IsDisabled;
}
#region ISettingsItem
public bool HasClassicDefault { get; init; }
public void ApplyClassicDefault()
{
// will be removed soon.
throw new NotSupportedException();
}
public void ApplyDefault()
{
if (!control.IsDisabled)
control.SetValueDefault();
}
public event Action SettingChanged
{
add => control.ValueChanged += value;
remove => control.ValueChanged -= value;
}
#endregion
#region Filtering
public const string CLASSIC_DEFAULT_SEARCH_TERM = @"has-classic-default";
public IEnumerable<string> Keywords { get; init; } = Enumerable.Empty<string>();
public IEnumerable<LocalisableString> FilterTerms
{
get
{
var filterTerms = new List<LocalisableString>(Keywords.Select(k => (LocalisableString)k));
filterTerms.AddRange(control.FilterTerms);
if (HasClassicDefault)
filterTerms.Add(CLASSIC_DEFAULT_SEARCH_TERM);
return filterTerms;
}
}
private bool matchingFilter = true;
public bool MatchingFilter
{
get => matchingFilter;
set
{
bool wasPresent = IsPresent;
matchingFilter = value;
if (IsPresent != wasPresent)
Invalidate(Invalidation.Presence);
}
}
public override bool IsPresent => base.IsPresent && MatchingFilter;
public bool FilteringActive { get; set; }
public BindableBool CanBeShown { get; } = new BindableBool(true);
IBindable<bool> IConditionalFilterable.CanBeShown => CanBeShown;
#endregion
}
}
+117
View File
@@ -0,0 +1,117 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK.Graphics;
namespace osu.Game.Overlays.Settings
{
public sealed partial class SettingsNote : CompositeDrawable
{
public readonly Bindable<Data?> Current = new Bindable<Data?>();
private Box background = null!;
private OsuTextFlowContainer text = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;
[BackgroundDependencyLoader]
private void load()
{
AutoSizeDuration = 300;
AutoSizeEasing = Easing.OutQuint;
InternalChild = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = 5, Bottom = 5 },
Child = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
CornerRadius = 5,
CornerExponent = 2.5f,
Masking = true,
Children = new Drawable[]
{
background = new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
},
text = new OsuTextFlowContainer(s => s.Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold))
{
Padding = new MarginPadding(8),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
}
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(_ => updateDisplay(), true);
FinishTransforms(true);
}
private void updateDisplay()
{
ClearTransforms();
if (Current.Value == null)
{
AutoSizeAxes = Axes.None;
this.ResizeHeightTo(0, 300, Easing.OutQuint);
this.FadeOut(250, Easing.OutQuint);
return;
}
AutoSizeAxes = Axes.Y;
this.FadeIn(250, Easing.OutQuint);
switch (Current.Value.Type)
{
case Type.Informational:
background.Colour = colourProvider.Dark2;
text.Colour = colourProvider.Content2;
break;
case Type.Warning:
background.Colour = colours.Orange1;
text.Colour = colourProvider.Background5;
break;
case Type.Critical:
background.Colour = colours.Red1;
text.Colour = colourProvider.Background5;
break;
}
text.Text = Current.Value.Text;
}
public record Data(LocalisableString Text, Type Type);
public enum Type
{
Informational,
Warning,
Critical,
}
}
}
@@ -0,0 +1,95 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK;
namespace osu.Game.Overlays.Settings
{
public partial class SettingsRevertToDefaultButton : OsuClickableContainer
{
public const float WIDTH = 32;
public float IconSize { get; init; } = 14;
private Box background = null!;
private SpriteIcon spriteIcon = null!;
[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;
// this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button.
public override bool AcceptsFocus => true;
public SettingsRevertToDefaultButton()
{
Size = new Vector2(WIDTH, 50);
}
[BackgroundDependencyLoader]
private void load()
{
Masking = true;
CornerRadius = 5;
CornerExponent = 2.5f;
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background3,
},
spriteIcon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = colourProvider.Light1,
Icon = OsuIcon.Undo,
Margin = new MarginPadding { Left = 12, Right = 5 },
Size = new Vector2(IconSize),
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Enabled.BindValueChanged(_ => updateDisplay(), true);
}
protected override bool OnHover(HoverEvent e)
{
updateDisplay();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateDisplay();
base.OnHoverLost(e);
}
public override void Show()
{
this.FadeIn().MoveToX(WIDTH - 10, 300, Easing.OutQuint);
}
public override void Hide()
{
this.MoveToX(0, 300, Easing.OutQuint).Then().FadeOut();
}
private void updateDisplay()
{
spriteIcon.FadeColour(IsHovered ? colourProvider.Content2 : colourProvider.Light1, 300, Easing.OutQuint);
background.FadeColour(IsHovered ? colourProvider.Background2 : colourProvider.Background3, 300, Easing.OutQuint);
}
}
}
+3
View File
@@ -31,6 +31,9 @@ namespace osu.Game.Overlays
{
public const float CONTENT_MARGINS = 20;
// extra margin to give room to the revert-to-default button in settings controls.
public const float CONTENT_MARGINS_RIGHT = 30;
public const float TRANSITION_LENGTH = 600;
private const float sidebar_width = SettingsSidebar.EXPANDED_WIDTH;