1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 15:07:44 +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
{
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)
{
}
protected override TextContainer CreateTextContainer() => new ToggleTextContainer
{
State = { BindTarget = Item.State }
};
protected override TextContainer CreateTextContainer() => new ToggleTextContainer(Item);
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()
public ToggleTextContainer(StatefulMenuItem menuItem)
{
Add(checkmark = new SpriteIcon
this.menuItem = menuItem;
state = menuItem.State.GetBoundCopy();
Add(stateIcon = new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Icon = FontAwesome.Solid.Check,
Size = new Vector2(10),
Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL },
AlwaysPresent = true,
@ -44,8 +44,7 @@ namespace osu.Game.Graphics.UserInterface
protected override void LoadComplete()
{
base.LoadComplete();
State.BindValueChanged(state => checkmark.Alpha = state.NewValue ? 1 : 0, true);
state.BindValueChanged(updateState, true);
}
protected override void Update()
@ -53,7 +52,20 @@ namespace osu.Game.Graphics.UserInterface
base.Update();
// 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)
{
case ToggleMenuItem toggle:
return new DrawableToggleMenuItem(toggle);
case StatefulMenuItem stateful:
return new DrawableStatefulMenuItem(stateful);
}
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.
using System;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
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)
: this(text, type, null)
{
}
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));
}
public override IconUsage? GetIconForState(bool state) => state ? (IconUsage?)FontAwesome.Solid.Check : null;
}
}