1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-14 16:37:26 +08:00

Abstract statefulness of new menu item type

This commit is contained in:
smoogipoo 2019-11-08 11:15:03 +09:00
parent 4fe69dbc89
commit ce08d664a5
6 changed files with 181 additions and 57 deletions

View File

@ -0,0 +1,90 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneStatefulMenuItem : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuMenu),
typeof(ToggleMenuItem),
typeof(DrawableStatefulMenuItem)
};
public TestSceneStatefulMenuItem()
{
Add(new OsuMenu(Direction.Vertical, true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Items = new[]
{
new TestMenuItem("First", MenuItemType.Standard, getNextState),
new TestMenuItem("Second", MenuItemType.Standard, getNextState) { State = { Value = TestStates.State2 } },
new TestMenuItem("Third", MenuItemType.Standard, getNextState) { State = { Value = TestStates.State3 } },
}
});
}
private TestStates getNextState(TestStates state)
{
switch (state)
{
case TestStates.State1:
return TestStates.State2;
case TestStates.State2:
return TestStates.State3;
case TestStates.State3:
return TestStates.State1;
}
return TestStates.State1;
}
private class TestMenuItem : StatefulMenuItem<TestStates>
{
public TestMenuItem(string text, MenuItemType type = MenuItemType.Standard)
: base(text, type)
{
}
public TestMenuItem(string text, MenuItemType type, Func<TestStates, TestStates> changeStateFunc)
: base(text, type, changeStateFunc)
{
}
public override IconUsage? GetIconForState(TestStates state)
{
switch (state)
{
case TestStates.State1:
return FontAwesome.Solid.DiceOne;
case TestStates.State2:
return FontAwesome.Solid.DiceTwo;
case TestStates.State3:
return FontAwesome.Solid.DiceThree;
}
return null;
}
}
private enum TestStates
{
State1,
State2,
State3
}
}
}

View File

@ -1,33 +0,0 @@
// 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 osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneToggleMenuItem : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuMenu),
typeof(ToggleMenuItem),
typeof(DrawableToggleMenuItem)
};
public TestSceneToggleMenuItem()
{
Add(new OsuMenu(Direction.Vertical, true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Items = new[]
{
new ToggleMenuItem("Toggle"),
}
});
}
}
}

View File

@ -8,33 +8,33 @@ using osuTK;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
public class DrawableToggleMenuItem : DrawableOsuMenuItem public class DrawableStatefulMenuItem : DrawableOsuMenuItem
{ {
protected new ToggleMenuItem Item => (ToggleMenuItem)base.Item; protected new StatefulMenuItem Item => (StatefulMenuItem)base.Item;
public DrawableToggleMenuItem(ToggleMenuItem item) public DrawableStatefulMenuItem(StatefulMenuItem item)
: base(item) : base(item)
{ {
} }
protected override TextContainer CreateTextContainer() => new ToggleTextContainer protected override TextContainer CreateTextContainer() => new ToggleTextContainer(Item);
{
State = { BindTarget = Item.State }
};
private class ToggleTextContainer : TextContainer private class ToggleTextContainer : TextContainer
{ {
public readonly Bindable<bool> State = new Bindable<bool>(); private readonly StatefulMenuItem menuItem;
private readonly Bindable<object> state;
private readonly SpriteIcon stateIcon;
private readonly SpriteIcon checkmark; public ToggleTextContainer(StatefulMenuItem menuItem)
public ToggleTextContainer()
{ {
Add(checkmark = new SpriteIcon this.menuItem = menuItem;
state = menuItem.State.GetBoundCopy();
Add(stateIcon = new SpriteIcon
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Icon = FontAwesome.Solid.Check,
Size = new Vector2(10), Size = new Vector2(10),
Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL }, Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL },
AlwaysPresent = true, AlwaysPresent = true,
@ -44,8 +44,7 @@ namespace osu.Game.Graphics.UserInterface
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
state.BindValueChanged(updateState, true);
State.BindValueChanged(state => checkmark.Alpha = state.NewValue ? 1 : 0, true);
} }
protected override void Update() protected override void Update()
@ -53,7 +52,20 @@ namespace osu.Game.Graphics.UserInterface
base.Update(); base.Update();
// Todo: This is bad. This can maybe be done better with a refactor of DrawableOsuMenuItem. // Todo: This is bad. This can maybe be done better with a refactor of DrawableOsuMenuItem.
checkmark.X = BoldText.DrawWidth + 10; stateIcon.X = BoldText.DrawWidth + 10;
}
private void updateState(ValueChangedEvent<object> state)
{
var icon = menuItem.GetIconForState(state.NewValue);
if (icon == null)
stateIcon.Alpha = 0;
else
{
stateIcon.Alpha = 1;
stateIcon.Icon = icon.Value;
}
} }
} }
} }

View File

@ -43,8 +43,8 @@ namespace osu.Game.Graphics.UserInterface
{ {
switch (item) switch (item)
{ {
case ToggleMenuItem toggle: case StatefulMenuItem stateful:
return new DrawableToggleMenuItem(toggle); return new DrawableStatefulMenuItem(stateful);
} }
return new DrawableOsuMenuItem(item); return new DrawableOsuMenuItem(item);

View File

@ -0,0 +1,56 @@
// 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 osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
{
public abstract class StatefulMenuItem : OsuMenuItem
{
public readonly Bindable<object> State = new Bindable<object>();
protected StatefulMenuItem(string text, MenuItemType type = MenuItemType.Standard)
: this(text, type, null)
{
}
protected StatefulMenuItem(string text, MenuItemType type, Func<object, object> changeStateFunc)
: base(text, type)
{
Action.Value = () => State.Value = changeStateFunc?.Invoke(State.Value) ?? State.Value;
}
public abstract IconUsage? GetIconForState(object state);
}
public abstract class StatefulMenuItem<T> : StatefulMenuItem
where T : struct
{
public new readonly Bindable<T> State = new Bindable<T>();
protected StatefulMenuItem(string text, MenuItemType type = MenuItemType.Standard)
: this(text, type, null)
{
}
protected StatefulMenuItem(string text, MenuItemType type, Func<T, T> changeStateFunc)
: base(text, type, o => changeStateFunc?.Invoke((T)o) ?? o)
{
base.State.BindValueChanged(state =>
{
if (state.NewValue == null)
base.State.Value = default(T);
State.Value = (T)base.State.Value;
}, true);
State.BindValueChanged(state => base.State.Value = state.NewValue);
}
public sealed override IconUsage? GetIconForState(object state) => GetIconForState((T)state);
public abstract IconUsage? GetIconForState(T state);
}
}

View File

@ -2,24 +2,23 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
public class ToggleMenuItem : OsuMenuItem public class ToggleMenuItem : StatefulMenuItem<bool>
{ {
public readonly BindableBool State = new BindableBool();
public ToggleMenuItem(string text, MenuItemType type = MenuItemType.Standard) public ToggleMenuItem(string text, MenuItemType type = MenuItemType.Standard)
: this(text, type, null) : this(text, type, null)
{ {
} }
public ToggleMenuItem(string text, MenuItemType type, Action<bool> action) public ToggleMenuItem(string text, MenuItemType type, Action<bool> action)
: base(text, type) : base(text, type, value => !value)
{ {
Action.Value = () => State.Toggle();
State.BindValueChanged(state => action?.Invoke(state.NewValue)); State.BindValueChanged(state => action?.Invoke(state.NewValue));
} }
public override IconUsage? GetIconForState(bool state) => state ? (IconUsage?)FontAwesome.Solid.Check : null;
} }
} }