mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 12:53:11 +08:00
Merge branch 'master' into refactor-osu-difficulty-hit-object
This commit is contained in:
commit
1a09a3469a
@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
speedBonus = 1 + 0.75 * Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2);
|
||||
|
||||
double travelDistance = osuPrevObj?.TravelDistance ?? 0;
|
||||
double distance = Math.Min(single_spacing_threshold, travelDistance + osuCurrObj.LazyJumpDistance);
|
||||
double distance = Math.Min(single_spacing_threshold, travelDistance + osuCurrObj.MinimumJumpDistance);
|
||||
|
||||
return (speedBonus + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / strainTime;
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
@ -227,7 +229,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
|
||||
new BasicScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FillFlowContainer
|
||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
@ -248,6 +250,17 @@ namespace osu.Game.Tests.Visual.Beatmaps
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormal() => createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo));
|
||||
public void TestNormal()
|
||||
{
|
||||
createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo));
|
||||
|
||||
AddToggleStep("toggle expanded state", expanded =>
|
||||
{
|
||||
var card = this.ChildrenOfType<BeatmapCard>().Last();
|
||||
if (!card.Expanded.Disabled)
|
||||
card.Expanded.Value = expanded;
|
||||
});
|
||||
AddToggleStep("disable/enable expansion", disabled => this.ChildrenOfType<BeatmapCard>().ForEach(card => card.Expanded.Disabled = disabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
|
||||
@ -89,6 +90,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
confirmEditingBeatmap(() => targetDifficulty);
|
||||
|
||||
AddAssert("no objects selected", () => !EditorBeatmap.SelectedHitObjects.Any());
|
||||
AddUntilStep("wait for drawable ruleset", () => Editor.ChildrenOfType<DrawableRuleset>().SingleOrDefault()?.IsLoaded == true);
|
||||
AddStep("paste object", () => Editor.Paste());
|
||||
|
||||
if (sameRuleset)
|
||||
|
@ -11,12 +11,14 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Online.Rooms.RoomStatuses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Match;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
|
||||
@ -172,6 +174,39 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultiplayerRooms()
|
||||
{
|
||||
AddStep("create rooms", () => Child = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(5),
|
||||
Children = new[]
|
||||
{
|
||||
new DrawableMatchRoom(new Room
|
||||
{
|
||||
Name = { Value = "A host-only room" },
|
||||
QueueMode = { Value = QueueMode.HostOnly },
|
||||
Type = { Value = MatchType.HeadToHead }
|
||||
}),
|
||||
new DrawableMatchRoom(new Room
|
||||
{
|
||||
Name = { Value = "An all-players, team-versus room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayers },
|
||||
Type = { Value = MatchType.TeamVersus }
|
||||
}),
|
||||
new DrawableMatchRoom(new Room
|
||||
{
|
||||
Name = { Value = "A round-robin room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayersRoundRobin },
|
||||
Type = { Value = MatchType.HeadToHead }
|
||||
}),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DrawableRoom createLoungeRoom(Room room)
|
||||
{
|
||||
room.Host.Value ??= new APIUser { Username = "peppy", Id = 2 };
|
||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddAssert("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus);
|
||||
AddUntilStep("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus);
|
||||
AddAssert("user state arrived", () => client.Room?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState);
|
||||
}
|
||||
|
||||
@ -162,13 +162,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddAssert("room type is head to head", () => client.Room?.Settings.MatchType == MatchType.HeadToHead);
|
||||
AddUntilStep("room type is head to head", () => client.Room?.Settings.MatchType == MatchType.HeadToHead);
|
||||
|
||||
AddUntilStep("team displays are not displaying teams", () => multiplayerScreenStack.ChildrenOfType<TeamDisplay>().All(d => d.DisplayedTeam == null));
|
||||
|
||||
AddStep("change to team vs", () => client.ChangeSettings(matchType: MatchType.TeamVersus));
|
||||
|
||||
AddAssert("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus);
|
||||
AddUntilStep("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus);
|
||||
|
||||
AddUntilStep("team displays are displaying teams", () => multiplayerScreenStack.ChildrenOfType<TeamDisplay>().All(d => d.DisplayedTeam != null));
|
||||
}
|
||||
|
@ -31,10 +31,12 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
public class BeatmapCard : OsuClickableContainer
|
||||
{
|
||||
public const float TRANSITION_DURATION = 400;
|
||||
public const float CORNER_RADIUS = 10;
|
||||
|
||||
public Bindable<bool> Expanded { get; } = new BindableBool();
|
||||
|
||||
private const float width = 408;
|
||||
private const float height = 100;
|
||||
private const float corner_radius = 10;
|
||||
private const float icon_area_width = 30;
|
||||
|
||||
private readonly APIBeatmapSet beatmapSet;
|
||||
@ -42,6 +44,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
|
||||
private readonly BeatmapDownloadTracker downloadTracker;
|
||||
|
||||
private BeatmapCardContent content = null!;
|
||||
|
||||
private BeatmapCardThumbnail thumbnail = null!;
|
||||
|
||||
private Container rightAreaBackground = null!;
|
||||
@ -73,242 +77,247 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
CornerRadius = corner_radius;
|
||||
Masking = true;
|
||||
|
||||
FillFlowContainer leftIconArea;
|
||||
GridContainer titleContainer;
|
||||
GridContainer artistContainer;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChild = content = new BeatmapCardContent(height)
|
||||
{
|
||||
downloadTracker,
|
||||
rightAreaBackground = new Container
|
||||
MainContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = icon_area_width + 2 * corner_radius,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
// workaround for masking artifacts at the top & bottom of card,
|
||||
// which become especially visible on downloaded beatmaps (when the icon area has a lime background).
|
||||
Padding = new MarginPadding { Vertical = 1 },
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Colour4.White
|
||||
},
|
||||
},
|
||||
thumbnail = new BeatmapCardThumbnail(beatmapSet)
|
||||
{
|
||||
Name = @"Left (icon) area",
|
||||
Size = new Vector2(height),
|
||||
Padding = new MarginPadding { Right = corner_radius },
|
||||
Child = leftIconArea = new FillFlowContainer
|
||||
{
|
||||
Margin = new MarginPadding(5),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(1)
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = @"Right (button) area",
|
||||
Width = 30,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Origin = Anchor.TopRight,
|
||||
Anchor = Anchor.TopRight,
|
||||
Padding = new MarginPadding { Vertical = 17.5f },
|
||||
Child = rightAreaButtons = new Container<BeatmapCardIconButton>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new BeatmapCardIconButton[]
|
||||
{
|
||||
new FavouriteButton(beatmapSet)
|
||||
{
|
||||
Current = favouriteState,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
},
|
||||
new DownloadButton(beatmapSet)
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
State = { BindTarget = downloadTracker.State }
|
||||
},
|
||||
new GoToBeatmapButton(beatmapSet)
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
State = { BindTarget = downloadTracker.State }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mainContent = new Container
|
||||
{
|
||||
Name = @"Main content",
|
||||
X = height - corner_radius,
|
||||
Height = height,
|
||||
CornerRadius = corner_radius,
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
mainContentBackground = new BeatmapCardContentBackground(beatmapSet)
|
||||
downloadTracker,
|
||||
rightAreaBackground = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = icon_area_width + 2 * CORNER_RADIUS,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
// workaround for masking artifacts at the top & bottom of card,
|
||||
// which become especially visible on downloaded beatmaps (when the icon area has a lime background).
|
||||
Padding = new MarginPadding { Vertical = 1 },
|
||||
Child = new Box
|
||||
{
|
||||
Horizontal = 10,
|
||||
Vertical = 4
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Colour4.White
|
||||
},
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
},
|
||||
thumbnail = new BeatmapCardThumbnail(beatmapSet)
|
||||
{
|
||||
Name = @"Left (icon) area",
|
||||
Size = new Vector2(height),
|
||||
Padding = new MarginPadding { Right = CORNER_RADIUS },
|
||||
Child = leftIconArea = new FillFlowContainer
|
||||
{
|
||||
titleContainer = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title),
|
||||
Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true
|
||||
},
|
||||
Empty()
|
||||
}
|
||||
}
|
||||
},
|
||||
artistContainer = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = createArtistText(),
|
||||
Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true
|
||||
},
|
||||
Empty()
|
||||
},
|
||||
}
|
||||
},
|
||||
new LinkFlowContainer(s =>
|
||||
{
|
||||
s.Shadow = false;
|
||||
s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold);
|
||||
}).With(d =>
|
||||
{
|
||||
d.AutoSizeAxes = Axes.Both;
|
||||
d.Margin = new MarginPadding { Top = 2 };
|
||||
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
||||
d.AddUserLink(beatmapSet.Author);
|
||||
}),
|
||||
Margin = new MarginPadding(5),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(1)
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = @"Bottom content",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Padding = new MarginPadding
|
||||
Name = @"Right (button) area",
|
||||
Width = 30,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Origin = Anchor.TopRight,
|
||||
Anchor = Anchor.TopRight,
|
||||
Padding = new MarginPadding { Vertical = 17.5f },
|
||||
Child = rightAreaButtons = new Container<BeatmapCardIconButton>
|
||||
{
|
||||
Horizontal = 10,
|
||||
Vertical = 4
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new BeatmapCardIconButton[]
|
||||
{
|
||||
new FavouriteButton(beatmapSet)
|
||||
{
|
||||
Current = favouriteState,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
},
|
||||
new DownloadButton(beatmapSet)
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
State = { BindTarget = downloadTracker.State }
|
||||
},
|
||||
new GoToBeatmapButton(beatmapSet)
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
State = { BindTarget = downloadTracker.State }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mainContent = new Container
|
||||
{
|
||||
Name = @"Main content",
|
||||
X = height - CORNER_RADIUS,
|
||||
Height = height,
|
||||
CornerRadius = CORNER_RADIUS,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
idleBottomContent = new FillFlowContainer
|
||||
mainContentBackground = new BeatmapCardContentBackground(beatmapSet)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 10,
|
||||
Vertical = 4
|
||||
},
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 3),
|
||||
AlwaysPresent = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statisticsContainer = new FillFlowContainer<BeatmapCardStatistic>
|
||||
titleContainer = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
ChildrenEnumerable = createStatistics()
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(4, 0),
|
||||
Children = new Drawable[]
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new BeatmapSetOnlineStatusPill
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Status = beatmapSet.Status,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
},
|
||||
new DifficultySpectrumDisplay(beatmapSet)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
DotSize = new Vector2(6, 12)
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title),
|
||||
Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true
|
||||
},
|
||||
Empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
artistContainer = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = createArtistText(),
|
||||
Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true
|
||||
},
|
||||
Empty()
|
||||
},
|
||||
}
|
||||
},
|
||||
new LinkFlowContainer(s =>
|
||||
{
|
||||
s.Shadow = false;
|
||||
s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold);
|
||||
}).With(d =>
|
||||
{
|
||||
d.AutoSizeAxes = Axes.Both;
|
||||
d.Margin = new MarginPadding { Top = 2 };
|
||||
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
||||
d.AddUserLink(beatmapSet.Author);
|
||||
}),
|
||||
}
|
||||
},
|
||||
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
||||
new Container
|
||||
{
|
||||
Name = @"Bottom content",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 6,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
State = { BindTarget = downloadTracker.State },
|
||||
Progress = { BindTarget = downloadTracker.Progress }
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 10,
|
||||
Vertical = 4
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
idleBottomContent = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 3),
|
||||
AlwaysPresent = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statisticsContainer = new FillFlowContainer<BeatmapCardStatistic>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
ChildrenEnumerable = createStatistics()
|
||||
},
|
||||
new BeatmapCardExtraInfoRow(beatmapSet)
|
||||
{
|
||||
Hovered = _ =>
|
||||
{
|
||||
content.ScheduleShow();
|
||||
return false;
|
||||
},
|
||||
Unhovered = _ =>
|
||||
{
|
||||
// This hide should only trigger if the expanded content has not shown yet.
|
||||
// ie. if the user has not shown intent to want to see it (quickly moved over the info row area).
|
||||
if (!Expanded.Value)
|
||||
content.ScheduleHide();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 6,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
State = { BindTarget = downloadTracker.State },
|
||||
Progress = { BindTarget = downloadTracker.Progress }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ExpandedContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = 10, Vertical = 13 },
|
||||
Child = new BeatmapCardDifficultyList(beatmapSet)
|
||||
},
|
||||
Expanded = { BindTarget = Expanded }
|
||||
};
|
||||
|
||||
if (beatmapSet.HasVideo)
|
||||
@ -344,7 +353,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
downloadTracker.State.BindValueChanged(_ => updateState(), true);
|
||||
downloadTracker.State.BindValueChanged(_ => updateState());
|
||||
Expanded.BindValueChanged(_ => updateState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
@ -356,6 +366,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
content.ScheduleHide();
|
||||
|
||||
updateState();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
@ -386,19 +398,25 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
float targetWidth = width - height;
|
||||
if (IsHovered)
|
||||
targetWidth = targetWidth - icon_area_width + corner_radius;
|
||||
bool showDetails = IsHovered || Expanded.Value;
|
||||
|
||||
thumbnail.Dimmed.Value = IsHovered;
|
||||
float targetWidth = width - height;
|
||||
if (showDetails)
|
||||
targetWidth = targetWidth - icon_area_width + CORNER_RADIUS;
|
||||
|
||||
thumbnail.Dimmed.Value = showDetails;
|
||||
|
||||
// Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards.
|
||||
// This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left.
|
||||
content.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint);
|
||||
|
||||
mainContent.ResizeWidthTo(targetWidth, TRANSITION_DURATION, Easing.OutQuint);
|
||||
mainContentBackground.Dimmed.Value = IsHovered;
|
||||
mainContentBackground.Dimmed.Value = showDetails;
|
||||
|
||||
statisticsContainer.FadeTo(IsHovered ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
|
||||
rightAreaBackground.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, TRANSITION_DURATION, Easing.OutQuint);
|
||||
rightAreaButtons.FadeTo(IsHovered ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
rightAreaButtons.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
|
||||
foreach (var button in rightAreaButtons)
|
||||
{
|
||||
|
234
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs
Normal file
234
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs
Normal file
@ -0,0 +1,234 @@
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
{
|
||||
public class BeatmapCardContent : CompositeDrawable
|
||||
{
|
||||
public Drawable MainContent
|
||||
{
|
||||
set => bodyContent.Child = value;
|
||||
}
|
||||
|
||||
public Drawable ExpandedContent
|
||||
{
|
||||
set => dropdownScroll.Child = value;
|
||||
}
|
||||
|
||||
public Bindable<bool> Expanded { get; } = new BindableBool();
|
||||
|
||||
private readonly Box background;
|
||||
private readonly Container content;
|
||||
private readonly Container bodyContent;
|
||||
private readonly Container dropdownContent;
|
||||
private readonly OsuScrollContainer dropdownScroll;
|
||||
private readonly Container borderContainer;
|
||||
|
||||
public BeatmapCardContent(float height)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChild = content = new HoverHandlingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
Masking = true,
|
||||
Unhovered = _ => checkForHide(),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
bodyContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = height,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
Masking = true,
|
||||
},
|
||||
dropdownContent = new HoverHandlingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = height },
|
||||
Alpha = 0,
|
||||
Hovered = _ =>
|
||||
{
|
||||
keep();
|
||||
return true;
|
||||
},
|
||||
Unhovered = _ => checkForHide(),
|
||||
Child = dropdownScroll = new ExpandedContentScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
ScrollbarVisible = false
|
||||
}
|
||||
},
|
||||
borderContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
Masking = true,
|
||||
BorderThickness = 3,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
background.Colour = colourProvider.Background2;
|
||||
borderContainer.BorderColour = colourProvider.Highlight1;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Expanded.BindValueChanged(_ => updateState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private ScheduledDelegate? scheduledExpandedChange;
|
||||
|
||||
public void ScheduleShow()
|
||||
{
|
||||
scheduledExpandedChange?.Cancel();
|
||||
if (Expanded.Disabled || Expanded.Value)
|
||||
return;
|
||||
|
||||
scheduledExpandedChange = Scheduler.AddDelayed(() =>
|
||||
{
|
||||
if (!Expanded.Disabled)
|
||||
Expanded.Value = true;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
public void ScheduleHide()
|
||||
{
|
||||
scheduledExpandedChange?.Cancel();
|
||||
if (Expanded.Disabled || !Expanded.Value)
|
||||
return;
|
||||
|
||||
scheduledExpandedChange = Scheduler.AddDelayed(() =>
|
||||
{
|
||||
if (!Expanded.Disabled)
|
||||
Expanded.Value = false;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
private void checkForHide()
|
||||
{
|
||||
if (Expanded.Disabled)
|
||||
return;
|
||||
|
||||
if (content.IsHovered || dropdownContent.IsHovered)
|
||||
return;
|
||||
|
||||
scheduledExpandedChange?.Cancel();
|
||||
Expanded.Value = false;
|
||||
}
|
||||
|
||||
private void keep()
|
||||
{
|
||||
if (Expanded.Disabled)
|
||||
return;
|
||||
|
||||
scheduledExpandedChange?.Cancel();
|
||||
Expanded.Value = true;
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
background.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
dropdownContent.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
borderContainer.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
|
||||
content.TweenEdgeEffectTo(new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0, 2),
|
||||
Radius = 10,
|
||||
Colour = Colour4.Black.Opacity(Expanded.Value ? 0.3f : 0f),
|
||||
Hollow = true,
|
||||
}, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private class ExpandedContentScrollContainer : OsuScrollContainer
|
||||
{
|
||||
public ExpandedContentScrollContainer()
|
||||
{
|
||||
ScrollbarVisible = false;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Height = Math.Min(Content.DrawHeight, 400);
|
||||
}
|
||||
|
||||
private bool allowScroll => !Precision.AlmostEquals(DrawSize, Content.DrawSize);
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
if (!allowScroll)
|
||||
return false;
|
||||
|
||||
return base.OnDragStart(e);
|
||||
}
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
if (!allowScroll)
|
||||
return;
|
||||
|
||||
base.OnDrag(e);
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
if (!allowScroll)
|
||||
return;
|
||||
|
||||
base.OnDragEnd(e);
|
||||
}
|
||||
|
||||
protected override bool OnScroll(ScrollEvent e)
|
||||
{
|
||||
if (!allowScroll)
|
||||
return false;
|
||||
|
||||
return base.OnScroll(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs
Normal file
43
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// 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;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
{
|
||||
public class BeatmapCardExtraInfoRow : HoverHandlingContainer
|
||||
{
|
||||
public BeatmapCardExtraInfoRow(APIBeatmapSet beatmapSet)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(4, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new BeatmapSetOnlineStatusPill
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Status = beatmapSet.Status,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
},
|
||||
new DifficultySpectrumDisplay(beatmapSet)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
DotSize = new Vector2(6, 12)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
29
osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs
Normal file
29
osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs
Normal file
@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
{
|
||||
public class HoverHandlingContainer : Container
|
||||
{
|
||||
public Func<HoverEvent, bool>? Hovered { get; set; }
|
||||
public Action<HoverLostEvent>? Unhovered { get; set; }
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
bool handledByBase = base.OnHover(e);
|
||||
return Hovered?.Invoke(e) ?? handledByBase;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
Unhovered?.Invoke(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -163,7 +163,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
Debug.Assert(joinedRoom != null);
|
||||
|
||||
// Populate playlist items.
|
||||
var playlistItems = await Task.WhenAll(joinedRoom.Playlist.Select(createPlaylistItem)).ConfigureAwait(false);
|
||||
var playlistItems = await Task.WhenAll(joinedRoom.Playlist.Select(item => createPlaylistItem(item, item.ID == joinedRoom.Settings.PlaylistItemId))).ConfigureAwait(false);
|
||||
|
||||
// Populate users.
|
||||
Debug.Assert(joinedRoom.Users != null);
|
||||
@ -470,7 +470,32 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
Task IMultiplayerClient.SettingsChanged(MultiplayerRoomSettings newSettings)
|
||||
{
|
||||
Scheduler.Add(() => updateLocalRoomSettings(newSettings));
|
||||
Debug.Assert(APIRoom != null);
|
||||
Debug.Assert(Room != null);
|
||||
|
||||
Scheduler.Add(() =>
|
||||
{
|
||||
// ensure the new selected item is populated immediately.
|
||||
var playlistItem = APIRoom.Playlist.Single(p => p.ID == newSettings.PlaylistItemId);
|
||||
|
||||
if (playlistItem != null)
|
||||
{
|
||||
GetAPIBeatmap(playlistItem.BeatmapID).ContinueWith(b =>
|
||||
{
|
||||
// Should be called outside of the `Scheduler` logic (and specifically accessing `Exception`) to suppress an exception from firing outwards.
|
||||
bool success = b.Exception == null;
|
||||
|
||||
Scheduler.Add(() =>
|
||||
{
|
||||
if (success)
|
||||
playlistItem.Beatmap.Value = b.Result;
|
||||
|
||||
updateLocalRoomSettings(newSettings);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@ -629,7 +654,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
if (Room == null)
|
||||
return;
|
||||
|
||||
var playlistItem = await createPlaylistItem(item).ConfigureAwait(false);
|
||||
var playlistItem = await createPlaylistItem(item, true).ConfigureAwait(false);
|
||||
|
||||
Scheduler.Add(() =>
|
||||
{
|
||||
@ -673,7 +698,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
if (Room == null)
|
||||
return;
|
||||
|
||||
var playlistItem = await createPlaylistItem(item).ConfigureAwait(false);
|
||||
var playlistItem = await createPlaylistItem(item, true).ConfigureAwait(false);
|
||||
|
||||
Scheduler.Add(() =>
|
||||
{
|
||||
@ -728,10 +753,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
CurrentMatchPlayingItem.Value = APIRoom.Playlist.SingleOrDefault(p => p.ID == settings.PlaylistItemId);
|
||||
}
|
||||
|
||||
private async Task<PlaylistItem> createPlaylistItem(MultiplayerPlaylistItem item)
|
||||
private async Task<PlaylistItem> createPlaylistItem(MultiplayerPlaylistItem item, bool populateBeatmapImmediately)
|
||||
{
|
||||
var apiBeatmap = await GetAPIBeatmap(item.BeatmapID).ConfigureAwait(false);
|
||||
|
||||
var ruleset = Rulesets.GetRuleset(item.RulesetID);
|
||||
|
||||
Debug.Assert(ruleset != null);
|
||||
@ -741,8 +764,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
var playlistItem = new PlaylistItem
|
||||
{
|
||||
ID = item.ID,
|
||||
BeatmapID = item.BeatmapID,
|
||||
OwnerID = item.OwnerID,
|
||||
Beatmap = { Value = apiBeatmap },
|
||||
Ruleset = { Value = ruleset },
|
||||
Expired = item.Expired,
|
||||
PlaylistOrder = item.PlaylistOrder,
|
||||
@ -752,6 +775,9 @@ namespace osu.Game.Online.Multiplayer
|
||||
playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance)));
|
||||
playlistItem.AllowedMods.AddRange(item.AllowedMods.Select(m => m.ToMod(rulesetInstance)));
|
||||
|
||||
if (populateBeatmapImmediately)
|
||||
playlistItem.Beatmap.Value = await GetAPIBeatmap(item.BeatmapID).ConfigureAwait(false);
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,8 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
// spawn new children with the contained so we only clear old content at the last moment.
|
||||
var content = new FillFlowContainer<BeatmapCard>
|
||||
// reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most).
|
||||
var content = new ReverseChildIDFillFlowContainer<BeatmapCard>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
@ -22,7 +23,7 @@ namespace osu.Game.Overlays.Profile
|
||||
|
||||
public abstract string Identifier { get; }
|
||||
|
||||
private readonly FillFlowContainer content;
|
||||
private readonly FillFlowContainer<Drawable> content;
|
||||
private readonly Box background;
|
||||
private readonly Box underscore;
|
||||
|
||||
@ -79,7 +80,9 @@ namespace osu.Game.Overlays.Profile
|
||||
}
|
||||
}
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
// reverse ID flow is required for correct Z-ordering of the content (last item should be front-most).
|
||||
// particularly important in BeatmapsSection, as it uses beatmap cards, which have expandable overhanging content.
|
||||
content = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
|
@ -13,6 +13,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
|
||||
@ -26,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Sections
|
||||
protected int VisiblePages;
|
||||
protected int ItemsPerPage;
|
||||
|
||||
protected FillFlowContainer ItemsContainer { get; private set; }
|
||||
protected ReverseChildIDFillFlowContainer<Drawable> ItemsContainer { get; private set; }
|
||||
|
||||
private APIRequest<List<TModel>> retrievalRequest;
|
||||
private CancellationTokenSource loadCancellation;
|
||||
@ -48,11 +49,15 @@ namespace osu.Game.Overlays.Profile.Sections
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
ItemsContainer = new FillFlowContainer
|
||||
// reverse ID flow is required for correct Z-ordering of the items (last item should be front-most).
|
||||
// particularly important in PaginatedBeatmapContainer, as it uses beatmap cards, which have expandable overhanging content.
|
||||
ItemsContainer = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Spacing = new Vector2(0, 2),
|
||||
// ensure the container and its contents are in front of the "more" button.
|
||||
Depth = float.MinValue
|
||||
},
|
||||
moreButton = new ShowMoreButton
|
||||
{
|
||||
|
@ -135,7 +135,8 @@ namespace osu.Game.Overlays.Rankings
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new ScoresTable(1, response.Users),
|
||||
new FillFlowContainer
|
||||
// reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most).
|
||||
new ReverseChildIDFillFlowContainer<BeatmapCard>
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
|
@ -83,7 +83,9 @@ namespace osu.Game.Screens.Edit.Compose
|
||||
{
|
||||
base.LoadComplete();
|
||||
EditorBeatmap.SelectedHitObjects.BindCollectionChanged((_, __) => updateClipboardActionAvailability());
|
||||
clipboard.BindValueChanged(_ => updateClipboardActionAvailability(), true);
|
||||
clipboard.BindValueChanged(_ => updateClipboardActionAvailability());
|
||||
composer.OnLoadComplete += _ => updateClipboardActionAvailability();
|
||||
updateClipboardActionAvailability();
|
||||
}
|
||||
|
||||
#region Clipboard operations
|
||||
@ -131,7 +133,7 @@ namespace osu.Game.Screens.Edit.Compose
|
||||
private void updateClipboardActionAvailability()
|
||||
{
|
||||
CanCut.Value = CanCopy.Value = EditorBeatmap.SelectedHitObjects.Any();
|
||||
CanPaste.Value = !string.IsNullOrEmpty(clipboard.Value);
|
||||
CanPaste.Value = composer.IsLoaded && !string.IsNullOrEmpty(clipboard.Value);
|
||||
}
|
||||
|
||||
private string formatSelectionAsString()
|
||||
|
@ -80,7 +80,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
|
||||
private void updateRange(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
var orderedDifficulties = Playlist.Select(p => p.Beatmap.Value).OrderBy(b => b.StarRating).ToArray();
|
||||
var orderedDifficulties = Playlist.Where(p => p.Beatmap.Value != null).Select(p => p.Beatmap.Value).OrderBy(b => b.StarRating).ToArray();
|
||||
|
||||
StarDifficulty minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarRating : 0, 0);
|
||||
StarDifficulty maxDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[^1].StarRating : 0, 0);
|
||||
|
@ -16,6 +16,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Database;
|
||||
@ -50,6 +51,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
private LinkFlowContainer authorText;
|
||||
private ExplicitContentBeatmapPill explicitContentPill;
|
||||
private ModDisplay modDisplay;
|
||||
private FillFlowContainer buttonsFlow;
|
||||
private UpdateableAvatar ownerAvatar;
|
||||
|
||||
private readonly IBindable<bool> valid = new Bindable<bool>();
|
||||
@ -66,10 +68,19 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
[Resolved]
|
||||
private UserLookupCache userLookupCache { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapLookupCache beatmapLookupCache { get; set; }
|
||||
|
||||
private PanelBackground panelBackground;
|
||||
|
||||
private readonly DelayedLoadWrapper onScreenLoader = new DelayedLoadWrapper(Empty) { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
private readonly bool allowEdit;
|
||||
private readonly bool allowSelection;
|
||||
private readonly bool showItemOwner;
|
||||
|
||||
private FillFlowContainer mainFillFlow;
|
||||
|
||||
protected override bool ShouldBeConsideredForInput(Drawable child) => allowEdit || !allowSelection || SelectedItem.Value == Model;
|
||||
|
||||
public DrawableRoomPlaylistItem(PlaylistItem item, bool allowEdit, bool allowSelection, bool showItemOwner)
|
||||
@ -130,11 +141,34 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
valid.BindValueChanged(_ => Scheduler.AddOnce(refresh));
|
||||
requiredMods.CollectionChanged += (_, __) => Scheduler.AddOnce(refresh);
|
||||
|
||||
onScreenLoader.DelayedLoadStarted += _ =>
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (showItemOwner)
|
||||
{
|
||||
var foundUser = await userLookupCache.GetUserAsync(Item.OwnerID).ConfigureAwait(false);
|
||||
Schedule(() => ownerAvatar.User = foundUser);
|
||||
}
|
||||
|
||||
if (Item.Beatmap.Value == null)
|
||||
{
|
||||
var foundBeatmap = await beatmapLookupCache.GetBeatmapAsync(Item.BeatmapID).ConfigureAwait(false);
|
||||
Schedule(() => Item.Beatmap.Value = foundBeatmap);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log($"Error while populating playlist item {e}");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
private PanelBackground panelBackground;
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
if (!valid.Value)
|
||||
@ -143,22 +177,22 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
maskingContainer.BorderColour = colours.Red;
|
||||
}
|
||||
|
||||
if (showItemOwner)
|
||||
{
|
||||
ownerAvatar.Show();
|
||||
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) };
|
||||
if (Item.Beatmap.Value != null)
|
||||
difficultyIconContainer.Child = new DifficultyIcon(Item.Beatmap.Value, ruleset.Value, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(ICON_HEIGHT) };
|
||||
else
|
||||
difficultyIconContainer.Clear();
|
||||
|
||||
panelBackground.Beatmap.Value = Item.Beatmap.Value;
|
||||
|
||||
beatmapText.Clear();
|
||||
beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineID.ToString(), null, text =>
|
||||
|
||||
if (Item.Beatmap.Value != null)
|
||||
{
|
||||
text.Truncate = true;
|
||||
});
|
||||
beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineID.ToString(), null, text =>
|
||||
{
|
||||
text.Truncate = true;
|
||||
});
|
||||
}
|
||||
|
||||
authorText.Clear();
|
||||
|
||||
@ -168,10 +202,16 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
authorText.AddUserLink(Item.Beatmap.Value.Metadata.Author);
|
||||
}
|
||||
|
||||
bool hasExplicitContent = (Item.Beatmap.Value.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true;
|
||||
bool hasExplicitContent = (Item.Beatmap.Value?.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true;
|
||||
explicitContentPill.Alpha = hasExplicitContent ? 1 : 0;
|
||||
|
||||
modDisplay.Current.Value = requiredMods.ToArray();
|
||||
|
||||
buttonsFlow.Clear();
|
||||
buttonsFlow.ChildrenEnumerable = CreateButtons();
|
||||
|
||||
difficultyIconContainer.FadeInFromZero(500, Easing.OutQuint);
|
||||
mainFillFlow.FadeInFromZero(500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override Drawable CreateContent()
|
||||
@ -192,6 +232,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
},
|
||||
onScreenLoader,
|
||||
panelBackground = new PanelBackground
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -217,7 +258,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Left = 8, Right = 8 },
|
||||
},
|
||||
new FillFlowContainer
|
||||
mainFillFlow = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
@ -273,7 +314,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
}
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
buttonsFlow = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
@ -305,9 +346,9 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<Drawable> CreateButtons() =>
|
||||
new Drawable[]
|
||||
new[]
|
||||
{
|
||||
new PlaylistDownloadButton(Item),
|
||||
Item.Beatmap.Value == null ? Empty() : new PlaylistDownloadButton(Item),
|
||||
new PlaylistRemoveButton
|
||||
{
|
||||
Size = new Vector2(30, 30),
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
@ -184,20 +185,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new PlaylistCountPill
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
new StarRatingRangeDisplay
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Scale = new Vector2(0.8f)
|
||||
}
|
||||
}
|
||||
ChildrenEnumerable = CreateBottomDetails()
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -287,6 +275,37 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
protected virtual Drawable CreateBackground() => new OnlinePlayBackgroundSprite();
|
||||
|
||||
protected virtual IEnumerable<Drawable> CreateBottomDetails()
|
||||
{
|
||||
var pills = new List<Drawable>();
|
||||
|
||||
if (Room.Type.Value != MatchType.Playlists)
|
||||
{
|
||||
pills.AddRange(new OnlinePlayComposite[]
|
||||
{
|
||||
new MatchTypePill(),
|
||||
new QueueModePill(),
|
||||
});
|
||||
}
|
||||
|
||||
pills.AddRange(new Drawable[]
|
||||
{
|
||||
new PlaylistCountPill
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
new StarRatingRangeDisplay
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Scale = new Vector2(0.8f)
|
||||
}
|
||||
});
|
||||
|
||||
return pills;
|
||||
}
|
||||
|
||||
private class RoomNameText : OsuSpriteText
|
||||
{
|
||||
[Resolved(typeof(Room), nameof(Online.Rooms.Room.Name))]
|
||||
|
@ -0,0 +1,50 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public class MatchTypePill : OnlinePlayComposite
|
||||
{
|
||||
private OsuTextFlowContainer textFlow;
|
||||
|
||||
public MatchTypePill()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new PillContainer
|
||||
{
|
||||
Child = textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12))
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Type.BindValueChanged(onMatchTypeChanged, true);
|
||||
}
|
||||
|
||||
private void onMatchTypeChanged(ValueChangedEvent<MatchType> type)
|
||||
{
|
||||
textFlow.Clear();
|
||||
textFlow.AddText(type.NewValue.GetLocalisableDescription());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public class QueueModePill : OnlinePlayComposite
|
||||
{
|
||||
private OsuTextFlowContainer textFlow;
|
||||
|
||||
public QueueModePill()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new PillContainer
|
||||
{
|
||||
Child = textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12))
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
QueueMode.BindValueChanged(onQueueModeChanged, true);
|
||||
}
|
||||
|
||||
private void onQueueModeChanged(ValueChangedEvent<QueueMode> mode)
|
||||
{
|
||||
textFlow.Clear();
|
||||
textFlow.AddText(mode.NewValue.GetLocalisableDescription());
|
||||
}
|
||||
}
|
||||
}
|
@ -228,7 +228,10 @@ namespace osu.Game.Screens.Play
|
||||
onlineBeatmapRequest.Success += beatmapSet => Schedule(() =>
|
||||
{
|
||||
this.beatmapSet = beatmapSet;
|
||||
beatmapPanelContainer.Child = new BeatmapCard(this.beatmapSet);
|
||||
beatmapPanelContainer.Child = new BeatmapCard(this.beatmapSet)
|
||||
{
|
||||
Expanded = { Disabled = true }
|
||||
};
|
||||
checkForAutomaticDownload();
|
||||
});
|
||||
|
||||
|
@ -4,9 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -89,15 +87,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true));
|
||||
return true;
|
||||
|
||||
case GetBeatmapSetRequest getBeatmapSetRequest:
|
||||
var onlineReq = new GetBeatmapSetRequest(getBeatmapSetRequest.ID, getBeatmapSetRequest.Type);
|
||||
onlineReq.Success += res => getBeatmapSetRequest.TriggerSuccess(res);
|
||||
onlineReq.Failure += e => getBeatmapSetRequest.TriggerFailure(e);
|
||||
|
||||
// Get the online API from the game's dependencies.
|
||||
game.Dependencies.Get<IAPIProvider>().Queue(onlineReq);
|
||||
return true;
|
||||
|
||||
case CreateRoomScoreRequest createRoomScoreRequest:
|
||||
createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 });
|
||||
return true;
|
||||
|
Loading…
Reference in New Issue
Block a user