diff --git a/osu-framework b/osu-framework index b04ebcf58a..e63cfd9ba4 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b04ebcf58a21ef17374383500f449fade3511c85 +Subproject commit e63cfd9ba44a40750dff0617ba6f08ffbfcc7fde diff --git a/osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs b/osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs new file mode 100644 index 0000000000..00d4d33c86 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs @@ -0,0 +1,125 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseContextMenu : TestCase + { + public override string Description => @"Menu visible on right click"; + + private const int start_time = 0; + private const int duration = 1000; + + private MyContextMenuContainer container; + + public override void Reset() + { + base.Reset(); + + Add(container = new MyContextMenuContainer + { + Size = new Vector2(200), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Green, + } + } + }); + + Add(new AnotherContextMenuContainer + { + Size = new Vector2(200), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Red, + } + } + }); + + container.Transforms.Add(new TransformPosition + { + StartValue = Vector2.Zero, + EndValue = new Vector2(0, 100), + StartTime = start_time, + EndTime = start_time + duration, + LoopCount = -1, + LoopDelay = duration * 3 + }); + container.Transforms.Add(new TransformPosition + { + StartValue = new Vector2(0, 100), + EndValue = new Vector2(100, 100), + StartTime = start_time + duration, + EndTime = start_time + duration * 2, + LoopCount = -1, + LoopDelay = duration * 3 + }); + container.Transforms.Add(new TransformPosition + { + StartValue = new Vector2(100, 100), + EndValue = new Vector2(100, 0), + StartTime = start_time + duration * 2, + EndTime = start_time + duration * 3, + LoopCount = -1, + LoopDelay = duration * 3 + }); + container.Transforms.Add(new TransformPosition + { + StartValue = new Vector2(100, 0), + EndValue = Vector2.Zero, + StartTime = start_time + duration * 3, + EndTime = start_time + duration * 4, + LoopCount = -1, + LoopDelay = duration * 3 + }); + } + + private class MyContextMenuContainer : Container, IHasContextMenu + { + public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[] + { + new OsuContextMenuItem(@"Some option"), + new OsuContextMenuItem(@"Highlighted option", MenuItemType.Highlighted), + new OsuContextMenuItem(@"Another option"), + new OsuContextMenuItem(@"Choose me please"), + new OsuContextMenuItem(@"And me too"), + new OsuContextMenuItem(@"Trying to fill"), + new OsuContextMenuItem(@"Destructive option", MenuItemType.Destructive), + }; + } + + private class AnotherContextMenuContainer : Container, IHasContextMenu + { + public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[] + { + new OsuContextMenuItem(@"Simple option"), + new OsuContextMenuItem(@"Simple very very long option"), + new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => ResizeWidthTo(Width * 2, 100, EasingTypes.OutQuint) }, + new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => ResizeHeightTo(Height * 2, 100, EasingTypes.OutQuint) }, + new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => ResizeWidthTo(Width / 2, 100, EasingTypes.OutQuint) }, + new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => ResizeHeightTo(Height / 2, 100, EasingTypes.OutQuint) }, + }; + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index e8debc50da..0712aa7278 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -189,6 +189,7 @@ + diff --git a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs new file mode 100644 index 0000000000..2cc6c3a46a --- /dev/null +++ b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.Cursor +{ + public class OsuContextMenuContainer : ContextMenuContainer + { + protected override ContextMenu CreateContextMenu() => new OsuContextMenu(); + + public OsuContextMenuContainer(CursorContainer cursor) : base(cursor) + { + } + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 3d83668d07..d2f4d4768c 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -87,5 +87,7 @@ namespace osu.Game.Graphics public readonly Color4 RedDarker = FromHex(@"870000"); public readonly Color4 ChatBlue = FromHex(@"17292e"); + + public readonly Color4 ContextMenuGray = FromHex(@"223034"); } } diff --git a/osu.Game/Graphics/UserInterface/MenuItemType.cs b/osu.Game/Graphics/UserInterface/MenuItemType.cs new file mode 100644 index 0000000000..bd89dbfced --- /dev/null +++ b/osu.Game/Graphics/UserInterface/MenuItemType.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Graphics.UserInterface +{ + public enum MenuItemType + { + Standard, + Highlighted, + Destructive, + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs new file mode 100644 index 0000000000..e17ce2a5b2 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuContextMenu : ContextMenu + where TItem : ContextMenuItem + { + protected override Menu CreateMenu() => new CustomMenu(); + + public class CustomMenu : Menu + { + private const int fade_duration = 250; + + public CustomMenu() + { + CornerRadius = 5; + ItemsContainer.Padding = new MarginPadding { Vertical = OsuContextMenuItem.MARGIN_VERTICAL }; + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.1f), + Radius = 4, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Background.Colour = colours.ContextMenuGray; + } + + protected override void AnimateOpen() => FadeIn(fade_duration, EasingTypes.OutQuint); + protected override void AnimateClose() => FadeOut(fade_duration, EasingTypes.OutQuint); + + protected override void UpdateContentHeight() + { + var actualHeight = (RelativeSizeAxes & Axes.Y) > 0 ? 1 : ContentHeight; + ResizeTo(new Vector2(1, State == MenuState.Opened ? actualHeight : 0), 300, EasingTypes.OutQuint); + } + } + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuContextMenuItem.cs new file mode 100644 index 0000000000..769df18566 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuContextMenuItem.cs @@ -0,0 +1,114 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuContextMenuItem : ContextMenuItem + { + private const int transition_length = 80; + private const int margin_horizontal = 17; + public const int MARGIN_VERTICAL = 4; + private const int text_size = 17; + + private OsuSpriteText text; + private OsuSpriteText textBold; + + private SampleChannel sampleClick; + private SampleChannel sampleHover; + + private readonly MenuItemType type; + + protected override Container CreateTextContainer(string title) => new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new Drawable[] + { + text = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = text_size, + Text = title, + Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, + }, + textBold = new OsuSpriteText + { + AlwaysPresent = true, + Alpha = 0, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = text_size, + Text = title, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, + } + } + }; + + public OsuContextMenuItem(string title, MenuItemType type = MenuItemType.Standard) : base(title) + { + this.type = type; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get(@"Menu/menuclick"); + sampleClick = audio.Sample.Get(@"Menu/menuback"); + + BackgroundColour = Color4.Transparent; + BackgroundColourHover = OsuColour.FromHex(@"172023"); + + updateTextColour(); + } + + private void updateTextColour() + { + switch (type) + { + case MenuItemType.Standard: + textBold.Colour = text.Colour = Color4.White; + break; + case MenuItemType.Destructive: + textBold.Colour = text.Colour = Color4.Red; + break; + case MenuItemType.Highlighted: + textBold.Colour = text.Colour = OsuColour.FromHex(@"ffcc22"); + break; + } + } + + protected override bool OnHover(InputState state) + { + sampleHover.Play(); + textBold.FadeIn(transition_length, EasingTypes.OutQuint); + text.FadeOut(transition_length, EasingTypes.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + textBold.FadeOut(transition_length, EasingTypes.OutQuint); + text.FadeIn(transition_length, EasingTypes.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + sampleClick.Play(); + return base.OnClick(state); + } + } +} \ No newline at end of file diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b228b6485a..306cdaddf0 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -158,6 +158,7 @@ namespace osu.Game Children = new Drawable[] { Cursor = new MenuCursor(), + new OsuContextMenuContainer(Cursor) { Depth = -2 }, new OsuTooltipContainer(Cursor) { Depth = -1 }, } }, diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 62c3f53221..20785463ea 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -44,8 +44,8 @@ True - $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll True + $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll @@ -76,8 +76,12 @@ + + + + @@ -506,4 +510,4 @@ --> - + \ No newline at end of file