1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 18:53:21 +08:00

Merge branch 'master' into select-recommended

This commit is contained in:
Dean Herbert 2020-04-11 16:40:07 +09:00 committed by GitHub
commit ff591299e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 620 additions and 337 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
typeof(ManiaRuleset), typeof(ManiaRuleset),
typeof(ManiaLegacySkinTransformer), typeof(ManiaLegacySkinTransformer),
typeof(ManiaSettingsSubsection)
}; };
protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset(); protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset();

View File

@ -0,0 +1,35 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Skinning;
using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
public class TestSceneStageBackground : ManiaSkinnableTestScene
{
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
{
typeof(DefaultStageBackground),
typeof(LegacyStageBackground),
}).ToList();
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
});
}
}
}

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 System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Skinning;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
public class TestSceneStageForeground : ManiaSkinnableTestScene
{
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
{
typeof(LegacyStageForeground),
}).ToList();
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
});
}
}
}

View File

@ -39,6 +39,8 @@ namespace osu.Game.Rulesets.Mania
HoldNoteHead, HoldNoteHead,
HoldNoteTail, HoldNoteTail,
HoldNoteBody, HoldNoteBody,
HitExplosion HitExplosion,
StageBackground,
StageForeground,
} }
} }

View File

@ -0,0 +1,61 @@
// 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.Sprites;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning
{
public class LegacyStageBackground : LegacyManiaElement
{
private Drawable leftSprite;
private Drawable rightSprite;
public LegacyStageBackground()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(ISkinSource skin)
{
string leftImage = GetManiaSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.LeftStageImage)?.Value
?? "mania-stage-left";
string rightImage = GetManiaSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.RightStageImage)?.Value
?? "mania-stage-right";
InternalChildren = new[]
{
leftSprite = new Sprite
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopRight,
X = 0.05f,
Texture = skin.GetTexture(leftImage),
},
rightSprite = new Sprite
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopLeft,
X = -0.05f,
Texture = skin.GetTexture(rightImage)
}
};
}
protected override void Update()
{
base.Update();
if (leftSprite?.Height > 0)
leftSprite.Scale = new Vector2(DrawHeight / leftSprite.Height);
if (rightSprite?.Height > 0)
rightSprite.Scale = new Vector2(DrawHeight / rightSprite.Height);
}
}
}

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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning
{
public class LegacyStageForeground : LegacyManiaElement
{
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Drawable sprite;
public LegacyStageForeground()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
{
string bottomImage = GetManiaSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.BottomStageImage)?.Value
?? "mania-stage-bottom";
sprite = skin.GetAnimation(bottomImage, true, true)?.With(d =>
{
if (d == null)
return;
d.Scale = new Vector2(1.6f);
});
if (sprite != null)
InternalChild = sprite;
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
}
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
if (sprite == null)
return;
if (direction.NewValue == ScrollingDirection.Up)
sprite.Anchor = sprite.Origin = Anchor.TopCentre;
else
sprite.Anchor = sprite.Origin = Anchor.BottomCentre;
}
}
}

View File

@ -81,6 +81,12 @@ namespace osu.Game.Rulesets.Mania.Skinning
case ManiaSkinComponents.HitExplosion: case ManiaSkinComponents.HitExplosion:
return new LegacyHitExplosion(); return new LegacyHitExplosion();
case ManiaSkinComponents.StageBackground:
return new LegacyStageBackground();
case ManiaSkinComponents.StageForeground:
return new LegacyStageForeground();
} }
break; break;

View File

@ -0,0 +1,30 @@
// 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.Framework.Graphics.Shapes;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI.Components
{
public class DefaultStageBackground : CompositeDrawable
{
public DefaultStageBackground()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
};
}
}
}

View File

@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
@ -72,11 +71,9 @@ namespace osu.Game.Rulesets.Mania.UI
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground())
{ {
Name = "Background", RelativeSizeAxes = Axes.Both
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
}, },
columnFlow = new FillFlowContainer<Column> columnFlow = new FillFlowContainer<Column>
{ {
@ -103,6 +100,10 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
} }
}, },
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null)
{
RelativeSizeAxes = Axes.Both
},
judgements = new JudgementContainer<DrawableManiaJudgement> judgements = new JudgementContainer<DrawableManiaJudgement>
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,

View File

@ -15,6 +15,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose;
using osuTK; using osuTK;
using osuTK.Input; using osuTK.Input;
@ -34,6 +35,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private IPlacementHandler placementHandler { get; set; } private IPlacementHandler placementHandler { get; set; }
[Resolved(CanBeNull = true)]
private EditorBeatmap editorBeatmap { get; set; }
public SliderSelectionBlueprint(DrawableSlider slider) public SliderSelectionBlueprint(DrawableSlider slider)
: base(slider) : base(slider)
{ {
@ -162,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private void updatePath() private void updatePath()
{ {
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
UpdateHitObject(); editorBeatmap?.UpdateHitObject(HitObject);
} }
public override MenuItem[] ContextMenuItems => new MenuItem[] public override MenuItem[] ContextMenuItems => new MenuItem[]

View File

@ -1,18 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Internal;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Tests.Visual;
namespace osu.Game.Tests.Beatmaps namespace osu.Game.Tests.Beatmaps
{ {
[TestFixture] [HeadlessTest]
public class EditorBeatmapTest public class TestSceneEditorBeatmap : EditorClockTestScene
{ {
/// <summary> /// <summary>
/// Tests that the addition event is correctly invoked after a hitobject is added. /// Tests that the addition event is correctly invoked after a hitobject is added.
@ -55,13 +58,19 @@ namespace osu.Game.Tests.Beatmaps
public void TestInitialHitObjectStartTimeChangeEvent() public void TestInitialHitObjectStartTimeChangeEvent()
{ {
var hitCircle = new HitCircle(); var hitCircle = new HitCircle();
var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
HitObject changedObject = null; HitObject changedObject = null;
editorBeatmap.StartTimeChanged += h => changedObject = h;
hitCircle.StartTime = 1000; AddStep("add beatmap", () =>
Assert.That(changedObject, Is.EqualTo(hitCircle)); {
EditorBeatmap editorBeatmap;
Child = editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
editorBeatmap.HitObjectUpdated += h => changedObject = h;
});
AddStep("change start time", () => hitCircle.StartTime = 1000);
AddAssert("received change event", () => changedObject == hitCircle);
} }
/// <summary> /// <summary>
@ -71,18 +80,22 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestAddedHitObjectStartTimeChangeEvent() public void TestAddedHitObjectStartTimeChangeEvent()
{ {
var editorBeatmap = new EditorBeatmap(new OsuBeatmap()); EditorBeatmap editorBeatmap = null;
HitObject changedObject = null; HitObject changedObject = null;
editorBeatmap.StartTimeChanged += h => changedObject = h;
AddStep("add beatmap", () =>
{
Child = editorBeatmap = new EditorBeatmap(new OsuBeatmap());
editorBeatmap.HitObjectUpdated += h => changedObject = h;
});
var hitCircle = new HitCircle(); var hitCircle = new HitCircle();
editorBeatmap.Add(hitCircle); AddStep("add object", () => editorBeatmap.Add(hitCircle));
Assert.That(changedObject, Is.Null); AddAssert("event not received", () => changedObject == null);
hitCircle.StartTime = 1000; AddStep("change start time", () => hitCircle.StartTime = 1000);
Assert.That(changedObject, Is.EqualTo(hitCircle)); AddAssert("event received", () => changedObject == hitCircle);
} }
/// <summary> /// <summary>
@ -95,7 +108,7 @@ namespace osu.Game.Tests.Beatmaps
var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } }); var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
HitObject changedObject = null; HitObject changedObject = null;
editorBeatmap.StartTimeChanged += h => changedObject = h; editorBeatmap.HitObjectUpdated += h => changedObject = h;
editorBeatmap.Remove(hitCircle); editorBeatmap.Remove(hitCircle);
Assert.That(changedObject, Is.Null); Assert.That(changedObject, Is.Null);
@ -150,5 +163,69 @@ namespace osu.Game.Tests.Beatmaps
Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1)); Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1));
Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(1)); Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(1));
} }
/// <summary>
/// Tests that multiple hitobjects are updated simultaneously.
/// </summary>
[Test]
public void TestMultipleHitObjectUpdate()
{
var updatedObjects = new List<HitObject>();
var allHitObjects = new List<HitObject>();
EditorBeatmap editorBeatmap = null;
AddStep("add beatmap", () =>
{
updatedObjects.Clear();
Child = editorBeatmap = new EditorBeatmap(new OsuBeatmap());
for (int i = 0; i < 10; i++)
{
var h = new HitCircle();
editorBeatmap.Add(h);
allHitObjects.Add(h);
}
});
AddStep("change all start times", () =>
{
editorBeatmap.HitObjectUpdated += h => updatedObjects.Add(h);
for (int i = 0; i < 10; i++)
allHitObjects[i].StartTime += 10;
});
// Distinct ensures that all hitobjects have been updated once, debounce is tested below.
AddAssert("all hitobjects updated", () => updatedObjects.Distinct().Count() == 10);
}
/// <summary>
/// Tests that hitobject updates are debounced when they happen too soon.
/// </summary>
[Test]
public void TestDebouncedUpdate()
{
var updatedObjects = new List<HitObject>();
EditorBeatmap editorBeatmap = null;
AddStep("add beatmap", () =>
{
updatedObjects.Clear();
Child = editorBeatmap = new EditorBeatmap(new OsuBeatmap());
editorBeatmap.Add(new HitCircle());
});
AddStep("change start time twice", () =>
{
editorBeatmap.HitObjectUpdated += h => updatedObjects.Add(h);
editorBeatmap.HitObjects[0].StartTime = 10;
editorBeatmap.HitObjects[0].StartTime = 20;
});
AddAssert("only updated once", () => updatedObjects.Count == 1);
}
} }
} }

View File

@ -8,7 +8,6 @@ using System.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osuTK.Graphics; using osuTK.Graphics;
@ -100,21 +99,21 @@ namespace osu.Game.Tests.Visual.UserInterface
private class TestNoBackgroundHeader : OverlayHeader private class TestNoBackgroundHeader : OverlayHeader
{ {
protected override ScreenTitle CreateTitle() => new TestTitle(); protected override OverlayTitle CreateTitle() => new TestTitle();
} }
private class TestNoControlHeader : OverlayHeader private class TestNoControlHeader : OverlayHeader
{ {
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog"); protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog");
protected override ScreenTitle CreateTitle() => new TestTitle(); protected override OverlayTitle CreateTitle() => new TestTitle();
} }
private class TestStringTabControlHeader : TabControlOverlayHeader<string> private class TestStringTabControlHeader : TabControlOverlayHeader<string>
{ {
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news"); protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
protected override ScreenTitle CreateTitle() => new TestTitle(); protected override OverlayTitle CreateTitle() => new TestTitle();
protected override Drawable CreateTitleContent() => new OverlayRulesetSelector(); protected override Drawable CreateTitleContent() => new OverlayRulesetSelector();
@ -129,7 +128,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings"); protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings");
protected override ScreenTitle CreateTitle() => new TestTitle(); protected override OverlayTitle CreateTitle() => new TestTitle();
} }
private enum TestEnum private enum TestEnum
@ -141,7 +140,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private class TestBreadcrumbControlHeader : BreadcrumbControlOverlayHeader private class TestBreadcrumbControlHeader : BreadcrumbControlOverlayHeader
{ {
protected override ScreenTitle CreateTitle() => new TestTitle(); protected override OverlayTitle CreateTitle() => new TestTitle();
public TestBreadcrumbControlHeader() public TestBreadcrumbControlHeader()
{ {
@ -151,15 +150,13 @@ namespace osu.Game.Tests.Visual.UserInterface
} }
} }
private class TestTitle : ScreenTitle private class TestTitle : OverlayTitle
{ {
public TestTitle() public TestTitle()
{ {
Title = "title"; Title = "title";
Section = "section"; IconTexture = "Icons/changelog";
} }
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
} }
} }
} }

View File

@ -111,7 +111,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}")); writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}"));
writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}")); writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}"));
writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID ?? 0}")); writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID ?? 0}"));
writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID ?? -1}")); writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID ?? -1}"));
} }
private void handleDifficulty(TextWriter writer) private void handleDifficulty(TextWriter writer)

View File

@ -113,13 +113,13 @@ namespace osu.Game.Graphics.UserInterface
private const float transition_length = 500; private const float transition_length = 500;
private void fadeActive() protected void FadeHovered()
{ {
Bar.FadeIn(transition_length, Easing.OutQuint); Bar.FadeIn(transition_length, Easing.OutQuint);
Text.FadeColour(Color4.White, transition_length, Easing.OutQuint); Text.FadeColour(Color4.White, transition_length, Easing.OutQuint);
} }
private void fadeInactive() protected void FadeUnhovered()
{ {
Bar.FadeOut(transition_length, Easing.OutQuint); Bar.FadeOut(transition_length, Easing.OutQuint);
Text.FadeColour(AccentColour, transition_length, Easing.OutQuint); Text.FadeColour(AccentColour, transition_length, Easing.OutQuint);
@ -128,14 +128,14 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
if (!Active.Value) if (!Active.Value)
fadeActive(); FadeHovered();
return true; return true;
} }
protected override void OnHoverLost(HoverLostEvent e) protected override void OnHoverLost(HoverLostEvent e)
{ {
if (!Active.Value) if (!Active.Value)
fadeInactive(); FadeUnhovered();
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -172,13 +172,19 @@ namespace osu.Game.Graphics.UserInterface
}, },
new HoverClickSounds() new HoverClickSounds()
}; };
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Torus, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
} }
protected override void OnActivated() => fadeActive(); protected override void OnActivated()
{
Text.Font = Text.Font.With(weight: FontWeight.Bold);
FadeHovered();
}
protected override void OnDeactivated() => fadeInactive(); protected override void OnDeactivated()
{
Text.Font = Text.Font.With(weight: FontWeight.Medium);
FadeUnhovered();
}
} }
} }
} }

View File

@ -1,102 +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 osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public abstract class ScreenTitle : CompositeDrawable, IHasAccentColour
{
public const float ICON_WIDTH = ICON_SIZE + spacing;
public const float ICON_SIZE = 25;
private const float spacing = 6;
private const int text_offset = 2;
private SpriteIcon iconSprite;
private readonly OsuSpriteText titleText, pageText;
protected IconUsage Icon
{
set
{
if (iconSprite == null)
throw new InvalidOperationException($"Cannot use {nameof(Icon)} with a custom {nameof(CreateIcon)} function.");
iconSprite.Icon = value;
}
}
protected string Title
{
set => titleText.Text = value;
}
protected string Section
{
set => pageText.Text = value;
}
public Color4 AccentColour
{
get => pageText.Colour;
set => pageText.Colour = value;
}
protected virtual Drawable CreateIcon() => iconSprite = new SpriteIcon
{
Size = new Vector2(ICON_SIZE),
};
protected ScreenTitle()
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(spacing, 0),
Direction = FillDirection.Horizontal,
Children = new[]
{
CreateIcon().With(t =>
{
t.Anchor = Anchor.Centre;
t.Origin = Anchor.Centre;
}),
titleText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
Margin = new MarginPadding { Bottom = text_offset }
},
new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(4),
Colour = Color4.Gray,
},
pageText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20),
Margin = new MarginPadding { Bottom = text_offset }
}
}
},
};
}
}
}

View File

@ -1,40 +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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osuTK;
namespace osu.Game.Graphics.UserInterface
{
/// <summary>
/// A custom icon class for use with <see cref="ScreenTitle.CreateIcon()"/> based off a texture resource.
/// </summary>
public class ScreenTitleTextureIcon : CompositeDrawable
{
private readonly string textureName;
public ScreenTitleTextureIcon(string textureName)
{
this.textureName = textureName;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Size = new Vector2(ScreenTitle.ICON_SIZE);
InternalChild = new Sprite
{
RelativeSizeAxes = Axes.Both,
Texture = textures.Get(textureName),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit
};
}
}
}

View File

@ -1,24 +1,19 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // 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.BeatmapListing namespace osu.Game.Overlays.BeatmapListing
{ {
public class BeatmapListingHeader : OverlayHeader public class BeatmapListingHeader : OverlayHeader
{ {
protected override ScreenTitle CreateTitle() => new BeatmapListingTitle(); protected override OverlayTitle CreateTitle() => new BeatmapListingTitle();
private class BeatmapListingTitle : ScreenTitle private class BeatmapListingTitle : OverlayTitle
{ {
public BeatmapListingTitle() public BeatmapListingTitle()
{ {
Title = @"beatmap"; Title = "beatmap listing";
Section = @"listing"; IconTexture = "Icons/changelog";
} }
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
} }
} }
} }

View File

@ -3,7 +3,6 @@
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets; using osu.Game.Rulesets;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet
@ -14,22 +13,20 @@ namespace osu.Game.Overlays.BeatmapSet
public BeatmapRulesetSelector RulesetSelector { get; private set; } public BeatmapRulesetSelector RulesetSelector { get; private set; }
protected override ScreenTitle CreateTitle() => new BeatmapHeaderTitle(); protected override OverlayTitle CreateTitle() => new BeatmapHeaderTitle();
protected override Drawable CreateTitleContent() => RulesetSelector = new BeatmapRulesetSelector protected override Drawable CreateTitleContent() => RulesetSelector = new BeatmapRulesetSelector
{ {
Current = Ruleset Current = Ruleset
}; };
private class BeatmapHeaderTitle : ScreenTitle private class BeatmapHeaderTitle : OverlayTitle
{ {
public BeatmapHeaderTitle() public BeatmapHeaderTitle()
{ {
Title = @"beatmap"; Title = "beatmap info";
Section = @"info"; IconTexture = "Icons/changelog";
} }
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
} }
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -16,6 +17,13 @@ namespace osu.Game.Overlays
public OverlayHeaderBreadcrumbControl() public OverlayHeaderBreadcrumbControl()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 47;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
AccentColour = colourProvider.Light2;
} }
protected override TabItem<string> CreateTabItem(string value) => new ControlTabItem(value); protected override TabItem<string> CreateTabItem(string value) => new ControlTabItem(value);
@ -27,10 +35,18 @@ namespace osu.Game.Overlays
public ControlTabItem(string value) public ControlTabItem(string value)
: base(value) : base(value)
{ {
RelativeSizeAxes = Axes.Y;
Text.Font = Text.Font.With(size: 14); Text.Font = Text.Font.With(size: 14);
Chevron.Y = 3; Text.Anchor = Anchor.CentreLeft;
Text.Origin = Anchor.CentreLeft;
Chevron.Y = 1;
Bar.Height = 0; Bar.Height = 0;
} }
// base OsuTabItem makes font bold on activation, we don't want that here
protected override void OnActivated() => FadeHovered();
protected override void OnDeactivated() => FadeUnhovered();
} }
} }
} }

View File

@ -9,7 +9,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Overlays.Changelog namespace osu.Game.Overlays.Changelog
@ -50,8 +49,6 @@ namespace osu.Game.Overlays.Changelog
streamsBackground.Colour = colourProvider.Background5; streamsBackground.Colour = colourProvider.Background5;
} }
private ChangelogHeaderTitle title;
private void showBuild(ValueChangedEvent<APIChangelogBuild> e) private void showBuild(ValueChangedEvent<APIChangelogBuild> e)
{ {
if (e.OldValue != null) if (e.OldValue != null)
@ -63,14 +60,11 @@ namespace osu.Game.Overlays.Changelog
Current.Value = e.NewValue.ToString(); Current.Value = e.NewValue.ToString();
updateCurrentStream(); updateCurrentStream();
title.Version = e.NewValue.UpdateStream.DisplayName;
} }
else else
{ {
Current.Value = listing_string; Current.Value = listing_string;
Streams.Current.Value = null; Streams.Current.Value = null;
title.Version = null;
} }
} }
@ -100,7 +94,7 @@ namespace osu.Game.Overlays.Changelog
} }
}; };
protected override ScreenTitle CreateTitle() => title = new ChangelogHeaderTitle(); protected override OverlayTitle CreateTitle() => new ChangelogHeaderTitle();
public void Populate(List<APIUpdateStream> streams) public void Populate(List<APIUpdateStream> streams)
{ {
@ -116,20 +110,13 @@ namespace osu.Game.Overlays.Changelog
Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == Build.Value.UpdateStream.Name); Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == Build.Value.UpdateStream.Name);
} }
private class ChangelogHeaderTitle : ScreenTitle private class ChangelogHeaderTitle : OverlayTitle
{ {
public string Version
{
set => Section = value ?? listing_string;
}
public ChangelogHeaderTitle() public ChangelogHeaderTitle()
{ {
Title = "changelog"; Title = "changelog";
Version = null; IconTexture = "Icons/changelog";
} }
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
} }
} }
} }

View File

@ -3,7 +3,6 @@
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using System; using System;
namespace osu.Game.Overlays.News namespace osu.Game.Overlays.News
@ -12,8 +11,6 @@ namespace osu.Game.Overlays.News
{ {
private const string front_page_string = "frontpage"; private const string front_page_string = "frontpage";
private NewsHeaderTitle title;
public readonly Bindable<string> Post = new Bindable<string>(null); public readonly Bindable<string> Post = new Bindable<string>(null);
public Action ShowFrontPage; public Action ShowFrontPage;
@ -40,36 +37,24 @@ namespace osu.Game.Overlays.News
{ {
TabControl.AddItem(e.NewValue); TabControl.AddItem(e.NewValue);
Current.Value = e.NewValue; Current.Value = e.NewValue;
title.IsReadingPost = true;
} }
else else
{ {
Current.Value = front_page_string; Current.Value = front_page_string;
title.IsReadingPost = false;
} }
} }
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news"); protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
protected override ScreenTitle CreateTitle() => title = new NewsHeaderTitle(); protected override OverlayTitle CreateTitle() => new NewsHeaderTitle();
private class NewsHeaderTitle : ScreenTitle private class NewsHeaderTitle : OverlayTitle
{ {
private const string post_string = "post";
public bool IsReadingPost
{
set => Section = value ? post_string : front_page_string;
}
public NewsHeaderTitle() public NewsHeaderTitle()
{ {
Title = "news"; Title = "news";
IsReadingPost = false; IconTexture = "Icons/news";
} }
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/news");
} }
} }
} }

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Overlays namespace osu.Game.Overlays
@ -14,7 +13,6 @@ namespace osu.Game.Overlays
public abstract class OverlayHeader : Container public abstract class OverlayHeader : Container
{ {
private readonly Box titleBackground; private readonly Box titleBackground;
private readonly ScreenTitle title;
protected readonly FillFlowContainer HeaderInfo; protected readonly FillFlowContainer HeaderInfo;
@ -57,11 +55,10 @@ namespace osu.Game.Overlays
Padding = new MarginPadding Padding = new MarginPadding
{ {
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Vertical = 10,
}, },
Children = new[] Children = new[]
{ {
title = CreateTitle().With(title => CreateTitle().With(title =>
{ {
title.Anchor = Anchor.CentreLeft; title.Anchor = Anchor.CentreLeft;
title.Origin = Anchor.CentreLeft; title.Origin = Anchor.CentreLeft;
@ -86,7 +83,6 @@ namespace osu.Game.Overlays
private void load(OverlayColourProvider colourProvider) private void load(OverlayColourProvider colourProvider)
{ {
titleBackground.Colour = colourProvider.Dark5; titleBackground.Colour = colourProvider.Dark5;
title.AccentColour = colourProvider.Highlight1;
} }
[NotNull] [NotNull]
@ -96,11 +92,11 @@ namespace osu.Game.Overlays
protected virtual Drawable CreateBackground() => Empty(); protected virtual Drawable CreateBackground() => Empty();
/// <summary> /// <summary>
/// Creates a <see cref="Drawable"/> on the opposite side of the <see cref="ScreenTitle"/>. Used mostly to create <see cref="OverlayRulesetSelector"/>. /// Creates a <see cref="Drawable"/> on the opposite side of the <see cref="OverlayTitle"/>. Used mostly to create <see cref="OverlayRulesetSelector"/>.
/// </summary> /// </summary>
[NotNull] [NotNull]
protected virtual Drawable CreateTitleContent() => Empty(); protected virtual Drawable CreateTitleContent() => Empty();
protected abstract ScreenTitle CreateTitle(); protected abstract OverlayTitle CreateTitle();
} }
} }

View File

@ -22,7 +22,7 @@ namespace osu.Game.Overlays
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Spacing = new Vector2(25, 0), Spacing = new Vector2(20, 0),
}; };
} }
} }

View File

@ -12,6 +12,7 @@ using osu.Game.Rulesets;
using osuTK.Graphics; using osuTK.Graphics;
using osuTK; using osuTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
@ -53,6 +54,8 @@ namespace osu.Game.Overlays
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Text = value.Name, Text = value.Name,
Font = OsuFont.GetFont(size: 14),
ShadowColour = Color4.Black.Opacity(0.75f)
} }
}, },
new HoverClickSounds() new HoverClickSounds()

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
@ -35,17 +36,22 @@ namespace osu.Game.Overlays
protected OverlayTabControl() protected OverlayTabControl()
{ {
TabContainer.Masking = false; TabContainer.Masking = false;
TabContainer.Spacing = new Vector2(15, 0); TabContainer.Spacing = new Vector2(20, 0);
AddInternal(bar = new Box AddInternal(bar = new Box
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 2,
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.CentreLeft Origin = Anchor.BottomLeft
}); });
} }
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
AccentColour = colourProvider.Highlight1;
}
protected override Dropdown<T> CreateDropdown() => null; protected override Dropdown<T> CreateDropdown() => null;
protected override TabItem<T> CreateTabItem(T value) => new OverlayTabItem(value); protected override TabItem<T> CreateTabItem(T value) => new OverlayTabItem(value);
@ -90,7 +96,7 @@ namespace osu.Game.Overlays
Bar = new ExpandingBar Bar = new ExpandingBar
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
ExpandedSize = 7.5f, ExpandedSize = 5f,
CollapsedSize = 0 CollapsedSize = 0
}, },
new HoverClickSounds() new HoverClickSounds()
@ -119,6 +125,7 @@ namespace osu.Game.Overlays
{ {
HoverAction(); HoverAction();
Text.Font = Text.Font.With(weight: FontWeight.Bold); Text.Font = Text.Font.With(weight: FontWeight.Bold);
Text.FadeColour(Color4.White, 120, Easing.InQuad);
} }
protected override void OnDeactivated() protected override void OnDeactivated()
@ -135,11 +142,7 @@ namespace osu.Game.Overlays
OnDeactivated(); OnDeactivated();
} }
protected virtual void HoverAction() protected virtual void HoverAction() => Bar.Expand();
{
Bar.Expand();
Text.FadeColour(Color4.White, 120, Easing.InQuad);
}
protected virtual void UnhoverAction() protected virtual void UnhoverAction()
{ {

View File

@ -0,0 +1,80 @@
// 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.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK;
namespace osu.Game.Overlays
{
public abstract class OverlayTitle : CompositeDrawable
{
private readonly OsuSpriteText title;
private readonly Container icon;
protected string Title
{
set => title.Text = value;
}
protected string IconTexture
{
set => icon.Child = new OverlayTitleIcon(value);
}
protected OverlayTitle()
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(10, 0),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
icon = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = 5 }, // compensates for osu-web sprites having around 5px of whitespace on each side
Size = new Vector2(30)
},
title = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular),
Margin = new MarginPadding { Vertical = 17.5f } // 15px padding + 2.5px line-height difference compensation
}
}
};
}
private class OverlayTitleIcon : Sprite
{
private readonly string textureName;
public OverlayTitleIcon(string textureName)
{
this.textureName = textureName;
RelativeSizeAxes = Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
FillMode = FillMode.Fit;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(textureName);
}
}
}
}

View File

@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Profile.Header; using osu.Game.Overlays.Profile.Header;
using osu.Game.Users; using osu.Game.Users;
@ -87,19 +86,17 @@ namespace osu.Game.Overlays.Profile
} }
}; };
protected override ScreenTitle CreateTitle() => new ProfileHeaderTitle(); protected override OverlayTitle CreateTitle() => new ProfileHeaderTitle();
private void updateDisplay(User user) => coverContainer.User = user; private void updateDisplay(User user) => coverContainer.User = user;
private class ProfileHeaderTitle : ScreenTitle private class ProfileHeaderTitle : OverlayTitle
{ {
public ProfileHeaderTitle() public ProfileHeaderTitle()
{ {
Title = "player"; Title = "player info";
Section = "info"; IconTexture = "Icons/profile";
} }
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/profile");
} }
} }
} }

View File

@ -3,7 +3,6 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Users; using osu.Game.Users;
@ -18,33 +17,21 @@ namespace osu.Game.Overlays.Rankings
private OverlayRulesetSelector rulesetSelector; private OverlayRulesetSelector rulesetSelector;
private CountryFilter countryFilter; private CountryFilter countryFilter;
protected override ScreenTitle CreateTitle() => new RankingsTitle protected override OverlayTitle CreateTitle() => new RankingsTitle();
{
Scope = { BindTarget = Current }
};
protected override Drawable CreateTitleContent() => rulesetSelector = new OverlayRulesetSelector(); protected override Drawable CreateTitleContent() => rulesetSelector = new OverlayRulesetSelector();
protected override Drawable CreateContent() => countryFilter = new CountryFilter(); protected override Drawable CreateContent() => countryFilter = new CountryFilter();
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings"); protected override Drawable CreateBackground() => new OverlayHeaderBackground("Headers/rankings");
private class RankingsTitle : ScreenTitle private class RankingsTitle : OverlayTitle
{ {
public readonly Bindable<RankingsScope> Scope = new Bindable<RankingsScope>();
public RankingsTitle() public RankingsTitle()
{ {
Title = "ranking"; Title = "ranking";
IconTexture = "Icons/rankings";
} }
protected override void LoadComplete()
{
base.LoadComplete();
Scope.BindValueChanged(scope => Section = scope.NewValue.ToString().ToLowerInvariant(), true);
}
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/rankings");
} }
} }

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
@ -22,6 +21,7 @@ namespace osu.Game.Overlays
{ {
protected OsuTabControl<T> TabControl; protected OsuTabControl<T> TabControl;
private readonly Box controlBackground;
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>(); private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
public Bindable<T> Current public Bindable<T> Current
@ -30,8 +30,6 @@ namespace osu.Game.Overlays
set => current.Current = value; set => current.Current = value;
} }
private readonly Box controlBackground;
protected TabControlOverlayHeader() protected TabControlOverlayHeader()
{ {
HeaderInfo.Add(new Container HeaderInfo.Add(new Container
@ -56,7 +54,6 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider) private void load(OverlayColourProvider colourProvider)
{ {
TabControl.AccentColour = colourProvider.Highlight1;
controlBackground.Colour = colourProvider.Dark4; controlBackground.Colour = colourProvider.Dark4;
} }
@ -65,14 +62,16 @@ namespace osu.Game.Overlays
public class OverlayHeaderTabControl : OverlayTabControl<T> public class OverlayHeaderTabControl : OverlayTabControl<T>
{ {
private const float bar_height = 1;
public OverlayHeaderTabControl() public OverlayHeaderTabControl()
{ {
BarHeight = 1;
RelativeSizeAxes = Axes.None; RelativeSizeAxes = Axes.None;
AutoSizeAxes = Axes.X; AutoSizeAxes = Axes.X;
Anchor = Anchor.BottomLeft; Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft; Origin = Anchor.BottomLeft;
Height = 35; Height = 47;
BarHeight = bar_height;
} }
protected override TabItem<T> CreateTabItem(T value) => new OverlayHeaderTabItem(value); protected override TabItem<T> CreateTabItem(T value) => new OverlayHeaderTabItem(value);
@ -82,7 +81,6 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
}; };
private class OverlayHeaderTabItem : OverlayTabItem private class OverlayHeaderTabItem : OverlayTabItem
@ -92,7 +90,8 @@ namespace osu.Game.Overlays
{ {
Text.Text = value.ToString().ToLower(); Text.Text = value.ToString().ToLower();
Text.Font = OsuFont.GetFont(size: 14); Text.Font = OsuFont.GetFont(size: 14);
Bar.ExpandedSize = 5; Text.Margin = new MarginPadding { Vertical = 16.5f }; // 15px padding + 1.5px line-height difference compensation
Bar.Margin = new MarginPadding { Bottom = bar_height };
} }
} }
} }

View File

@ -24,7 +24,7 @@ namespace osu.Game.Overlays
private GetUserRequest userReq; private GetUserRequest userReq;
protected ProfileHeader Header; protected ProfileHeader Header;
private ProfileSectionsContainer sectionsContainer; private ProfileSectionsContainer sectionsContainer;
private ProfileTabControl tabs; private ProfileSectionTabControl tabs;
public const float CONTENT_X_MARGIN = 70; public const float CONTENT_X_MARGIN = 70;
@ -62,12 +62,11 @@ namespace osu.Game.Overlays
} }
: Array.Empty<ProfileSection>(); : Array.Empty<ProfileSection>();
tabs = new ProfileTabControl tabs = new ProfileSectionTabControl
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Height = 34
}; };
Add(new Box Add(new Box
@ -149,19 +148,24 @@ namespace osu.Game.Overlays
} }
} }
private class ProfileTabControl : OverlayTabControl<ProfileSection> private class ProfileSectionTabControl : OverlayTabControl<ProfileSection>
{ {
public ProfileTabControl() private const float bar_height = 2;
public ProfileSectionTabControl()
{ {
TabContainer.RelativeSizeAxes &= ~Axes.X; TabContainer.RelativeSizeAxes &= ~Axes.X;
TabContainer.AutoSizeAxes |= Axes.X; TabContainer.AutoSizeAxes |= Axes.X;
TabContainer.Anchor |= Anchor.x1; TabContainer.Anchor |= Anchor.x1;
TabContainer.Origin |= Anchor.x1; TabContainer.Origin |= Anchor.x1;
Height = 36 + bar_height;
BarHeight = bar_height;
} }
protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileTabItem(value) protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileSectionTabItem(value)
{ {
AccentColour = AccentColour AccentColour = AccentColour,
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -170,12 +174,16 @@ namespace osu.Game.Overlays
AccentColour = colourProvider.Highlight1; AccentColour = colourProvider.Highlight1;
} }
private class ProfileTabItem : OverlayTabItem private class ProfileSectionTabItem : OverlayTabItem
{ {
public ProfileTabItem(ProfileSection value) public ProfileSectionTabItem(ProfileSection value)
: base(value) : base(value)
{ {
Text.Text = value.Title; Text.Text = value.Title;
Text.Font = Text.Font.With(size: 16);
Text.Margin = new MarginPadding { Bottom = 10 + bar_height };
Bar.ExpandedSize = 10;
Bar.Margin = new MarginPadding { Bottom = bar_height };
} }
} }
} }

View File

@ -69,10 +69,6 @@ namespace osu.Game.Rulesets.Edit
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(IFrameBasedClock framedClock) private void load(IFrameBasedClock framedClock)
{ {
EditorBeatmap.HitObjectAdded += addHitObject;
EditorBeatmap.HitObjectRemoved += removeHitObject;
EditorBeatmap.StartTimeChanged += UpdateHitObject;
Config = Dependencies.Get<RulesetConfigCache>().GetConfigFor(Ruleset); Config = Dependencies.Get<RulesetConfigCache>().GetConfigFor(Ruleset);
try try
@ -236,10 +232,6 @@ namespace osu.Game.Rulesets.Edit
lastGridUpdateTime = EditorClock.CurrentTime; lastGridUpdateTime = EditorClock.CurrentTime;
} }
private void addHitObject(HitObject hitObject) => UpdateHitObject(hitObject);
private void removeHitObject(HitObject hitObject) => UpdateHitObject(null);
public override IEnumerable<DrawableHitObject> HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; public override IEnumerable<DrawableHitObject> HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects;
public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position); public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
@ -302,19 +294,6 @@ namespace osu.Game.Rulesets.Edit
return DurationToDistance(referenceTime, snappedEndTime - referenceTime); return DurationToDistance(referenceTime, snappedEndTime - referenceTime);
} }
public override void UpdateHitObject(HitObject hitObject) => EditorBeatmap.UpdateHitObject(hitObject);
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (EditorBeatmap != null)
{
EditorBeatmap.HitObjectAdded -= addHitObject;
EditorBeatmap.HitObjectRemoved -= removeHitObject;
}
}
} }
[Cached(typeof(HitObjectComposer))] [Cached(typeof(HitObjectComposer))]
@ -344,12 +323,6 @@ namespace osu.Game.Rulesets.Edit
[CanBeNull] [CanBeNull]
protected virtual DistanceSnapGrid CreateDistanceSnapGrid([NotNull] IEnumerable<HitObject> selectedHitObjects) => null; protected virtual DistanceSnapGrid CreateDistanceSnapGrid([NotNull] IEnumerable<HitObject> selectedHitObjects) => null;
/// <summary>
/// Updates a <see cref="HitObject"/>, invoking <see cref="HitObject.ApplyDefaults"/> and re-processing the beatmap.
/// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to update.</param>
public abstract void UpdateHitObject([CanBeNull] HitObject hitObject);
public abstract (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time); public abstract (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time);
public abstract float GetBeatSnapDistanceAt(double referenceTime); public abstract float GetBeatSnapDistanceAt(double referenceTime);

View File

@ -108,11 +108,6 @@ namespace osu.Game.Rulesets.Edit
public bool IsSelected => State == SelectionState.Selected; public bool IsSelected => State == SelectionState.Selected;
/// <summary>
/// Updates the <see cref="Objects.HitObject"/>, invoking <see cref="Objects.HitObject.ApplyDefaults"/> and re-processing the beatmap.
/// </summary>
protected void UpdateHitObject() => composer?.UpdateHitObject(HitObject);
/// <summary> /// <summary>
/// The <see cref="MenuItem"/>s to be displayed in the context menu for this <see cref="OverlaySelectionBlueprint"/>. /// The <see cref="MenuItem"/>s to be displayed in the context menu for this <see cref="OverlaySelectionBlueprint"/>.
/// </summary> /// </summary>

View File

@ -60,8 +60,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
waveform.Waveform = b.NewValue.Waveform; waveform.Waveform = b.NewValue.Waveform;
track = b.NewValue.Track; track = b.NewValue.Track;
MinZoom = getZoomLevelForVisibleMilliseconds(10000);
MaxZoom = getZoomLevelForVisibleMilliseconds(500); MaxZoom = getZoomLevelForVisibleMilliseconds(500);
MinZoom = getZoomLevelForVisibleMilliseconds(10000);
Zoom = getZoomLevelForVisibleMilliseconds(2000); Zoom = getZoomLevelForVisibleMilliseconds(2000);
}, true); }, true);
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -29,9 +30,9 @@ namespace osu.Game.Screens.Edit
public event Action<HitObject> HitObjectRemoved; public event Action<HitObject> HitObjectRemoved;
/// <summary> /// <summary>
/// Invoked when the start time of a <see cref="HitObject"/> in this <see cref="EditorBeatmap"/> was changed. /// Invoked when a <see cref="HitObject"/> is updated.
/// </summary> /// </summary>
public event Action<HitObject> StartTimeChanged; public event Action<HitObject> HitObjectUpdated;
/// <summary> /// <summary>
/// All currently selected <see cref="HitObject"/>s. /// All currently selected <see cref="HitObject"/>s.
@ -62,21 +63,39 @@ namespace osu.Game.Screens.Edit
trackStartTime(obj); trackStartTime(obj);
} }
private readonly HashSet<HitObject> pendingUpdates = new HashSet<HitObject>();
private ScheduledDelegate scheduledUpdate; private ScheduledDelegate scheduledUpdate;
/// <summary> /// <summary>
/// Updates a <see cref="HitObject"/>, invoking <see cref="HitObject.ApplyDefaults"/> and re-processing the beatmap. /// Updates a <see cref="HitObject"/>, invoking <see cref="HitObject.ApplyDefaults"/> and re-processing the beatmap.
/// </summary> /// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to update.</param> /// <param name="hitObject">The <see cref="HitObject"/> to update.</param>
public void UpdateHitObject(HitObject hitObject) public void UpdateHitObject([NotNull] HitObject hitObject) => updateHitObject(hitObject, false);
private void updateHitObject([CanBeNull] HitObject hitObject, bool silent)
{ {
scheduledUpdate?.Cancel(); scheduledUpdate?.Cancel();
scheduledUpdate = Scheduler.AddDelayed(() =>
if (hitObject != null)
pendingUpdates.Add(hitObject);
scheduledUpdate = Schedule(() =>
{ {
beatmapProcessor?.PreProcess(); beatmapProcessor?.PreProcess();
hitObject?.ApplyDefaults(ControlPointInfo, BeatmapInfo.BaseDifficulty);
foreach (var obj in pendingUpdates)
obj.ApplyDefaults(ControlPointInfo, BeatmapInfo.BaseDifficulty);
beatmapProcessor?.PostProcess(); beatmapProcessor?.PostProcess();
}, 0);
if (!silent)
{
foreach (var obj in pendingUpdates)
HitObjectUpdated?.Invoke(obj);
}
pendingUpdates.Clear();
});
} }
public BeatmapInfo BeatmapInfo public BeatmapInfo BeatmapInfo
@ -114,6 +133,8 @@ namespace osu.Game.Screens.Edit
mutableHitObjects.Insert(insertionIndex + 1, hitObject); mutableHitObjects.Insert(insertionIndex + 1, hitObject);
HitObjectAdded?.Invoke(hitObject); HitObjectAdded?.Invoke(hitObject);
updateHitObject(hitObject, true);
} }
/// <summary> /// <summary>
@ -132,6 +153,8 @@ namespace osu.Game.Screens.Edit
startTimeBindables.Remove(hitObject); startTimeBindables.Remove(hitObject);
HitObjectRemoved?.Invoke(hitObject); HitObjectRemoved?.Invoke(hitObject);
updateHitObject(null, true);
} }
private void trackStartTime(HitObject hitObject) private void trackStartTime(HitObject hitObject)
@ -145,7 +168,7 @@ namespace osu.Game.Screens.Edit
var insertionIndex = findInsertionIndex(PlayableBeatmap.HitObjects, hitObject.StartTime); var insertionIndex = findInsertionIndex(PlayableBeatmap.HitObjects, hitObject.StartTime);
mutableHitObjects.Insert(insertionIndex + 1, hitObject); mutableHitObjects.Insert(insertionIndex + 1, hitObject);
StartTimeChanged?.Invoke(hitObject); UpdateHitObject(hitObject);
}; };
} }

View File

@ -6,10 +6,13 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.SearchableList; using osu.Game.Overlays.SearchableList;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Screens.Multi namespace osu.Game.Screens.Multi
@ -43,7 +46,7 @@ namespace osu.Game.Screens.Multi
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
X = -ScreenTitle.ICON_WIDTH, X = -MultiHeaderTitle.ICON_WIDTH,
}, },
breadcrumbs = new HeaderBreadcrumbControl(stack) breadcrumbs = new HeaderBreadcrumbControl(stack)
{ {
@ -70,18 +73,78 @@ namespace osu.Game.Screens.Multi
breadcrumbs.StripColour = colours.Green; breadcrumbs.StripColour = colours.Green;
} }
private class MultiHeaderTitle : ScreenTitle private class MultiHeaderTitle : CompositeDrawable, IHasAccentColour
{ {
public const float ICON_WIDTH = icon_size + spacing;
private const float icon_size = 25;
private const float spacing = 6;
private const int text_offset = 2;
private readonly SpriteIcon iconSprite;
private readonly OsuSpriteText title, pageText;
public IMultiplayerSubScreen Screen public IMultiplayerSubScreen Screen
{ {
set => Section = value.ShortTitle.ToLowerInvariant(); set => pageText.Text = value.ShortTitle.ToLowerInvariant();
}
public Color4 AccentColour
{
get => pageText.Colour;
set => pageText.Colour = value;
}
public MultiHeaderTitle()
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(spacing, 0),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
iconSprite = new SpriteIcon
{
Size = new Vector2(icon_size),
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
title = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
Margin = new MarginPadding { Bottom = text_offset }
},
new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(4),
Colour = Color4.Gray,
},
pageText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20),
Margin = new MarginPadding { Bottom = text_offset }
}
}
},
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Title = "multi"; title.Text = "multi";
Icon = OsuIcon.Multi; iconSprite.Icon = OsuIcon.Multi;
AccentColour = colours.Yellow; AccentColour = colours.Yellow;
} }
} }

View File

@ -40,6 +40,9 @@ namespace osu.Game.Skinning
JudgementLineColour, JudgementLineColour,
ColumnBackgroundColour, ColumnBackgroundColour,
ColumnLightColour, ColumnLightColour,
MinimumColumnWidth MinimumColumnWidth,
LeftStageImage,
RightStageImage,
BottomStageImage
} }
} }

View File

@ -243,6 +243,12 @@ namespace osu.Game.Skinning
case LegacyManiaSkinConfigurationLookups.KeyImageDown: case LegacyManiaSkinConfigurationLookups.KeyImageDown:
Debug.Assert(maniaLookup.TargetColumn != null); Debug.Assert(maniaLookup.TargetColumn != null);
return SkinUtils.As<TValue>(getManiaImage(existing, $"KeyImage{maniaLookup.TargetColumn}D")); return SkinUtils.As<TValue>(getManiaImage(existing, $"KeyImage{maniaLookup.TargetColumn}D"));
case LegacyManiaSkinConfigurationLookups.LeftStageImage:
return SkinUtils.As<TValue>(getManiaImage(existing, "StageLeft"));
case LegacyManiaSkinConfigurationLookups.RightStageImage:
return SkinUtils.As<TValue>(getManiaImage(existing, "StageRight"));
} }
return null; return null;