mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 03:25:11 +08:00
Merge branch 'master' into new-multiplayer-playlist
This commit is contained in:
commit
ef170471f0
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1124.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1127.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -1,4 +1,4 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
@ -26,12 +26,12 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public BindableFloat Scale { get; } = new BindableFloat(4)
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 2,
|
||||
MinValue = 1.5f,
|
||||
MaxValue = 10,
|
||||
};
|
||||
|
||||
[SettingSource("Style", "Change the animation style of the approach circles.", 1)]
|
||||
public Bindable<AnimationStyle> Style { get; } = new Bindable<AnimationStyle>();
|
||||
public Bindable<AnimationStyle> Style { get; } = new Bindable<AnimationStyle>(AnimationStyle.Gravity);
|
||||
|
||||
public void ApplyToDrawableHitObject(DrawableHitObject drawable)
|
||||
{
|
||||
@ -52,9 +52,18 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
default:
|
||||
case AnimationStyle.Linear:
|
||||
return Easing.None;
|
||||
|
||||
case AnimationStyle.Gravity:
|
||||
return Easing.InBack;
|
||||
|
||||
case AnimationStyle.InOut1:
|
||||
return Easing.InOutCubic;
|
||||
|
||||
case AnimationStyle.InOut2:
|
||||
return Easing.InOutQuint;
|
||||
|
||||
case AnimationStyle.Accelerate1:
|
||||
return Easing.In;
|
||||
|
||||
@ -64,9 +73,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
case AnimationStyle.Accelerate3:
|
||||
return Easing.InQuint;
|
||||
|
||||
case AnimationStyle.Gravity:
|
||||
return Easing.InBack;
|
||||
|
||||
case AnimationStyle.Decelerate1:
|
||||
return Easing.Out;
|
||||
|
||||
@ -76,16 +82,14 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
case AnimationStyle.Decelerate3:
|
||||
return Easing.OutQuint;
|
||||
|
||||
case AnimationStyle.InOut1:
|
||||
return Easing.InOutCubic;
|
||||
|
||||
case AnimationStyle.InOut2:
|
||||
return Easing.InOutQuint;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(style), style, @"Unsupported animation style");
|
||||
}
|
||||
}
|
||||
|
||||
public enum AnimationStyle
|
||||
{
|
||||
Linear,
|
||||
Gravity,
|
||||
InOut1,
|
||||
InOut2,
|
||||
|
@ -12,6 +12,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
@ -474,7 +475,7 @@ namespace osu.Game.Tests.Database
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImportThenDeleteThenImport()
|
||||
public void TestImportThenDeleteThenImportOptimisedPath()
|
||||
{
|
||||
RunTestWithRealmAsync(async (realmFactory, storage) =>
|
||||
{
|
||||
@ -485,11 +486,39 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
deleteBeatmapSet(imported, realmFactory.Context);
|
||||
|
||||
Assert.IsTrue(imported.DeletePending);
|
||||
|
||||
var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context);
|
||||
|
||||
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
|
||||
Assert.IsTrue(imported.ID == importedSecondTime.ID);
|
||||
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
|
||||
Assert.IsFalse(imported.DeletePending);
|
||||
Assert.IsFalse(importedSecondTime.DeletePending);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImportThenDeleteThenImportNonOptimisedPath()
|
||||
{
|
||||
RunTestWithRealmAsync(async (realmFactory, storage) =>
|
||||
{
|
||||
using var importer = new NonOptimisedBeatmapImporter(realmFactory, storage);
|
||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realmFactory.Context);
|
||||
|
||||
deleteBeatmapSet(imported, realmFactory.Context);
|
||||
|
||||
Assert.IsTrue(imported.DeletePending);
|
||||
|
||||
var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context);
|
||||
|
||||
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
|
||||
Assert.IsTrue(imported.ID == importedSecondTime.ID);
|
||||
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
|
||||
Assert.IsFalse(imported.DeletePending);
|
||||
Assert.IsFalse(importedSecondTime.DeletePending);
|
||||
});
|
||||
}
|
||||
|
||||
@ -823,7 +852,11 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
IQueryable<RealmBeatmapSet>? resultSets = null;
|
||||
|
||||
waitForOrAssert(() => (resultSets = realm.All<RealmBeatmapSet>().Where(s => !s.DeletePending && s.OnlineID == 241526)).Any(),
|
||||
waitForOrAssert(() =>
|
||||
{
|
||||
realm.Refresh();
|
||||
return (resultSets = realm.All<RealmBeatmapSet>().Where(s => !s.DeletePending && s.OnlineID == 241526)).Any();
|
||||
},
|
||||
@"BeatmapSet did not import to the database in allocated time.", timeout);
|
||||
|
||||
// ensure we were stored to beatmap database backing...
|
||||
@ -836,16 +869,16 @@ namespace osu.Game.Tests.Database
|
||||
// ReSharper disable once PossibleUnintendedReferenceComparison
|
||||
IEnumerable<RealmBeatmap> queryBeatmaps() => realm.All<RealmBeatmap>().Where(s => s.BeatmapSet != null && s.BeatmapSet == set);
|
||||
|
||||
waitForOrAssert(() => queryBeatmaps().Count() == 12, @"Beatmaps did not import to the database in allocated time", timeout);
|
||||
waitForOrAssert(() => queryBeatmapSets().Count() == 1, @"BeatmapSet did not import to the database in allocated time", timeout);
|
||||
Assert.AreEqual(12, queryBeatmaps().Count(), @"Beatmap count was not correct");
|
||||
Assert.AreEqual(1, queryBeatmapSets().Count(), @"Beatmapset count was not correct");
|
||||
|
||||
int countBeatmapSetBeatmaps = 0;
|
||||
int countBeatmaps = 0;
|
||||
int countBeatmapSetBeatmaps;
|
||||
int countBeatmaps;
|
||||
|
||||
waitForOrAssert(() =>
|
||||
(countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count) ==
|
||||
(countBeatmaps = queryBeatmaps().Count()),
|
||||
$@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).", timeout);
|
||||
Assert.AreEqual(
|
||||
countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count,
|
||||
countBeatmaps = queryBeatmaps().Count(),
|
||||
$@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).");
|
||||
|
||||
foreach (RealmBeatmap b in set.Beatmaps)
|
||||
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineID == b.OnlineID));
|
||||
@ -867,5 +900,15 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
Assert.Fail(failureMessage);
|
||||
}
|
||||
|
||||
public class NonOptimisedBeatmapImporter : BeatmapImporter
|
||||
{
|
||||
public NonOptimisedBeatmapImporter(RealmContextFactory realmFactory, Storage storage)
|
||||
: base(realmFactory, storage)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool HasCustomHashFunction => true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,22 @@ namespace osu.Game.Tests.Database
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAccessAfterAttach()
|
||||
{
|
||||
RunTestWithRealm((realmFactory, _) =>
|
||||
{
|
||||
var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata());
|
||||
|
||||
var liveBeatmap = beatmap.ToLive();
|
||||
|
||||
using (var context = realmFactory.CreateContext())
|
||||
context.Write(r => r.Add(beatmap));
|
||||
|
||||
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAccessNonManaged()
|
||||
{
|
||||
@ -51,7 +67,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealm((realmFactory, _) =>
|
||||
{
|
||||
RealmLive<RealmBeatmap>? liveBeatmap = null;
|
||||
ILive<RealmBeatmap>? liveBeatmap = null;
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
using (var threadContext = realmFactory.CreateContext())
|
||||
@ -88,7 +104,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealm((realmFactory, _) =>
|
||||
{
|
||||
RealmLive<RealmBeatmap>? liveBeatmap = null;
|
||||
ILive<RealmBeatmap>? liveBeatmap = null;
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
using (var threadContext = realmFactory.CreateContext())
|
||||
@ -117,7 +133,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealm((realmFactory, _) =>
|
||||
{
|
||||
RealmLive<RealmBeatmap>? liveBeatmap = null;
|
||||
ILive<RealmBeatmap>? liveBeatmap = null;
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
using (var threadContext = realmFactory.CreateContext())
|
||||
@ -143,7 +159,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealm((realmFactory, _) =>
|
||||
{
|
||||
RealmLive<RealmBeatmap>? liveBeatmap = null;
|
||||
ILive<RealmBeatmap>? liveBeatmap = null;
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
using (var threadContext = realmFactory.CreateContext())
|
||||
@ -176,7 +192,7 @@ namespace osu.Game.Tests.Database
|
||||
using (var updateThreadContext = realmFactory.CreateContext())
|
||||
{
|
||||
updateThreadContext.All<RealmBeatmap>().SubscribeForNotifications(gotChange);
|
||||
RealmLive<RealmBeatmap>? liveBeatmap = null;
|
||||
ILive<RealmBeatmap>? liveBeatmap = null;
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
|
@ -0,0 +1,71 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Beatmaps
|
||||
{
|
||||
public class TestSceneBeatmapCardDifficultyList : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
var beatmapSet = new APIBeatmapSet
|
||||
{
|
||||
Beatmaps = new[]
|
||||
{
|
||||
new APIBeatmap { RulesetID = 1, StarRating = 5.76, DifficultyName = "Oni" },
|
||||
new APIBeatmap { RulesetID = 1, StarRating = 3.20, DifficultyName = "Muzukashii" },
|
||||
new APIBeatmap { RulesetID = 1, StarRating = 2.45, DifficultyName = "Futsuu" },
|
||||
|
||||
new APIBeatmap { RulesetID = 0, StarRating = 2.04, DifficultyName = "Normal" },
|
||||
new APIBeatmap { RulesetID = 0, StarRating = 3.51, DifficultyName = "Hard" },
|
||||
new APIBeatmap { RulesetID = 0, StarRating = 5.25, DifficultyName = "Insane" },
|
||||
|
||||
new APIBeatmap { RulesetID = 2, StarRating = 2.64, DifficultyName = "Salad" },
|
||||
new APIBeatmap { RulesetID = 2, StarRating = 3.56, DifficultyName = "Platter" },
|
||||
new APIBeatmap { RulesetID = 2, StarRating = 4.65, DifficultyName = "Rain" },
|
||||
|
||||
new APIBeatmap { RulesetID = 3, StarRating = 1.93, DifficultyName = "[7K] Normal" },
|
||||
new APIBeatmap { RulesetID = 3, StarRating = 3.18, DifficultyName = "[7K] Hyper" },
|
||||
new APIBeatmap { RulesetID = 3, StarRating = 4.82, DifficultyName = "[7K] Another" },
|
||||
|
||||
new APIBeatmap { RulesetID = 4, StarRating = 9.99, DifficultyName = "Unknown?!" },
|
||||
}
|
||||
};
|
||||
|
||||
Child = new Container
|
||||
{
|
||||
Width = 300,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background2
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(10),
|
||||
Child = new BeatmapCardDifficultyList(beatmapSet)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@ using osu.Game.Database;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
@ -236,7 +235,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
assertDownloadButtonVisible(false);
|
||||
|
||||
void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}",
|
||||
() => playlist.ChildrenOfType<BeatmapPanelDownloadButton>().Single().Alpha == (visible ? 1 : 0));
|
||||
() => playlist.ChildrenOfType<BeatmapDownloadButton>().Single().Alpha == (visible ? 1 : 0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -250,7 +249,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
createPlaylist(byOnlineId, byChecksum);
|
||||
|
||||
AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapPanelDownloadButton>().All(d => d.IsPresent));
|
||||
AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapDownloadButton>().All(d => d.IsPresent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -6,15 +6,15 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneDirectDownloadButton : OsuTestScene
|
||||
public class TestSceneBeatmapDownloadButton : OsuTestScene
|
||||
{
|
||||
private TestDownloadButton downloadButton;
|
||||
|
||||
@ -137,7 +137,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
return apiBeatmapSet;
|
||||
}
|
||||
|
||||
private class TestDownloadButton : BeatmapPanelDownloadButton
|
||||
private class TestDownloadButton : BeatmapDownloadButton
|
||||
{
|
||||
public new bool DownloadEnabled => base.DownloadEnabled;
|
||||
|
@ -1,137 +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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
[Cached(typeof(IPreviewTrackOwner))]
|
||||
public class TestSceneDirectPanel : OsuTestScene, IPreviewTrackOwner
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
var normal = getBeatmapSet();
|
||||
normal.HasVideo = true;
|
||||
normal.HasStoryboard = true;
|
||||
|
||||
var undownloadable = getUndownloadableBeatmapSet();
|
||||
var manyDifficulties = getManyDifficultiesBeatmapSet();
|
||||
|
||||
var explicitMap = getBeatmapSet();
|
||||
explicitMap.HasExplicitContent = true;
|
||||
|
||||
var featuredMap = getBeatmapSet();
|
||||
featuredMap.TrackId = 1;
|
||||
|
||||
var explicitFeaturedMap = getBeatmapSet();
|
||||
explicitFeaturedMap.HasExplicitContent = true;
|
||||
explicitFeaturedMap.TrackId = 2;
|
||||
|
||||
Child = new BasicScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Full,
|
||||
Padding = new MarginPadding(20),
|
||||
Spacing = new Vector2(5, 20),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new GridBeatmapPanel(normal),
|
||||
new GridBeatmapPanel(undownloadable),
|
||||
new GridBeatmapPanel(manyDifficulties),
|
||||
new GridBeatmapPanel(explicitMap),
|
||||
new GridBeatmapPanel(featuredMap),
|
||||
new GridBeatmapPanel(explicitFeaturedMap),
|
||||
new ListBeatmapPanel(normal),
|
||||
new ListBeatmapPanel(undownloadable),
|
||||
new ListBeatmapPanel(manyDifficulties),
|
||||
new ListBeatmapPanel(explicitMap),
|
||||
new ListBeatmapPanel(featuredMap),
|
||||
new ListBeatmapPanel(explicitFeaturedMap)
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
APIBeatmapSet getBeatmapSet() => CreateAPIBeatmapSet(Ruleset.Value);
|
||||
|
||||
APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet
|
||||
{
|
||||
OnlineID = 123,
|
||||
Title = "undownloadable beatmap",
|
||||
Artist = "test",
|
||||
Source = "more tests",
|
||||
Author = new APIUser
|
||||
{
|
||||
Username = "BanchoBot",
|
||||
Id = 3,
|
||||
},
|
||||
Availability = new BeatmapSetOnlineAvailability
|
||||
{
|
||||
DownloadDisabled = true,
|
||||
},
|
||||
Preview = @"https://b.ppy.sh/preview/12345.mp3",
|
||||
PlayCount = 123,
|
||||
FavouriteCount = 456,
|
||||
BPM = 111,
|
||||
HasVideo = true,
|
||||
HasStoryboard = true,
|
||||
Covers = new BeatmapSetOnlineCovers(),
|
||||
Beatmaps = new[]
|
||||
{
|
||||
new APIBeatmap
|
||||
{
|
||||
RulesetID = Ruleset.Value.OnlineID,
|
||||
DifficultyName = "Test",
|
||||
StarRating = 6.42,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
APIBeatmapSet getManyDifficultiesBeatmapSet()
|
||||
{
|
||||
var beatmaps = new List<APIBeatmap>();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
beatmaps.Add(new APIBeatmap
|
||||
{
|
||||
RulesetID = i % 4,
|
||||
StarRating = 2 + i % 4 * 2,
|
||||
OverallDifficulty = 3.5f,
|
||||
});
|
||||
}
|
||||
|
||||
return new APIBeatmapSet
|
||||
{
|
||||
OnlineID = 1,
|
||||
Title = "undownloadable beatmap",
|
||||
Artist = "test",
|
||||
Source = "more tests",
|
||||
Author = new APIUser
|
||||
{
|
||||
Username = "BanchoBot",
|
||||
Id = 3,
|
||||
},
|
||||
HasVideo = true,
|
||||
HasStoryboard = true,
|
||||
Covers = new BeatmapSetOnlineCovers(),
|
||||
Beatmaps = beatmaps.ToArray(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
beatmaps.Add(testBeatmap);
|
||||
|
||||
AddStep("set ruleset", () => Ruleset.Value = rulesetInfo);
|
||||
setRuleset(rulesetInfo);
|
||||
|
||||
selectBeatmap(testBeatmap);
|
||||
|
||||
@ -167,6 +167,22 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
label => label.Statistic.Name == "BPM" && label.Statistic.Content == target.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
private void setRuleset(RulesetInfo rulesetInfo)
|
||||
{
|
||||
Container containerBefore = null;
|
||||
|
||||
AddStep("set ruleset", () =>
|
||||
{
|
||||
// wedge content is only refreshed if the ruleset changes, so only wait for load in that case.
|
||||
if (!rulesetInfo.Equals(Ruleset.Value))
|
||||
containerBefore = infoWedge.DisplayedContent;
|
||||
|
||||
Ruleset.Value = rulesetInfo;
|
||||
});
|
||||
|
||||
AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore);
|
||||
}
|
||||
|
||||
private void selectBeatmap([CanBeNull] IBeatmap b)
|
||||
{
|
||||
Container containerBefore = null;
|
||||
|
@ -87,9 +87,9 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
// Recreate the old setup that uses "tournament" as the base path.
|
||||
string oldPath = Path.Combine(osuRoot, "tournament");
|
||||
|
||||
string videosPath = Path.Combine(oldPath, "videos");
|
||||
string modsPath = Path.Combine(oldPath, "mods");
|
||||
string flagsPath = Path.Combine(oldPath, "flags");
|
||||
string videosPath = Path.Combine(oldPath, "Videos");
|
||||
string modsPath = Path.Combine(oldPath, "Mods");
|
||||
string flagsPath = Path.Combine(oldPath, "Flags");
|
||||
|
||||
Directory.CreateDirectory(videosPath);
|
||||
Directory.CreateDirectory(modsPath);
|
||||
@ -123,9 +123,9 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
|
||||
string migratedPath = Path.Combine(host.Storage.GetFullPath("."), "tournaments", "default");
|
||||
|
||||
videosPath = Path.Combine(migratedPath, "videos");
|
||||
modsPath = Path.Combine(migratedPath, "mods");
|
||||
flagsPath = Path.Combine(migratedPath, "flags");
|
||||
videosPath = Path.Combine(migratedPath, "Videos");
|
||||
modsPath = Path.Combine(migratedPath, "Mods");
|
||||
flagsPath = Path.Combine(migratedPath, "Flags");
|
||||
|
||||
videoFile = Path.Combine(videosPath, "video.mp4");
|
||||
modFile = Path.Combine(modsPath, "mod.png");
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Tournament.Components
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures, LadderInfo ladderInfo)
|
||||
{
|
||||
var customTexture = textures.Get($"mods/{modAcronym}");
|
||||
var customTexture = textures.Get($"Mods/{modAcronym}");
|
||||
|
||||
if (customTexture != null)
|
||||
{
|
||||
|
@ -51,6 +51,23 @@ namespace osu.Game.Tournament.IO
|
||||
Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty));
|
||||
}
|
||||
|
||||
protected override void ChangeTargetStorage(Storage newStorage)
|
||||
{
|
||||
// due to an unfortunate oversight, on OSes that are sensitive to pathname casing
|
||||
// the custom flags directory needed to be named `Flags` (uppercase),
|
||||
// while custom mods and videos directories needed to be named `mods` and `videos` respectively (lowercase).
|
||||
// to unify handling to uppercase, move any non-compliant directories automatically for the user to migrate.
|
||||
// can be removed 20220528
|
||||
if (newStorage.ExistsDirectory("flags"))
|
||||
AttemptOperation(() => Directory.Move(newStorage.GetFullPath("flags"), newStorage.GetFullPath("Flags")));
|
||||
if (newStorage.ExistsDirectory("mods"))
|
||||
AttemptOperation(() => Directory.Move(newStorage.GetFullPath("mods"), newStorage.GetFullPath("Mods")));
|
||||
if (newStorage.ExistsDirectory("videos"))
|
||||
AttemptOperation(() => Directory.Move(newStorage.GetFullPath("videos"), newStorage.GetFullPath("Videos")));
|
||||
|
||||
base.ChangeTargetStorage(newStorage);
|
||||
}
|
||||
|
||||
public IEnumerable<string> ListTournaments() => AllTournaments.GetDirectories(string.Empty);
|
||||
|
||||
public override void Migrate(Storage newStorage)
|
||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Tournament.IO
|
||||
public class TournamentVideoResourceStore : NamespacedResourceStore<byte[]>
|
||||
{
|
||||
public TournamentVideoResourceStore(Storage storage)
|
||||
: base(new StorageBackedResourceStore(storage), "videos")
|
||||
: base(new StorageBackedResourceStore(storage), "Videos")
|
||||
{
|
||||
AddExtension("m4v");
|
||||
AddExtension("avi");
|
||||
|
@ -197,7 +197,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
||||
{
|
||||
row.Add(new Sprite
|
||||
{
|
||||
Texture = textures.Get($"mods/{mods.ToLower()}"),
|
||||
Texture = textures.Get($"Mods/{mods.ToLower()}"),
|
||||
Scale = new Vector2(0.5f)
|
||||
});
|
||||
}
|
||||
|
@ -105,7 +105,9 @@ namespace osu.Game.Audio
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Logger.Log($"A {nameof(PreviewTrack)} was created without a containing {nameof(IPreviewTrackOwner)}. An owner should be added for correct behaviour.");
|
||||
|
||||
if (Owner == null)
|
||||
Logger.Log($"A {nameof(PreviewTrack)} was created without a containing {nameof(IPreviewTrackOwner)}. An owner should be added for correct behaviour.");
|
||||
}
|
||||
|
||||
protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineID}.mp3");
|
||||
|
@ -288,9 +288,9 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
#region Implementation of IModelFileManager<in BeatmapSetInfo,in BeatmapSetFileInfo>
|
||||
|
||||
public void ReplaceFile(BeatmapSetInfo model, BeatmapSetFileInfo file, Stream contents, string filename = null)
|
||||
public void ReplaceFile(BeatmapSetInfo model, BeatmapSetFileInfo file, Stream contents)
|
||||
{
|
||||
beatmapModelManager.ReplaceFile(model, file, contents, filename);
|
||||
beatmapModelManager.ReplaceFile(model, file, contents);
|
||||
}
|
||||
|
||||
public void DeleteFile(BeatmapSetInfo model, BeatmapSetFileInfo file)
|
||||
|
@ -6,7 +6,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -14,9 +13,9 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
public class BeatmapPanelDownloadButton : CompositeDrawable
|
||||
public class BeatmapDownloadButton : CompositeDrawable
|
||||
{
|
||||
protected bool DownloadEnabled => button.Enabled.Value;
|
||||
|
||||
@ -35,7 +34,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
|
||||
private readonly IBeatmapSetInfo beatmapSet;
|
||||
|
||||
public BeatmapPanelDownloadButton(IBeatmapSetInfo beatmapSet)
|
||||
public BeatmapDownloadButton(IBeatmapSetInfo beatmapSet)
|
||||
{
|
||||
this.beatmapSet = beatmapSet;
|
||||
|
@ -23,7 +23,6 @@ using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osuTK;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using DownloadButton = osu.Game.Beatmaps.Drawables.Cards.Buttons.DownloadButton;
|
||||
|
||||
|
103
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDifficultyList.cs
Normal file
103
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDifficultyList.cs
Normal file
@ -0,0 +1,103 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
{
|
||||
public class BeatmapCardDifficultyList : CompositeDrawable
|
||||
{
|
||||
public BeatmapCardDifficultyList(IBeatmapSetInfo beatmapSetInfo)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
FillFlowContainer flow;
|
||||
|
||||
InternalChild = flow = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 3)
|
||||
};
|
||||
|
||||
bool firstGroup = true;
|
||||
|
||||
foreach (var group in beatmapSetInfo.Beatmaps.GroupBy(beatmap => beatmap.Ruleset.OnlineID).OrderBy(group => group.Key))
|
||||
{
|
||||
if (!firstGroup)
|
||||
{
|
||||
flow.Add(Empty().With(s =>
|
||||
{
|
||||
s.RelativeSizeAxes = Axes.X;
|
||||
s.Height = 4;
|
||||
}));
|
||||
}
|
||||
|
||||
foreach (var difficulty in group.OrderBy(b => b.StarRating))
|
||||
flow.Add(new BeatmapCardDifficultyRow(difficulty));
|
||||
|
||||
firstGroup = false;
|
||||
}
|
||||
}
|
||||
|
||||
private class BeatmapCardDifficultyRow : CompositeDrawable
|
||||
{
|
||||
private readonly IBeatmapInfo beatmapInfo;
|
||||
|
||||
public BeatmapCardDifficultyRow(IBeatmapInfo beatmapInfo)
|
||||
{
|
||||
this.beatmapInfo = beatmapInfo;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(4, 0),
|
||||
Children = new[]
|
||||
{
|
||||
(rulesets.GetRuleset(beatmapInfo.Ruleset.OnlineID)?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }).With(icon =>
|
||||
{
|
||||
icon.Anchor = icon.Origin = Anchor.CentreLeft;
|
||||
icon.Size = new Vector2(16);
|
||||
}),
|
||||
new StarRatingDisplay(new StarDifficulty(beatmapInfo.StarRating, 0), StarRatingDisplaySize.Small)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
},
|
||||
new LinkFlowContainer(s =>
|
||||
{
|
||||
s.Font = OsuFont.Default.With(size: 14, weight: FontWeight.SemiBold);
|
||||
}).With(d =>
|
||||
{
|
||||
d.AutoSizeAxes = Axes.Both;
|
||||
d.Anchor = Anchor.CentreLeft;
|
||||
d.Origin = Anchor.CentreLeft;
|
||||
d.Padding = new MarginPadding { Bottom = 2 };
|
||||
d.AddLink(beatmapInfo.DifficultyName, LinkAction.OpenBeatmap, beatmapInfo.OnlineID.ToString());
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -97,6 +97,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons
|
||||
if (previewTrack == null)
|
||||
{
|
||||
toggleLoading(true);
|
||||
|
||||
LoadComponentAsync(previewTrack = previewTrackManager.Get(beatmapSetInfo), onPreviewLoaded);
|
||||
}
|
||||
else
|
||||
@ -112,18 +113,23 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons
|
||||
|
||||
private void onPreviewLoaded(PreviewTrack loadedPreview)
|
||||
{
|
||||
// another async load might have completed before this one.
|
||||
// if so, do not make any changes.
|
||||
if (loadedPreview != previewTrack)
|
||||
return;
|
||||
// Make sure that we schedule to after the next audio frame to fix crashes in single-threaded execution.
|
||||
// See: https://github.com/ppy/osu-framework/issues/4692
|
||||
Schedule(() =>
|
||||
{
|
||||
// another async load might have completed before this one.
|
||||
// if so, do not make any changes.
|
||||
if (loadedPreview != previewTrack)
|
||||
return;
|
||||
|
||||
AddInternal(loadedPreview);
|
||||
toggleLoading(false);
|
||||
AddInternal(loadedPreview);
|
||||
toggleLoading(false);
|
||||
|
||||
loadedPreview.Stopped += () => Schedule(() => Playing.Value = false);
|
||||
loadedPreview.Stopped += () => Schedule(() => Playing.Value = false);
|
||||
|
||||
if (Playing.Value)
|
||||
tryStartPreview();
|
||||
if (Playing.Value)
|
||||
tryStartPreview();
|
||||
});
|
||||
}
|
||||
|
||||
private void tryStartPreview()
|
||||
|
@ -8,7 +8,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
{
|
||||
public class IconPill : CircularContainer
|
||||
{
|
@ -62,7 +62,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
// matching web: https://github.com/ppy/osu-web/blob/d06d8c5e735eb1f48799b1654b528e9a7afb0a35/resources/assets/lib/beatmapset-panel.tsx#L127
|
||||
bool collapsed = beatmapSet.Beatmaps.Count() > 12;
|
||||
|
||||
foreach (var rulesetGrouping in beatmapSet.Beatmaps.GroupBy(beatmap => beatmap.Ruleset.OnlineID))
|
||||
foreach (var rulesetGrouping in beatmapSet.Beatmaps.GroupBy(beatmap => beatmap.Ruleset.OnlineID).OrderBy(group => group.Key))
|
||||
{
|
||||
flow.Add(new RulesetDifficultyGroup(rulesetGrouping.Key, rulesetGrouping, collapsed));
|
||||
}
|
||||
|
@ -5,13 +5,12 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
public class DownloadProgressBar : CompositeDrawable
|
||||
{
|
@ -453,13 +453,12 @@ namespace osu.Game.Database
|
||||
/// <param name="model">The item to operate on.</param>
|
||||
/// <param name="file">The existing file to be replaced.</param>
|
||||
/// <param name="contents">The new file contents.</param>
|
||||
/// <param name="filename">An optional filename for the new file. Will use the previous filename if not specified.</param>
|
||||
public void ReplaceFile(TModel model, TFileModel file, Stream contents, string filename = null)
|
||||
public void ReplaceFile(TModel model, TFileModel file, Stream contents)
|
||||
{
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
DeleteFile(model, file);
|
||||
AddFile(model, contents, filename ?? file.Filename);
|
||||
AddFile(model, contents, file.Filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,7 @@ namespace osu.Game.Database
|
||||
/// <param name="model">The item to operate on.</param>
|
||||
/// <param name="file">The existing file to be replaced.</param>
|
||||
/// <param name="contents">The new file contents.</param>
|
||||
/// <param name="filename">An optional filename for the new file. Will use the previous filename if not specified.</param>
|
||||
void ReplaceFile(TModel model, TFileModel file, Stream contents, string filename = null);
|
||||
void ReplaceFile(TModel model, TFileModel file, Stream contents);
|
||||
|
||||
/// <summary>
|
||||
/// Delete an existing file.
|
||||
|
@ -307,6 +307,9 @@ namespace osu.Game.Database
|
||||
case 10:
|
||||
string rulesetSettingClassName = getMappedOrOriginalName(typeof(RealmRulesetSetting));
|
||||
|
||||
if (!migration.OldRealm.Schema.TryFindObjectSchema(rulesetSettingClassName, out _))
|
||||
return;
|
||||
|
||||
var oldSettings = migration.OldRealm.DynamicApi.All(rulesetSettingClassName);
|
||||
var newSettings = migration.NewRealm.All<RealmRulesetSetting>().ToList();
|
||||
|
||||
@ -329,6 +332,9 @@ namespace osu.Game.Database
|
||||
case 11:
|
||||
string keyBindingClassName = getMappedOrOriginalName(typeof(RealmKeyBinding));
|
||||
|
||||
if (!migration.OldRealm.Schema.TryFindObjectSchema(keyBindingClassName, out _))
|
||||
return;
|
||||
|
||||
var oldKeyBindings = migration.OldRealm.DynamicApi.All(keyBindingClassName);
|
||||
var newKeyBindings = migration.NewRealm.All<RealmKeyBinding>().ToList();
|
||||
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Database
|
||||
{
|
||||
public Guid ID { get; }
|
||||
|
||||
public bool IsManaged { get; }
|
||||
public bool IsManaged => data.IsManaged;
|
||||
|
||||
private readonly SynchronizationContext? fetchedContext;
|
||||
private readonly int fetchedThreadId;
|
||||
@ -37,8 +37,6 @@ namespace osu.Game.Database
|
||||
|
||||
if (data.IsManaged)
|
||||
{
|
||||
IsManaged = true;
|
||||
|
||||
fetchedContext = SynchronizationContext.Current;
|
||||
fetchedThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
@ -49,13 +49,13 @@ namespace osu.Game.Database
|
||||
return mapper.Map<T>(item);
|
||||
}
|
||||
|
||||
public static List<RealmLive<T>> ToLive<T>(this IEnumerable<T> realmList)
|
||||
public static List<ILive<T>> ToLive<T>(this IEnumerable<T> realmList)
|
||||
where T : RealmObject, IHasGuidPrimaryKey
|
||||
{
|
||||
return realmList.Select(l => new RealmLive<T>(l)).ToList();
|
||||
return realmList.Select(l => new RealmLive<T>(l)).Cast<ILive<T>>().ToList();
|
||||
}
|
||||
|
||||
public static RealmLive<T> ToLive<T>(this T realmObject)
|
||||
public static ILive<T> ToLive<T>(this T realmObject)
|
||||
where T : RealmObject, IHasGuidPrimaryKey
|
||||
{
|
||||
return new RealmLive<T>(realmObject);
|
||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Origin = Anchor.TopRight;
|
||||
|
||||
BackgroundColour = Color4.Black.Opacity(0.7f);
|
||||
MaxHeight = 400;
|
||||
MaxHeight = 200;
|
||||
}
|
||||
|
||||
protected override DrawableDropdownMenuItem CreateDrawableDropdownMenuItem(MenuItem item) => new DrawableOsuTabDropdownMenuItem(item);
|
||||
|
@ -16,6 +16,7 @@ namespace osu.Game.Models
|
||||
{
|
||||
public RealmFile File { get; set; } = null!;
|
||||
|
||||
// [Indexed] cannot be used on `EmbeddedObject`s as it only applies to top-level queries. May need to reconsider this if performance becomes a concern.
|
||||
public string Filename { get; set; } = null!;
|
||||
|
||||
public RealmNamedFileUsage(RealmFile file, string filename)
|
||||
|
@ -377,6 +377,13 @@ namespace osu.Game
|
||||
FrameStatistics.ValueChanged += e => fpsDisplayVisible.Value = e.NewValue != FrameStatisticsMode.None;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
realmFactory.Refresh();
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
|
||||
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
@ -442,10 +449,6 @@ namespace osu.Game
|
||||
|
||||
protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage);
|
||||
|
||||
private void migrateDataToRealm()
|
||||
{
|
||||
}
|
||||
|
||||
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> r)
|
||||
{
|
||||
if (r.NewValue?.Available != true)
|
||||
|
@ -1,216 +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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
{
|
||||
public abstract class BeatmapPanel : OsuClickableContainer, IHasContextMenu
|
||||
{
|
||||
public readonly APIBeatmapSet SetInfo;
|
||||
|
||||
private const double hover_transition_time = 400;
|
||||
private const int maximum_difficulty_icons = 10;
|
||||
|
||||
private Container content;
|
||||
|
||||
public PreviewTrack Preview => PlayButton.Preview;
|
||||
public IBindable<bool> PreviewPlaying => PlayButton?.Playing;
|
||||
|
||||
protected abstract PlayButton PlayButton { get; }
|
||||
protected abstract Box PreviewBar { get; }
|
||||
|
||||
protected virtual bool FadePlayButton => true;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
protected Action ViewBeatmap;
|
||||
|
||||
protected BeatmapPanel(APIBeatmapSet setInfo)
|
||||
: base(HoverSampleSet.Submit)
|
||||
{
|
||||
Debug.Assert(setInfo.OnlineID > 0);
|
||||
|
||||
SetInfo = setInfo;
|
||||
}
|
||||
|
||||
private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
Radius = 2f,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
};
|
||||
|
||||
private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0f, 5f),
|
||||
Radius = 10f,
|
||||
Colour = Color4.Black.Opacity(0.3f),
|
||||
};
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
|
||||
{
|
||||
AddInternal(content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
EdgeEffect = edgeEffectNormal,
|
||||
Children = new[]
|
||||
{
|
||||
CreateBackground(),
|
||||
new DownloadProgressBar(SetInfo)
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Depth = -1,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Action = ViewBeatmap = () =>
|
||||
{
|
||||
Debug.Assert(SetInfo.OnlineID > 0);
|
||||
beatmapSetOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineID);
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (PreviewPlaying.Value && Preview != null && Preview.TrackLoaded)
|
||||
{
|
||||
PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
|
||||
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
|
||||
if (FadePlayButton)
|
||||
PlayButton.FadeIn(120, Easing.InOutQuint);
|
||||
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
|
||||
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
|
||||
if (FadePlayButton && !PreviewPlaying.Value)
|
||||
PlayButton.FadeOut(120, Easing.InOutQuint);
|
||||
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
this.FadeInFromZero(200, Easing.Out);
|
||||
|
||||
PreviewPlaying.ValueChanged += playing =>
|
||||
{
|
||||
PlayButton.FadeTo(playing.NewValue || IsHovered || !FadePlayButton ? 1 : 0, 120, Easing.InOutQuint);
|
||||
PreviewBar.FadeTo(playing.NewValue ? 1 : 0, 120, Easing.InOutQuint);
|
||||
};
|
||||
}
|
||||
|
||||
protected List<DifficultyIcon> GetDifficultyIcons(OsuColour colours)
|
||||
{
|
||||
var icons = new List<DifficultyIcon>();
|
||||
|
||||
if (SetInfo.Beatmaps.Length > maximum_difficulty_icons)
|
||||
{
|
||||
foreach (var ruleset in SetInfo.Beatmaps.Select(b => b.Ruleset).Distinct())
|
||||
icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.Where(b => b.RulesetID == ruleset.OnlineID).ToList(), ruleset, this is ListBeatmapPanel ? Color4.White : colours.Gray5));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.RulesetID).ThenBy(beatmap => beatmap.StarRating))
|
||||
icons.Add(new DifficultyIcon(b));
|
||||
}
|
||||
|
||||
return icons;
|
||||
}
|
||||
|
||||
protected Drawable CreateBackground() => new UpdateableOnlineBeatmapSetCover
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
OnlineInfo = SetInfo,
|
||||
};
|
||||
|
||||
public class Statistic : FillFlowContainer
|
||||
{
|
||||
private readonly SpriteText text;
|
||||
|
||||
private int value;
|
||||
|
||||
public int Value
|
||||
{
|
||||
get => value;
|
||||
set
|
||||
{
|
||||
this.value = value;
|
||||
text.Text = Value.ToString(@"N0");
|
||||
}
|
||||
}
|
||||
|
||||
public Statistic(IconUsage icon, int value = 0)
|
||||
{
|
||||
Anchor = Anchor.TopRight;
|
||||
Origin = Anchor.TopRight;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Direction = FillDirection.Horizontal;
|
||||
Spacing = new Vector2(5f, 0f);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText { Font = OsuFont.GetFont(weight: FontWeight.SemiBold, italics: true) },
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Icon = icon,
|
||||
Shadow = true,
|
||||
Size = new Vector2(14),
|
||||
},
|
||||
};
|
||||
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem("View Beatmap", MenuItemType.Highlighted, ViewBeatmap),
|
||||
};
|
||||
}
|
||||
}
|
@ -1,269 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
{
|
||||
public class GridBeatmapPanel : BeatmapPanel
|
||||
{
|
||||
private const float horizontal_padding = 10;
|
||||
private const float vertical_padding = 5;
|
||||
|
||||
private FillFlowContainer bottomPanel, statusContainer, titleContainer, artistContainer;
|
||||
private PlayButton playButton;
|
||||
private Box progressBar;
|
||||
|
||||
protected override PlayButton PlayButton => playButton;
|
||||
protected override Box PreviewBar => progressBar;
|
||||
|
||||
public GridBeatmapPanel(APIBeatmapSet beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
Width = 380;
|
||||
Height = 140 + vertical_padding; // full height of all the elements plus vertical padding (autosize uses the image)
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
bottomPanel.LayoutDuration = 200;
|
||||
bottomPanel.LayoutEasing = Easing.Out;
|
||||
bottomPanel.Origin = Anchor.BottomLeft;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Content.CornerRadius = 4;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
bottomPanel = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0f, vertical_padding),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding },
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
titleContainer = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new RomanisableString(SetInfo.TitleUnicode, SetInfo.Title),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
}
|
||||
},
|
||||
artistContainer = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new RomanisableString(SetInfo.ArtistUnicode, SetInfo.Artist),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
progressBar = new Box
|
||||
{
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0, 3),
|
||||
Alpha = 0,
|
||||
Colour = colours.Yellow,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Top = vertical_padding,
|
||||
Bottom = vertical_padding,
|
||||
Left = horizontal_padding,
|
||||
Right = horizontal_padding,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new LinkFlowContainer(s =>
|
||||
{
|
||||
s.Shadow = false;
|
||||
s.Font = OsuFont.GetFont(size: 14);
|
||||
}).With(d =>
|
||||
{
|
||||
d.AutoSizeAxes = Axes.Both;
|
||||
d.AddText("mapped by ", t => t.Colour = colours.Gray5);
|
||||
d.AddUserLink(SetInfo.Author);
|
||||
}),
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 14,
|
||||
Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = SetInfo.Source,
|
||||
Font = OsuFont.GetFont(size: 14),
|
||||
Shadow = false,
|
||||
Colour = colours.Gray5,
|
||||
Alpha = string.IsNullOrEmpty(SetInfo.Source) ? 0f : 1f,
|
||||
},
|
||||
},
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 20,
|
||||
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
|
||||
Spacing = new Vector2(3),
|
||||
Children = GetDifficultyIcons(colours),
|
||||
},
|
||||
},
|
||||
},
|
||||
new BeatmapPanelDownloadButton(SetInfo)
|
||||
{
|
||||
Size = new Vector2(50, 30),
|
||||
Margin = new MarginPadding(horizontal_padding),
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding },
|
||||
Children = new[]
|
||||
{
|
||||
new Statistic(FontAwesome.Solid.PlayCircle, SetInfo.PlayCount),
|
||||
new Statistic(FontAwesome.Solid.Heart, SetInfo.FavouriteCount),
|
||||
},
|
||||
},
|
||||
statusContainer = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Top = 5, Left = 5 },
|
||||
Spacing = new Vector2(5),
|
||||
},
|
||||
playButton = new PlayButton(SetInfo)
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5, Left = 10 },
|
||||
Size = new Vector2(30),
|
||||
Alpha = 0,
|
||||
},
|
||||
});
|
||||
|
||||
if (SetInfo.HasExplicitContent)
|
||||
{
|
||||
titleContainer.Add(new ExplicitContentBeatmapPill
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 10f, Top = 2f },
|
||||
});
|
||||
}
|
||||
|
||||
if (SetInfo.TrackId != null)
|
||||
{
|
||||
artistContainer.Add(new FeaturedArtistBeatmapPill
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 10f, Top = 2f },
|
||||
});
|
||||
}
|
||||
|
||||
if (SetInfo.HasVideo)
|
||||
{
|
||||
statusContainer.Add(new IconPill(FontAwesome.Solid.Film));
|
||||
}
|
||||
|
||||
if (SetInfo.HasStoryboard)
|
||||
{
|
||||
statusContainer.Add(new IconPill(FontAwesome.Solid.Image));
|
||||
}
|
||||
|
||||
statusContainer.Add(new BeatmapSetOnlineStatusPill
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
TextSize = 12,
|
||||
TextPadding = new MarginPadding { Horizontal = 10, Vertical = 5 },
|
||||
Status = SetInfo.Status,
|
||||
});
|
||||
|
||||
PreviewPlaying.ValueChanged += _ => updateStatusContainer();
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateStatusContainer();
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
updateStatusContainer();
|
||||
}
|
||||
|
||||
private void updateStatusContainer() => statusContainer.FadeTo(IsHovered || PreviewPlaying.Value ? 0 : 1, 120, Easing.InOutQuint);
|
||||
}
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
{
|
||||
public class ListBeatmapPanel : BeatmapPanel
|
||||
{
|
||||
private const float transition_duration = 120;
|
||||
private const float horizontal_padding = 10;
|
||||
private const float vertical_padding = 5;
|
||||
private const float height = 70;
|
||||
|
||||
private FillFlowContainer statusContainer, titleContainer, artistContainer;
|
||||
protected BeatmapPanelDownloadButton DownloadButton;
|
||||
private PlayButton playButton;
|
||||
private Box progressBar;
|
||||
|
||||
protected override bool FadePlayButton => false;
|
||||
|
||||
protected override PlayButton PlayButton => playButton;
|
||||
protected override Box PreviewBar => progressBar;
|
||||
|
||||
public ListBeatmapPanel(APIBeatmapSet beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Content.CornerRadius = 5;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.25f), Color4.Black.Opacity(0.75f)),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding, Left = horizontal_padding, Right = vertical_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
LayoutEasing = Easing.OutQuint,
|
||||
LayoutDuration = transition_duration,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
playButton = new PlayButton(SetInfo)
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Size = new Vector2(height / 3),
|
||||
FillMode = FillMode.Fit,
|
||||
Margin = new MarginPadding { Right = 10 },
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
titleContainer = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new RomanisableString(SetInfo.TitleUnicode, SetInfo.Title),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
}
|
||||
},
|
||||
artistContainer = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new RomanisableString(SetInfo.ArtistUnicode, SetInfo.Artist),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statusContainer = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Vertical = vertical_padding, Horizontal = 5 },
|
||||
Spacing = new Vector2(5),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 20,
|
||||
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
|
||||
Spacing = new Vector2(3),
|
||||
Children = GetDifficultyIcons(colours),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Child = DownloadButton = new BeatmapPanelDownloadButton(SetInfo)
|
||||
{
|
||||
Size = new Vector2(height - vertical_padding * 3),
|
||||
Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding },
|
||||
},
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Statistic(FontAwesome.Solid.PlayCircle, SetInfo.PlayCount),
|
||||
new Statistic(FontAwesome.Solid.Heart, SetInfo.FavouriteCount),
|
||||
new LinkFlowContainer(s =>
|
||||
{
|
||||
s.Shadow = false;
|
||||
s.Font = OsuFont.GetFont(size: 14);
|
||||
})
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
}.With(d =>
|
||||
{
|
||||
d.AutoSizeAxes = Axes.Both;
|
||||
d.AddText("mapped by ");
|
||||
d.AddUserLink(SetInfo.Author);
|
||||
}),
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = SetInfo.Source,
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Font = OsuFont.GetFont(size: 14),
|
||||
Alpha = string.IsNullOrEmpty(SetInfo.Source) ? 0f : 1f,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
progressBar = new Box
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
BypassAutoSizeAxes = Axes.Y,
|
||||
Size = new Vector2(0, 3),
|
||||
Alpha = 0,
|
||||
Colour = colours.Yellow,
|
||||
},
|
||||
});
|
||||
|
||||
if (SetInfo.HasExplicitContent)
|
||||
{
|
||||
titleContainer.Add(new ExplicitContentBeatmapPill
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 10f, Top = 2f },
|
||||
});
|
||||
}
|
||||
|
||||
if (SetInfo.TrackId != null)
|
||||
{
|
||||
artistContainer.Add(new FeaturedArtistBeatmapPill
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 10f, Top = 2f },
|
||||
});
|
||||
}
|
||||
|
||||
if (SetInfo.HasVideo)
|
||||
{
|
||||
statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) });
|
||||
}
|
||||
|
||||
if (SetInfo.HasStoryboard)
|
||||
{
|
||||
statusContainer.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) });
|
||||
}
|
||||
|
||||
statusContainer.Add(new BeatmapSetOnlineStatusPill
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
TextSize = 12,
|
||||
TextPadding = new MarginPadding { Horizontal = 10, Vertical = 4 },
|
||||
Status = SetInfo.Status,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Overlays.BeatmapSet.Buttons;
|
||||
using osuTK;
|
||||
|
||||
@ -286,7 +285,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
case DownloadState.LocallyAvailable:
|
||||
// temporary for UX until new design is implemented.
|
||||
downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value)
|
||||
downloadButtonsContainer.Child = new BeatmapDownloadButton(BeatmapSet.Value)
|
||||
{
|
||||
Width = 50,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
|
@ -9,13 +9,13 @@ using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
@ -14,7 +14,7 @@ using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
{
|
||||
public class PlayButton : Container
|
||||
{
|
@ -11,7 +11,6 @@ using osu.Game.Audio;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
|
@ -214,7 +214,8 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
|
||||
beatmap.BindDisabledChanged(_ => Scheduler.AddOnce(beatmapDisabledChanged));
|
||||
beatmapDisabledChanged();
|
||||
|
||||
musicController.TrackChanged += trackChanged;
|
||||
trackChanged(beatmap.Value);
|
||||
@ -318,8 +319,10 @@ namespace osu.Game.Overlays
|
||||
};
|
||||
}
|
||||
|
||||
private void beatmapDisabledChanged(bool disabled)
|
||||
private void beatmapDisabledChanged()
|
||||
{
|
||||
bool disabled = beatmap.Disabled;
|
||||
|
||||
if (disabled)
|
||||
playlist?.Hide();
|
||||
|
||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
Text = MaintenanceSettingsStrings.DeleteAllBeatmaps,
|
||||
Action = () =>
|
||||
{
|
||||
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
|
||||
dialogOverlay?.Push(new MassDeleteConfirmationDialog(() =>
|
||||
{
|
||||
deleteBeatmapsButton.Enabled.Value = false;
|
||||
Task.Run(() => beatmaps.Delete(beatmaps.GetAllUsableBeatmapSets())).ContinueWith(t => Schedule(() => deleteBeatmapsButton.Enabled.Value = true));
|
||||
@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
Text = MaintenanceSettingsStrings.DeleteAllScores,
|
||||
Action = () =>
|
||||
{
|
||||
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
|
||||
dialogOverlay?.Push(new MassDeleteConfirmationDialog(() =>
|
||||
{
|
||||
deleteScoresButton.Enabled.Value = false;
|
||||
Task.Run(() => scores.Delete(scores.GetAllUsableScores())).ContinueWith(t => Schedule(() => deleteScoresButton.Enabled.Value = true));
|
||||
@ -103,7 +103,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
Text = MaintenanceSettingsStrings.DeleteAllSkins,
|
||||
Action = () =>
|
||||
{
|
||||
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
|
||||
dialogOverlay?.Push(new MassDeleteConfirmationDialog(() =>
|
||||
{
|
||||
deleteSkinsButton.Enabled.Value = false;
|
||||
Task.Run(() => skins.Delete(skins.GetAllUserSkins())).ContinueWith(t => Schedule(() => deleteSkinsButton.Enabled.Value = true));
|
||||
@ -131,7 +131,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
Text = MaintenanceSettingsStrings.DeleteAllCollections,
|
||||
Action = () =>
|
||||
{
|
||||
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(collectionManager.DeleteAll));
|
||||
dialogOverlay?.Push(new MassDeleteConfirmationDialog(collectionManager.DeleteAll));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public class DeleteAllBeatmapsDialog : PopupDialog
|
||||
public class MassDeleteConfirmationDialog : PopupDialog
|
||||
{
|
||||
public DeleteAllBeatmapsDialog(Action deleteAction)
|
||||
public MassDeleteConfirmationDialog(Action deleteAction)
|
||||
{
|
||||
BodyText = "Everything?";
|
||||
|
@ -68,13 +68,19 @@ namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Current.BindDisabledChanged(disabled => this.FadeColour(disabled ? Color4.Gray : Color4.White, 300), true);
|
||||
Current.BindValueChanged(_ => moveLineToCurrent());
|
||||
Current.BindDisabledChanged(_ => Scheduler.AddOnce(currentDisabledChanged));
|
||||
currentDisabledChanged();
|
||||
|
||||
Current.BindValueChanged(_ => moveLineToCurrent());
|
||||
// Scheduled to allow the button flow layout to be computed before the line position is updated
|
||||
ScheduleAfterChildren(moveLineToCurrent);
|
||||
}
|
||||
|
||||
private void currentDisabledChanged()
|
||||
{
|
||||
this.FadeColour(Current.Disabled ? Color4.Gray : Color4.White, 300);
|
||||
}
|
||||
|
||||
private bool hasInitialPosition;
|
||||
|
||||
private void moveLineToCurrent()
|
||||
|
@ -78,9 +78,9 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
using (var stream = info.OpenRead())
|
||||
{
|
||||
if (oldFile != null)
|
||||
beatmaps.ReplaceFile(set, oldFile, stream, info.Name);
|
||||
else
|
||||
beatmaps.AddFile(set, stream, info.Name);
|
||||
beatmaps.DeleteFile(set, oldFile);
|
||||
|
||||
beatmaps.AddFile(set, stream, info.Name);
|
||||
}
|
||||
|
||||
working.Value.Metadata.BackgroundFile = info.Name;
|
||||
@ -105,9 +105,8 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
using (var stream = info.OpenRead())
|
||||
{
|
||||
if (oldFile != null)
|
||||
beatmaps.ReplaceFile(set, oldFile, stream, info.Name);
|
||||
else
|
||||
beatmaps.AddFile(set, stream, info.Name);
|
||||
beatmaps.DeleteFile(set, oldFile);
|
||||
beatmaps.AddFile(set, stream, info.Name);
|
||||
}
|
||||
|
||||
working.Value.Metadata.AudioFile = info.Name;
|
||||
|
@ -25,7 +25,6 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -147,7 +146,8 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
if (showItemOwner)
|
||||
{
|
||||
ownerAvatar.Show();
|
||||
userLookupCache.GetUserAsync(Item.OwnerID).ContinueWith(u => Schedule(() => ownerAvatar.User = u.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
userLookupCache.GetUserAsync(Item.OwnerID)
|
||||
.ContinueWith(u => Schedule(() => ownerAvatar.User = u.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
}
|
||||
|
||||
difficultyIconContainer.Child = new DifficultyIcon(Item.Beatmap.Value, ruleset.Value, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(ICON_HEIGHT) };
|
||||
@ -338,7 +338,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
return true;
|
||||
}
|
||||
|
||||
private sealed class PlaylistDownloadButton : BeatmapPanelDownloadButton
|
||||
private sealed class PlaylistDownloadButton : BeatmapDownloadButton
|
||||
{
|
||||
private readonly PlaylistItem playlistItem;
|
||||
|
||||
@ -458,7 +458,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
});
|
||||
}
|
||||
|
||||
public LocalisableString TooltipText => User == null ? "loading user..." : $"queued by {User.Username}";
|
||||
public LocalisableString TooltipText => User == null ? string.Empty : $"queued by {User.Username}";
|
||||
|
||||
private class TooltipArea : Component, IHasTooltip
|
||||
{
|
||||
|
@ -171,7 +171,7 @@ namespace osu.Game.Skinning
|
||||
var oldFile = skin.SkinInfo.Files.FirstOrDefault(f => f.Filename == filename);
|
||||
|
||||
if (oldFile != null)
|
||||
skinModelManager.ReplaceFile(skin.SkinInfo, oldFile, streamContent, oldFile.Filename);
|
||||
skinModelManager.ReplaceFile(skin.SkinInfo, oldFile, streamContent);
|
||||
else
|
||||
skinModelManager.AddFile(skin.SkinInfo, streamContent, filename);
|
||||
}
|
||||
|
@ -294,12 +294,8 @@ namespace osu.Game.Stores
|
||||
/// <remarks>
|
||||
/// In the case of no matching files, a hash will be generated from the passed archive's <see cref="ArchiveReader.Name"/>.
|
||||
/// </remarks>
|
||||
protected virtual string ComputeHash(TModel item, ArchiveReader? reader = null)
|
||||
protected virtual string ComputeHash(TModel item)
|
||||
{
|
||||
if (reader != null)
|
||||
// fast hashing for cases where the item's files may not be populated.
|
||||
return computeHashFast(reader);
|
||||
|
||||
// for now, concatenate all hashable files in the set to create a unique hash.
|
||||
MemoryStream hashable = new MemoryStream();
|
||||
|
||||
@ -374,7 +370,7 @@ namespace osu.Game.Stores
|
||||
// TODO: look into rollback of file additions (or delayed commit).
|
||||
item.Files.AddRange(createFileInfos(archive, Files, realm));
|
||||
|
||||
item.Hash = ComputeHash(item, archive);
|
||||
item.Hash = ComputeHash(item);
|
||||
|
||||
// TODO: we may want to run this outside of the transaction.
|
||||
await Populate(item, archive, realm, cancellationToken).ConfigureAwait(false);
|
||||
@ -387,7 +383,9 @@ namespace osu.Game.Stores
|
||||
if (CanReuseExisting(existing, item))
|
||||
{
|
||||
LogForModel(item, @$"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import.");
|
||||
|
||||
existing.DeletePending = false;
|
||||
transaction.Commit();
|
||||
|
||||
return existing.ToLive();
|
||||
}
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.7.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1124.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1127.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
|
||||
<PackageReference Include="Sentry" Version="3.11.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.0" />
|
||||
|
@ -60,7 +60,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1124.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1127.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
@ -83,7 +83,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1124.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1127.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user