mirror of
https://github.com/ppy/osu.git
synced 2024-09-22 04:07:25 +08:00
Merge branch 'master' into mod-accuracy-challenge
This commit is contained in:
commit
a805f78909
2
.github/workflows/sentry-release.yml
vendored
2
.github/workflows/sentry-release.yml
vendored
@ -23,4 +23,4 @@ jobs:
|
|||||||
SENTRY_URL: https://sentry.ppy.sh/
|
SENTRY_URL: https://sentry.ppy.sh/
|
||||||
with:
|
with:
|
||||||
environment: production
|
environment: production
|
||||||
version: ${{ github.ref }}
|
version: osu@${{ github.ref_name }}
|
||||||
|
@ -51,11 +51,11 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.513.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.527.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.521.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.605.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
<PackageReference Include="Realm" Version="10.11.2" />
|
<PackageReference Include="Realm" Version="10.14.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
@ -45,6 +46,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
{
|
{
|
||||||
(typeof(EditorBeatmap), editorBeatmap),
|
(typeof(EditorBeatmap), editorBeatmap),
|
||||||
(typeof(IBeatSnapProvider), editorBeatmap),
|
(typeof(IBeatSnapProvider), editorBeatmap),
|
||||||
|
(typeof(OverlayColourProvider), new OverlayColourProvider(OverlayColourScheme.Green)),
|
||||||
},
|
},
|
||||||
Child = new ComposeScreen { State = { Value = Visibility.Visible } },
|
Child = new ComposeScreen { State = { Value = Visibility.Visible } },
|
||||||
};
|
};
|
||||||
|
@ -14,11 +14,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||||
|
|
||||||
[TestCase(2.3449735700206298d, 151, "diffcalc-test")]
|
[TestCase(2.3449735700206298d, 242, "diffcalc-test")]
|
||||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(2.7879104989252959d, 151, "diffcalc-test")]
|
[TestCase(2.7879104989252959d, 242, "diffcalc-test")]
|
||||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new ManiaModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new ManiaModDoubleTime());
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
foreach (var v in base.ToDatabaseAttributes())
|
foreach (var v in base.ToDatabaseAttributes())
|
||||||
yield return v;
|
yield return v;
|
||||||
|
|
||||||
// Todo: osu!mania doesn't output MaxCombo attribute for some reason.
|
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
|
||||||
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
|
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
|
||||||
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
|
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
|
||||||
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
|
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
|
||||||
@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
{
|
{
|
||||||
base.FromDatabaseAttributes(values);
|
base.FromDatabaseAttributes(values);
|
||||||
|
|
||||||
|
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
|
||||||
StarRating = values[ATTRIB_ID_DIFFICULTY];
|
StarRating = values[ATTRIB_ID_DIFFICULTY];
|
||||||
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
|
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
|
||||||
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
|
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
|
||||||
|
@ -52,10 +52,18 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
// This is done the way it is to introduce fractional differences in order to match osu-stable for the time being.
|
// This is done the way it is to introduce fractional differences in order to match osu-stable for the time being.
|
||||||
GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate),
|
GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate),
|
||||||
ScoreMultiplier = getScoreMultiplier(mods),
|
ScoreMultiplier = getScoreMultiplier(mods),
|
||||||
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
|
MaxCombo = beatmap.HitObjects.Sum(maxComboForObject)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int maxComboForObject(HitObject hitObject)
|
||||||
|
{
|
||||||
|
if (hitObject is HoldNote hold)
|
||||||
|
return 1 + (int)((hold.EndTime - hold.StartTime) / 100);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||||
{
|
{
|
||||||
var sortedObjects = beatmap.HitObjects.ToArray();
|
var sortedObjects = beatmap.HitObjects.ToArray();
|
||||||
|
@ -17,11 +17,11 @@ using osu.Framework.Testing.Input;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Gameplay;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public TestSceneGameplayCursor()
|
public TestSceneGameplayCursor()
|
||||||
{
|
{
|
||||||
var ruleset = new OsuRuleset();
|
var ruleset = new OsuRuleset();
|
||||||
gameplayState = new GameplayState(CreateBeatmap(ruleset.RulesetInfo), ruleset, Array.Empty<Mod>());
|
gameplayState = TestGameplayState.Create(ruleset);
|
||||||
|
|
||||||
AddStep("change background colour", () =>
|
AddStep("change background colour", () =>
|
||||||
{
|
{
|
||||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
if (positionInfo == positionInfos.First())
|
if (positionInfo == positionInfos.First())
|
||||||
{
|
{
|
||||||
positionInfo.DistanceFromPrevious = (float)(rng.NextDouble() * OsuPlayfield.BASE_SIZE.X / 2);
|
positionInfo.DistanceFromPrevious = (float)(rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y / 2);
|
||||||
positionInfo.RelativeAngle = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
|
positionInfo.RelativeAngle = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -78,7 +78,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!(source.FindProvider(s => s.GetTexture("spinner-top") != null) is DefaultLegacySkin))
|
var topProvider = source.FindProvider(s => s.GetTexture("spinner-top") != null);
|
||||||
|
|
||||||
|
if (topProvider is LegacySkinTransformer transformer && !(transformer.Skin is DefaultLegacySkin))
|
||||||
{
|
{
|
||||||
AddInternal(ApproachCircle = new Sprite
|
AddInternal(ApproachCircle = new Sprite
|
||||||
{
|
{
|
||||||
|
@ -116,6 +116,7 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
if (!(osuObject is Slider slider))
|
if (!(osuObject is Slider slider))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// No need to update the head and tail circles, since slider handles that when the new slider path is set
|
||||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
||||||
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
||||||
|
|
||||||
@ -137,6 +138,7 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
if (!(osuObject is Slider slider))
|
if (!(osuObject is Slider slider))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// No need to update the head and tail circles, since slider handles that when the new slider path is set
|
||||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
|
|
||||||
@ -146,5 +148,41 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
|
|
||||||
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotate a slider about its start position by the specified angle.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slider">The slider to be rotated.</param>
|
||||||
|
/// <param name="rotation">The angle, measured in radians, to rotate the slider by.</param>
|
||||||
|
public static void RotateSlider(Slider slider, float rotation)
|
||||||
|
{
|
||||||
|
void rotateNestedObject(OsuHitObject nested) => nested.Position = rotateVector(nested.Position - slider.Position, rotation) + slider.Position;
|
||||||
|
|
||||||
|
// No need to update the head and tail circles, since slider handles that when the new slider path is set
|
||||||
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(rotateNestedObject);
|
||||||
|
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(rotateNestedObject);
|
||||||
|
|
||||||
|
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
|
||||||
|
foreach (var point in controlPoints)
|
||||||
|
point.Position = rotateVector(point.Position, rotation);
|
||||||
|
|
||||||
|
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotate a vector by the specified angle.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vector">The vector to be rotated.</param>
|
||||||
|
/// <param name="rotation">The angle, measured in radians, to rotate the vector by.</param>
|
||||||
|
/// <returns>The rotated vector.</returns>
|
||||||
|
private static Vector2 rotateVector(Vector2 vector, float rotation)
|
||||||
|
{
|
||||||
|
float angle = MathF.Atan2(vector.Y, vector.X) + rotation;
|
||||||
|
float length = vector.Length;
|
||||||
|
return new Vector2(
|
||||||
|
length * MathF.Cos(angle),
|
||||||
|
length * MathF.Sin(angle)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -37,15 +38,23 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
foreach (OsuHitObject hitObject in hitObjects)
|
foreach (OsuHitObject hitObject in hitObjects)
|
||||||
{
|
{
|
||||||
Vector2 relativePosition = hitObject.Position - previousPosition;
|
Vector2 relativePosition = hitObject.Position - previousPosition;
|
||||||
float absoluteAngle = (float)Math.Atan2(relativePosition.Y, relativePosition.X);
|
float absoluteAngle = MathF.Atan2(relativePosition.Y, relativePosition.X);
|
||||||
float relativeAngle = absoluteAngle - previousAngle;
|
float relativeAngle = absoluteAngle - previousAngle;
|
||||||
|
|
||||||
positionInfos.Add(new ObjectPositionInfo(hitObject)
|
ObjectPositionInfo positionInfo;
|
||||||
|
positionInfos.Add(positionInfo = new ObjectPositionInfo(hitObject)
|
||||||
{
|
{
|
||||||
RelativeAngle = relativeAngle,
|
RelativeAngle = relativeAngle,
|
||||||
DistanceFromPrevious = relativePosition.Length
|
DistanceFromPrevious = relativePosition.Length
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (hitObject is Slider slider)
|
||||||
|
{
|
||||||
|
float absoluteRotation = getSliderRotation(slider);
|
||||||
|
positionInfo.Rotation = absoluteRotation - absoluteAngle;
|
||||||
|
absoluteAngle = absoluteRotation;
|
||||||
|
}
|
||||||
|
|
||||||
previousPosition = hitObject.EndPosition;
|
previousPosition = hitObject.EndPosition;
|
||||||
previousAngle = absoluteAngle;
|
previousAngle = absoluteAngle;
|
||||||
}
|
}
|
||||||
@ -70,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
|
|
||||||
if (hitObject is Spinner)
|
if (hitObject is Spinner)
|
||||||
{
|
{
|
||||||
previous = null;
|
previous = current;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,17 +132,24 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
float previousAbsoluteAngle = 0f;
|
float previousAbsoluteAngle = 0f;
|
||||||
|
|
||||||
if (previous != null)
|
if (previous != null)
|
||||||
|
{
|
||||||
|
if (previous.HitObject is Slider s)
|
||||||
|
{
|
||||||
|
previousAbsoluteAngle = getSliderRotation(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Vector2 earliestPosition = beforePrevious?.HitObject.EndPosition ?? playfield_centre;
|
Vector2 earliestPosition = beforePrevious?.HitObject.EndPosition ?? playfield_centre;
|
||||||
Vector2 relativePosition = previous.HitObject.Position - earliestPosition;
|
Vector2 relativePosition = previous.HitObject.Position - earliestPosition;
|
||||||
previousAbsoluteAngle = (float)Math.Atan2(relativePosition.Y, relativePosition.X);
|
previousAbsoluteAngle = MathF.Atan2(relativePosition.Y, relativePosition.X);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float absoluteAngle = previousAbsoluteAngle + current.PositionInfo.RelativeAngle;
|
float absoluteAngle = previousAbsoluteAngle + current.PositionInfo.RelativeAngle;
|
||||||
|
|
||||||
var posRelativeToPrev = new Vector2(
|
var posRelativeToPrev = new Vector2(
|
||||||
current.PositionInfo.DistanceFromPrevious * (float)Math.Cos(absoluteAngle),
|
current.PositionInfo.DistanceFromPrevious * MathF.Cos(absoluteAngle),
|
||||||
current.PositionInfo.DistanceFromPrevious * (float)Math.Sin(absoluteAngle)
|
current.PositionInfo.DistanceFromPrevious * MathF.Sin(absoluteAngle)
|
||||||
);
|
);
|
||||||
|
|
||||||
Vector2 lastEndPosition = previous?.EndPositionModified ?? playfield_centre;
|
Vector2 lastEndPosition = previous?.EndPositionModified ?? playfield_centre;
|
||||||
@ -141,6 +157,19 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
posRelativeToPrev = RotateAwayFromEdge(lastEndPosition, posRelativeToPrev);
|
posRelativeToPrev = RotateAwayFromEdge(lastEndPosition, posRelativeToPrev);
|
||||||
|
|
||||||
current.PositionModified = lastEndPosition + posRelativeToPrev;
|
current.PositionModified = lastEndPosition + posRelativeToPrev;
|
||||||
|
|
||||||
|
if (!(current.HitObject is Slider slider))
|
||||||
|
return;
|
||||||
|
|
||||||
|
absoluteAngle = MathF.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);
|
||||||
|
|
||||||
|
Vector2 centreOfMassOriginal = calculateCentreOfMass(slider);
|
||||||
|
Vector2 centreOfMassModified = rotateVector(centreOfMassOriginal, current.PositionInfo.Rotation + absoluteAngle - getSliderRotation(slider));
|
||||||
|
centreOfMassModified = RotateAwayFromEdge(current.PositionModified, centreOfMassModified);
|
||||||
|
|
||||||
|
float relativeRotation = MathF.Atan2(centreOfMassModified.Y, centreOfMassModified.X) - MathF.Atan2(centreOfMassOriginal.Y, centreOfMassOriginal.X);
|
||||||
|
if (!Precision.AlmostEquals(relativeRotation, 0))
|
||||||
|
RotateSlider(slider, relativeRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -172,13 +201,13 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
var previousPosition = workingObject.PositionModified;
|
var previousPosition = workingObject.PositionModified;
|
||||||
|
|
||||||
// Clamp slider position to the placement area
|
// Clamp slider position to the placement area
|
||||||
// If the slider is larger than the playfield, force it to stay at the original position
|
// If the slider is larger than the playfield, at least make sure that the head circle is inside the playfield
|
||||||
float newX = possibleMovementBounds.Width < 0
|
float newX = possibleMovementBounds.Width < 0
|
||||||
? workingObject.PositionOriginal.X
|
? Math.Clamp(possibleMovementBounds.Left, 0, OsuPlayfield.BASE_SIZE.X)
|
||||||
: Math.Clamp(previousPosition.X, possibleMovementBounds.Left, possibleMovementBounds.Right);
|
: Math.Clamp(previousPosition.X, possibleMovementBounds.Left, possibleMovementBounds.Right);
|
||||||
|
|
||||||
float newY = possibleMovementBounds.Height < 0
|
float newY = possibleMovementBounds.Height < 0
|
||||||
? workingObject.PositionOriginal.Y
|
? Math.Clamp(possibleMovementBounds.Top, 0, OsuPlayfield.BASE_SIZE.Y)
|
||||||
: Math.Clamp(previousPosition.Y, possibleMovementBounds.Top, possibleMovementBounds.Bottom);
|
: Math.Clamp(previousPosition.Y, possibleMovementBounds.Top, possibleMovementBounds.Bottom);
|
||||||
|
|
||||||
slider.Position = workingObject.PositionModified = new Vector2(newX, newY);
|
slider.Position = workingObject.PositionModified = new Vector2(newX, newY);
|
||||||
@ -287,6 +316,45 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Estimate the centre of mass of a slider relative to its start position.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slider">The slider to process.</param>
|
||||||
|
/// <returns>The centre of mass of the slider.</returns>
|
||||||
|
private static Vector2 calculateCentreOfMass(Slider slider)
|
||||||
|
{
|
||||||
|
const double sample_step = 50;
|
||||||
|
|
||||||
|
// just sample the start and end positions if the slider is too short
|
||||||
|
if (slider.Distance <= sample_step)
|
||||||
|
{
|
||||||
|
return Vector2.Divide(slider.Path.PositionAt(1), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
Vector2 sum = Vector2.Zero;
|
||||||
|
double pathDistance = slider.Distance;
|
||||||
|
|
||||||
|
for (double i = 0; i < pathDistance; i += sample_step)
|
||||||
|
{
|
||||||
|
sum += slider.Path.PositionAt(i / pathDistance);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum / count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the absolute rotation of a slider, defined as the angle from its start position to the end of its path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slider">The slider to process.</param>
|
||||||
|
/// <returns>The angle in radians.</returns>
|
||||||
|
private static float getSliderRotation(Slider slider)
|
||||||
|
{
|
||||||
|
var endPositionVector = slider.Path.PositionAt(1);
|
||||||
|
return MathF.Atan2(endPositionVector.Y, endPositionVector.X);
|
||||||
|
}
|
||||||
|
|
||||||
public class ObjectPositionInfo
|
public class ObjectPositionInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -309,6 +377,13 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public float DistanceFromPrevious { get; set; }
|
public float DistanceFromPrevious { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rotation of the hit object, relative to its jump angle.
|
||||||
|
/// For sliders, this is defined as the angle from the slider's start position to the end of its path, relative to its jump angle.
|
||||||
|
/// For hit circles and spinners, this property is ignored.
|
||||||
|
/// </summary>
|
||||||
|
public float Rotation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hit object associated with this <see cref="ObjectPositionInfo"/>.
|
/// The hit object associated with this <see cref="ObjectPositionInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -898,5 +898,24 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.That(controlPoints[3].Type, Is.Null);
|
Assert.That(controlPoints[3].Type, Is.Null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLegacyDuplicateInitialCatmullPointIsMerged()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("catmull-duplicate-initial-controlpoint.osu"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var decoded = decoder.Decode(stream);
|
||||||
|
var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints;
|
||||||
|
|
||||||
|
Assert.That(controlPoints.Count, Is.EqualTo(4));
|
||||||
|
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull));
|
||||||
|
Assert.That(controlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
|
Assert.That(controlPoints[1].Type, Is.Null);
|
||||||
|
Assert.That(controlPoints[1].Position, Is.Not.EqualTo(Vector2.Zero));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -710,7 +710,7 @@ namespace osu.Game.Tests.Database
|
|||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
realm.Realm.Write(() =>
|
await realm.Realm.WriteAsync(() =>
|
||||||
{
|
{
|
||||||
foreach (var b in imported.Beatmaps)
|
foreach (var b in imported.Beatmaps)
|
||||||
b.OnlineID = -1;
|
b.OnlineID = -1;
|
||||||
|
@ -59,30 +59,34 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TestJudgement(HitResult.Great)) { Type = HitResult.Great });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TestJudgement(HitResult.Great)) { Type = HitResult.Great });
|
||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
||||||
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(1));
|
||||||
|
|
||||||
// No header shouldn't cause any change
|
// No header shouldn't cause any change
|
||||||
scoreProcessor.ResetFromReplayFrame(new OsuRuleset(), new OsuReplayFrame());
|
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame());
|
||||||
|
|
||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
||||||
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(1));
|
||||||
|
|
||||||
// Reset with a miss instead.
|
// Reset with a miss instead.
|
||||||
scoreProcessor.ResetFromReplayFrame(new OsuRuleset(), new OsuReplayFrame
|
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame
|
||||||
{
|
{
|
||||||
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int> { { HitResult.Miss, 1 } }, DateTimeOffset.Now)
|
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int> { { HitResult.Miss, 1 } }, DateTimeOffset.Now)
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||||
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(0));
|
||||||
|
|
||||||
// Reset with no judged hit.
|
// Reset with no judged hit.
|
||||||
scoreProcessor.ResetFromReplayFrame(new OsuRuleset(), new OsuReplayFrame
|
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame
|
||||||
{
|
{
|
||||||
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int>(), DateTimeOffset.Now)
|
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int>(), DateTimeOffset.Now)
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||||
Assert.That(scoreProcessor.JudgedHits, Is.Zero);
|
Assert.That(scoreProcessor.JudgedHits, Is.Zero);
|
||||||
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestJudgement : Judgement
|
private class TestJudgement : Judgement
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
[HitObjects]
|
||||||
|
200,304,23875,6,0,C|200:304|288:304|288:208|352:208,1,260,8|0
|
@ -5,11 +5,13 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -23,7 +25,10 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
private BindableBeatDivisor bindableBeatDivisor;
|
private BindableBeatDivisor bindableBeatDivisor;
|
||||||
|
|
||||||
private SliderBar<int> tickSliderBar => beatDivisorControl.ChildrenOfType<SliderBar<int>>().Single();
|
private SliderBar<int> tickSliderBar => beatDivisorControl.ChildrenOfType<SliderBar<int>>().Single();
|
||||||
private EquilateralTriangle tickMarkerHead => tickSliderBar.ChildrenOfType<EquilateralTriangle>().Single();
|
private Triangle tickMarkerHead => tickSliderBar.ChildrenOfType<Triangle>().Single();
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Extensions.ObjectExtensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
@ -47,6 +48,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
(typeof(EditorBeatmap), editorBeatmap),
|
(typeof(EditorBeatmap), editorBeatmap),
|
||||||
(typeof(IBeatSnapProvider), editorBeatmap),
|
(typeof(IBeatSnapProvider), editorBeatmap),
|
||||||
|
(typeof(OverlayColourProvider), new OverlayColourProvider(OverlayColourScheme.Green)),
|
||||||
},
|
},
|
||||||
Child = new ComposeScreen { State = { Value = Visibility.Visible } },
|
Child = new ComposeScreen { State = { Value = Visibility.Visible } },
|
||||||
};
|
};
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
// 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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Components;
|
using osu.Game.Screens.Edit.Components;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
@ -13,6 +16,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditorClock : EditorClockTestScene
|
public class TestSceneEditorClock : EditorClockTestScene
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
|
private EditorBeatmap editorBeatmap = new EditorBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo));
|
||||||
|
|
||||||
public TestSceneEditorClock()
|
public TestSceneEditorClock()
|
||||||
{
|
{
|
||||||
Add(new FillFlowContainer
|
Add(new FillFlowContainer
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
// 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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
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.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Edit.Components.Menus;
|
using osu.Game.Screens.Edit.Components.Menus;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
@ -13,6 +15,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditorMenuBar : OsuTestScene
|
public class TestSceneEditorMenuBar : OsuTestScene
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
public TestSceneEditorMenuBar()
|
public TestSceneEditorMenuBar()
|
||||||
{
|
{
|
||||||
Add(new Container
|
Add(new Container
|
||||||
|
@ -12,7 +12,7 @@ using osuTK;
|
|||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestScenePlaybackControl : OsuTestScene
|
public class TestScenePlaybackControl : EditorClockTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
|
48
osu.Game.Tests/Visual/Editing/TestSceneTapButton.cs
Normal file
48
osu.Game.Tests/Visual/Editing/TestSceneTapButton.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Screens.Edit.Timing;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public class TestSceneTapButton : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private TapButton tapButton;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasic()
|
||||||
|
{
|
||||||
|
AddStep("create button", () =>
|
||||||
|
{
|
||||||
|
Child = tapButton = new TapButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(4),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
bool pressed = false;
|
||||||
|
|
||||||
|
AddRepeatStep("Press button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(tapButton);
|
||||||
|
if (!pressed)
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
else
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
|
||||||
|
pressed = !pressed;
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,15 +4,15 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Bindables;
|
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.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Timing;
|
using osu.Game.Screens.Edit.Timing;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -22,9 +22,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneTapTimingControl : EditorClockTestScene
|
public class TestSceneTapTimingControl : EditorClockTestScene
|
||||||
{
|
{
|
||||||
[Cached(typeof(EditorBeatmap))]
|
private EditorBeatmap editorBeatmap => editorBeatmapContainer?.EditorBeatmap;
|
||||||
[Cached(typeof(IBeatSnapProvider))]
|
|
||||||
private readonly EditorBeatmap editorBeatmap;
|
private TestSceneHitObjectComposer.EditorBeatmapContainer editorBeatmapContainer;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||||
@ -33,26 +33,23 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
private Bindable<ControlPointGroup> selectedGroup = new Bindable<ControlPointGroup>();
|
private Bindable<ControlPointGroup> selectedGroup = new Bindable<ControlPointGroup>();
|
||||||
|
|
||||||
private TapTimingControl control;
|
private TapTimingControl control;
|
||||||
|
private OsuSpriteText timingInfo;
|
||||||
|
|
||||||
public TestSceneTapTimingControl()
|
[Resolved]
|
||||||
|
private AudioManager audio { get; set; }
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
{
|
{
|
||||||
var playableBeatmap = CreateBeatmap(new OsuRuleset().RulesetInfo);
|
AddStep("create beatmap", () =>
|
||||||
|
|
||||||
// Ensure time doesn't end while testing
|
|
||||||
playableBeatmap.BeatmapInfo.Length = 1200000;
|
|
||||||
|
|
||||||
editorBeatmap = new EditorBeatmap(playableBeatmap);
|
|
||||||
|
|
||||||
selectedGroup.Value = editorBeatmap.ControlPointInfo.Groups.First();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
Beatmap.Value = new WaveformTestBeatmap(audio);
|
||||||
|
});
|
||||||
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
|
||||||
Beatmap.Disabled = true;
|
|
||||||
|
|
||||||
|
AddStep("Create component", () =>
|
||||||
|
{
|
||||||
|
Child = editorBeatmapContainer = new TestSceneHitObjectComposer.EditorBeatmapContainer(Beatmap.Value)
|
||||||
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
@ -63,30 +60,21 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
Width = 400,
|
Width = 400,
|
||||||
Scale = new Vector2(1.5f),
|
Scale = new Vector2(1.5f),
|
||||||
Child = control = new TapTimingControl(),
|
Child = control = new TapTimingControl(),
|
||||||
|
},
|
||||||
|
timingInfo = new OsuSpriteText(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
selectedGroup.Value = editorBeatmap.ControlPointInfo.Groups.First();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
protected override void Update()
|
||||||
public void TestTapThenReset()
|
|
||||||
{
|
{
|
||||||
AddStep("click tap button", () =>
|
base.Update();
|
||||||
{
|
|
||||||
control.ChildrenOfType<RoundedButton>()
|
|
||||||
.Last()
|
|
||||||
.TriggerClick();
|
|
||||||
});
|
|
||||||
|
|
||||||
AddUntilStep("wait for track playing", () => Clock.IsRunning);
|
if (selectedGroup.Value != null)
|
||||||
|
timingInfo.Text = $"offset: {selectedGroup.Value.Time:N2} bpm: {selectedGroup.Value.ControlPoints.OfType<TimingControlPoint>().First().BPM:N2}";
|
||||||
AddStep("click reset button", () =>
|
|
||||||
{
|
|
||||||
control.ChildrenOfType<RoundedButton>()
|
|
||||||
.First()
|
|
||||||
.TriggerClick();
|
|
||||||
});
|
|
||||||
|
|
||||||
AddUntilStep("wait for track stopped", () => !Clock.IsRunning);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -99,12 +87,40 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("click tap button", () =>
|
AddStep("click tap button", () =>
|
||||||
{
|
{
|
||||||
control.ChildrenOfType<RoundedButton>()
|
control.ChildrenOfType<OsuButton>()
|
||||||
.Last()
|
.Last()
|
||||||
.TriggerClick();
|
.TriggerClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
AddSliderStep("BPM", 30, 400, 60, bpm => editorBeatmap.ControlPointInfo.TimingPoints.First().BeatLength = 60000f / bpm);
|
AddSliderStep("BPM", 30, 400, 128, bpm =>
|
||||||
|
{
|
||||||
|
if (editorBeatmap == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
editorBeatmap.ControlPointInfo.TimingPoints.First().BeatLength = 60000f / bpm;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTapThenReset()
|
||||||
|
{
|
||||||
|
AddStep("click tap button", () =>
|
||||||
|
{
|
||||||
|
control.ChildrenOfType<OsuButton>()
|
||||||
|
.Last()
|
||||||
|
.TriggerClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for track playing", () => Clock.IsRunning);
|
||||||
|
|
||||||
|
AddStep("click reset button", () =>
|
||||||
|
{
|
||||||
|
control.ChildrenOfType<OsuButton>()
|
||||||
|
.First()
|
||||||
|
.TriggerClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for track stopped", () => !Clock.IsRunning);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
@ -18,6 +20,28 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
public override Drawable CreateTestComponent() => new TimelineBlueprintContainer(Composer);
|
public override Drawable CreateTestComponent() => new TimelineBlueprintContainer(Composer);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestContextMenu()
|
||||||
|
{
|
||||||
|
TimelineHitObjectBlueprint blueprint;
|
||||||
|
|
||||||
|
AddStep("add object", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.Clear();
|
||||||
|
EditorBeatmap.Add(new HitCircle { StartTime = 3000 });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("click object", () =>
|
||||||
|
{
|
||||||
|
blueprint = this.ChildrenOfType<TimelineHitObjectBlueprint>().Single();
|
||||||
|
InputManager.MoveMouseTo(blueprint);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||||
|
AddAssert("context menu open", () => this.ChildrenOfType<OsuContextMenu>().SingleOrDefault()?.State == MenuState.Open);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDisallowZeroDurationObjects()
|
public void TestDisallowZeroDurationObjects()
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -14,6 +15,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
public override Drawable CreateTestComponent() => Empty(); // tick display is implicitly inside the timeline.
|
public override Drawable CreateTestComponent() => Empty(); // tick display is implicitly inside the timeline.
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
48
osu.Game.Tests/Visual/Editing/TestSceneTimelineZoom.cs
Normal file
48
osu.Game.Tests/Visual/Editing/TestSceneTimelineZoom.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public class TestSceneTimelineZoom : TimelineTestScene
|
||||||
|
{
|
||||||
|
public override Drawable CreateTestComponent() => Empty();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVisibleRangeUpdatesOnZoomChange()
|
||||||
|
{
|
||||||
|
double initialVisibleRange = 0;
|
||||||
|
|
||||||
|
AddStep("reset zoom", () => TimelineArea.Timeline.Zoom = 100);
|
||||||
|
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);
|
||||||
|
|
||||||
|
AddStep("scale zoom", () => TimelineArea.Timeline.Zoom = 200);
|
||||||
|
AddAssert("range halved", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange / 2, 1));
|
||||||
|
AddStep("descale zoom", () => TimelineArea.Timeline.Zoom = 50);
|
||||||
|
AddAssert("range doubled", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange * 2, 1));
|
||||||
|
|
||||||
|
AddStep("restore zoom", () => TimelineArea.Timeline.Zoom = 100);
|
||||||
|
AddAssert("range restored", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVisibleRangeConstantOnSizeChange()
|
||||||
|
{
|
||||||
|
double initialVisibleRange = 0;
|
||||||
|
|
||||||
|
AddStep("reset timeline size", () => TimelineArea.Timeline.Width = 1);
|
||||||
|
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);
|
||||||
|
|
||||||
|
AddStep("scale timeline size", () => TimelineArea.Timeline.Width = 2);
|
||||||
|
AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange);
|
||||||
|
AddStep("descale timeline size", () => TimelineArea.Timeline.Width = 0.5f);
|
||||||
|
AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange);
|
||||||
|
|
||||||
|
AddStep("restore timeline size", () => TimelineArea.Timeline.Width = 1);
|
||||||
|
AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,18 @@
|
|||||||
// 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.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Timing;
|
using osu.Game.Screens.Edit.Timing;
|
||||||
|
using osu.Game.Screens.Edit.Timing.RowAttributes;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
@ -22,6 +26,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||||
|
|
||||||
|
private TimingScreen timingScreen;
|
||||||
|
|
||||||
protected override bool ScrollUsingMouseWheel => false;
|
protected override bool ScrollUsingMouseWheel => false;
|
||||||
|
|
||||||
public TestSceneTimingScreen()
|
public TestSceneTimingScreen()
|
||||||
@ -36,12 +42,54 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||||
Beatmap.Disabled = true;
|
Beatmap.Disabled = true;
|
||||||
|
|
||||||
Child = new TimingScreen
|
Child = timingScreen = new TimingScreen
|
||||||
{
|
{
|
||||||
State = { Value = Visibility.Visible },
|
State = { Value = Visibility.Visible },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("Stop clock", () => Clock.Stop());
|
||||||
|
|
||||||
|
AddUntilStep("wait for rows to load", () => Child.ChildrenOfType<EffectRowAttribute>().Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTrackingCurrentTimeWhileRunning()
|
||||||
|
{
|
||||||
|
AddStep("Select first effect point", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(Child.ChildrenOfType<EffectRowAttribute>().First());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 54670);
|
||||||
|
AddUntilStep("Ensure seeked to correct time", () => Clock.CurrentTimeAccurate == 54670);
|
||||||
|
|
||||||
|
AddStep("Seek to just before next point", () => Clock.Seek(69000));
|
||||||
|
AddStep("Start clock", () => Clock.Start());
|
||||||
|
|
||||||
|
AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 69670);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTrackingCurrentTimeWhilePaused()
|
||||||
|
{
|
||||||
|
AddStep("Select first effect point", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(Child.ChildrenOfType<EffectRowAttribute>().First());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 54670);
|
||||||
|
AddUntilStep("Ensure seeked to correct time", () => Clock.CurrentTimeAccurate == 54670);
|
||||||
|
|
||||||
|
AddStep("Seek to later", () => Clock.Seek(80000));
|
||||||
|
AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 69670);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
Beatmap.Disabled = false;
|
Beatmap.Disabled = false;
|
||||||
|
@ -44,7 +44,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = OsuColour.Gray(30)
|
Colour = OsuColour.Gray(30)
|
||||||
},
|
},
|
||||||
scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both }
|
scrollContainer = new ZoomableScrollContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new MenuCursor()
|
new MenuCursor()
|
||||||
@ -62,7 +67,15 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestWidthInitialization()
|
public void TestWidthInitialization()
|
||||||
{
|
{
|
||||||
AddAssert("Inner container width was initialized", () => innerBox.DrawWidth > 0);
|
AddAssert("Inner container width was initialized", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestWidthUpdatesOnDrawSizeChanges()
|
||||||
|
{
|
||||||
|
AddStep("Shrink scroll container", () => scrollContainer.Width = 0.5f);
|
||||||
|
AddAssert("Scroll container width shrunk", () => scrollContainer.DrawWidth == scrollContainer.Parent.DrawWidth / 2);
|
||||||
|
AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -8,6 +8,7 @@ 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.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
@ -38,7 +39,10 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
Composer = playable.BeatmapInfo.Ruleset.CreateInstance().CreateHitObjectComposer().With(d => d.Alpha = 0);
|
Composer = playable.BeatmapInfo.Ruleset.CreateInstance().CreateHitObjectComposer().With(d => d.Alpha = 0);
|
||||||
|
|
||||||
AddRange(new Drawable[]
|
Add(new OsuContextMenuContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
EditorBeatmap,
|
EditorBeatmap,
|
||||||
Composer,
|
Composer,
|
||||||
@ -58,6 +62,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Lists;
|
using osu.Framework.Lists;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
@ -22,7 +21,6 @@ using osu.Game.Screens.Play;
|
|||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
using osu.Game.Tests.Beatmaps;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -33,18 +31,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private SkinManager skinManager { get; set; }
|
private SkinManager skinManager { get; set; }
|
||||||
|
|
||||||
[Cached]
|
|
||||||
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
|
||||||
|
|
||||||
[Cached(typeof(HealthProcessor))]
|
|
||||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
|
||||||
|
|
||||||
[Cached]
|
|
||||||
private GameplayState gameplayState = new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset());
|
|
||||||
|
|
||||||
[Cached]
|
|
||||||
private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
|
||||||
|
|
||||||
protected override bool HasCustomSteps => true;
|
protected override bool HasCustomSteps => true;
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -81,11 +67,19 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
if (expectedComponentsContainer == null)
|
if (expectedComponentsContainer == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var expectedComponentsAdjustmentContainer = new Container
|
var expectedComponentsAdjustmentContainer = new DependencyProvidingContainer
|
||||||
{
|
{
|
||||||
Position = actualComponentsContainer.Parent.ToSpaceOfOtherDrawable(actualComponentsContainer.DrawPosition, Content),
|
Position = actualComponentsContainer.Parent.ToSpaceOfOtherDrawable(actualComponentsContainer.DrawPosition, Content),
|
||||||
Size = actualComponentsContainer.DrawSize,
|
Size = actualComponentsContainer.DrawSize,
|
||||||
Child = expectedComponentsContainer,
|
Child = expectedComponentsContainer,
|
||||||
|
// proxy the same required dependencies that `actualComponentsContainer` is using.
|
||||||
|
CachedDependencies = new (Type, object)[]
|
||||||
|
{
|
||||||
|
(typeof(ScoreProcessor), actualComponentsContainer.Dependencies.Get<ScoreProcessor>()),
|
||||||
|
(typeof(HealthProcessor), actualComponentsContainer.Dependencies.Get<HealthProcessor>()),
|
||||||
|
(typeof(GameplayState), actualComponentsContainer.Dependencies.Get<GameplayState>()),
|
||||||
|
(typeof(GameplayClock), actualComponentsContainer.Dependencies.Get<GameplayClock>())
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Add(expectedComponentsAdjustmentContainer);
|
Add(expectedComponentsAdjustmentContainer);
|
||||||
|
@ -15,7 +15,7 @@ using osu.Game.Rulesets.Osu;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Gameplay;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private GameplayState gameplayState = new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset());
|
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -14,16 +13,15 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Framework.Input.StateChanges;
|
using osu.Framework.Input.StateChanges;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Tests.Gameplay;
|
||||||
using osu.Game.Tests.Mods;
|
using osu.Game.Tests.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -41,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
private TestReplayRecorder recorder;
|
private TestReplayRecorder recorder;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>());
|
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUpSteps()
|
public void SetUpSteps()
|
||||||
|
@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Skinning.Editor;
|
using osu.Game.Skinning.Editor;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Gameplay;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private GameplayState gameplayState = new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset());
|
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
||||||
|
@ -16,7 +16,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Gameplay;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private GameplayState gameplayState = new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset());
|
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
private readonly GameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
||||||
|
@ -18,8 +18,8 @@ using osu.Game.Rulesets.UI;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Tests.Beatmaps;
|
|
||||||
using osu.Game.Tests.Beatmaps.IO;
|
using osu.Game.Tests.Beatmaps.IO;
|
||||||
|
using osu.Game.Tests.Gameplay;
|
||||||
using osu.Game.Tests.Visual.Multiplayer;
|
using osu.Game.Tests.Visual.Multiplayer;
|
||||||
using osu.Game.Tests.Visual.Spectator;
|
using osu.Game.Tests.Visual.Spectator;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -259,12 +259,15 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestFinalFramesPurgedBeforeEndingPlay()
|
public void TestFinalFramesPurgedBeforeEndingPlay()
|
||||||
{
|
{
|
||||||
AddStep("begin playing", () => spectatorClient.BeginPlaying(new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset()), new Score()));
|
AddStep("begin playing", () => spectatorClient.BeginPlaying(TestGameplayState.Create(new OsuRuleset()), new Score()));
|
||||||
|
|
||||||
AddStep("send frames and finish play", () =>
|
AddStep("send frames and finish play", () =>
|
||||||
{
|
{
|
||||||
spectatorClient.HandleFrame(new OsuReplayFrame(1000, Vector2.Zero));
|
spectatorClient.HandleFrame(new OsuReplayFrame(1000, Vector2.Zero));
|
||||||
spectatorClient.EndPlaying(new GameplayState(new TestBeatmap(new OsuRuleset().RulesetInfo), new OsuRuleset()) { HasPassed = true });
|
|
||||||
|
var completedGameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||||
|
completedGameplayState.HasPassed = true;
|
||||||
|
spectatorClient.EndPlaying(completedGameplayState);
|
||||||
});
|
});
|
||||||
|
|
||||||
// We can't access API because we're an "online" test.
|
// We can't access API because we're an "online" test.
|
||||||
|
@ -20,13 +20,13 @@ using osu.Game.Online.Spectator;
|
|||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Replays.Legacy;
|
using osu.Game.Replays.Legacy;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Tests.Gameplay;
|
||||||
using osu.Game.Tests.Mods;
|
using osu.Game.Tests.Mods;
|
||||||
using osu.Game.Tests.Visual.Spectator;
|
using osu.Game.Tests.Visual.Spectator;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
CachedDependencies = new[]
|
CachedDependencies = new[]
|
||||||
{
|
{
|
||||||
(typeof(SpectatorClient), (object)(spectatorClient = new TestSpectatorClient())),
|
(typeof(SpectatorClient), (object)(spectatorClient = new TestSpectatorClient())),
|
||||||
(typeof(GameplayState), new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty<Mod>()))
|
(typeof(GameplayState), TestGameplayState.Create(new OsuRuleset()))
|
||||||
},
|
},
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -124,13 +124,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
Status = { Value = new RoomStatusOpen() },
|
Status = { Value = new RoomStatusOpen() },
|
||||||
Category = { Value = RoomCategory.Spotlight },
|
Category = { Value = RoomCategory.Spotlight },
|
||||||
}),
|
}),
|
||||||
|
createLoungeRoom(new Room
|
||||||
|
{
|
||||||
|
Name = { Value = "Featured artist room" },
|
||||||
|
Status = { Value = new RoomStatusOpen() },
|
||||||
|
Category = { Value = RoomCategory.FeaturedArtist },
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for panel load", () => rooms.Count == 5);
|
AddUntilStep("wait for panel load", () => rooms.Count == 6);
|
||||||
AddUntilStep("correct status text", () => rooms.ChildrenOfType<OsuSpriteText>().Count(s => s.Text.ToString().StartsWith("Currently playing", StringComparison.Ordinal)) == 2);
|
AddUntilStep("correct status text", () => rooms.ChildrenOfType<OsuSpriteText>().Count(s => s.Text.ToString().StartsWith("Currently playing", StringComparison.Ordinal)) == 2);
|
||||||
AddUntilStep("correct status text", () => rooms.ChildrenOfType<OsuSpriteText>().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)) == 3);
|
AddUntilStep("correct status text", () => rooms.ChildrenOfType<OsuSpriteText>().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)) == 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -73,19 +72,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
createFreeModSelect();
|
createFreeModSelect();
|
||||||
|
|
||||||
|
AddAssert("select all button enabled", () => this.ChildrenOfType<SelectAllModsButton>().Single().Enabled.Value);
|
||||||
|
|
||||||
AddStep("click select all button", () =>
|
AddStep("click select all button", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().ElementAt(1));
|
InputManager.MoveMouseTo(this.ChildrenOfType<SelectAllModsButton>().Single());
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
AddUntilStep("all mods selected", assertAllAvailableModsSelected);
|
AddUntilStep("all mods selected", assertAllAvailableModsSelected);
|
||||||
|
AddAssert("select all button disabled", () => !this.ChildrenOfType<SelectAllModsButton>().Single().Enabled.Value);
|
||||||
|
|
||||||
AddStep("click deselect all button", () =>
|
AddStep("click deselect all button", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().Last());
|
InputManager.MoveMouseTo(this.ChildrenOfType<DeselectAllModsButton>().Single());
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any());
|
AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any());
|
||||||
|
AddAssert("select all button enabled", () => this.ChildrenOfType<SelectAllModsButton>().Single().Enabled.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createFreeModSelect()
|
private void createFreeModSelect()
|
||||||
|
@ -10,7 +10,10 @@ using osu.Game.Rulesets;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps.Drawables;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||||
using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
|
using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
@ -101,6 +104,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
AddStep("show many difficulties", () => overlay.ShowBeatmapSet(createManyDifficultiesBeatmapSet()));
|
AddStep("show many difficulties", () => overlay.ShowBeatmapSet(createManyDifficultiesBeatmapSet()));
|
||||||
downloadAssert(true);
|
downloadAssert(true);
|
||||||
|
|
||||||
|
AddAssert("status is loved", () => overlay.ChildrenOfType<BeatmapSetOnlineStatusPill>().Single().Status == BeatmapOnlineStatus.Loved);
|
||||||
|
AddAssert("scores container is visible", () => overlay.ChildrenOfType<ScoresContainer>().Single().Alpha == 1);
|
||||||
|
|
||||||
|
AddStep("go to second beatmap", () => overlay.ChildrenOfType<BeatmapPicker.DifficultySelectorButton>().ElementAt(1).TriggerClick());
|
||||||
|
|
||||||
|
AddAssert("status is graveyard", () => overlay.ChildrenOfType<BeatmapSetOnlineStatusPill>().Single().Status == BeatmapOnlineStatus.Graveyard);
|
||||||
|
AddAssert("scores container is hidden", () => overlay.ChildrenOfType<ScoresContainer>().Single().Alpha == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -232,6 +243,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
|
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
|
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
|
||||||
},
|
},
|
||||||
|
Status = i % 2 == 0 ? BeatmapOnlineStatus.Graveyard : BeatmapOnlineStatus.Loved,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
leaveText.Text = $"OnRequestLeave: {channel.Name}";
|
leaveText.Text = $"OnRequestLeave: {channel.Name}";
|
||||||
leaveText.FadeOutFromOne(1000, Easing.InQuint);
|
leaveText.FadeOutFromOne(1000, Easing.InQuint);
|
||||||
selected.Value = null;
|
selected.Value = channelList.ChannelListingChannel;
|
||||||
channelList.RemoveChannel(channel);
|
channelList.RemoveChannel(channel);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,6 +112,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
channelList.AddChannel(createRandomPrivateChannel());
|
channelList.AddChannel(createRandomPrivateChannel());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddStep("Add Announce Channels", () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
channelList.AddChannel(createRandomAnnounceChannel());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -170,5 +176,16 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Username = $"test user {id}",
|
Username = $"test user {id}",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Channel createRandomAnnounceChannel()
|
||||||
|
{
|
||||||
|
int id = RNG.Next(0, 10000);
|
||||||
|
return new Channel
|
||||||
|
{
|
||||||
|
Name = $"Announce {id}",
|
||||||
|
Type = ChannelType.Announce,
|
||||||
|
Id = id,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,129 +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.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Online.Chat;
|
|
||||||
using osu.Game.Overlays.Chat.Tabs;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
|
||||||
{
|
|
||||||
public class TestSceneChannelTabControl : OsuTestScene
|
|
||||||
{
|
|
||||||
private readonly TestTabControl channelTabControl;
|
|
||||||
|
|
||||||
public TestSceneChannelTabControl()
|
|
||||||
{
|
|
||||||
SpriteText currentText;
|
|
||||||
Add(new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
channelTabControl = new TestTabControl
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Height = 50
|
|
||||||
},
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = Color4.Black.Opacity(0.1f),
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 50,
|
|
||||||
Depth = -1,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Add(new Container
|
|
||||||
{
|
|
||||||
Origin = Anchor.TopLeft,
|
|
||||||
Anchor = Anchor.TopLeft,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
currentText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Text = "Currently selected channel:"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel);
|
|
||||||
channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue;
|
|
||||||
|
|
||||||
AddStep("Add random private channel", addRandomPrivateChannel);
|
|
||||||
AddAssert("There is only one channels", () => channelTabControl.Items.Count == 2);
|
|
||||||
AddRepeatStep("Add 3 random private channels", addRandomPrivateChannel, 3);
|
|
||||||
AddAssert("There are four channels", () => channelTabControl.Items.Count == 5);
|
|
||||||
AddStep("Add random public channel", () => addChannel(RNG.Next().ToString()));
|
|
||||||
|
|
||||||
AddRepeatStep("Select a random channel", () =>
|
|
||||||
{
|
|
||||||
List<Channel> validChannels = channelTabControl.Items.Where(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel)).ToList();
|
|
||||||
channelTabControl.SelectChannel(validChannels[RNG.Next(0, validChannels.Count)]);
|
|
||||||
}, 20);
|
|
||||||
|
|
||||||
Channel channelBefore = null;
|
|
||||||
AddStep("set first channel", () => channelTabControl.SelectChannel(channelBefore = channelTabControl.Items.First(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel))));
|
|
||||||
|
|
||||||
AddStep("select selector tab", () => channelTabControl.SelectChannel(channelTabControl.Items.Single(c => c is ChannelSelectorTabItem.ChannelSelectorTabChannel)));
|
|
||||||
AddAssert("selector tab is active", () => channelTabControl.ChannelSelectorActive.Value);
|
|
||||||
|
|
||||||
AddAssert("check channel unchanged", () => channelBefore == channelTabControl.Current.Value);
|
|
||||||
|
|
||||||
AddStep("set second channel", () => channelTabControl.SelectChannel(channelTabControl.Items.GetNext(channelBefore)));
|
|
||||||
AddAssert("selector tab is inactive", () => !channelTabControl.ChannelSelectorActive.Value);
|
|
||||||
|
|
||||||
AddUntilStep("remove all channels", () =>
|
|
||||||
{
|
|
||||||
foreach (var item in channelTabControl.Items.ToList())
|
|
||||||
{
|
|
||||||
if (item is ChannelSelectorTabItem.ChannelSelectorTabChannel)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
channelTabControl.RemoveChannel(item);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
AddAssert("selector tab is active", () => channelTabControl.ChannelSelectorActive.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addRandomPrivateChannel() =>
|
|
||||||
channelTabControl.AddChannel(new Channel(new APIUser
|
|
||||||
{
|
|
||||||
Id = RNG.Next(1000, 10000000),
|
|
||||||
Username = "Test User " + RNG.Next(1000)
|
|
||||||
}));
|
|
||||||
|
|
||||||
private void addChannel(string name) =>
|
|
||||||
channelTabControl.AddChannel(new Channel
|
|
||||||
{
|
|
||||||
Type = ChannelType.Public,
|
|
||||||
Name = name
|
|
||||||
});
|
|
||||||
|
|
||||||
private class TestTabControl : ChannelTabControl
|
|
||||||
{
|
|
||||||
public void SelectChannel(Channel channel) => base.SelectTab(TabMap[channel]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
using osu.Game.Overlays;
|
|
||||||
using osu.Game.Overlays.Chat;
|
using osu.Game.Overlays.Chat;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -22,12 +21,10 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
public class TestSceneChatLink : OsuTestScene
|
public class TestSceneChatLink : OsuTestScene
|
||||||
{
|
{
|
||||||
private readonly TestChatLineContainer textContainer;
|
private readonly TestChatLineContainer textContainer;
|
||||||
private readonly DialogOverlay dialogOverlay;
|
|
||||||
private Color4 linkColour;
|
private Color4 linkColour;
|
||||||
|
|
||||||
public TestSceneChatLink()
|
public TestSceneChatLink()
|
||||||
{
|
{
|
||||||
Add(dialogOverlay = new DialogOverlay { Depth = float.MinValue });
|
|
||||||
Add(textContainer = new TestChatLineContainer
|
Add(textContainer = new TestChatLineContainer
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Left = 20, Right = 20 },
|
Padding = new MarginPadding { Left = 20, Right = 20 },
|
||||||
@ -47,9 +44,6 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
availableChannels.Add(new Channel { Name = "#english" });
|
availableChannels.Add(new Channel { Name = "#english" });
|
||||||
availableChannels.Add(new Channel { Name = "#japanese" });
|
availableChannels.Add(new Channel { Name = "#japanese" });
|
||||||
Dependencies.Cache(chatManager);
|
Dependencies.Cache(chatManager);
|
||||||
|
|
||||||
Dependencies.Cache(new ChatOverlay());
|
|
||||||
Dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,500 +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 System.Linq;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Logging;
|
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Online.API;
|
|
||||||
using osu.Game.Online.API.Requests;
|
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Online.Chat;
|
|
||||||
using osu.Game.Overlays;
|
|
||||||
using osu.Game.Overlays.Chat;
|
|
||||||
using osu.Game.Overlays.Chat.Listing;
|
|
||||||
using osu.Game.Overlays.Chat.ChannelList;
|
|
||||||
using osuTK;
|
|
||||||
using osuTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class TestSceneChatOverlayV2 : OsuManualInputManagerTestScene
|
|
||||||
{
|
|
||||||
private TestChatOverlayV2 chatOverlay;
|
|
||||||
private ChannelManager channelManager;
|
|
||||||
|
|
||||||
private APIUser testUser;
|
|
||||||
private Channel testPMChannel;
|
|
||||||
private Channel[] testChannels;
|
|
||||||
|
|
||||||
private Channel testChannel1 => testChannels[0];
|
|
||||||
private Channel testChannel2 => testChannels[1];
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OsuConfigManager config { get; set; } = null!;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void SetUp() => Schedule(() =>
|
|
||||||
{
|
|
||||||
testUser = new APIUser { Username = "test user", Id = 5071479 };
|
|
||||||
testPMChannel = new Channel(testUser);
|
|
||||||
testChannels = Enumerable.Range(1, 10).Select(createPublicChannel).ToArray();
|
|
||||||
|
|
||||||
Child = new DependencyProvidingContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
CachedDependencies = new (Type, object)[]
|
|
||||||
{
|
|
||||||
(typeof(ChannelManager), channelManager = new ChannelManager()),
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
channelManager,
|
|
||||||
chatOverlay = new TestChatOverlayV2 { RelativeSizeAxes = Axes.Both },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
[SetUpSteps]
|
|
||||||
public void SetUpSteps()
|
|
||||||
{
|
|
||||||
AddStep("Setup request handler", () =>
|
|
||||||
{
|
|
||||||
((DummyAPIAccess)API).HandleRequest = req =>
|
|
||||||
{
|
|
||||||
switch (req)
|
|
||||||
{
|
|
||||||
case GetUpdatesRequest getUpdates:
|
|
||||||
getUpdates.TriggerFailure(new WebException());
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case JoinChannelRequest joinChannel:
|
|
||||||
joinChannel.TriggerSuccess();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case LeaveChannelRequest leaveChannel:
|
|
||||||
leaveChannel.TriggerSuccess();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case GetMessagesRequest getMessages:
|
|
||||||
getMessages.TriggerSuccess(createChannelMessages(getMessages.Channel));
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case GetUserRequest getUser:
|
|
||||||
if (getUser.Lookup == testUser.Username)
|
|
||||||
getUser.TriggerSuccess(testUser);
|
|
||||||
else
|
|
||||||
getUser.TriggerFailure(new WebException());
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case PostMessageRequest postMessage:
|
|
||||||
postMessage.TriggerSuccess(new Message(RNG.Next(0, 10000000))
|
|
||||||
{
|
|
||||||
Content = postMessage.Message.Content,
|
|
||||||
ChannelId = postMessage.Message.ChannelId,
|
|
||||||
Sender = postMessage.Message.Sender,
|
|
||||||
Timestamp = new DateTimeOffset(DateTime.Now),
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Logger.Log($"Unhandled Request Type: {req.GetType()}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
AddStep("Add test channels", () =>
|
|
||||||
{
|
|
||||||
(channelManager.AvailableChannels as BindableList<Channel>)?.AddRange(testChannels);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestBasic()
|
|
||||||
{
|
|
||||||
AddStep("Show overlay with channel", () =>
|
|
||||||
{
|
|
||||||
chatOverlay.Show();
|
|
||||||
Channel joinedChannel = channelManager.JoinChannel(testChannel1);
|
|
||||||
channelManager.CurrentChannel.Value = joinedChannel;
|
|
||||||
});
|
|
||||||
AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible);
|
|
||||||
AddUntilStep("Channel is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestShowHide()
|
|
||||||
{
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible);
|
|
||||||
AddStep("Hide overlay", () => chatOverlay.Hide());
|
|
||||||
AddAssert("Overlay is hidden", () => chatOverlay.State.Value == Visibility.Hidden);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestChatHeight()
|
|
||||||
{
|
|
||||||
BindableFloat configChatHeight = new BindableFloat();
|
|
||||||
config.BindWith(OsuSetting.ChatDisplayHeight, configChatHeight);
|
|
||||||
float newHeight = 0;
|
|
||||||
|
|
||||||
AddStep("Reset config chat height", () => configChatHeight.SetDefault());
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddAssert("Overlay uses config height", () => chatOverlay.Height == configChatHeight.Default);
|
|
||||||
AddStep("Click top bar", () =>
|
|
||||||
{
|
|
||||||
InputManager.MoveMouseTo(chatOverlayTopBar);
|
|
||||||
InputManager.PressButton(MouseButton.Left);
|
|
||||||
});
|
|
||||||
AddStep("Drag overlay to new height", () => InputManager.MoveMouseTo(chatOverlayTopBar, new Vector2(0, -300)));
|
|
||||||
AddStep("Stop dragging", () => InputManager.ReleaseButton(MouseButton.Left));
|
|
||||||
AddStep("Store new height", () => newHeight = chatOverlay.Height);
|
|
||||||
AddAssert("Config height changed", () => !configChatHeight.IsDefault && configChatHeight.Value == newHeight);
|
|
||||||
AddStep("Hide overlay", () => chatOverlay.Hide());
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddAssert("Overlay uses new height", () => chatOverlay.Height == newHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestChannelSelection()
|
|
||||||
{
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddAssert("Listing is visible", () => listingIsVisible);
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestSearchInListing()
|
|
||||||
{
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddAssert("Listing is visible", () => listingIsVisible);
|
|
||||||
AddStep("Search for 'number 2'", () => chatOverlayTextBox.Text = "number 2");
|
|
||||||
AddUntilStep("Only channel 2 visibile", () =>
|
|
||||||
{
|
|
||||||
IEnumerable<ChannelListingItem> listingItems = chatOverlay.ChildrenOfType<ChannelListingItem>()
|
|
||||||
.Where(item => item.IsPresent);
|
|
||||||
return listingItems.Count() == 1 && listingItems.Single().Channel == testChannel2;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestChannelCloseButton()
|
|
||||||
{
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddStep("Join PM and public channels", () =>
|
|
||||||
{
|
|
||||||
channelManager.JoinChannel(testChannel1);
|
|
||||||
channelManager.JoinChannel(testPMChannel);
|
|
||||||
});
|
|
||||||
AddStep("Select PM channel", () => clickDrawable(getChannelListItem(testPMChannel)));
|
|
||||||
AddStep("Click close button", () =>
|
|
||||||
{
|
|
||||||
ChannelListItemCloseButton closeButton = getChannelListItem(testPMChannel).ChildrenOfType<ChannelListItemCloseButton>().Single();
|
|
||||||
clickDrawable(closeButton);
|
|
||||||
});
|
|
||||||
AddAssert("PM channel closed", () => !channelManager.JoinedChannels.Contains(testPMChannel));
|
|
||||||
AddStep("Select normal channel", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddStep("Click close button", () =>
|
|
||||||
{
|
|
||||||
ChannelListItemCloseButton closeButton = getChannelListItem(testChannel1).ChildrenOfType<ChannelListItemCloseButton>().Single();
|
|
||||||
clickDrawable(closeButton);
|
|
||||||
});
|
|
||||||
AddAssert("Normal channel closed", () => !channelManager.JoinedChannels.Contains(testChannel1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestChatCommand()
|
|
||||||
{
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}"));
|
|
||||||
AddAssert("PM channel is selected", () =>
|
|
||||||
channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single() == testUser);
|
|
||||||
AddStep("Open chat with non-existent user", () => channelManager.PostCommand("chat user_doesnt_exist"));
|
|
||||||
AddAssert("Last message is error", () => channelManager.CurrentChannel.Value.Messages.Last() is ErrorMessage);
|
|
||||||
|
|
||||||
// Make sure no unnecessary requests are made when the PM channel is already open.
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddStep("Unregister request handling", () => ((DummyAPIAccess)API).HandleRequest = null);
|
|
||||||
AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}"));
|
|
||||||
AddAssert("PM channel is selected", () =>
|
|
||||||
channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single() == testUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestMultiplayerChannelIsNotShown()
|
|
||||||
{
|
|
||||||
Channel multiplayerChannel = null;
|
|
||||||
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddStep("Join multiplayer channel", () => channelManager.JoinChannel(multiplayerChannel = new Channel(new APIUser())
|
|
||||||
{
|
|
||||||
Name = "#mp_1",
|
|
||||||
Type = ChannelType.Multiplayer,
|
|
||||||
}));
|
|
||||||
AddAssert("Channel is joined", () => channelManager.JoinedChannels.Contains(multiplayerChannel));
|
|
||||||
AddUntilStep("Channel not present in listing", () => !chatOverlay.ChildrenOfType<ChannelListingItem>()
|
|
||||||
.Where(item => item.IsPresent)
|
|
||||||
.Select(item => item.Channel)
|
|
||||||
.Contains(multiplayerChannel));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestHighlightOnCurrentChannel()
|
|
||||||
{
|
|
||||||
Message message = null;
|
|
||||||
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddStep("Send message in channel 1", () =>
|
|
||||||
{
|
|
||||||
testChannel1.AddNewMessages(message = new Message
|
|
||||||
{
|
|
||||||
ChannelId = testChannel1.Id,
|
|
||||||
Content = "Message to highlight!",
|
|
||||||
Timestamp = DateTimeOffset.Now,
|
|
||||||
Sender = testUser,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1));
|
|
||||||
AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestHighlightOnAnotherChannel()
|
|
||||||
{
|
|
||||||
Message message = null;
|
|
||||||
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2));
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddStep("Send message in channel 2", () =>
|
|
||||||
{
|
|
||||||
testChannel2.AddNewMessages(message = new Message
|
|
||||||
{
|
|
||||||
ChannelId = testChannel2.Id,
|
|
||||||
Content = "Message to highlight!",
|
|
||||||
Timestamp = DateTimeOffset.Now,
|
|
||||||
Sender = testUser,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2));
|
|
||||||
AddUntilStep("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestHighlightOnLeftChannel()
|
|
||||||
{
|
|
||||||
Message message = null;
|
|
||||||
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2));
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddStep("Send message in channel 2", () =>
|
|
||||||
{
|
|
||||||
testChannel2.AddNewMessages(message = new Message
|
|
||||||
{
|
|
||||||
ChannelId = testChannel2.Id,
|
|
||||||
Content = "Message to highlight!",
|
|
||||||
Timestamp = DateTimeOffset.Now,
|
|
||||||
Sender = testUser,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
AddStep("Leave channel 2", () => channelManager.LeaveChannel(testChannel2));
|
|
||||||
AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2));
|
|
||||||
AddUntilStep("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestHighlightWhileChatNeverOpen()
|
|
||||||
{
|
|
||||||
Message message = null;
|
|
||||||
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Send message in channel 1", () =>
|
|
||||||
{
|
|
||||||
testChannel1.AddNewMessages(message = new Message
|
|
||||||
{
|
|
||||||
ChannelId = testChannel1.Id,
|
|
||||||
Content = "Message to highlight!",
|
|
||||||
Timestamp = DateTimeOffset.Now,
|
|
||||||
Sender = testUser,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1));
|
|
||||||
AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestHighlightWithNullChannel()
|
|
||||||
{
|
|
||||||
Message message = null;
|
|
||||||
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Send message in channel 1", () =>
|
|
||||||
{
|
|
||||||
testChannel1.AddNewMessages(message = new Message
|
|
||||||
{
|
|
||||||
ChannelId = testChannel1.Id,
|
|
||||||
Content = "Message to highlight!",
|
|
||||||
Timestamp = DateTimeOffset.Now,
|
|
||||||
Sender = testUser,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
AddStep("Set null channel", () => channelManager.CurrentChannel.Value = null);
|
|
||||||
AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1));
|
|
||||||
AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestTextBoxRetainsFocus()
|
|
||||||
{
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
|
||||||
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
|
|
||||||
AddStep("Click drawable channel", () => clickDrawable(currentDrawableChannel));
|
|
||||||
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
|
|
||||||
AddStep("Click selector", () => clickDrawable(channelSelectorButton));
|
|
||||||
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
|
|
||||||
AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType<ChannelListing>().Single()));
|
|
||||||
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
|
|
||||||
AddStep("Click channel list", () => clickDrawable(chatOverlay.ChildrenOfType<ChannelList>().Single()));
|
|
||||||
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
|
|
||||||
AddStep("Click top bar", () => clickDrawable(chatOverlay.ChildrenOfType<ChatOverlayTopBar>().Single()));
|
|
||||||
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
|
|
||||||
AddStep("Hide overlay", () => chatOverlay.Hide());
|
|
||||||
AddAssert("TextBox is not focused", () => InputManager.FocusedDrawable == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestSlowLoadingChannel()
|
|
||||||
{
|
|
||||||
AddStep("Show overlay (slow-loading)", () =>
|
|
||||||
{
|
|
||||||
chatOverlay.Show();
|
|
||||||
chatOverlay.SlowLoading = true;
|
|
||||||
});
|
|
||||||
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddAssert("Channel 1 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Loading);
|
|
||||||
|
|
||||||
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2));
|
|
||||||
AddStep("Select channel 2", () => clickDrawable(getChannelListItem(testChannel2)));
|
|
||||||
AddAssert("Channel 2 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel2).LoadState == LoadState.Loading);
|
|
||||||
|
|
||||||
AddStep("Finish channel 1 load", () => chatOverlay.GetSlowLoadingChannel(testChannel1).LoadEvent.Set());
|
|
||||||
AddAssert("Channel 1 ready", () => chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Ready);
|
|
||||||
AddAssert("Channel 1 not displayed", () => !channelIsVisible);
|
|
||||||
|
|
||||||
AddStep("Finish channel 2 load", () => chatOverlay.GetSlowLoadingChannel(testChannel2).LoadEvent.Set());
|
|
||||||
AddAssert("Channel 2 loaded", () => chatOverlay.GetSlowLoadingChannel(testChannel2).IsLoaded);
|
|
||||||
AddAssert("Channel 2 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2);
|
|
||||||
|
|
||||||
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
|
|
||||||
AddAssert("Channel 1 loaded", () => chatOverlay.GetSlowLoadingChannel(testChannel1).IsLoaded);
|
|
||||||
AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool listingIsVisible =>
|
|
||||||
chatOverlay.ChildrenOfType<ChannelListing>().Single().State.Value == Visibility.Visible;
|
|
||||||
|
|
||||||
private bool loadingIsVisible =>
|
|
||||||
chatOverlay.ChildrenOfType<LoadingLayer>().Single().State.Value == Visibility.Visible;
|
|
||||||
|
|
||||||
private bool channelIsVisible =>
|
|
||||||
!listingIsVisible && !loadingIsVisible;
|
|
||||||
|
|
||||||
private DrawableChannel currentDrawableChannel =>
|
|
||||||
chatOverlay.ChildrenOfType<DrawableChannel>().Single();
|
|
||||||
|
|
||||||
private ChannelListItem getChannelListItem(Channel channel) =>
|
|
||||||
chatOverlay.ChildrenOfType<ChannelListItem>().Single(item => item.Channel == channel);
|
|
||||||
|
|
||||||
private ChatTextBox chatOverlayTextBox =>
|
|
||||||
chatOverlay.ChildrenOfType<ChatTextBox>().Single();
|
|
||||||
|
|
||||||
private ChatOverlayTopBar chatOverlayTopBar =>
|
|
||||||
chatOverlay.ChildrenOfType<ChatOverlayTopBar>().Single();
|
|
||||||
|
|
||||||
private ChannelListItem channelSelectorButton =>
|
|
||||||
chatOverlay.ChildrenOfType<ChannelListItem>().Single(item => item.Channel is ChannelListing.ChannelListingChannel);
|
|
||||||
|
|
||||||
private void clickDrawable(Drawable d)
|
|
||||||
{
|
|
||||||
InputManager.MoveMouseTo(d);
|
|
||||||
InputManager.Click(MouseButton.Left);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Message> createChannelMessages(Channel channel)
|
|
||||||
{
|
|
||||||
var message = new Message
|
|
||||||
{
|
|
||||||
ChannelId = channel.Id,
|
|
||||||
Content = $"Hello, this is a message in {channel.Name}",
|
|
||||||
Sender = testUser,
|
|
||||||
Timestamp = new DateTimeOffset(DateTime.Now),
|
|
||||||
};
|
|
||||||
return new List<Message> { message };
|
|
||||||
}
|
|
||||||
|
|
||||||
private Channel createPublicChannel(int id) => new Channel
|
|
||||||
{
|
|
||||||
Id = id,
|
|
||||||
Name = $"#channel-{id}",
|
|
||||||
Topic = $"We talk about the number {id} here",
|
|
||||||
Type = ChannelType.Public,
|
|
||||||
};
|
|
||||||
|
|
||||||
private class TestChatOverlayV2 : ChatOverlayV2
|
|
||||||
{
|
|
||||||
public bool SlowLoading { get; set; }
|
|
||||||
|
|
||||||
public SlowLoadingDrawableChannel GetSlowLoadingChannel(Channel channel) => DrawableChannels.OfType<SlowLoadingDrawableChannel>().Single(c => c.Channel == channel);
|
|
||||||
|
|
||||||
protected override ChatOverlayDrawableChannel CreateDrawableChannel(Channel newChannel)
|
|
||||||
{
|
|
||||||
return SlowLoading
|
|
||||||
? new SlowLoadingDrawableChannel(newChannel)
|
|
||||||
: new ChatOverlayDrawableChannel(newChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SlowLoadingDrawableChannel : ChatOverlayDrawableChannel
|
|
||||||
{
|
|
||||||
public readonly ManualResetEventSlim LoadEvent = new ManualResetEventSlim();
|
|
||||||
|
|
||||||
public SlowLoadingDrawableChannel([NotNull] Channel channel)
|
|
||||||
: base(channel)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
LoadEvent.Wait(10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Dashboard;
|
using osu.Game.Overlays.Dashboard;
|
||||||
using osu.Game.Tests.Visual.Spectator;
|
using osu.Game.Tests.Visual.Spectator;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -42,7 +43,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
CachedDependencies = new (Type, object)[]
|
CachedDependencies = new (Type, object)[]
|
||||||
{
|
{
|
||||||
(typeof(SpectatorClient), spectatorClient),
|
(typeof(SpectatorClient), spectatorClient),
|
||||||
(typeof(UserLookupCache), lookupCache)
|
(typeof(UserLookupCache), lookupCache),
|
||||||
|
(typeof(OverlayColourProvider), new OverlayColourProvider(OverlayColourScheme.Purple)),
|
||||||
},
|
},
|
||||||
Child = currentlyPlaying = new CurrentlyPlayingDisplay
|
Child = currentlyPlaying = new CurrentlyPlayingDisplay
|
||||||
{
|
{
|
||||||
|
@ -128,11 +128,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
AddAssert("Ensure no adjacent day separators", () =>
|
AddAssert("Ensure no adjacent day separators", () =>
|
||||||
{
|
{
|
||||||
var indices = chatDisplay.FillFlow.OfType<DrawableChannel.DaySeparator>().Select(ds => chatDisplay.FillFlow.IndexOf(ds));
|
var indices = chatDisplay.FillFlow.OfType<DaySeparator>().Select(ds => chatDisplay.FillFlow.IndexOf(ds));
|
||||||
|
|
||||||
foreach (int i in indices)
|
foreach (int i in indices)
|
||||||
{
|
{
|
||||||
if (i < chatDisplay.FillFlow.Count && chatDisplay.FillFlow[i + 1] is DrawableChannel.DaySeparator)
|
if (i < chatDisplay.FillFlow.Count && chatDisplay.FillFlow[i + 1] is DaySeparator)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +173,8 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddUntilStep("wait for scores loaded", () =>
|
AddUntilStep("wait for scores loaded", () =>
|
||||||
requestComplete
|
requestComplete
|
||||||
|
// request handler may need to fire more than once to get scores.
|
||||||
|
&& totalCount > 0
|
||||||
&& resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount
|
&& resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount
|
||||||
&& resultsScreen.ScorePanelList.AllPanelsVisible);
|
&& resultsScreen.ScorePanelList.AllPanelsVisible);
|
||||||
AddWaitStep("wait for display", 5);
|
AddWaitStep("wait for display", 5);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -13,6 +14,7 @@ using osu.Game.Graphics.Sprites;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Ranking.Statistics;
|
using osu.Game.Screens.Ranking.Statistics;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -114,10 +116,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap)
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap);
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap)
|
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap)
|
||||||
{
|
{
|
||||||
@ -151,6 +150,24 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private class TestBeatmapConverter : IBeatmapConverter
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0067 // The event is never used
|
||||||
|
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
||||||
|
#pragma warning restore CS0067
|
||||||
|
|
||||||
|
public IBeatmap Beatmap { get; }
|
||||||
|
|
||||||
|
public TestBeatmapConverter(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
Beatmap = beatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanConvert() => true;
|
||||||
|
|
||||||
|
public IBeatmap Convert(CancellationToken cancellationToken = default) => Beatmap.Clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestRulesetAllStatsRequireHitEvents : TestRuleset
|
private class TestRulesetAllStatsRequireHitEvents : TestRuleset
|
||||||
|
@ -6,6 +6,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
@ -83,7 +84,7 @@ namespace osu.Game.Tests.Visual.Settings
|
|||||||
AddStep("clear label", () => textBox.LabelText = default);
|
AddStep("clear label", () => textBox.LabelText = default);
|
||||||
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(restoreDefaultValueButton.Parent.DrawHeight, control.DrawHeight, 1));
|
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(restoreDefaultValueButton.Parent.DrawHeight, control.DrawHeight, 1));
|
||||||
|
|
||||||
AddStep("set warning text", () => textBox.WarningText = "This is some very important warning text! Hopefully it doesn't break the alignment of the default value indicator...");
|
AddStep("set warning text", () => textBox.SetNoticeText("This is some very important warning text! Hopefully it doesn't break the alignment of the default value indicator...", true));
|
||||||
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(restoreDefaultValueButton.Parent.DrawHeight, control.DrawHeight, 1));
|
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(restoreDefaultValueButton.Parent.DrawHeight, control.DrawHeight, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,16 +130,18 @@ namespace osu.Game.Tests.Visual.Settings
|
|||||||
SettingsNumberBox numberBox = null;
|
SettingsNumberBox numberBox = null;
|
||||||
|
|
||||||
AddStep("create settings item", () => Child = numberBox = new SettingsNumberBox());
|
AddStep("create settings item", () => Child = numberBox = new SettingsNumberBox());
|
||||||
AddAssert("warning text not created", () => !numberBox.ChildrenOfType<SettingsNoticeText>().Any());
|
AddAssert("warning text not created", () => !numberBox.ChildrenOfType<LinkFlowContainer>().Any());
|
||||||
|
|
||||||
AddStep("set warning text", () => numberBox.WarningText = "this is a warning!");
|
AddStep("set warning text", () => numberBox.SetNoticeText("this is a warning!", true));
|
||||||
AddAssert("warning text created", () => numberBox.ChildrenOfType<SettingsNoticeText>().Single().Alpha == 1);
|
AddAssert("warning text created", () => numberBox.ChildrenOfType<LinkFlowContainer>().Single().Alpha == 1);
|
||||||
|
|
||||||
AddStep("unset warning text", () => numberBox.WarningText = default);
|
AddStep("unset warning text", () => numberBox.ClearNoticeText());
|
||||||
AddAssert("warning text hidden", () => numberBox.ChildrenOfType<SettingsNoticeText>().Single().Alpha == 0);
|
AddAssert("warning text hidden", () => !numberBox.ChildrenOfType<LinkFlowContainer>().Any());
|
||||||
|
|
||||||
AddStep("set warning text again", () => numberBox.WarningText = "another warning!");
|
AddStep("set warning text again", () => numberBox.SetNoticeText("another warning!", true));
|
||||||
AddAssert("warning text shown again", () => numberBox.ChildrenOfType<SettingsNoticeText>().Single().Alpha == 1);
|
AddAssert("warning text shown again", () => numberBox.ChildrenOfType<LinkFlowContainer>().Single().Alpha == 1);
|
||||||
|
|
||||||
|
AddStep("set non warning text", () => numberBox.SetNoticeText("you did good!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
AddStep("step to next", () => overlay.NextButton.TriggerClick());
|
AddStep("step to next", () => overlay.NextButton.TriggerClick());
|
||||||
|
|
||||||
AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenBeatmaps);
|
AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenUIScale);
|
||||||
|
|
||||||
AddStep("hide", () => overlay.Hide());
|
AddStep("hide", () => overlay.Hide());
|
||||||
AddAssert("overlay hidden", () => overlay.State.Value == Visibility.Hidden);
|
AddAssert("overlay hidden", () => overlay.State.Value == Visibility.Hidden);
|
||||||
@ -195,7 +195,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("run notification action", () => lastNotification.Activated());
|
AddStep("run notification action", () => lastNotification.Activated());
|
||||||
|
|
||||||
AddAssert("overlay shown", () => overlay.State.Value == Visibility.Visible);
|
AddAssert("overlay shown", () => overlay.State.Value == Visibility.Visible);
|
||||||
AddAssert("is resumed", () => overlay.CurrentScreen is ScreenBeatmaps);
|
AddAssert("is resumed", () => overlay.CurrentScreen is ScreenUIScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface mocks break hot reload, mocking this stub implementation instead works around it.
|
// interface mocks break hot reload, mocking this stub implementation instead works around it.
|
||||||
|
@ -435,15 +435,19 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
createScreen();
|
createScreen();
|
||||||
changeRuleset(0);
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddAssert("deselect all button disabled", () => !this.ChildrenOfType<DeselectAllModsButton>().Single().Enabled.Value);
|
||||||
|
|
||||||
AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() });
|
AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() });
|
||||||
AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
|
AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
|
||||||
|
AddAssert("deselect all button enabled", () => this.ChildrenOfType<DeselectAllModsButton>().Single().Enabled.Value);
|
||||||
|
|
||||||
AddStep("click deselect all button", () =>
|
AddStep("click deselect all button", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().Last());
|
InputManager.MoveMouseTo(this.ChildrenOfType<DeselectAllModsButton>().Single());
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any());
|
AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any());
|
||||||
|
AddAssert("deselect all button disabled", () => !this.ChildrenOfType<DeselectAllModsButton>().Single().Enabled.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -5,9 +5,12 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
{
|
{
|
||||||
@ -15,14 +18,31 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
private readonly BindableBool enabled = new BindableBool(true);
|
private readonly BindableBool enabled = new BindableBool(true);
|
||||||
|
|
||||||
protected override Drawable CreateContent() => new RoundedButton
|
protected override Drawable CreateContent()
|
||||||
|
{
|
||||||
|
return new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new RoundedButton
|
||||||
{
|
{
|
||||||
Width = 400,
|
Width = 400,
|
||||||
Text = "Test button",
|
Text = "Test button",
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Enabled = { BindTarget = enabled },
|
Enabled = { BindTarget = enabled },
|
||||||
|
},
|
||||||
|
new SettingsButton
|
||||||
|
{
|
||||||
|
Text = "Test button",
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Enabled = { BindTarget = enabled },
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDisabled()
|
public void TestDisabled()
|
||||||
@ -34,7 +54,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
public void TestBackgroundColour()
|
public void TestBackgroundColour()
|
||||||
{
|
{
|
||||||
AddStep("set red scheme", () => CreateThemedContent(OverlayColourScheme.Red));
|
AddStep("set red scheme", () => CreateThemedContent(OverlayColourScheme.Red));
|
||||||
AddAssert("first button has correct colour", () => Cell(0, 1).ChildrenOfType<RoundedButton>().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Highlight1);
|
AddAssert("rounded button has correct colour", () => Cell(0, 1).ChildrenOfType<RoundedButton>().First().BackgroundColour == new OsuColour().Blue3);
|
||||||
|
AddAssert("settings button has correct colour", () => Cell(0, 1).ChildrenOfType<SettingsButton>().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Highlight1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,6 +319,15 @@ namespace osu.Game.Beatmaps
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DeleteAllVideos()
|
||||||
|
{
|
||||||
|
realm.Write(r =>
|
||||||
|
{
|
||||||
|
var items = r.All<BeatmapSetInfo>().Where(s => !s.DeletePending && !s.Protected);
|
||||||
|
beatmapModelManager.DeleteVideos(items.ToList());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void UndeleteAll()
|
public void UndeleteAll()
|
||||||
{
|
{
|
||||||
realm.Run(r => beatmapModelManager.Undelete(r.All<BeatmapSetInfo>().Where(s => s.DeletePending).ToList()));
|
realm.Run(r => beatmapModelManager.Undelete(r.All<BeatmapSetInfo>().Where(s => s.DeletePending).ToList()));
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Database;
|
|||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Stores;
|
using osu.Game.Stores;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -33,6 +34,8 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override string[] HashableFileTypes => new[] { ".osu" };
|
protected override string[] HashableFileTypes => new[] { ".osu" };
|
||||||
|
|
||||||
|
public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv" };
|
||||||
|
|
||||||
public BeatmapModelManager(RealmAccess realm, Storage storage, BeatmapOnlineLookupQueue? onlineLookupQueue = null)
|
public BeatmapModelManager(RealmAccess realm, Storage storage, BeatmapOnlineLookupQueue? onlineLookupQueue = null)
|
||||||
: base(realm, storage, onlineLookupQueue)
|
: base(realm, storage, onlineLookupQueue)
|
||||||
{
|
{
|
||||||
@ -114,5 +117,50 @@ namespace osu.Game.Beatmaps
|
|||||||
item.CopyChangesToRealm(existing);
|
item.CopyChangesToRealm(existing);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete videos from a list of beatmaps.
|
||||||
|
/// This will post notifications tracking progress.
|
||||||
|
/// </summary>
|
||||||
|
public void DeleteVideos(List<BeatmapSetInfo> items, bool silent = false)
|
||||||
|
{
|
||||||
|
if (items.Count == 0) return;
|
||||||
|
|
||||||
|
var notification = new ProgressNotification
|
||||||
|
{
|
||||||
|
Progress = 0,
|
||||||
|
Text = $"Preparing to delete all {HumanisedModelName} videos...",
|
||||||
|
CompletionText = "No videos found to delete!",
|
||||||
|
State = ProgressNotificationState.Active,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!silent)
|
||||||
|
PostNotification?.Invoke(notification);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int deleted = 0;
|
||||||
|
|
||||||
|
foreach (var b in items)
|
||||||
|
{
|
||||||
|
if (notification.State == ProgressNotificationState.Cancelled)
|
||||||
|
// user requested abort
|
||||||
|
return;
|
||||||
|
|
||||||
|
var video = b.Files.FirstOrDefault(f => VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.Ordinal)));
|
||||||
|
|
||||||
|
if (video != null)
|
||||||
|
{
|
||||||
|
DeleteFile(b, video);
|
||||||
|
deleted++;
|
||||||
|
notification.CompletionText = $"Deleted {deleted} {HumanisedModelName} video(s)!";
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.Text = $"Deleting videos from {HumanisedModelName}s ({deleted} deleted)";
|
||||||
|
|
||||||
|
notification.Progress = (float)++i / items.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.State = ProgressNotificationState.Completed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
@ -85,6 +85,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
downloadTrackers.Add(beatmapDownloadTracker);
|
downloadTrackers.Add(beatmapDownloadTracker);
|
||||||
AddInternal(beatmapDownloadTracker);
|
AddInternal(beatmapDownloadTracker);
|
||||||
|
|
||||||
|
// Note that this is downloading the beatmaps even if they are already downloaded.
|
||||||
|
// We could rely more on `BeatmapDownloadTracker`'s exposed state to avoid this.
|
||||||
beatmapDownloader.Download(beatmapSet);
|
beatmapDownloader.Download(beatmapSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
protected readonly BeatmapDownloadTracker DownloadTracker;
|
protected readonly BeatmapDownloadTracker DownloadTracker;
|
||||||
|
|
||||||
protected BeatmapCard(APIBeatmapSet beatmapSet, bool allowExpansion = true)
|
protected BeatmapCard(APIBeatmapSet beatmapSet, bool allowExpansion = true)
|
||||||
: base(HoverSampleSet.Submit)
|
: base(HoverSampleSet.Button)
|
||||||
{
|
{
|
||||||
Expanded = new BindableBool { Disabled = !allowExpansion };
|
Expanded = new BindableBool { Disabled = !allowExpansion };
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
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.Sprites;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -245,10 +244,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (BeatmapSet.HasVideo)
|
if (BeatmapSet.HasVideo)
|
||||||
leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) });
|
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(20) });
|
||||||
|
|
||||||
if (BeatmapSet.HasStoryboard)
|
if (BeatmapSet.HasStoryboard)
|
||||||
leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) });
|
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(20) });
|
||||||
|
|
||||||
if (BeatmapSet.FeaturedInSpotlight)
|
if (BeatmapSet.FeaturedInSpotlight)
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,6 @@ using System.Collections.Generic;
|
|||||||
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.Sprites;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -226,10 +225,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (BeatmapSet.HasVideo)
|
if (BeatmapSet.HasVideo)
|
||||||
leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) });
|
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(20) });
|
||||||
|
|
||||||
if (BeatmapSet.HasStoryboard)
|
if (BeatmapSet.HasStoryboard)
|
||||||
leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) });
|
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(20) });
|
||||||
|
|
||||||
if (BeatmapSet.FeaturedInSpotlight)
|
if (BeatmapSet.FeaturedInSpotlight)
|
||||||
{
|
{
|
||||||
|
@ -3,14 +3,16 @@
|
|||||||
|
|
||||||
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.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||||
{
|
{
|
||||||
public class IconPill : CircularContainer
|
public abstract class IconPill : CircularContainer, IHasTooltip
|
||||||
{
|
{
|
||||||
public Vector2 IconSize
|
public Vector2 IconSize
|
||||||
{
|
{
|
||||||
@ -20,7 +22,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
|
|
||||||
private readonly Container iconContainer;
|
private readonly Container iconContainer;
|
||||||
|
|
||||||
public IconPill(IconUsage icon)
|
protected IconPill(IconUsage icon)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
@ -47,5 +49,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract LocalisableString TooltipText { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
osu.Game/Beatmaps/Drawables/Cards/StoryboardIconPill.cs
Normal file
19
osu.Game/Beatmaps/Drawables/Cards/StoryboardIconPill.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||||
|
{
|
||||||
|
public class StoryboardIconPill : IconPill
|
||||||
|
{
|
||||||
|
public StoryboardIconPill()
|
||||||
|
: base(FontAwesome.Solid.Image)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override LocalisableString TooltipText => BeatmapsetsStrings.ShowInfoStoryboard;
|
||||||
|
}
|
||||||
|
}
|
19
osu.Game/Beatmaps/Drawables/Cards/VideoIconPill.cs
Normal file
19
osu.Game/Beatmaps/Drawables/Cards/VideoIconPill.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||||
|
{
|
||||||
|
public class VideoIconPill : IconPill
|
||||||
|
{
|
||||||
|
public VideoIconPill()
|
||||||
|
: base(FontAwesome.Solid.Film)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override LocalisableString TooltipText => BeatmapsetsStrings.ShowInfoVideo;
|
||||||
|
}
|
||||||
|
}
|
@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Log($"Failed to process line \"{line}\" into \"{output}\": {e.Message}", LoggingTarget.Runtime, LogLevel.Important);
|
Logger.Log($"Failed to process line \"{line}\" into \"{output}\": {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performLookup()
|
private async Task performLookup()
|
||||||
{
|
{
|
||||||
// contains at most 50 unique IDs from tasks, which is used to perform the lookup.
|
// contains at most 50 unique IDs from tasks, which is used to perform the lookup.
|
||||||
var nextTaskBatch = new Dictionary<TLookup, List<TaskCompletionSource<TValue>>>();
|
var nextTaskBatch = new Dictionary<TLookup, List<TaskCompletionSource<TValue>>>();
|
||||||
@ -127,7 +127,7 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
// rather than queueing, we maintain our own single-threaded request stream.
|
// rather than queueing, we maintain our own single-threaded request stream.
|
||||||
// todo: we probably want retry logic here.
|
// todo: we probably want retry logic here.
|
||||||
api.Perform(request);
|
await api.PerformAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
finishPendingTask();
|
finishPendingTask();
|
||||||
|
|
||||||
|
@ -392,7 +392,7 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
total_writes_async.Value++;
|
total_writes_async.Value++;
|
||||||
using (var realm = getRealmInstance())
|
using (var realm = getRealmInstance())
|
||||||
await realm.WriteAsync(action);
|
await realm.WriteAsync(() => action(realm));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -188,6 +189,24 @@ namespace osu.Game.Graphics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the main accent colour for a <see cref="RoomCategory"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Color4? ForRoomCategory(RoomCategory roomCategory)
|
||||||
|
{
|
||||||
|
switch (roomCategory)
|
||||||
|
{
|
||||||
|
case RoomCategory.Spotlight:
|
||||||
|
return SpotlightColour;
|
||||||
|
|
||||||
|
case RoomCategory.FeaturedArtist:
|
||||||
|
return FeaturedArtistColour;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a foreground text colour that is supposed to contrast well with
|
/// Returns a foreground text colour that is supposed to contrast well with
|
||||||
/// the supplied <paramref name="backgroundColour"/>.
|
/// the supplied <paramref name="backgroundColour"/>.
|
||||||
@ -360,5 +379,8 @@ namespace osu.Game.Graphics
|
|||||||
public readonly Color4 ChatBlue = Color4Extensions.FromHex(@"17292e");
|
public readonly Color4 ChatBlue = Color4Extensions.FromHex(@"17292e");
|
||||||
|
|
||||||
public readonly Color4 ContextMenuGray = Color4Extensions.FromHex(@"223034");
|
public readonly Color4 ContextMenuGray = Color4Extensions.FromHex(@"223034");
|
||||||
|
|
||||||
|
public Color4 SpotlightColour => Green2;
|
||||||
|
public Color4 FeaturedArtistColour => Blue2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
Size = TwoLayerButton.SIZE_EXTENDED;
|
Size = TwoLayerButton.SIZE_EXTENDED;
|
||||||
|
|
||||||
Child = button = new TwoLayerButton(HoverSampleSet.Submit)
|
Child = button = new TwoLayerButton
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopLeft,
|
Anchor = Anchor.TopLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
|
@ -56,8 +56,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private readonly SpriteText spriteText;
|
private readonly SpriteText spriteText;
|
||||||
private Vector2 hoverSpacing => new Vector2(3f, 0f);
|
private Vector2 hoverSpacing => new Vector2(3f, 0f);
|
||||||
|
|
||||||
public DialogButton()
|
public DialogButton(HoverSampleSet sampleSet = HoverSampleSet.Button)
|
||||||
: base(HoverSampleSet.Submit)
|
: base(sampleSet)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Icon = FontAwesome.Solid.ExternalLinkAlt,
|
Icon = FontAwesome.Solid.ExternalLinkAlt,
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
new HoverClickSounds(HoverSampleSet.Submit)
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
@ -37,7 +38,10 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition))
|
if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition))
|
||||||
sampleClick?.Play();
|
{
|
||||||
|
sampleClick.Frequency.Value = 0.99 + RNG.NextDouble(0.02);
|
||||||
|
sampleClick.Play();
|
||||||
|
}
|
||||||
|
|
||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[Description("default")]
|
[Description("default")]
|
||||||
Default,
|
Default,
|
||||||
|
|
||||||
[Description("submit")]
|
|
||||||
Submit,
|
|
||||||
|
|
||||||
[Description("button")]
|
[Description("button")]
|
||||||
Button,
|
Button,
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
public class ShearedToggleButton : ShearedButton
|
public class ShearedToggleButton : ShearedButton
|
||||||
{
|
{
|
||||||
|
private Sample? sampleClick;
|
||||||
private Sample? sampleOff;
|
private Sample? sampleOff;
|
||||||
private Sample? sampleOn;
|
private Sample? sampleOn;
|
||||||
|
|
||||||
@ -39,8 +40,9 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
sampleOn = audio.Samples.Get(@"UI/check-on");
|
sampleClick = audio.Samples.Get(@"UI/default-select");
|
||||||
sampleOff = audio.Samples.Get(@"UI/check-off");
|
sampleOn = audio.Samples.Get(@"UI/dropdown-open");
|
||||||
|
sampleOff = audio.Samples.Get(@"UI/dropdown-close");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet);
|
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet);
|
||||||
@ -67,6 +69,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
private void playSample()
|
private void playSample()
|
||||||
{
|
{
|
||||||
|
sampleClick?.Play();
|
||||||
|
|
||||||
if (Active.Value)
|
if (Active.Value)
|
||||||
sampleOn?.Play();
|
sampleOn?.Play();
|
||||||
else
|
else
|
||||||
|
@ -154,7 +154,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OverlayColourProvider? colourProvider, OsuColour osuColour)
|
private void load(OverlayColourProvider? colourProvider, OsuColour osuColour)
|
||||||
{
|
{
|
||||||
background.Colour = colourProvider?.Background4 ?? Color4Extensions.FromHex(@"1c2125");
|
background.Colour = colourProvider?.Background5 ?? Color4Extensions.FromHex(@"1c2125");
|
||||||
descriptionText.Colour = osuColour.Yellow;
|
descriptionText.Colour = osuColour.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
// 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.IO;
|
using System.IO;
|
||||||
|
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.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -65,6 +67,9 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
if (BeatmapModelManager.VIDEO_EXTENSIONS.Contains(File.Extension))
|
||||||
|
return FontAwesome.Regular.FileVideo;
|
||||||
|
|
||||||
switch (File.Extension)
|
switch (File.Extension)
|
||||||
{
|
{
|
||||||
case @".ogg":
|
case @".ogg":
|
||||||
@ -77,12 +82,6 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
case @".png":
|
case @".png":
|
||||||
return FontAwesome.Regular.FileImage;
|
return FontAwesome.Regular.FileImage;
|
||||||
|
|
||||||
case @".mp4":
|
|
||||||
case @".avi":
|
|
||||||
case @".mov":
|
|
||||||
case @".flv":
|
|
||||||
return FontAwesome.Regular.FileVideo;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FontAwesome.Regular.File;
|
return FontAwesome.Regular.File;
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,11 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
using JetBrains.Annotations;
|
|
||||||
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.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays;
|
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
{
|
{
|
||||||
@ -27,9 +25,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
DefaultBackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3;
|
// According to flyte, buttons are supposed to have explicit colours for now.
|
||||||
|
// Not sure this is the correct direction, but we haven't decided on an `OverlayColourProvider` stand-in yet.
|
||||||
|
// This is a better default. See `SettingsButton` for an override which uses `OverlayColourProvider`.
|
||||||
|
DefaultBackgroundColour = colours.Blue3;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
// 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.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.Toolkit.HighPerformance;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using SharpCompress.Archives.Zip;
|
using SharpCompress.Archives.Zip;
|
||||||
|
using SixLabors.ImageSharp.Memory;
|
||||||
|
|
||||||
namespace osu.Game.IO.Archives
|
namespace osu.Game.IO.Archives
|
||||||
{
|
{
|
||||||
@ -27,15 +31,12 @@ namespace osu.Game.IO.Archives
|
|||||||
if (entry == null)
|
if (entry == null)
|
||||||
throw new FileNotFoundException();
|
throw new FileNotFoundException();
|
||||||
|
|
||||||
// allow seeking
|
var owner = MemoryAllocator.Default.Allocate<byte>((int)entry.Size);
|
||||||
MemoryStream copy = new MemoryStream();
|
|
||||||
|
|
||||||
using (Stream s = entry.OpenEntryStream())
|
using (Stream s = entry.OpenEntryStream())
|
||||||
s.CopyTo(copy);
|
s.ReadToFill(owner.Memory.Span);
|
||||||
|
|
||||||
copy.Position = 0;
|
return new MemoryOwnerMemoryStream(owner);
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
@ -45,5 +46,48 @@ namespace osu.Game.IO.Archives
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Filenames => archive.Entries.Select(e => e.Key).ExcludeSystemFileNames();
|
public override IEnumerable<string> Filenames => archive.Entries.Select(e => e.Key).ExcludeSystemFileNames();
|
||||||
|
|
||||||
|
private class MemoryOwnerMemoryStream : Stream
|
||||||
|
{
|
||||||
|
private readonly IMemoryOwner<byte> owner;
|
||||||
|
private readonly Stream stream;
|
||||||
|
|
||||||
|
public MemoryOwnerMemoryStream(IMemoryOwner<byte> owner)
|
||||||
|
{
|
||||||
|
this.owner = owner;
|
||||||
|
|
||||||
|
stream = owner.Memory.AsStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
owner?.Dispose();
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush() => stream.Flush();
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count) => stream.Read(buffer, offset, count);
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin) => stream.Seek(offset, origin);
|
||||||
|
|
||||||
|
public override void SetLength(long value) => stream.SetLength(value);
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count) => stream.Write(buffer, offset, count);
|
||||||
|
|
||||||
|
public override bool CanRead => stream.CanRead;
|
||||||
|
|
||||||
|
public override bool CanSeek => stream.CanSeek;
|
||||||
|
|
||||||
|
public override bool CanWrite => stream.CanWrite;
|
||||||
|
|
||||||
|
public override long Length => stream.Length;
|
||||||
|
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => stream.Position;
|
||||||
|
set => stream.Position = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ namespace osu.Game.Input.Bindings
|
|||||||
new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight),
|
new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight),
|
||||||
new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode),
|
new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode),
|
||||||
new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay),
|
new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay),
|
||||||
|
new KeyBinding(new[] { InputKey.T }, GlobalAction.EditorTapForBPM),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.EditorFlipHorizontally),
|
new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.EditorFlipHorizontally),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically),
|
new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.EditorDecreaseDistanceSpacing),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.EditorDecreaseDistanceSpacing),
|
||||||
@ -322,5 +323,8 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DeselectAllMods))]
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DeselectAllMods))]
|
||||||
DeselectAllMods,
|
DeselectAllMods,
|
||||||
|
|
||||||
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTapForBPM))]
|
||||||
|
EditorTapForBPM,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString EditorTimingMode => new TranslatableString(getKey(@"editor_timing_mode"), @"Timing mode");
|
public static LocalisableString EditorTimingMode => new TranslatableString(getKey(@"editor_timing_mode"), @"Timing mode");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Tap for BPM"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString EditorTapForBPM => new TranslatableString(getKey(@"editor_tap_for_bpm"), @"Tap for BPM");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Cycle grid display mode"
|
/// "Cycle grid display mode"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
34
osu.Game/Localisation/LayoutSettingsStrings.cs
Normal file
34
osu.Game/Localisation/LayoutSettingsStrings.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// 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.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Localisation
|
||||||
|
{
|
||||||
|
public static class LayoutSettingsStrings
|
||||||
|
{
|
||||||
|
private const string prefix = @"osu.Game.Resources.Localisation.LayoutSettings";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Checking for fullscreen capabilities..."
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString CheckingForFullscreenCapabilities => new TranslatableString(getKey(@"checking_for_fullscreen_capabilities"), @"Checking for fullscreen capabilities...");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "osu! is running exclusive fullscreen, guaranteeing low latency!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString OsuIsRunningExclusiveFullscreen => new TranslatableString(getKey(@"osu_is_running_exclusive_fullscreen"), @"osu! is running exclusive fullscreen, guaranteeing low latency!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Unable to run exclusive fullscreen. You'll still experience some input latency."
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString UnableToRunExclusiveFullscreen => new TranslatableString(getKey(@"unable_to_run_exclusive_fullscreen"), @"Unable to run exclusive fullscreen. You'll still experience some input latency.");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Using fullscreen on macOS makes interacting with the menu bar and spaces no longer work, and may lead to freezes if a system dialog is presented. Using borderless is recommended."
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString FullscreenMacOSNote => new TranslatableString(getKey(@"fullscreen_macos_note"), @"Using fullscreen on macOS makes interacting with the menu bar and spaces no longer work, and may lead to freezes if a system dialog is presented. Using borderless is recommended.");
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString DeleteAllBeatmaps => new TranslatableString(getKey(@"delete_all_beatmaps"), @"Delete ALL beatmaps");
|
public static LocalisableString DeleteAllBeatmaps => new TranslatableString(getKey(@"delete_all_beatmaps"), @"Delete ALL beatmaps");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Delete ALL beatmap videos"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DeleteAllBeatmapVideos => new TranslatableString(getKey(@"delete_all_beatmap_videos"), @"Delete ALL beatmap videos");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Import scores from stable"
|
/// "Import scores from stable"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -121,8 +121,16 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
if (isFailing) return;
|
if (isFailing) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
Logger.Log($@"Performing request {this}", LoggingTarget.Network);
|
Logger.Log($@"Performing request {this}", LoggingTarget.Network);
|
||||||
WebRequest.Perform();
|
WebRequest.Perform();
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// ignore this. internally Perform is running async and the fail state may have changed since
|
||||||
|
// the last check of `isFailing` above.
|
||||||
|
}
|
||||||
|
|
||||||
if (isFailing) return;
|
if (isFailing) return;
|
||||||
|
|
||||||
|
@ -62,13 +62,14 @@ namespace osu.Game.Online.API
|
|||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Queue(APIRequest request)
|
public virtual void Queue(APIRequest request)
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
{
|
{
|
||||||
if (HandleRequest?.Invoke(request) != true)
|
if (HandleRequest?.Invoke(request) != true)
|
||||||
{
|
{
|
||||||
// this will fail due to not receiving an APIAccess, and trigger a failure on the request.
|
request.Fail(new InvalidOperationException($@"{nameof(DummyAPIAccess)} cannot process this request."));
|
||||||
// this is intended - any request in testing that needs non-failures should use HandleRequest.
|
|
||||||
request.Perform(this);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Perform(APIRequest request) => HandleRequest?.Invoke(request);
|
public void Perform(APIRequest request) => HandleRequest?.Invoke(request);
|
||||||
|
@ -15,7 +15,6 @@ using osu.Game.Online.API;
|
|||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays.Chat.Listing;
|
using osu.Game.Overlays.Chat.Listing;
|
||||||
using osu.Game.Overlays.Chat.Tabs;
|
|
||||||
|
|
||||||
namespace osu.Game.Online.Chat
|
namespace osu.Game.Online.Chat
|
||||||
{
|
{
|
||||||
@ -134,7 +133,7 @@ namespace osu.Game.Online.Chat
|
|||||||
|
|
||||||
private void currentChannelChanged(ValueChangedEvent<Channel> e)
|
private void currentChannelChanged(ValueChangedEvent<Channel> e)
|
||||||
{
|
{
|
||||||
bool isSelectorChannel = e.NewValue is ChannelSelectorTabItem.ChannelSelectorTabChannel || e.NewValue is ChannelListing.ChannelListingChannel;
|
bool isSelectorChannel = e.NewValue is ChannelListing.ChannelListingChannel;
|
||||||
|
|
||||||
if (!isSelectorChannel)
|
if (!isSelectorChannel)
|
||||||
JoinChannel(e.NewValue);
|
JoinChannel(e.NewValue);
|
||||||
|
@ -13,5 +13,6 @@ namespace osu.Game.Online.Chat
|
|||||||
PM,
|
PM,
|
||||||
Group,
|
Group,
|
||||||
System,
|
System,
|
||||||
|
Announce,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ namespace osu.Game.Online.Chat
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DrawableLinkCompiler(IEnumerable<Drawable> parts)
|
public DrawableLinkCompiler(IEnumerable<Drawable> parts)
|
||||||
: base(HoverSampleSet.Submit)
|
|
||||||
{
|
{
|
||||||
Parts = parts.ToList();
|
Parts = parts.ToList();
|
||||||
}
|
}
|
||||||
|
@ -155,9 +155,6 @@ namespace osu.Game.Online.Chat
|
|||||||
{
|
{
|
||||||
public Func<Message, ChatLine> CreateChatLineAction;
|
public Func<Message, ChatLine> CreateChatLineAction;
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OsuColour colours { get; set; }
|
|
||||||
|
|
||||||
public StandAloneDrawableChannel(Channel channel)
|
public StandAloneDrawableChannel(Channel channel)
|
||||||
: base(channel)
|
: base(channel)
|
||||||
{
|
{
|
||||||
@ -166,25 +163,40 @@ namespace osu.Game.Online.Chat
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
// TODO: Remove once DrawableChannel & ChatLine padding is fixed
|
||||||
ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 };
|
ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m);
|
protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m);
|
||||||
|
|
||||||
protected override Drawable CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time)
|
protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new StandAloneDaySeparator(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class StandAloneDaySeparator : DaySeparator
|
||||||
{
|
{
|
||||||
TextSize = 14,
|
protected override float TextSize => 14;
|
||||||
Colour = colours.Yellow,
|
protected override float LineHeight => 1;
|
||||||
LineHeight = 1,
|
protected override float Spacing => 10;
|
||||||
Padding = new MarginPadding { Horizontal = 10 },
|
protected override float DateAlign => 120;
|
||||||
Margin = new MarginPadding { Vertical = 5 },
|
|
||||||
};
|
public StandAloneDaySeparator(DateTimeOffset time)
|
||||||
|
: base(time)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Height = 25;
|
||||||
|
Colour = colours.Yellow;
|
||||||
|
// TODO: Remove once DrawableChannel & ChatLine padding is fixed
|
||||||
|
Padding = new MarginPadding { Horizontal = 10 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class StandAloneMessage : ChatLine
|
protected class StandAloneMessage : ChatLine
|
||||||
{
|
{
|
||||||
protected override float TextSize => 15;
|
protected override float TextSize => 15;
|
||||||
|
|
||||||
protected override float HorizontalPadding => 10;
|
protected override float HorizontalPadding => 10;
|
||||||
protected override float MessagePadding => 120;
|
protected override float MessagePadding => 120;
|
||||||
protected override float TimestampPadding => 50;
|
protected override float TimestampPadding => 50;
|
||||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Online
|
|||||||
{
|
{
|
||||||
public class HubClientConnector : IHubClientConnector
|
public class HubClientConnector : IHubClientConnector
|
||||||
{
|
{
|
||||||
|
public const string SERVER_SHUTDOWN_MESSAGE = "Server is shutting down.";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked whenever a new hub connection is built, to configure it before it's started.
|
/// Invoked whenever a new hub connection is built, to configure it before it's started.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -64,9 +66,18 @@ namespace osu.Game.Online
|
|||||||
this.preferMessagePack = preferMessagePack;
|
this.preferMessagePack = preferMessagePack;
|
||||||
|
|
||||||
apiState.BindTo(api.State);
|
apiState.BindTo(api.State);
|
||||||
apiState.BindValueChanged(state =>
|
apiState.BindValueChanged(state => connectIfPossible(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reconnect()
|
||||||
{
|
{
|
||||||
switch (state.NewValue)
|
Logger.Log($"{clientName} reconnecting...", LoggingTarget.Network);
|
||||||
|
Task.Run(connectIfPossible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectIfPossible()
|
||||||
|
{
|
||||||
|
switch (apiState.Value)
|
||||||
{
|
{
|
||||||
case APIState.Failing:
|
case APIState.Failing:
|
||||||
case APIState.Offline:
|
case APIState.Offline:
|
||||||
@ -77,7 +88,6 @@ namespace osu.Game.Online
|
|||||||
Task.Run(connect);
|
Task.Run(connect);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task connect()
|
private async Task connect()
|
||||||
|
@ -30,5 +30,10 @@ namespace osu.Game.Online
|
|||||||
/// Invoked whenever a new hub connection is built, to configure it before it's started.
|
/// Invoked whenever a new hub connection is built, to configure it before it's started.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<HubConnection>? ConfigureConnection { get; set; }
|
public Action<HubConnection>? ConfigureConnection { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reconnect if already connected.
|
||||||
|
/// </summary>
|
||||||
|
void Reconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
|
|
||||||
APIRoom.Playlist.Clear();
|
APIRoom.Playlist.Clear();
|
||||||
APIRoom.Playlist.AddRange(joinedRoom.Playlist.Select(createPlaylistItem));
|
APIRoom.Playlist.AddRange(joinedRoom.Playlist.Select(createPlaylistItem));
|
||||||
|
APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == joinedRoom.Settings.PlaylistItemId);
|
||||||
|
|
||||||
Debug.Assert(LocalUser != null);
|
Debug.Assert(LocalUser != null);
|
||||||
addUserToAPIRoom(LocalUser);
|
addUserToAPIRoom(LocalUser);
|
||||||
@ -737,6 +738,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
APIRoom.Type.Value = Room.Settings.MatchType;
|
APIRoom.Type.Value = Room.Settings.MatchType;
|
||||||
APIRoom.QueueMode.Value = Room.Settings.QueueMode;
|
APIRoom.QueueMode.Value = Room.Settings.QueueMode;
|
||||||
APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration;
|
APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration;
|
||||||
|
APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId);
|
||||||
|
|
||||||
RoomUpdated?.Invoke();
|
RoomUpdated?.Invoke();
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
|
|
||||||
Debug.Assert(exception != null);
|
Debug.Assert(exception != null);
|
||||||
|
|
||||||
string message = exception is HubException
|
string message = exception.GetHubExceptionMessage() ?? exception.Message;
|
||||||
// HubExceptions arrive with additional message context added, but we want to display the human readable message:
|
|
||||||
// "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once."
|
|
||||||
// We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now.
|
|
||||||
? exception.Message.Substring(exception.Message.IndexOf(':') + 1).Trim()
|
|
||||||
: exception.Message;
|
|
||||||
|
|
||||||
Logger.Log(message, level: LogLevel.Important);
|
Logger.Log(message, level: LogLevel.Important);
|
||||||
onError?.Invoke(exception);
|
onError?.Invoke(exception);
|
||||||
@ -40,5 +35,16 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
onSuccess?.Invoke();
|
onSuccess?.Invoke();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
public static string? GetHubExceptionMessage(this Exception exception)
|
||||||
|
{
|
||||||
|
if (exception is HubException hubException)
|
||||||
|
// HubExceptions arrive with additional message context added, but we want to display the human readable message:
|
||||||
|
// "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once."
|
||||||
|
// We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now.
|
||||||
|
return hubException.Message.Substring(exception.Message.IndexOf(':') + 1).Trim();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.AspNetCore.SignalR.Client;
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -71,14 +73,23 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<MultiplayerRoom> JoinRoom(long roomId, string? password = null)
|
protected override async Task<MultiplayerRoom> JoinRoom(long roomId, string? password = null)
|
||||||
{
|
{
|
||||||
if (!IsConnected.Value)
|
if (!IsConnected.Value)
|
||||||
return Task.FromCanceled<MultiplayerRoom>(new CancellationToken(true));
|
throw new OperationCanceledException();
|
||||||
|
|
||||||
Debug.Assert(connection != null);
|
Debug.Assert(connection != null);
|
||||||
|
|
||||||
return connection.InvokeAsync<MultiplayerRoom>(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty);
|
try
|
||||||
|
{
|
||||||
|
return await connection.InvokeAsync<MultiplayerRoom>(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty);
|
||||||
|
}
|
||||||
|
catch (HubException exception)
|
||||||
|
{
|
||||||
|
if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
|
||||||
|
connector?.Reconnect();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task LeaveRoomInternal()
|
protected override Task LeaveRoomInternal()
|
||||||
|
@ -61,7 +61,13 @@ namespace osu.Game.Online.Rooms
|
|||||||
/// Used for serialising to the API.
|
/// Used for serialising to the API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("beatmap_id")]
|
[JsonProperty("beatmap_id")]
|
||||||
private int onlineBeatmapId => Beatmap.OnlineID;
|
private int onlineBeatmapId
|
||||||
|
{
|
||||||
|
get => Beatmap.OnlineID;
|
||||||
|
// This setter is only required for client-side serialise-then-deserialise operations.
|
||||||
|
// Serialisation is supposed to emit only a `beatmap_id`, but a (non-null) `beatmap` is required on deserialise.
|
||||||
|
set => Beatmap = new APIBeatmap { OnlineID = value };
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A beatmap representing this playlist item.
|
/// A beatmap representing this playlist item.
|
||||||
|
@ -162,6 +162,13 @@ namespace osu.Game.Online.Rooms
|
|||||||
Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue));
|
Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies values from another <see cref="Room"/> into this one.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// **Beware**: This will store references between <see cref="Room"/>s.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="other">The <see cref="Room"/> to copy values from.</param>
|
||||||
public void CopyFrom(Room other)
|
public void CopyFrom(Room other)
|
||||||
{
|
{
|
||||||
RoomID.Value = other.RoomID.Value;
|
RoomID.Value = other.RoomID.Value;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// 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.ComponentModel;
|
||||||
|
|
||||||
namespace osu.Game.Online.Rooms
|
namespace osu.Game.Online.Rooms
|
||||||
{
|
{
|
||||||
public enum RoomCategory
|
public enum RoomCategory
|
||||||
@ -8,5 +10,8 @@ namespace osu.Game.Online.Rooms
|
|||||||
// used for osu-web deserialization so names shouldn't be changed.
|
// used for osu-web deserialization so names shouldn't be changed.
|
||||||
Normal,
|
Normal,
|
||||||
Spotlight,
|
Spotlight,
|
||||||
|
|
||||||
|
[Description("Featured Artist")]
|
||||||
|
FeaturedArtist,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,12 @@
|
|||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.AspNetCore.SignalR.Client;
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.Multiplayer;
|
||||||
|
|
||||||
namespace osu.Game.Online.Spectator
|
namespace osu.Game.Online.Spectator
|
||||||
{
|
{
|
||||||
@ -47,14 +49,23 @@ namespace osu.Game.Online.Spectator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task BeginPlayingInternal(SpectatorState state)
|
protected override async Task BeginPlayingInternal(SpectatorState state)
|
||||||
{
|
{
|
||||||
if (!IsConnected.Value)
|
if (!IsConnected.Value)
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
|
|
||||||
Debug.Assert(connection != null);
|
Debug.Assert(connection != null);
|
||||||
|
|
||||||
return connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state);
|
try
|
||||||
|
{
|
||||||
|
await connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state);
|
||||||
|
}
|
||||||
|
catch (HubException exception)
|
||||||
|
{
|
||||||
|
if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
|
||||||
|
connector?.Reconnect();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task SendFramesInternal(FrameDataBundle bundle)
|
protected override Task SendFramesInternal(FrameDataBundle bundle)
|
||||||
|
@ -9,6 +9,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -658,11 +659,14 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override IDictionary<FrameworkSetting, object> GetFrameworkConfigDefaults()
|
protected override IDictionary<FrameworkSetting, object> GetFrameworkConfigDefaults()
|
||||||
=> new Dictionary<FrameworkSetting, object>
|
|
||||||
{
|
{
|
||||||
// General expectation that osu! starts in fullscreen by default (also gives the most predictable performance)
|
return new Dictionary<FrameworkSetting, object>
|
||||||
{ FrameworkSetting.WindowMode, WindowMode.Fullscreen }
|
{
|
||||||
|
// General expectation that osu! starts in fullscreen by default (also gives the most predictable performance).
|
||||||
|
// However, macOS is bound to have issues when using exclusive fullscreen as it takes full control away from OS, therefore borderless is default there.
|
||||||
|
{ FrameworkSetting.WindowMode, RuntimeInfo.OS == RuntimeInfo.Platform.macOS ? WindowMode.Borderless : WindowMode.Fullscreen }
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
|
@ -80,6 +80,9 @@ namespace osu.Game
|
|||||||
|
|
||||||
public virtual bool UseDevelopmentServer => DebugUtils.IsDebugBuild;
|
public virtual bool UseDevelopmentServer => DebugUtils.IsDebugBuild;
|
||||||
|
|
||||||
|
internal EndpointConfiguration CreateEndpoints() =>
|
||||||
|
UseDevelopmentServer ? (EndpointConfiguration)new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration();
|
||||||
|
|
||||||
public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version();
|
public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -268,7 +271,7 @@ namespace osu.Game
|
|||||||
dependencies.Cache(SkinManager = new SkinManager(Storage, realm, Host, Resources, Audio, Scheduler));
|
dependencies.Cache(SkinManager = new SkinManager(Storage, realm, Host, Resources, Audio, Scheduler));
|
||||||
dependencies.CacheAs<ISkinSource>(SkinManager);
|
dependencies.CacheAs<ISkinSource>(SkinManager);
|
||||||
|
|
||||||
EndpointConfiguration endpoints = UseDevelopmentServer ? (EndpointConfiguration)new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration();
|
EndpointConfiguration endpoints = CreateEndpoints();
|
||||||
|
|
||||||
MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl;
|
MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -69,7 +70,7 @@ namespace osu.Game.Overlays.AccountCreation
|
|||||||
},
|
},
|
||||||
usernameTextBox = new OsuTextBox
|
usernameTextBox = new OsuTextBox
|
||||||
{
|
{
|
||||||
PlaceholderText = UsersStrings.LoginUsername,
|
PlaceholderText = UsersStrings.LoginUsername.ToLower(),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
TabbableContentContainer = this
|
TabbableContentContainer = this
|
||||||
},
|
},
|
||||||
@ -91,7 +92,7 @@ namespace osu.Game.Overlays.AccountCreation
|
|||||||
},
|
},
|
||||||
passwordTextBox = new OsuPasswordTextBox
|
passwordTextBox = new OsuPasswordTextBox
|
||||||
{
|
{
|
||||||
PlaceholderText = "password",
|
PlaceholderText = UsersStrings.LoginPassword.ToLower(),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
TabbableContentContainer = this,
|
TabbableContentContainer = this,
|
||||||
},
|
},
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Regular),
|
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Regular),
|
||||||
Text = LabelFor(Value)
|
Text = LabelFor(Value)
|
||||||
},
|
},
|
||||||
new HoverClickSounds()
|
new HoverClickSounds(HoverSampleSet.TabSelect)
|
||||||
});
|
});
|
||||||
|
|
||||||
Enabled.Value = true;
|
Enabled.Value = true;
|
||||||
|
@ -10,6 +10,7 @@ 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.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
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.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -229,6 +230,8 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
Details.BeatmapInfo = b.NewValue;
|
Details.BeatmapInfo = b.NewValue;
|
||||||
externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineID}";
|
externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineID}";
|
||||||
|
|
||||||
|
onlineStatusPill.Status = b.NewValue?.Status ?? BeatmapOnlineStatus.None;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,7 +275,6 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
featuredArtist.Alpha = setInfo.NewValue.TrackId != null ? 1 : 0;
|
featuredArtist.Alpha = setInfo.NewValue.TrackId != null ? 1 : 0;
|
||||||
|
|
||||||
onlineStatusPill.FadeIn(500, Easing.OutQuint);
|
onlineStatusPill.FadeIn(500, Easing.OutQuint);
|
||||||
onlineStatusPill.Status = setInfo.NewValue.Status;
|
|
||||||
|
|
||||||
downloadButtonsContainer.FadeIn(transition_duration);
|
downloadButtonsContainer.FadeIn(transition_duration);
|
||||||
favouriteButton.FadeIn(transition_duration);
|
favouriteButton.FadeIn(transition_duration);
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
BadgeText = BeatmapsetsStrings.FeaturedArtistBadgeLabel;
|
BadgeText = BeatmapsetsStrings.FeaturedArtistBadgeLabel;
|
||||||
BadgeColour = colours.Blue1;
|
BadgeColour = colours.FeaturedArtistColour;
|
||||||
// todo: add linking support to allow redirecting featured artist badge to corresponding track.
|
// todo: add linking support to allow redirecting featured artist badge to corresponding track.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user