1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 12:57:36 +08:00

Merge branch 'master' into higher-aspect-ratio-max

This commit is contained in:
Bartłomiej Dach 2022-11-21 19:51:39 +01:00 committed by GitHub
commit 8dbe31a172
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 256 additions and 55 deletions

View File

@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.82f,
Masking = true,
@ -54,6 +56,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
{
RelativeSizeAxes = Axes.X,
Height = ArgonNotePiece.CORNER_RADIUS * 2,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
},
};
}

View File

@ -3,6 +3,7 @@
#nullable disable
using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
@ -32,6 +33,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i));
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
AddStep("empty values", () => graph.Values = Array.Empty<float>());
AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop);
AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom);
AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight);

View File

@ -11,6 +11,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
@ -118,6 +119,15 @@ namespace osu.Game.Tests.Visual.SongSelect
}
}
[Test]
public void TestDeletion()
{
loadBeatmaps(count: 5, randomDifficulties: true);
AddStep("remove first set", () => carousel.RemoveBeatmapSet(carousel.Items.Select(item => item.Item).OfType<CarouselBeatmapSet>().First().BeatmapSet));
AddUntilStep("4 beatmap sets visible", () => this.ChildrenOfType<DrawableCarouselBeatmapSet>().Count(set => set.Alpha > 0) == 4);
}
[Test]
public void TestScrollPositionMaintainedOnDelete()
{

View File

@ -136,7 +136,9 @@ namespace osu.Game.Beatmaps.Drawables
private static readonly string[] always_bundled_beatmaps =
{
// This thing is 40mb, I'm not sure we want it here...
@"1388906 Raphlesia & BilliumMoto - My Love.osz"
@"1388906 Raphlesia & BilliumMoto - My Love.osz",
// Winner of Triangles mapping competition: https://osu.ppy.sh/home/news/2022-10-06-results-triangles
@"1841885 cYsmix - triangles.osz",
};
private static readonly string[] bundled_osu =

View File

@ -109,15 +109,11 @@ namespace osu.Game.Graphics.UserInterface
}
}
[Flags]
public enum BarDirection
{
LeftToRight = 1,
RightToLeft = 1 << 1,
TopToBottom = 1 << 2,
BottomToTop = 1 << 3,
Vertical = TopToBottom | BottomToTop,
Horizontal = LeftToRight | RightToLeft,
LeftToRight,
RightToLeft,
TopToBottom,
BottomToTop
}
}

View File

@ -5,15 +5,23 @@
using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Utils;
using System;
namespace osu.Game.Graphics.UserInterface
{
public class BarGraph : FillFlowContainer<Bar>
public class BarGraph : Drawable
{
private const int resize_duration = 250;
private const Easing easing = Easing.InOutCubic;
/// <summary>
/// Manually sets the max value, if null <see cref="Enumerable.Max(IEnumerable{float})"/> is instead used
/// </summary>
@ -21,22 +29,21 @@ namespace osu.Game.Graphics.UserInterface
private BarDirection direction = BarDirection.BottomToTop;
public new BarDirection Direction
public BarDirection Direction
{
get => direction;
set
{
direction = value;
base.Direction = direction.HasFlagFast(BarDirection.Horizontal) ? FillDirection.Vertical : FillDirection.Horizontal;
if (direction == value)
return;
foreach (var bar in Children)
{
bar.Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1);
bar.Direction = direction;
}
direction = value;
Invalidate(Invalidation.DrawNode);
}
}
private readonly BarsInfo bars = new BarsInfo();
/// <summary>
/// A list of floats that defines the length of each <see cref="Bar"/>
/// </summary>
@ -44,37 +51,199 @@ namespace osu.Game.Graphics.UserInterface
{
set
{
List<Bar> bars = Children.ToList();
foreach (var bar in value.Select((length, index) => new { Value = length, Bar = bars.Count > index ? bars[index] : null }))
if (!value.Any())
{
float length = MaxValue ?? value.Max();
if (length != 0)
length = bar.Value / length;
float size = value.Count();
if (size != 0)
size = 1.0f / size;
if (bar.Bar != null)
{
bar.Bar.Length = length;
bar.Bar.Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1);
}
else
{
Add(new Bar
{
RelativeSizeAxes = Axes.Both,
Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1),
Length = length,
Direction = Direction,
});
}
bars.Clear();
Invalidate(Invalidation.DrawNode);
return;
}
//I'm using ToList() here because Where() returns an Enumerable which can change it's elements afterwards
RemoveRange(Children.Where((_, index) => index >= value.Count()).ToList(), true);
float maxLength = MaxValue ?? value.Max();
bars.SetLengths(value.Select(v => maxLength == 0 ? 0 : Math.Max(0f, v / maxLength)).ToArray());
animationStartTime = Clock.CurrentTime;
animationComplete = false;
}
}
private double animationStartTime;
private bool animationComplete;
private IShader shader = null!;
private Texture texture = null!;
[BackgroundDependencyLoader]
private void load(IRenderer renderer, ShaderManager shaders)
{
texture = renderer.WhitePixel;
shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
}
protected override void Update()
{
base.Update();
if (!bars.Any)
return;
double currentTime = Clock.CurrentTime;
if (currentTime < animationStartTime + resize_duration)
{
bars.Animate(animationStartTime, currentTime);
Invalidate(Invalidation.DrawNode);
}
else if (!animationComplete)
{
bars.FinishAnimation();
Invalidate(Invalidation.DrawNode);
animationComplete = true;
}
}
protected override DrawNode CreateDrawNode() => new BarGraphDrawNode(this);
private class BarGraphDrawNode : DrawNode
{
public new BarGraph Source => (BarGraph)base.Source;
public BarGraphDrawNode(BarGraph source)
: base(source)
{
}
private IShader shader = null!;
private Texture texture = null!;
private Vector2 drawSize;
private BarDirection direction;
private float barBreadth;
private readonly List<float> lengths = new List<float>();
public override void ApplyState()
{
base.ApplyState();
shader = Source.shader;
texture = Source.texture;
drawSize = Source.DrawSize;
direction = Source.direction;
barBreadth = Source.bars.Breadth;
lengths.Clear();
lengths.AddRange(Source.bars.InstantaneousLengths);
}
public override void Draw(IRenderer renderer)
{
base.Draw(renderer);
shader.Bind();
for (int i = 0; i < lengths.Count; i++)
{
float barHeight = drawSize.Y * ((direction == BarDirection.TopToBottom || direction == BarDirection.BottomToTop) ? lengths[i] : barBreadth);
float barWidth = drawSize.X * ((direction == BarDirection.LeftToRight || direction == BarDirection.RightToLeft) ? lengths[i] : barBreadth);
Vector2 topLeft;
switch (direction)
{
default:
case BarDirection.LeftToRight:
topLeft = new Vector2(0, i * barHeight);
break;
case BarDirection.RightToLeft:
topLeft = new Vector2(drawSize.X - barWidth, i * barHeight);
break;
case BarDirection.TopToBottom:
topLeft = new Vector2(i * barWidth, 0);
break;
case BarDirection.BottomToTop:
topLeft = new Vector2(i * barWidth, drawSize.Y - barHeight);
break;
}
renderer.DrawQuad(
texture,
new Quad(
Vector2Extensions.Transform(topLeft, DrawInfo.Matrix),
Vector2Extensions.Transform(topLeft + new Vector2(barWidth, 0), DrawInfo.Matrix),
Vector2Extensions.Transform(topLeft + new Vector2(0, barHeight), DrawInfo.Matrix),
Vector2Extensions.Transform(topLeft + new Vector2(barWidth, barHeight), DrawInfo.Matrix)
),
DrawColourInfo.Colour);
}
shader.Unbind();
}
}
private class BarsInfo
{
public bool Any => Count > 0;
public int Count { get; private set; }
public float Breadth { get; private set; }
public List<float> InstantaneousLengths { get; } = new List<float>();
private readonly List<float> initialLengths = new List<float>();
private readonly List<float> finalLengths = new List<float>();
public void Clear() => SetLengths(Array.Empty<float>());
public void SetLengths(float[] newLengths)
{
int newCount = newLengths.Length;
for (int i = 0; i < newCount; i++)
{
// If we have an old bar at this index - change it's length
if (i < Count)
{
initialLengths[i] = finalLengths[i];
finalLengths[i] = newLengths[i];
continue;
}
// If exceeded old bars count - add new one
initialLengths.Add(0);
finalLengths.Add(newLengths[i]);
InstantaneousLengths.Add(0);
}
// Remove excessive bars
if (Count > newCount)
{
int barsToRemove = Count - newCount;
initialLengths.RemoveRange(newCount, barsToRemove);
finalLengths.RemoveRange(newCount, barsToRemove);
InstantaneousLengths.RemoveRange(newCount, barsToRemove);
}
Count = newCount;
Breadth = Count == 0 ? 0 : (1f / Count);
}
public void Animate(double animationStartTime, double currentTime)
{
for (int i = 0; i < Count; i++)
InstantaneousLengths[i] = Interpolation.ValueAt(currentTime, initialLengths[i], finalLengths[i], animationStartTime, animationStartTime + resize_duration, easing);
}
public void FinishAnimation()
{
for (int i = 0; i < Count; i++)
InstantaneousLengths[i] = finalLengths[i];
}
}
}

View File

@ -18,6 +18,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Localisation;
using osu.Game.Overlays.Settings;
using osuTK;
namespace osu.Game.Overlays.FirstRunSetup
@ -26,7 +27,7 @@ namespace osu.Game.Overlays.FirstRunSetup
public class ScreenWelcome : FirstRunSetupScreen
{
[BackgroundDependencyLoader]
private void load()
private void load(FrameworkConfigManager frameworkConfig)
{
Content.Children = new Drawable[]
{
@ -52,6 +53,11 @@ namespace osu.Game.Overlays.FirstRunSetup
},
}
},
new SettingsCheckbox
{
LabelText = GeneralSettingsStrings.PreferOriginalMetadataLanguage,
Current = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowUnicode)
},
new LanguageSelectionFlow
{
RelativeSizeAxes = Axes.X,

View File

@ -59,7 +59,11 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
// the dropdown. BASS does not give us a simple mechanism to select
// specific audio devices in such a case anyways. Such
// functionality would require involved OS-specific code.
dropdown.Items = deviceItems.Distinct().ToList();
dropdown.Items = deviceItems
// Dropdown doesn't like null items. Somehow we are seeing some arrive here (see https://github.com/ppy/osu/issues/21271)
.Where(i => i != null)
.Distinct()
.ToList();
}
protected override void Dispose(bool isDisposing)

View File

@ -296,6 +296,13 @@ namespace osu.Game.Scoring
break;
}
case HitResult.LargeBonus:
case HitResult.SmallBonus:
if (MaximumStatistics.TryGetValue(r.result, out int count) && count > 0)
yield return new HitResultDisplayStatistic(r.result, value, null, r.displayName);
break;
case HitResult.SmallTickMiss:
case HitResult.LargeTickMiss:
break;

View File

@ -770,7 +770,7 @@ namespace osu.Game.Screens.Select
{
updateItem(item);
if (!item.Item.Filtered.Value)
if (item.Item.Visible)
{
bool isSelected = item.Item.State.Value == CarouselItemState.Selected;

View File

@ -204,10 +204,11 @@ namespace osu.Game.Screens.Select.Leaderboards
}
else if (filterMods)
{
// otherwise find all the scores that have *any* of the currently selected mods (similar to how web applies mod filters)
// we're creating and using a string list representation of selected mods so that it can be translated into the DB query itself
var selectedMods = mods.Value.Select(m => m.Acronym);
scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym)));
// otherwise find all the scores that have all of the currently selected mods (similar to how web applies mod filters)
// we're creating and using a string HashSet representation of selected mods so that it can be translated into the DB query itself
var selectedMods = mods.Value.Select(m => m.Acronym).ToHashSet();
scores = scores.Where(s => selectedMods.SetEquals(s.Mods.Select(m => m.Acronym)));
}
scores = scoreManager.OrderByTotalScore(scores.Detach());