1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-28 20:47:22 +08:00

Merge branch 'master' into overlay-headers-update-three

This commit is contained in:
Dean Herbert 2019-12-30 13:42:23 +09:00 committed by GitHub
commit 9d5d61a64d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 184 additions and 75 deletions

View File

@ -22,26 +22,15 @@ namespace osu.Game.Rulesets.Mania.UI.Components
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>(); private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Container hitTargetLine; private readonly Drawable hitTarget;
private readonly Drawable hitTargetBar;
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer) public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
{ {
InternalChildren = new[] InternalChildren = new[]
{ {
hitTargetBar = new Box hitTarget = new DefaultHitTarget
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = NotePiece.NOTE_HEIGHT,
Alpha = 0.6f,
Colour = Color4.Black
},
hitTargetLine = new Container
{
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
}, },
hitObjectContainer hitObjectContainer
}; };
@ -55,17 +44,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{ {
Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
hitTargetBar.Anchor = hitTargetBar.Origin = anchor; hitTarget.Anchor = hitTarget.Origin = anchor;
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
}, true); }, true);
} }
protected override void LoadComplete()
{
base.LoadComplete();
updateColours();
}
private Color4 accentColour; private Color4 accentColour;
public Color4 AccentColour public Color4 AccentColour
@ -78,21 +60,86 @@ namespace osu.Game.Rulesets.Mania.UI.Components
accentColour = value; accentColour = value;
updateColours(); if (hitTarget is IHasAccentColour colouredHitTarget)
colouredHitTarget.AccentColour = accentColour;
} }
} }
private void updateColours() private class DefaultHitTarget : CompositeDrawable, IHasAccentColour
{ {
if (!IsLoaded) private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
return;
hitTargetLine.EdgeEffect = new EdgeEffectParameters private readonly Container hitTargetLine;
private readonly Drawable hitTargetBar;
public DefaultHitTarget()
{ {
Type = EdgeEffectType.Glow, InternalChildren = new[]
Radius = 5, {
Colour = accentColour.Opacity(0.5f), hitTargetBar = new Box
}; {
RelativeSizeAxes = Axes.X,
Height = NotePiece.NOTE_HEIGHT,
Alpha = 0.6f,
Colour = Color4.Black
},
hitTargetLine = new Container
{
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
};
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(dir =>
{
Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
}, true);
}
protected override void LoadComplete()
{
base.LoadComplete();
updateColours();
}
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
updateColours();
}
}
private void updateColours()
{
if (!IsLoaded)
return;
hitTargetLine.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
}
} }
} }
} }

View File

@ -3,12 +3,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
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.Threading;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -28,12 +30,16 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached(typeof(IReadOnlyList<Mod>))] [Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>(); private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private const int spawn_interval = 5000;
private readonly ScrollingTestContainer[] scrollContainers = new ScrollingTestContainer[4]; private readonly ScrollingTestContainer[] scrollContainers = new ScrollingTestContainer[4];
private readonly TestPlayfield[] playfields = new TestPlayfield[4]; private readonly TestPlayfield[] playfields = new TestPlayfield[4];
private ScheduledDelegate hitObjectSpawnDelegate;
public TestSceneScrollingHitObjects() [SetUp]
public void Setup() => Schedule(() =>
{ {
Add(new GridContainer Child = new GridContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Content = new[] Content = new[]
@ -43,48 +49,66 @@ namespace osu.Game.Tests.Visual.Gameplay
scrollContainers[0] = new ScrollingTestContainer(ScrollingDirection.Up) scrollContainers[0] = new ScrollingTestContainer(ScrollingDirection.Up)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = playfields[0] = new TestPlayfield() Child = playfields[0] = new TestPlayfield(),
TimeRange = spawn_interval
}, },
scrollContainers[1] = new ScrollingTestContainer(ScrollingDirection.Up) scrollContainers[1] = new ScrollingTestContainer(ScrollingDirection.Down)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = playfields[1] = new TestPlayfield() Child = playfields[1] = new TestPlayfield(),
TimeRange = spawn_interval
}, },
}, },
new Drawable[] new Drawable[]
{ {
scrollContainers[2] = new ScrollingTestContainer(ScrollingDirection.Up) scrollContainers[2] = new ScrollingTestContainer(ScrollingDirection.Left)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = playfields[2] = new TestPlayfield() Child = playfields[2] = new TestPlayfield(),
TimeRange = spawn_interval
}, },
scrollContainers[3] = new ScrollingTestContainer(ScrollingDirection.Up) scrollContainers[3] = new ScrollingTestContainer(ScrollingDirection.Right)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = playfields[3] = new TestPlayfield() Child = playfields[3] = new TestPlayfield(),
TimeRange = spawn_interval
} }
} }
} }
}); };
setUpHitObjects();
});
private void setUpHitObjects()
{
scrollContainers.ForEach(c => c.ControlPoints.Add(new MultiplierControlPoint(0)));
for (int i = 0; i <= spawn_interval; i += 1000)
addHitObject(Time.Current + i);
hitObjectSpawnDelegate?.Cancel();
hitObjectSpawnDelegate = Scheduler.AddDelayed(() => addHitObject(Time.Current + spawn_interval), 1000, true);
}
[Test]
public void TestScrollAlgorithms()
{
AddStep("Constant scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Constant)); AddStep("Constant scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Constant));
AddStep("Overlapping scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Overlapping)); AddStep("Overlapping scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Overlapping));
AddStep("Sequential scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Sequential)); AddStep("Sequential scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Sequential));
AddSliderStep("Time range", 100, 10000, 5000, v => scrollContainers.ForEach(c => c.TimeRange = v)); AddSliderStep("Time range", 100, 10000, spawn_interval, v => scrollContainers.Where(c => c != null).ForEach(c => c.TimeRange = v));
AddStep("Add control point", () => addControlPoint(Time.Current + 5000)); AddStep("Add control point", () => addControlPoint(Time.Current + spawn_interval));
} }
protected override void LoadComplete() [Test]
public void TestScrollLifetime()
{ {
base.LoadComplete(); AddStep("Set constant scroll", () => setScrollAlgorithm(ScrollVisualisationMethod.Constant));
// scroll container time range must be less than the rate of spawning hitobjects
scrollContainers.ForEach(c => c.ControlPoints.Add(new MultiplierControlPoint(0))); // otherwise the hitobjects will spawn already partly visible on screen and look wrong
AddStep("Set time range", () => scrollContainers.ForEach(c => c.TimeRange = spawn_interval / 2.0));
for (int i = 0; i <= 5000; i += 1000)
addHitObject(Time.Current + i);
Scheduler.AddDelayed(() => addHitObject(Time.Current + 5000), 1000, true);
} }
private void addHitObject(double time) private void addHitObject(double time)
@ -207,7 +231,9 @@ namespace osu.Game.Tests.Visual.Gameplay
public TestDrawableHitObject(double time) public TestDrawableHitObject(double time)
: base(new HitObject { StartTime = time }) : base(new HitObject { StartTime = time })
{ {
Origin = Anchor.Centre; Origin = Anchor.Custom;
OriginPosition = new Vector2(75 / 4.0f);
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
AddInternal(new Box { Size = new Vector2(75) }); AddInternal(new Box { Size = new Vector2(75) });

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 System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@ -9,21 +10,25 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; 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.Cursor;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Overlays.Direct namespace osu.Game.Overlays.Direct
{ {
public abstract class DirectPanel : Container public abstract class DirectPanel : OsuClickableContainer, IHasContextMenu
{ {
public readonly BeatmapSetInfo SetInfo; public readonly BeatmapSetInfo SetInfo;
@ -32,8 +37,6 @@ namespace osu.Game.Overlays.Direct
private Container content; private Container content;
private BeatmapSetOverlay beatmapSetOverlay;
public PreviewTrack Preview => PlayButton.Preview; public PreviewTrack Preview => PlayButton.Preview;
public Bindable<bool> PreviewPlaying => PlayButton?.Playing; public Bindable<bool> PreviewPlaying => PlayButton?.Playing;
@ -44,6 +47,8 @@ namespace osu.Game.Overlays.Direct
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
protected Action ViewBeatmap;
protected DirectPanel(BeatmapSetInfo setInfo) protected DirectPanel(BeatmapSetInfo setInfo)
{ {
Debug.Assert(setInfo.OnlineBeatmapSetID != null); Debug.Assert(setInfo.OnlineBeatmapSetID != null);
@ -70,8 +75,6 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{ {
this.beatmapSetOverlay = beatmapSetOverlay;
AddInternal(content = new Container AddInternal(content = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -88,6 +91,12 @@ namespace osu.Game.Overlays.Direct
}, },
} }
}); });
Action = ViewBeatmap = () =>
{
Debug.Assert(SetInfo.OnlineBeatmapSetID != null);
beatmapSetOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value);
};
} }
protected override void Update() protected override void Update()
@ -120,13 +129,6 @@ namespace osu.Game.Overlays.Direct
base.OnHoverLost(e); base.OnHoverLost(e);
} }
protected override bool OnClick(ClickEvent e)
{
Debug.Assert(SetInfo.OnlineBeatmapSetID != null);
beatmapSetOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value);
return true;
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -203,5 +205,10 @@ namespace osu.Game.Overlays.Direct
Value = value; Value = value;
} }
} }
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem("View Beatmap", MenuItemType.Highlighted, ViewBeatmap),
};
} }
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
namespace osu.Game.Overlays.SearchableList namespace osu.Game.Overlays.SearchableList
{ {
@ -61,21 +62,20 @@ namespace osu.Game.Overlays.SearchableList
scrollContainer = new Container scrollContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new[] Child = new OsuContextMenuContainer
{ {
new OsuScrollContainer RelativeSizeAxes = Axes.Both,
Masking = true,
Child = new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false, ScrollbarVisible = false,
Children = new[] Child = ScrollFlow = new FillFlowContainer
{ {
ScrollFlow = new FillFlowContainer RelativeSizeAxes = Axes.X,
{ AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X, Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 },
AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical,
Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 },
Direction = FillDirection.Vertical,
},
}, },
}, },
}, },

View File

@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
private void computeLifetimeStartRecursive(DrawableHitObject hitObject) private void computeLifetimeStartRecursive(DrawableHitObject hitObject)
{ {
hitObject.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, timeRange.Value); hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject);
foreach (var obj in hitObject.NestedHitObjects) foreach (var obj in hitObject.NestedHitObjects)
computeLifetimeStartRecursive(obj); computeLifetimeStartRecursive(obj);
@ -108,6 +108,35 @@ namespace osu.Game.Rulesets.UI.Scrolling
private readonly Dictionary<DrawableHitObject, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>(); private readonly Dictionary<DrawableHitObject, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>();
private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject)
{
float originAdjustment = 0.0f;
// calculate the dimension of the part of the hitobject that should already be visible
// when the hitobject origin first appears inside the scrolling container
switch (direction.Value)
{
case ScrollingDirection.Up:
originAdjustment = hitObject.OriginPosition.Y;
break;
case ScrollingDirection.Down:
originAdjustment = hitObject.DrawHeight - hitObject.OriginPosition.Y;
break;
case ScrollingDirection.Left:
originAdjustment = hitObject.OriginPosition.X;
break;
case ScrollingDirection.Right:
originAdjustment = hitObject.DrawWidth - hitObject.OriginPosition.X;
break;
}
var adjustedStartTime = scrollingInfo.Algorithm.TimeAt(-originAdjustment, hitObject.HitObject.StartTime, timeRange.Value, scrollLength);
return scrollingInfo.Algorithm.GetDisplayStartTime(adjustedStartTime, timeRange.Value);
}
// Cant use AddOnce() since the delegate is re-constructed every invocation // Cant use AddOnce() since the delegate is re-constructed every invocation
private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() => private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() =>
{ {