1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 01:52:55 +08:00

Merge remote-tracking branch 'refs/remotes/ppy/master' into toolbar-rulesets-refactor

This commit is contained in:
Andrei Zavatski 2019-06-24 22:52:24 +03:00
commit 35516e7304
25 changed files with 692 additions and 290 deletions

View File

@ -0,0 +1,105 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Tests.Visual;
using System;
using System.Collections.Generic;
using osu.Game.Skinning;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osuTK.Graphics;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestSceneCatcher : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(CatcherSprite),
};
private readonly Container container;
public TestSceneCatcher()
{
Child = container = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
}
[BackgroundDependencyLoader]
private void load()
{
AddStep("show default catcher implementation", () => { container.Child = new CatcherSprite(); });
AddStep("show custom catcher implementation", () =>
{
container.Child = new CatchCustomSkinSourceContainer
{
Child = new CatcherSprite()
};
});
}
private class CatcherCustomSkin : Container
{
public CatcherCustomSkin()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Blue
},
new SpriteText
{
Text = "custom"
}
};
}
}
[Cached(typeof(ISkinSource))]
private class CatchCustomSkinSourceContainer : Container, ISkinSource
{
public event Action SourceChanged
{
add { }
remove { }
}
public Drawable GetDrawableComponent(string componentName)
{
switch (componentName)
{
case "Play/Catch/fruit-catcher-idle":
return new CatcherCustomSkin();
}
return null;
}
public SampleChannel GetSample(string sampleName) =>
throw new NotImplementedException();
public Texture GetTexture(string componentName) =>
throw new NotImplementedException();
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration =>
throw new NotImplementedException();
}
}
}

View File

@ -6,8 +6,6 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
@ -141,7 +139,7 @@ namespace osu.Game.Rulesets.Catch.UI
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
Children = new[]
{
caughtFruit = new Container<DrawableHitObject>
{
@ -212,7 +210,7 @@ namespace osu.Game.Rulesets.Catch.UI
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
}
private Sprite createCatcherSprite() => new CatcherSprite();
private Drawable createCatcherSprite() => new CatcherSprite();
/// <summary>
/// Add a caught fruit to the catcher's stack.
@ -444,23 +442,6 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.Expire();
}
private class CatcherSprite : Sprite
{
public CatcherSprite()
{
Size = new Vector2(CATCHER_SIZE);
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
OriginPosition = new Vector2(-0.02f, 0.06f) * CATCHER_SIZE;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
}
}
}
}
}

View File

@ -0,0 +1,33 @@
// 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.Containers;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatcherSprite : CompositeDrawable
{
public CatcherSprite()
{
Size = new Vector2(CatcherArea.CATCHER_SIZE);
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
OriginPosition = new Vector2(-0.02f, 0.06f) * CatcherArea.CATCHER_SIZE;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new SkinnableSprite(@"Play/Catch/fruit-catcher-idle")
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
};
}
}
}

View File

@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Skinning;
@ -25,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Child = new SkinnableDrawable("Play/osu/approachcircle", name => new Sprite { Texture = textures.Get(name) });
Child = new SkinnableSprite("Play/osu/approachcircle");
}
}
}

View File

@ -14,16 +14,21 @@ namespace osu.Game.Tests.Visual.Components
[TestFixture]
public class TestSceneIdleTracker : ManualInputManagerTestScene
{
private readonly IdleTrackingBox box1;
private readonly IdleTrackingBox box2;
private readonly IdleTrackingBox box3;
private readonly IdleTrackingBox box4;
private IdleTrackingBox box1;
private IdleTrackingBox box2;
private IdleTrackingBox box3;
private IdleTrackingBox box4;
public TestSceneIdleTracker()
private IdleTrackingBox[] boxes;
[SetUp]
public void SetUp() => Schedule(() =>
{
Children = new Drawable[]
InputManager.MoveMouseTo(Vector2.Zero);
Children = boxes = new[]
{
box1 = new IdleTrackingBox(1000)
box1 = new IdleTrackingBox(2000)
{
Name = "TopLeft",
RelativeSizeAxes = Axes.Both,
@ -31,7 +36,7 @@ namespace osu.Game.Tests.Visual.Components
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
},
box2 = new IdleTrackingBox(2000)
box2 = new IdleTrackingBox(4000)
{
Name = "TopRight",
RelativeSizeAxes = Axes.Both,
@ -39,7 +44,7 @@ namespace osu.Game.Tests.Visual.Components
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
box3 = new IdleTrackingBox(3000)
box3 = new IdleTrackingBox(6000)
{
Name = "BottomLeft",
RelativeSizeAxes = Axes.Both,
@ -47,7 +52,7 @@ namespace osu.Game.Tests.Visual.Components
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
},
box4 = new IdleTrackingBox(4000)
box4 = new IdleTrackingBox(8000)
{
Name = "BottomRight",
RelativeSizeAxes = Axes.Both,
@ -56,7 +61,7 @@ namespace osu.Game.Tests.Visual.Components
Origin = Anchor.BottomRight,
},
};
}
});
[Test]
public void TestNudge()
@ -67,10 +72,10 @@ namespace osu.Game.Tests.Visual.Components
AddStep("nudge mouse", () => InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.Centre + new Vector2(1)));
checkIdleStatus(box1, false);
checkIdleStatus(box2, true);
checkIdleStatus(box3, true);
checkIdleStatus(box4, true);
checkIdleStatus(1, false);
checkIdleStatus(2, true);
checkIdleStatus(3, true);
checkIdleStatus(4, true);
}
[Test]
@ -78,18 +83,18 @@ namespace osu.Game.Tests.Visual.Components
{
AddStep("move to top right", () => InputManager.MoveMouseTo(box2));
checkIdleStatus(box1, true);
checkIdleStatus(box2, false);
checkIdleStatus(box3, true);
checkIdleStatus(box4, true);
checkIdleStatus(1, true);
checkIdleStatus(2, false);
checkIdleStatus(3, true);
checkIdleStatus(4, true);
AddStep("move to bottom left", () => InputManager.MoveMouseTo(box3));
AddStep("move to bottom right", () => InputManager.MoveMouseTo(box4));
checkIdleStatus(box1, true);
checkIdleStatus(box2, false);
checkIdleStatus(box3, false);
checkIdleStatus(box4, false);
checkIdleStatus(1, true);
checkIdleStatus(2, false);
checkIdleStatus(3, false);
checkIdleStatus(4, false);
waitForAllIdle();
}
@ -99,38 +104,38 @@ namespace osu.Game.Tests.Visual.Components
{
AddStep("move to centre", () => InputManager.MoveMouseTo(Content));
checkIdleStatus(box1, false);
checkIdleStatus(box2, false);
checkIdleStatus(box3, false);
checkIdleStatus(box4, false);
checkIdleStatus(1, false);
checkIdleStatus(2, false);
checkIdleStatus(3, false);
checkIdleStatus(4, false);
AddUntilStep("Wait for idle", () => box1.IsIdle);
checkIdleStatus(box1, true);
checkIdleStatus(box2, false);
checkIdleStatus(box3, false);
checkIdleStatus(box4, false);
checkIdleStatus(1, true);
checkIdleStatus(2, false);
checkIdleStatus(3, false);
checkIdleStatus(4, false);
AddUntilStep("Wait for idle", () => box2.IsIdle);
checkIdleStatus(box1, true);
checkIdleStatus(box2, true);
checkIdleStatus(box3, false);
checkIdleStatus(box4, false);
checkIdleStatus(1, true);
checkIdleStatus(2, true);
checkIdleStatus(3, false);
checkIdleStatus(4, false);
AddUntilStep("Wait for idle", () => box3.IsIdle);
checkIdleStatus(box1, true);
checkIdleStatus(box2, true);
checkIdleStatus(box3, true);
checkIdleStatus(box4, false);
checkIdleStatus(1, true);
checkIdleStatus(2, true);
checkIdleStatus(3, true);
checkIdleStatus(4, false);
waitForAllIdle();
}
private void checkIdleStatus(IdleTrackingBox box, bool expectedIdle)
private void checkIdleStatus(int box, bool expectedIdle)
{
AddAssert($"{box.Name} is {(expectedIdle ? "idle" : "active")}", () => box.IsIdle == expectedIdle);
AddAssert($"box {box} is {(expectedIdle ? "idle" : "active")}", () => boxes[box - 1].IsIdle == expectedIdle);
}
private void waitForAllIdle()

View File

@ -0,0 +1,55 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneNumberBox : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuNumberBox),
};
private OsuNumberBox numberBox;
[BackgroundDependencyLoader]
private void load()
{
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding { Horizontal = 250 },
Child = numberBox = new OsuNumberBox
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
PlaceholderText = "Insert numbers here"
}
};
clearInput();
AddStep("enter numbers", () => numberBox.Text = "987654321");
expectedValue("987654321");
clearInput();
AddStep("enter text + single number", () => numberBox.Text = "1 hello 2 world 3");
expectedValue("123");
clearInput();
}
private void clearInput() => AddStep("clear input", () => numberBox.Text = null);
private void expectedValue(string value) => AddAssert("expect number", () => numberBox.Text == value);
}
}

View File

@ -183,7 +183,7 @@ namespace osu.Game.Tournament.Screens.Editors
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new SettingsTextBox
new SettingsNumberBox
{
LabelText = "Beatmap ID",
RelativeSizeAxes = Axes.None,

View File

@ -231,7 +231,7 @@ namespace osu.Game.Tournament.Screens.Editors
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new SettingsTextBox
new SettingsNumberBox
{
LabelText = "User ID",
RelativeSizeAxes = Axes.None,

View File

@ -5,7 +5,6 @@ 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.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
@ -191,8 +190,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
public RoundDisplay()
{
CornerRadius = 10;
Masking = true;
Width = 200;
Height = 20;
}
@ -208,11 +205,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
InternalChildren = new Drawable[]
{
new Box
{
Colour = new Color4(47, 71, 67, 255),
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Anchor = Anchor.Centre,

View File

@ -6,17 +6,25 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osuTK;
namespace osu.Game.Tournament.Screens.Showcase
{
public class TournamentLogo : CompositeDrawable
{
public TournamentLogo()
public TournamentLogo(bool includeRoundBackground = true)
{
RelativeSizeAxes = Axes.X;
Height = 95;
Margin = new MarginPadding { Vertical = 5 };
if (includeRoundBackground)
{
AutoSizeAxes = Axes.Y;
}
else
{
Masking = true;
Height = 100;
}
}
[BackgroundDependencyLoader]
@ -27,9 +35,6 @@ namespace osu.Game.Tournament.Screens.Showcase
Texture = textures.Get("game-screen-logo"),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
Size = Vector2.One
};
}
}

View File

@ -34,7 +34,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
RelativeSizeAxes = Axes.Both,
Loop = true,
},
new TournamentLogo(),
new TournamentLogo(false),
mainContainer = new Container
{
RelativeSizeAxes = Axes.Both,

View File

@ -0,0 +1,10 @@
// 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.
namespace osu.Game.Graphics.UserInterface
{
public class OsuNumberBox : OsuTextBox
{
protected override bool CanAddCharacter(char character) => char.IsNumber(character);
}
}

View File

@ -50,6 +50,7 @@ namespace osu.Game.Input.Bindings
{
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene),
new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry),
new KeyBinding(new[] { InputKey.Control, InputKey.Tilde }, GlobalAction.QuickExit),
new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed),
new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed),
};
@ -94,6 +95,9 @@ namespace osu.Game.Input.Bindings
[Description("Quick retry (hold)")]
QuickRetry,
[Description("Quick exit (Hold)")]
QuickExit,
[Description("Take screenshot")]
TakeScreenshot,

View File

@ -134,9 +134,9 @@ namespace osu.Game.Overlays.Dialog
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Position = new Vector2(0f, -50f),
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Padding = new MarginPadding { Bottom = 10 },
Children = new Drawable[]
{
new Container
@ -144,10 +144,6 @@ namespace osu.Game.Overlays.Dialog
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Size = ringSize,
Margin = new MarginPadding
{
Bottom = 30,
},
Children = new Drawable[]
{
ring = new CircularContainer
@ -181,15 +177,15 @@ namespace osu.Game.Overlays.Dialog
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(15),
TextAnchor = Anchor.TopCentre,
},
body = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 18))
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
TextAnchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(15),
TextAnchor = Anchor.TopCentre,
},
},
},

View File

@ -1,153 +1,23 @@
// 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.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
public class OverlayHeaderTabControl : TabControl<string>
public class OverlayHeaderTabControl : OverlayTabControl<string>
{
private readonly Box bar;
private Color4 accentColour = Color4.White;
public Color4 AccentColour
protected override TabItem<string> CreateTabItem(string value) => new OverlayHeaderTabItem(value)
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
bar.Colour = value;
foreach (TabItem<string> tabItem in TabContainer)
{
((HeaderTabItem)tabItem).AccentColour = value;
}
}
}
public new MarginPadding Padding
{
get => TabContainer.Padding;
set => TabContainer.Padding = value;
}
public OverlayHeaderTabControl()
{
TabContainer.Masking = false;
TabContainer.Spacing = new Vector2(15, 0);
AddInternal(bar = new Box
{
RelativeSizeAxes = Axes.X,
Height = 2,
Anchor = Anchor.BottomLeft,
Origin = Anchor.CentreLeft
});
}
protected override Dropdown<string> CreateDropdown() => null;
protected override TabItem<string> CreateTabItem(string value) => new HeaderTabItem(value)
{
AccentColour = AccentColour
AccentColour = AccentColour,
};
private class HeaderTabItem : TabItem<string>
private class OverlayHeaderTabItem : OverlayTabItem<string>
{
private readonly OsuSpriteText text;
private readonly ExpandingBar bar;
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
bar.Colour = value;
updateState();
}
}
public HeaderTabItem(string value)
public OverlayHeaderTabItem(string value)
: base(value)
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
Children = new Drawable[]
{
text = new OsuSpriteText
{
Margin = new MarginPadding { Bottom = 10 },
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Text = value,
Font = OsuFont.GetFont()
},
bar = new ExpandingBar
{
Anchor = Anchor.BottomCentre,
ExpandedSize = 7.5f,
CollapsedSize = 0
},
new HoverClickSounds()
};
}
protected override bool OnHover(HoverEvent e)
{
base.OnHover(e);
updateState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
updateState();
}
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
private void updateState()
{
if (Active.Value || IsHovered)
{
text.FadeColour(Color4.White, 120, Easing.InQuad);
bar.Expand();
if (Active.Value)
text.Font = text.Font.With(weight: FontWeight.Bold);
}
else
{
text.FadeColour(AccentColour, 120, Easing.InQuad);
bar.Collapse();
text.Font = text.Font.With(weight: FontWeight.Medium);
}
Text.Text = value;
}
}
}

View File

@ -0,0 +1,163 @@
// 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.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
public abstract class OverlayTabControl<T> : TabControl<T>
{
private readonly Box bar;
private Color4 accentColour = Color4.White;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
bar.Colour = value;
foreach (TabItem<T> tabItem in TabContainer)
{
((OverlayTabItem<T>)tabItem).AccentColour = value;
}
}
}
public new MarginPadding Padding
{
get => TabContainer.Padding;
set => TabContainer.Padding = value;
}
protected OverlayTabControl()
{
TabContainer.Masking = false;
TabContainer.Spacing = new Vector2(15, 0);
AddInternal(bar = new Box
{
RelativeSizeAxes = Axes.X,
Height = 2,
Anchor = Anchor.BottomLeft,
Origin = Anchor.CentreLeft
});
}
protected override Dropdown<T> CreateDropdown() => null;
protected override TabItem<T> CreateTabItem(T value) => new OverlayTabItem<T>(value);
protected class OverlayTabItem<U> : TabItem<U>
{
private readonly ExpandingBar bar;
protected readonly OsuSpriteText Text;
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
bar.Colour = value;
updateState();
}
}
public OverlayTabItem(U value)
: base(value)
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
Children = new Drawable[]
{
Text = new OsuSpriteText
{
Margin = new MarginPadding { Bottom = 10 },
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Font = OsuFont.GetFont(),
},
bar = new ExpandingBar
{
Anchor = Anchor.BottomCentre,
ExpandedSize = 7.5f,
CollapsedSize = 0
},
new HoverClickSounds()
};
}
protected override bool OnHover(HoverEvent e)
{
base.OnHover(e);
if (!Active.Value)
HoverAction();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
if (!Active.Value)
UnhoverAction();
}
protected override void OnActivated()
{
HoverAction();
Text.Font = Text.Font.With(weight: FontWeight.Bold);
}
protected override void OnDeactivated()
{
UnhoverAction();
Text.Font = Text.Font.With(weight: FontWeight.Medium);
}
private void updateState()
{
if (Active.Value)
OnActivated();
else
OnDeactivated();
}
protected virtual void HoverAction()
{
bar.Expand();
Text.FadeColour(Color4.White, 120, Easing.InQuad);
}
protected virtual void UnhoverAction()
{
bar.Collapse();
Text.FadeColour(AccentColour, 120, Easing.InQuad);
}
}
}
}

View File

@ -1,25 +1,29 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Profile
{
public abstract class ProfileSection : FillFlowContainer
public abstract class ProfileSection : Container
{
public abstract string Title { get; }
public abstract string Identifier { get; }
private readonly FillFlowContainer content;
private readonly Box background;
private readonly Box underscore;
protected override Container<Drawable> Content => content;
@ -27,50 +31,109 @@ namespace osu.Game.Overlays.Profile
protected ProfileSection()
{
Direction = FillDirection.Vertical;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new OsuSpriteText
background = new Box
{
Text = Title,
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular, italics: true),
Margin = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Vertical = 10
}
RelativeSizeAxes = Axes.Both,
},
content = new FillFlowContainer
new SectionTriangles
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
},
new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding
Children = new Drawable[]
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Bottom = 20
}
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = OsuColour.Gray(34),
EdgeSmoothness = new Vector2(1)
new Container
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Top = 15,
Bottom = 10,
},
Children = new Drawable[]
{
new OsuSpriteText
{
Text = Title,
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
},
underscore = new Box
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = 4 },
RelativeSizeAxes = Axes.X,
Height = 2,
}
}
},
content = new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Bottom = 20
}
},
},
}
};
}
// placeholder
Add(new OsuSpriteText
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.GreySeafoamDarker;
underscore.Colour = colours.Seafoam;
}
private class SectionTriangles : Container
{
private readonly Triangles triangles;
private readonly Box foreground;
public SectionTriangles()
{
Text = @"coming soon!",
Colour = Color4.Gray,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Top = 100, Bottom = 100 }
});
RelativeSizeAxes = Axes.X;
Height = 100;
Masking = true;
Children = new Drawable[]
{
triangles = new Triangles
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
TriangleScale = 3,
},
foreground = new Box
{
RelativeSizeAxes = Axes.Both,
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
triangles.ColourLight = colours.GreySeafoamDark;
triangles.ColourDark = colours.GreySeafoamDarker.Darken(0.2f);
foreground.Colour = ColourInfo.GradientVertical(colours.GreySeafoamDarker, colours.GreySeafoamDarker.Opacity(0));
}
}
}
}

View File

@ -0,0 +1,17 @@
// 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.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
public class SettingsNumberBox : SettingsItem<string>
{
protected override Drawable CreateControl() => new OsuNumberBox
{
Margin = new MarginPadding { Top = 5 },
RelativeSizeAxes = Axes.X,
};
}
}

View File

@ -4,11 +4,11 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile;
using osu.Game.Overlays.Profile.Sections;
@ -23,7 +23,7 @@ namespace osu.Game.Overlays
private ProfileSection[] sections;
private GetUserRequest userReq;
protected ProfileHeader Header;
private SectionsContainer<ProfileSection> sectionsContainer;
private ProfileSectionsContainer sectionsContainer;
private ProfileTabControl tabs;
public const float CONTENT_X_MARGIN = 70;
@ -65,12 +65,11 @@ namespace osu.Game.Overlays
Add(new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
Colour = OsuColour.Gray(0.1f)
});
Add(sectionsContainer = new SectionsContainer<ProfileSection>
Add(sectionsContainer = new ProfileSectionsContainer
{
RelativeSizeAxes = Axes.Both,
ExpandableHeader = Header = new ProfileHeader(),
FixedHeader = tabs,
HeaderBackground = new Box
@ -141,31 +140,28 @@ namespace osu.Game.Overlays
}
}
private class ProfileTabControl : PageTabControl<ProfileSection>
private class ProfileTabControl : OverlayTabControl<ProfileSection>
{
private readonly Box bottom;
public ProfileTabControl()
{
TabContainer.RelativeSizeAxes &= ~Axes.X;
TabContainer.AutoSizeAxes |= Axes.X;
TabContainer.Anchor |= Anchor.x1;
TabContainer.Origin |= Anchor.x1;
AddInternal(bottom = new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
EdgeSmoothness = new Vector2(1)
});
}
protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileTabItem(value);
protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileTabItem(value)
{
AccentColour = AccentColour
};
protected override Dropdown<ProfileSection> CreateDropdown() => null;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Seafoam;
}
private class ProfileTabItem : PageTabItem
private class ProfileTabItem : OverlayTabItem<ProfileSection>
{
public ProfileTabItem(ProfileSection value)
: base(value)
@ -173,12 +169,22 @@ namespace osu.Game.Overlays
Text.Text = value.Title;
}
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private class ProfileSectionsContainer : SectionsContainer<ProfileSection>
{
public ProfileSectionsContainer()
{
bottom.Colour = colours.Yellow;
RelativeSizeAxes = Axes.Both;
}
protected override FlowContainer<ProfileSection> CreateScrollContentContainer() => new FillFlowContainer<ProfileSection>
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Spacing = new Vector2(0, 20),
};
}
}
}

View File

@ -102,6 +102,10 @@ namespace osu.Game.Overlays
case GlobalAction.DecreaseVolume:
if (State.Value == Visibility.Hidden)
Show();
else if (volumeMeterMusic.IsHovered)
volumeMeterMusic.Decrease(amount, isPrecise);
else if (volumeMeterEffect.IsHovered)
volumeMeterEffect.Decrease(amount, isPrecise);
else
volumeMeterMaster.Decrease(amount, isPrecise);
return true;
@ -109,6 +113,10 @@ namespace osu.Game.Overlays
case GlobalAction.IncreaseVolume:
if (State.Value == Visibility.Hidden)
Show();
else if (volumeMeterMusic.IsHovered)
volumeMeterMusic.Increase(amount, isPrecise);
else if (volumeMeterEffect.IsHovered)
volumeMeterEffect.Increase(amount, isPrecise);
else
volumeMeterMaster.Increase(amount, isPrecise);
return true;

View File

@ -257,7 +257,7 @@ namespace osu.Game.Screens.Multi
private void subScreenChanged(IScreen newScreen)
{
updatePollingRate(isIdle.Value);
createButton.FadeTo(newScreen is MatchSubScreen ? 0 : 1, 200);
createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200);
updateTrack();
}

View File

@ -0,0 +1,28 @@
// 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.Input.Bindings;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
namespace osu.Game.Screens.Play
{
public class HotkeyExitOverlay : HoldToConfirmOverlay, IKeyBindingHandler<GlobalAction>
{
public bool OnPressed(GlobalAction action)
{
if (action != GlobalAction.QuickExit) return false;
BeginConfirm();
return true;
}
public bool OnReleased(GlobalAction action)
{
if (action != GlobalAction.QuickExit) return false;
AbortConfirm();
return true;
}
}
}

View File

@ -177,6 +177,16 @@ namespace osu.Game.Screens.Play
Restart();
},
},
new HotkeyExitOverlay
{
Action = () =>
{
if (!this.IsCurrentScreen()) return;
fadeOut(true);
performUserRequestedExit();
},
},
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }
};
@ -245,6 +255,11 @@ namespace osu.Game.Screens.Play
{
if (!this.IsCurrentScreen()) return;
// if a restart has been requested, cancel any pending completion (user has shown intent to restart).
onCompletionEvent = null;
ValidForResume = false;
this.Exit();
}

View File

@ -15,6 +15,10 @@ namespace osu.Game.Skinning
}
}
/// <summary>
/// A drawable which can be skinned via an <see cref="ISkinSource"/>.
/// </summary>
/// <typeparam name="T">The type of drawable.</typeparam>
public class SkinnableDrawable<T> : SkinReloadableDrawable
where T : Drawable
{
@ -23,48 +27,63 @@ namespace osu.Game.Skinning
/// </summary>
protected Drawable Drawable { get; private set; }
private readonly Func<string, T> createDefault;
private readonly string componentName;
private readonly bool restrictSize;
/// <summary>
///
/// Create a new skinnable drawable.
/// </summary>
/// <param name="name">The namespace-complete resource name for this skinnable element.</param>
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
/// <param name="restrictSize">Whether a user-skin drawable should be limited to the size of our parent.</param>
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
: this(name, allowFallback, restrictSize)
{
createDefault = defaultImplementation;
}
protected SkinnableDrawable(string name, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
: base(allowFallback)
{
componentName = name;
createDefault = defaultImplementation;
this.restrictSize = restrictSize;
RelativeSizeAxes = Axes.Both;
}
private readonly Func<string, T> createDefault;
protected virtual T CreateDefault(string name) => createDefault(name);
/// <summary>
/// Whether to apply size restrictions (specified via <see cref="restrictSize"/>) to the default implementation.
/// </summary>
protected virtual bool ApplySizeRestrictionsToDefault => false;
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
Drawable = skin.GetDrawableComponent(componentName);
bool isDefault = false;
if (Drawable == null && allowFallback)
{
Drawable = CreateDefault(componentName);
isDefault = true;
}
if (Drawable != null)
{
if (restrictSize)
if (restrictSize && (!isDefault || ApplySizeRestrictionsToDefault))
{
Drawable.RelativeSizeAxes = Axes.Both;
Drawable.Size = Vector2.One;
Drawable.Scale = Vector2.One;
Drawable.FillMode = FillMode.Fit;
}
}
else if (allowFallback)
Drawable = createDefault(componentName);
if (Drawable != null)
{
Drawable.Origin = Anchor.Centre;
Drawable.Anchor = Anchor.Centre;

View File

@ -0,0 +1,28 @@
// 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.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Skinning
{
/// <summary>
/// A skinnable element which uses a stable sprite and can therefore share implementation logic.
/// </summary>
public class SkinnableSprite : SkinnableDrawable<Sprite>
{
protected override bool ApplySizeRestrictionsToDefault => true;
[Resolved]
private TextureStore textures { get; set; }
public SkinnableSprite(string name, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
: base(name, allowFallback, restrictSize)
{
}
protected override Sprite CreateDefault(string name) => new Sprite { Texture = textures.Get(name) };
}
}