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:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user