mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 09:02:58 +08:00
Merge branch 'master' into fix-mod-settings-fuckery
This commit is contained in:
commit
9e400acd1a
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
protected override bool Handle(UIEvent e)
|
||||
{
|
||||
if (e is MouseMoveEvent && !AllowUserCursorMovement) return false;
|
||||
if ((e is MouseMoveEvent || e is TouchMoveEvent) && !AllowUserCursorMovement) return false;
|
||||
|
||||
return base.Handle(e);
|
||||
}
|
||||
|
@ -7,7 +7,10 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
|
||||
@ -19,16 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public class TestSceneMultiplayerParticipantsList : MultiplayerTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
Child = new ParticipantsList
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Size = new Vector2(380, 0.7f)
|
||||
};
|
||||
});
|
||||
public new void Setup() => Schedule(createNewParticipantsList);
|
||||
|
||||
[Test]
|
||||
public void TestAddUser()
|
||||
@ -75,6 +69,50 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("single panel is for second user", () => this.ChildrenOfType<ParticipantPanel>().Single().User.User == secondUser);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGameStateHasPriorityOverDownloadState()
|
||||
{
|
||||
AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
|
||||
checkProgressBarVisibility(true);
|
||||
|
||||
AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Results));
|
||||
checkProgressBarVisibility(false);
|
||||
AddUntilStep("ready mark visible", () => this.ChildrenOfType<StateDisplay>().Single().IsPresent);
|
||||
|
||||
AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Idle));
|
||||
checkProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCorrectInitialState()
|
||||
{
|
||||
AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
|
||||
AddStep("recreate list", createNewParticipantsList);
|
||||
checkProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapDownloadingStates()
|
||||
{
|
||||
AddStep("set to no map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.NotDownloaded()));
|
||||
AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
|
||||
|
||||
checkProgressBarVisibility(true);
|
||||
|
||||
AddRepeatStep("increment progress", () =>
|
||||
{
|
||||
var progress = this.ChildrenOfType<ParticipantPanel>().Single().User.BeatmapAvailability.DownloadProgress ?? 0;
|
||||
Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(progress + RNG.NextSingle(0.1f)));
|
||||
}, 25);
|
||||
|
||||
AddAssert("progress bar increased", () => this.ChildrenOfType<ProgressBar>().Single().Current.Value > 0);
|
||||
|
||||
AddStep("set to importing map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Importing()));
|
||||
checkProgressBarVisibility(false);
|
||||
|
||||
AddStep("set to available", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.LocallyAvailable()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToggleReadyState()
|
||||
{
|
||||
@ -122,6 +160,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
});
|
||||
|
||||
Client.ChangeUserState(i, (MultiplayerUserState)RNG.Next(0, (int)MultiplayerUserState.Results + 1));
|
||||
|
||||
if (RNG.NextBool())
|
||||
{
|
||||
var beatmapState = (DownloadState)RNG.Next(0, (int)DownloadState.LocallyAvailable + 1);
|
||||
|
||||
switch (beatmapState)
|
||||
{
|
||||
case DownloadState.NotDownloaded:
|
||||
Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.NotDownloaded());
|
||||
break;
|
||||
|
||||
case DownloadState.Downloading:
|
||||
Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.Downloading(RNG.NextSingle()));
|
||||
break;
|
||||
|
||||
case DownloadState.Importing:
|
||||
Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.Importing());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -152,5 +210,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep($"set state: {state}", () => Client.ChangeUserState(0, state));
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewParticipantsList()
|
||||
{
|
||||
Child = new ParticipantsList { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, Size = new Vector2(380, 0.7f) };
|
||||
}
|
||||
|
||||
private void checkProgressBarVisibility(bool visible) =>
|
||||
AddUntilStep($"progress bar {(visible ? "is" : "is not")}visible", () =>
|
||||
this.ChildrenOfType<ProgressBar>().Single().IsPresent == visible);
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
private class TestFullscreenOverlay : FullscreenOverlay<OverlayHeader>
|
||||
{
|
||||
public TestFullscreenOverlay()
|
||||
: base(OverlayColourScheme.Pink, null)
|
||||
: base(OverlayColourScheme.Pink)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -52,6 +52,17 @@ namespace osu.Game.Tests.Visual.Online
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override OverlayHeader CreateHeader() => new TestHeader();
|
||||
|
||||
internal class TestHeader : OverlayHeader
|
||||
{
|
||||
protected override OverlayTitle CreateTitle() => new TestTitle();
|
||||
|
||||
internal class TestTitle : OverlayTitle
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Add(rankingsOverlay = new TestRankingsOverlay
|
||||
{
|
||||
Country = { BindTarget = countryBindable },
|
||||
Scope = { BindTarget = scope },
|
||||
Header = { Current = { BindTarget = scope } },
|
||||
});
|
||||
}
|
||||
|
||||
@ -65,8 +65,6 @@ namespace osu.Game.Tests.Visual.Online
|
||||
private class TestRankingsOverlay : RankingsOverlay
|
||||
{
|
||||
public new Bindable<Country> Country => base.Country;
|
||||
|
||||
public new Bindable<RankingsScope> Scope => base.Scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,19 @@ namespace osu.Game.Graphics.UserInterface
|
||||
set => CurrentNumber.Value = value;
|
||||
}
|
||||
|
||||
public ProgressBar()
|
||||
private readonly bool allowSeek;
|
||||
|
||||
public override bool HandlePositionalInput => allowSeek;
|
||||
public override bool HandleNonPositionalInput => allowSeek;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new progress bar.
|
||||
/// </summary>
|
||||
/// <param name="allowSeek">Whether the user should be allowed to click/drag to adjust the value.</param>
|
||||
public ProgressBar(bool allowSeek)
|
||||
{
|
||||
this.allowSeek = allowSeek;
|
||||
|
||||
CurrentNumber.MinValue = 0;
|
||||
CurrentNumber.MaxValue = 1;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
@ -239,6 +239,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
newConnection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted);
|
||||
newConnection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady);
|
||||
newConnection.On<int, IEnumerable<APIMod>>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged);
|
||||
newConnection.On<int, BeatmapAvailability>(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged);
|
||||
|
||||
newConnection.Closed += ex =>
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Online.Rooms
|
||||
@ -24,19 +25,33 @@ namespace osu.Game.Online.Rooms
|
||||
/// </summary>
|
||||
public IBindable<BeatmapAvailability> Availability => availability;
|
||||
|
||||
private readonly Bindable<BeatmapAvailability> availability = new Bindable<BeatmapAvailability>();
|
||||
private readonly Bindable<BeatmapAvailability> availability = new Bindable<BeatmapAvailability>(BeatmapAvailability.LocallyAvailable());
|
||||
|
||||
public OnlinePlayBeatmapAvailablilityTracker()
|
||||
{
|
||||
State.BindValueChanged(_ => updateAvailability());
|
||||
Progress.BindValueChanged(_ => updateAvailability(), true);
|
||||
}
|
||||
private ScheduledDelegate progressUpdate;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
SelectedItem.BindValueChanged(item => Model.Value = item.NewValue?.Beatmap.Value.BeatmapSet, true);
|
||||
SelectedItem.BindValueChanged(item =>
|
||||
{
|
||||
// the underlying playlist is regularly cleared for maintenance purposes (things which probably need to be fixed eventually).
|
||||
// to avoid exposing a state change when there may actually be none, ignore all nulls for now.
|
||||
if (item.NewValue == null)
|
||||
return;
|
||||
|
||||
Model.Value = item.NewValue.Beatmap.Value.BeatmapSet;
|
||||
}, true);
|
||||
|
||||
Progress.BindValueChanged(_ =>
|
||||
{
|
||||
// incoming progress changes are going to be at a very high rate.
|
||||
// we don't want to flood the network with this, so rate limit how often we send progress updates.
|
||||
if (progressUpdate?.Completed != false)
|
||||
progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500);
|
||||
});
|
||||
|
||||
State.BindValueChanged(_ => updateAvailability(), true);
|
||||
}
|
||||
|
||||
protected override bool VerifyDatabasedModel(BeatmapSetInfo databasedSet)
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
public DownloadProgressBar(BeatmapSetInfo beatmapSet)
|
||||
: base(beatmapSet)
|
||||
{
|
||||
AddInternal(progressBar = new InteractionDisabledProgressBar
|
||||
AddInternal(progressBar = new ProgressBar(false)
|
||||
{
|
||||
Height = 0,
|
||||
Alpha = 0,
|
||||
@ -64,11 +64,5 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
private class InteractionDisabledProgressBar : ProgressBar
|
||||
{
|
||||
public override bool HandlePositionalInput => false;
|
||||
public override bool HandleNonPositionalInput => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,98 +15,82 @@ using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class BeatmapListingOverlay : FullscreenOverlay<BeatmapListingHeader>
|
||||
public class BeatmapListingOverlay : OnlineOverlay<BeatmapListingHeader>
|
||||
{
|
||||
[Resolved]
|
||||
private PreviewTrackManager previewTrackManager { get; set; }
|
||||
|
||||
private Drawable currentContent;
|
||||
private LoadingLayer loadingLayer;
|
||||
private Container panelTarget;
|
||||
private FillFlowContainer<BeatmapPanel> foundContent;
|
||||
private NotFoundDrawable notFoundContent;
|
||||
|
||||
private OverlayScrollContainer resultScrollContainer;
|
||||
private BeatmapListingFilterControl filterControl;
|
||||
|
||||
public BeatmapListingOverlay()
|
||||
: base(OverlayColourScheme.Blue, new BeatmapListingHeader())
|
||||
: base(OverlayColourScheme.Blue)
|
||||
{
|
||||
}
|
||||
|
||||
private BeatmapListingFilterControl filterControl;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
new Box
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background6
|
||||
},
|
||||
resultScrollContainer = new OverlayScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
filterControl = new BeatmapListingFilterControl
|
||||
{
|
||||
TypingStarted = onTypingStarted,
|
||||
SearchStarted = onSearchStarted,
|
||||
SearchFinished = onSearchFinished,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header,
|
||||
filterControl = new BeatmapListingFilterControl
|
||||
new Box
|
||||
{
|
||||
TypingStarted = onTypingStarted,
|
||||
SearchStarted = onSearchStarted,
|
||||
SearchFinished = onSearchFinished,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background4,
|
||||
},
|
||||
new Container
|
||||
panelTarget = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding { Horizontal = 20 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background4,
|
||||
},
|
||||
panelTarget = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding { Horizontal = 20 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
foundContent = new FillFlowContainer<BeatmapPanel>(),
|
||||
notFoundContent = new NotFoundDrawable(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
foundContent = new FillFlowContainer<BeatmapPanel>(),
|
||||
notFoundContent = new NotFoundDrawable(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
loadingLayer = new LoadingLayer(true)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override BeatmapListingHeader CreateHeader() => new BeatmapListingHeader();
|
||||
|
||||
protected override Color4 BackgroundColour => ColourProvider.Background6;
|
||||
|
||||
private void onTypingStarted()
|
||||
{
|
||||
// temporary until the textbox/header is updated to always stay on screen.
|
||||
resultScrollContainer.ScrollToStart();
|
||||
ScrollFlow.ScrollToStart();
|
||||
}
|
||||
|
||||
protected override void OnFocus(FocusEvent e)
|
||||
@ -125,7 +109,7 @@ namespace osu.Game.Overlays
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
|
||||
if (panelTarget.Any())
|
||||
loadingLayer.Show();
|
||||
Loading.Show();
|
||||
}
|
||||
|
||||
private Task panelLoadDelegate;
|
||||
@ -173,7 +157,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
private void addContentToPlaceholder(Drawable content)
|
||||
{
|
||||
loadingLayer.Hide();
|
||||
Loading.Hide();
|
||||
lastFetchDisplayedTime = Time.Current;
|
||||
|
||||
if (content == currentContent)
|
||||
@ -267,7 +251,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
bool shouldShowMore = panelLoadDelegate?.IsCompleted != false
|
||||
&& Time.Current - lastFetchDisplayedTime > time_between_fetches
|
||||
&& (resultScrollContainer.ScrollableExtent > 0 && resultScrollContainer.IsScrolledToEnd(pagination_scroll_distance));
|
||||
&& (ScrollFlow.ScrollableExtent > 0 && ScrollFlow.IsScrolledToEnd(pagination_scroll_distance));
|
||||
|
||||
if (shouldShowMore)
|
||||
filterControl.FetchNextPage();
|
||||
|
@ -6,20 +6,19 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||
using osu.Game.Overlays.Comments;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class BeatmapSetOverlay : FullscreenOverlay<BeatmapSetHeader>
|
||||
public class BeatmapSetOverlay : OnlineOverlay<BeatmapSetHeader>
|
||||
{
|
||||
public const float X_PADDING = 40;
|
||||
public const float Y_PADDING = 25;
|
||||
@ -33,55 +32,27 @@ namespace osu.Game.Overlays
|
||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
private readonly Box background;
|
||||
|
||||
public BeatmapSetOverlay()
|
||||
: base(OverlayColourScheme.Blue, new BeatmapSetHeader())
|
||||
: base(OverlayColourScheme.Blue)
|
||||
{
|
||||
OverlayScrollContainer scroll;
|
||||
Info info;
|
||||
CommentsSection comments;
|
||||
|
||||
Children = new Drawable[]
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
background = new Box
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 20),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
scroll = new OverlayScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new ReverseChildIDFillFlowContainer<BeatmapSetLayoutSection>
|
||||
info = new Info(),
|
||||
new ScoresContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 20),
|
||||
Children = new[]
|
||||
{
|
||||
new BeatmapSetLayoutSection
|
||||
{
|
||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header,
|
||||
info = new Info()
|
||||
}
|
||||
},
|
||||
},
|
||||
new ScoresContainer
|
||||
{
|
||||
Beatmap = { BindTarget = Header.HeaderContent.Picker.Beatmap }
|
||||
},
|
||||
comments = new CommentsSection()
|
||||
},
|
||||
Beatmap = { BindTarget = Header.HeaderContent.Picker.Beatmap }
|
||||
},
|
||||
},
|
||||
comments = new CommentsSection()
|
||||
}
|
||||
};
|
||||
|
||||
Header.BeatmapSet.BindTo(beatmapSet);
|
||||
@ -91,16 +62,13 @@ namespace osu.Game.Overlays
|
||||
Header.HeaderContent.Picker.Beatmap.ValueChanged += b =>
|
||||
{
|
||||
info.Beatmap = b.NewValue;
|
||||
|
||||
scroll.ScrollToStart();
|
||||
ScrollFlow.ScrollToStart();
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
background.Colour = ColourProvider.Background6;
|
||||
}
|
||||
protected override BeatmapSetHeader CreateHeader() => new BeatmapSetHeader();
|
||||
|
||||
protected override Color4 BackgroundColour => ColourProvider.Background6;
|
||||
|
||||
protected override void PopOutComplete()
|
||||
{
|
||||
|
@ -11,22 +11,18 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.Changelog;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class ChangelogOverlay : FullscreenOverlay<ChangelogHeader>
|
||||
public class ChangelogOverlay : OnlineOverlay<ChangelogHeader>
|
||||
{
|
||||
public readonly Bindable<APIChangelogBuild> Current = new Bindable<APIChangelogBuild>();
|
||||
|
||||
private Container<ChangelogContent> content;
|
||||
|
||||
private SampleChannel sampleBack;
|
||||
|
||||
private List<APIChangelogBuild> builds;
|
||||
@ -34,45 +30,14 @@ namespace osu.Game.Overlays
|
||||
protected List<APIUpdateStream> Streams;
|
||||
|
||||
public ChangelogOverlay()
|
||||
: base(OverlayColourScheme.Purple, new ChangelogHeader())
|
||||
: base(OverlayColourScheme.Purple)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background4,
|
||||
},
|
||||
new OverlayScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header.With(h =>
|
||||
{
|
||||
h.ListingSelected = ShowListing;
|
||||
h.Build.BindTarget = Current;
|
||||
}),
|
||||
content = new Container<ChangelogContent>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
Header.Build.BindTarget = Current;
|
||||
|
||||
sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
|
||||
|
||||
@ -85,6 +50,13 @@ namespace osu.Game.Overlays
|
||||
});
|
||||
}
|
||||
|
||||
protected override ChangelogHeader CreateHeader() => new ChangelogHeader
|
||||
{
|
||||
ListingSelected = ShowListing,
|
||||
};
|
||||
|
||||
protected override Color4 BackgroundColour => ColourProvider.Background4;
|
||||
|
||||
public void ShowListing()
|
||||
{
|
||||
Current.Value = null;
|
||||
@ -198,16 +170,16 @@ namespace osu.Game.Overlays
|
||||
|
||||
private void loadContent(ChangelogContent newContent)
|
||||
{
|
||||
content.FadeTo(0.2f, 300, Easing.OutQuint);
|
||||
Content.FadeTo(0.2f, 300, Easing.OutQuint);
|
||||
|
||||
loadContentCancellation?.Cancel();
|
||||
|
||||
LoadComponentAsync(newContent, c =>
|
||||
{
|
||||
content.FadeIn(300, Easing.OutQuint);
|
||||
Content.FadeIn(300, Easing.OutQuint);
|
||||
|
||||
c.BuildSelected = ShowBuild;
|
||||
content.Child = c;
|
||||
Child = c;
|
||||
}, (loadContentCancellation = new CancellationTokenSource()).Token);
|
||||
}
|
||||
}
|
||||
|
@ -2,155 +2,35 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays.Dashboard;
|
||||
using osu.Game.Overlays.Dashboard.Friends;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class DashboardOverlay : FullscreenOverlay<DashboardOverlayHeader>
|
||||
public class DashboardOverlay : TabbableOnlineOverlay<DashboardOverlayHeader, DashboardOverlayTabs>
|
||||
{
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
private Container content;
|
||||
private LoadingLayer loading;
|
||||
private OverlayScrollContainer scrollFlow;
|
||||
|
||||
public DashboardOverlay()
|
||||
: base(OverlayColourScheme.Purple, new DashboardOverlayHeader
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Depth = -float.MaxValue
|
||||
})
|
||||
: base(OverlayColourScheme.Purple)
|
||||
{
|
||||
}
|
||||
|
||||
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
||||
protected override DashboardOverlayHeader CreateHeader() => new DashboardOverlayHeader();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api)
|
||||
protected override void CreateDisplayToLoad(DashboardOverlayTabs tab)
|
||||
{
|
||||
apiState.BindTo(api.State);
|
||||
apiState.BindValueChanged(onlineStateChanged, true);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background5
|
||||
},
|
||||
scrollFlow = new OverlayScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header,
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
loading = new LoadingLayer(true),
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Header.Current.BindValueChanged(onTabChanged);
|
||||
}
|
||||
|
||||
private bool displayUpdateRequired = true;
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
// We don't want to create a new display on every call, only when exiting from fully closed state.
|
||||
if (displayUpdateRequired)
|
||||
{
|
||||
Header.Current.TriggerChange();
|
||||
displayUpdateRequired = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopOutComplete()
|
||||
{
|
||||
base.PopOutComplete();
|
||||
loadDisplay(Empty());
|
||||
displayUpdateRequired = true;
|
||||
}
|
||||
|
||||
private void loadDisplay(Drawable display)
|
||||
{
|
||||
scrollFlow.ScrollToStart();
|
||||
|
||||
LoadComponentAsync(display, loaded =>
|
||||
{
|
||||
if (API.IsLoggedIn)
|
||||
loading.Hide();
|
||||
|
||||
content.Child = loaded;
|
||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
private void onTabChanged(ValueChangedEvent<DashboardOverlayTabs> tab)
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
loading.Show();
|
||||
|
||||
if (!API.IsLoggedIn)
|
||||
{
|
||||
loadDisplay(Empty());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (tab.NewValue)
|
||||
switch (tab)
|
||||
{
|
||||
case DashboardOverlayTabs.Friends:
|
||||
loadDisplay(new FriendDisplay());
|
||||
LoadDisplay(new FriendDisplay());
|
||||
break;
|
||||
|
||||
case DashboardOverlayTabs.CurrentlyPlaying:
|
||||
loadDisplay(new CurrentlyPlayingDisplay());
|
||||
LoadDisplay(new CurrentlyPlayingDisplay());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Display for {tab.NewValue} tab is not implemented");
|
||||
throw new NotImplementedException($"Display for {tab} tab is not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
|
||||
{
|
||||
if (State.Value == Visibility.Hidden)
|
||||
return;
|
||||
|
||||
Header.Current.TriggerChange();
|
||||
});
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osuTK.Graphics;
|
||||
@ -15,21 +17,27 @@ namespace osu.Game.Overlays
|
||||
public abstract class FullscreenOverlay<T> : WaveOverlayContainer, INamedOverlayComponent
|
||||
where T : OverlayHeader
|
||||
{
|
||||
public virtual string IconTexture => Header?.Title.IconTexture ?? string.Empty;
|
||||
public virtual string Title => Header?.Title.Title ?? string.Empty;
|
||||
public virtual string Description => Header?.Title.Description ?? string.Empty;
|
||||
public virtual string IconTexture => Header.Title.IconTexture ?? string.Empty;
|
||||
public virtual string Title => Header.Title.Title ?? string.Empty;
|
||||
public virtual string Description => Header.Title.Description ?? string.Empty;
|
||||
|
||||
public T Header { get; }
|
||||
|
||||
protected virtual Color4 BackgroundColour => ColourProvider.Background5;
|
||||
|
||||
[Resolved]
|
||||
protected IAPIProvider API { get; private set; }
|
||||
|
||||
[Cached]
|
||||
protected readonly OverlayColourProvider ColourProvider;
|
||||
|
||||
protected FullscreenOverlay(OverlayColourScheme colourScheme, T header)
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private readonly Container content;
|
||||
|
||||
protected FullscreenOverlay(OverlayColourScheme colourScheme)
|
||||
{
|
||||
Header = header;
|
||||
Header = CreateHeader();
|
||||
|
||||
ColourProvider = new OverlayColourProvider(colourScheme);
|
||||
|
||||
@ -47,6 +55,19 @@ namespace osu.Game.Overlays
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 10
|
||||
};
|
||||
|
||||
base.Content.AddRange(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = BackgroundColour
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -58,6 +79,9 @@ namespace osu.Game.Overlays
|
||||
Waves.FourthWaveColour = ColourProvider.Dark3;
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
protected abstract T CreateHeader();
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
if (State.Value == Visibility.Visible)
|
||||
|
@ -2,67 +2,22 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.News;
|
||||
using osu.Game.Overlays.News.Displays;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class NewsOverlay : FullscreenOverlay<NewsHeader>
|
||||
public class NewsOverlay : OnlineOverlay<NewsHeader>
|
||||
{
|
||||
private readonly Bindable<string> article = new Bindable<string>(null);
|
||||
|
||||
private Container content;
|
||||
private LoadingLayer loading;
|
||||
private OverlayScrollContainer scrollFlow;
|
||||
|
||||
public NewsOverlay()
|
||||
: base(OverlayColourScheme.Purple, new NewsHeader())
|
||||
: base(OverlayColourScheme.Purple)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background5,
|
||||
},
|
||||
scrollFlow = new OverlayScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header.With(h =>
|
||||
{
|
||||
h.ShowFrontPage = ShowFrontPage;
|
||||
}),
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
loading = new LoadingLayer(true),
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -71,6 +26,11 @@ namespace osu.Game.Overlays
|
||||
article.BindValueChanged(onArticleChanged);
|
||||
}
|
||||
|
||||
protected override NewsHeader CreateHeader() => new NewsHeader
|
||||
{
|
||||
ShowFrontPage = ShowFrontPage
|
||||
};
|
||||
|
||||
private bool displayUpdateRequired = true;
|
||||
|
||||
protected override void PopIn()
|
||||
@ -107,7 +67,7 @@ namespace osu.Game.Overlays
|
||||
private void onArticleChanged(ValueChangedEvent<string> e)
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
loading.Show();
|
||||
Loading.Show();
|
||||
|
||||
if (e.NewValue == null)
|
||||
{
|
||||
@ -122,11 +82,11 @@ namespace osu.Game.Overlays
|
||||
|
||||
protected void LoadDisplay(Drawable display)
|
||||
{
|
||||
scrollFlow.ScrollToStart();
|
||||
ScrollFlow.ScrollToStart();
|
||||
LoadComponentAsync(display, loaded =>
|
||||
{
|
||||
content.Child = loaded;
|
||||
loading.Hide();
|
||||
Child = loaded;
|
||||
Loading.Hide();
|
||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
|
@ -84,11 +84,6 @@ namespace osu.Game.Overlays
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
playlist = new PlaylistOverlay
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Y = player_height + 10,
|
||||
},
|
||||
playerContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -171,7 +166,7 @@ namespace osu.Game.Overlays
|
||||
Anchor = Anchor.CentreRight,
|
||||
Position = new Vector2(-bottom_black_area_height / 2, 0),
|
||||
Icon = FontAwesome.Solid.Bars,
|
||||
Action = () => playlist.ToggleVisibility(),
|
||||
Action = togglePlaylist
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -191,13 +186,35 @@ namespace osu.Game.Overlays
|
||||
};
|
||||
}
|
||||
|
||||
private void togglePlaylist()
|
||||
{
|
||||
if (playlist == null)
|
||||
{
|
||||
LoadComponentAsync(playlist = new PlaylistOverlay
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Y = player_height + 10,
|
||||
}, _ =>
|
||||
{
|
||||
dragContainer.Add(playlist);
|
||||
|
||||
playlist.BeatmapSets.BindTo(musicController.BeatmapSets);
|
||||
playlist.State.BindValueChanged(s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint), true);
|
||||
|
||||
togglePlaylist();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!beatmap.Disabled)
|
||||
playlist.ToggleVisibility();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
playlist.BeatmapSets.BindTo(musicController.BeatmapSets);
|
||||
playlist.State.BindValueChanged(s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint), true);
|
||||
|
||||
beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
|
||||
|
||||
musicController.TrackChanged += trackChanged;
|
||||
@ -306,7 +323,7 @@ namespace osu.Game.Overlays
|
||||
private void beatmapDisabledChanged(bool disabled)
|
||||
{
|
||||
if (disabled)
|
||||
playlist.Hide();
|
||||
playlist?.Hide();
|
||||
|
||||
prevButton.Enabled.Value = !disabled;
|
||||
nextButton.Enabled.Value = !disabled;
|
||||
@ -411,6 +428,11 @@ namespace osu.Game.Overlays
|
||||
|
||||
private class HoverableProgressBar : ProgressBar
|
||||
{
|
||||
public HoverableProgressBar()
|
||||
: base(true)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
this.ResizeHeightTo(progress_height, 500, Easing.OutQuint);
|
||||
|
48
osu.Game/Overlays/OnlineOverlay.cs
Normal file
48
osu.Game/Overlays/OnlineOverlay.cs
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public abstract class OnlineOverlay<T> : FullscreenOverlay<T>
|
||||
where T : OverlayHeader
|
||||
{
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
protected readonly OverlayScrollContainer ScrollFlow;
|
||||
protected readonly LoadingLayer Loading;
|
||||
private readonly Container content;
|
||||
|
||||
protected OnlineOverlay(OverlayColourScheme colourScheme)
|
||||
: base(colourScheme)
|
||||
{
|
||||
base.Content.AddRange(new Drawable[]
|
||||
{
|
||||
ScrollFlow = new OverlayScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header.With(h => h.Depth = float.MinValue),
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Loading = new LoadingLayer(true)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -4,96 +4,32 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Overlays.Rankings;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Online.API;
|
||||
using System.Threading;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Rankings.Tables;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class RankingsOverlay : FullscreenOverlay<RankingsOverlayHeader>
|
||||
public class RankingsOverlay : TabbableOnlineOverlay<RankingsOverlayHeader, RankingsScope>
|
||||
{
|
||||
protected Bindable<Country> Country => Header.Country;
|
||||
|
||||
protected Bindable<RankingsScope> Scope => Header.Current;
|
||||
|
||||
private readonly OverlayScrollContainer scrollFlow;
|
||||
private readonly Container contentContainer;
|
||||
private readonly LoadingLayer loading;
|
||||
private readonly Box background;
|
||||
|
||||
private APIRequest lastRequest;
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
public RankingsOverlay()
|
||||
: base(OverlayColourScheme.Green, new RankingsOverlayHeader
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Depth = -float.MaxValue
|
||||
})
|
||||
{
|
||||
loading = new LoadingLayer(true);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
scrollFlow = new OverlayScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header,
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
contentContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Margin = new MarginPadding { Bottom = 10 }
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
loading
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
background.Colour = ColourProvider.Background5;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private Bindable<RulesetInfo> ruleset { get; set; }
|
||||
|
||||
public RankingsOverlay()
|
||||
: base(OverlayColourScheme.Green)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -104,31 +40,33 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
// if a country is requested, force performance scope.
|
||||
if (Country.Value != null)
|
||||
Scope.Value = RankingsScope.Performance;
|
||||
Header.Current.Value = RankingsScope.Performance;
|
||||
|
||||
Scheduler.AddOnce(loadNewContent);
|
||||
});
|
||||
|
||||
Scope.BindValueChanged(_ =>
|
||||
{
|
||||
// country filtering is only valid for performance scope.
|
||||
if (Scope.Value != RankingsScope.Performance)
|
||||
Country.Value = null;
|
||||
|
||||
Scheduler.AddOnce(loadNewContent);
|
||||
Scheduler.AddOnce(triggerTabChanged);
|
||||
});
|
||||
|
||||
ruleset.BindValueChanged(_ =>
|
||||
{
|
||||
if (Scope.Value == RankingsScope.Spotlights)
|
||||
if (Header.Current.Value == RankingsScope.Spotlights)
|
||||
return;
|
||||
|
||||
Scheduler.AddOnce(loadNewContent);
|
||||
Scheduler.AddOnce(triggerTabChanged);
|
||||
});
|
||||
|
||||
Scheduler.AddOnce(loadNewContent);
|
||||
}
|
||||
|
||||
protected override void OnTabChanged(RankingsScope tab)
|
||||
{
|
||||
// country filtering is only valid for performance scope.
|
||||
if (Header.Current.Value != RankingsScope.Performance)
|
||||
Country.Value = null;
|
||||
|
||||
Scheduler.AddOnce(triggerTabChanged);
|
||||
}
|
||||
|
||||
private void triggerTabChanged() => base.OnTabChanged(Header.Current.Value);
|
||||
|
||||
protected override RankingsOverlayHeader CreateHeader() => new RankingsOverlayHeader();
|
||||
|
||||
public void ShowCountry(Country requested)
|
||||
{
|
||||
if (requested == null)
|
||||
@ -139,22 +77,13 @@ namespace osu.Game.Overlays
|
||||
Country.Value = requested;
|
||||
}
|
||||
|
||||
public void ShowSpotlights()
|
||||
protected override void CreateDisplayToLoad(RankingsScope tab)
|
||||
{
|
||||
Scope.Value = RankingsScope.Spotlights;
|
||||
Show();
|
||||
}
|
||||
|
||||
private void loadNewContent()
|
||||
{
|
||||
loading.Show();
|
||||
|
||||
cancellationToken?.Cancel();
|
||||
lastRequest?.Cancel();
|
||||
|
||||
if (Scope.Value == RankingsScope.Spotlights)
|
||||
if (Header.Current.Value == RankingsScope.Spotlights)
|
||||
{
|
||||
loadContent(new SpotlightsLayout
|
||||
LoadDisplay(new SpotlightsLayout
|
||||
{
|
||||
Ruleset = { BindTarget = ruleset }
|
||||
});
|
||||
@ -166,19 +95,19 @@ namespace osu.Game.Overlays
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
loadContent(null);
|
||||
LoadDisplay(Empty());
|
||||
return;
|
||||
}
|
||||
|
||||
request.Success += () => Schedule(() => loadContent(createTableFromResponse(request)));
|
||||
request.Failure += _ => Schedule(() => loadContent(null));
|
||||
request.Success += () => Schedule(() => LoadDisplay(createTableFromResponse(request)));
|
||||
request.Failure += _ => Schedule(() => LoadDisplay(Empty()));
|
||||
|
||||
api.Queue(request);
|
||||
}
|
||||
|
||||
private APIRequest createScopedRequest()
|
||||
{
|
||||
switch (Scope.Value)
|
||||
switch (Header.Current.Value)
|
||||
{
|
||||
case RankingsScope.Performance:
|
||||
return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName);
|
||||
@ -216,29 +145,9 @@ namespace osu.Game.Overlays
|
||||
return null;
|
||||
}
|
||||
|
||||
private void loadContent(Drawable content)
|
||||
{
|
||||
scrollFlow.ScrollToStart();
|
||||
|
||||
if (content == null)
|
||||
{
|
||||
contentContainer.Clear();
|
||||
loading.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
LoadComponentAsync(content, loaded =>
|
||||
{
|
||||
loading.Hide();
|
||||
contentContainer.Child = loaded;
|
||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
lastRequest?.Cancel();
|
||||
cancellationToken?.Cancel();
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
|
101
osu.Game/Overlays/TabbableOnlineOverlay.cs
Normal file
101
osu.Game/Overlays/TabbableOnlineOverlay.cs
Normal file
@ -0,0 +1,101 @@
|
||||
// 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.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public abstract class TabbableOnlineOverlay<THeader, TEnum> : OnlineOverlay<THeader>
|
||||
where THeader : TabControlOverlayHeader<TEnum>
|
||||
{
|
||||
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
||||
|
||||
private CancellationTokenSource cancellationToken;
|
||||
private bool displayUpdateRequired = true;
|
||||
|
||||
protected TabbableOnlineOverlay(OverlayColourScheme colourScheme)
|
||||
: base(colourScheme)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api)
|
||||
{
|
||||
apiState.BindTo(api.State);
|
||||
apiState.BindValueChanged(onlineStateChanged, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Header.Current.BindValueChanged(tab => OnTabChanged(tab.NewValue));
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
// We don't want to create a new display on every call, only when exiting from fully closed state.
|
||||
if (displayUpdateRequired)
|
||||
{
|
||||
Header.Current.TriggerChange();
|
||||
displayUpdateRequired = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopOutComplete()
|
||||
{
|
||||
base.PopOutComplete();
|
||||
LoadDisplay(Empty());
|
||||
displayUpdateRequired = true;
|
||||
}
|
||||
|
||||
protected void LoadDisplay(Drawable display)
|
||||
{
|
||||
ScrollFlow.ScrollToStart();
|
||||
|
||||
LoadComponentAsync(display, loaded =>
|
||||
{
|
||||
if (API.IsLoggedIn)
|
||||
Loading.Hide();
|
||||
|
||||
Child = loaded;
|
||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
protected virtual void OnTabChanged(TEnum tab)
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
Loading.Show();
|
||||
|
||||
if (!API.IsLoggedIn)
|
||||
{
|
||||
LoadDisplay(Empty());
|
||||
return;
|
||||
}
|
||||
|
||||
CreateDisplayToLoad(tab);
|
||||
}
|
||||
|
||||
protected abstract void CreateDisplayToLoad(TEnum tab);
|
||||
|
||||
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
|
||||
{
|
||||
if (State.Value == Visibility.Hidden)
|
||||
return;
|
||||
|
||||
Header.Current.TriggerChange();
|
||||
});
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ using osu.Game.Overlays.Profile;
|
||||
using osu.Game.Overlays.Profile.Sections;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -29,10 +30,14 @@ namespace osu.Game.Overlays
|
||||
public const float CONTENT_X_MARGIN = 70;
|
||||
|
||||
public UserProfileOverlay()
|
||||
: base(OverlayColourScheme.Pink, new ProfileHeader())
|
||||
: base(OverlayColourScheme.Pink)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ProfileHeader CreateHeader() => new ProfileHeader();
|
||||
|
||||
protected override Color4 BackgroundColour => ColourProvider.Background6;
|
||||
|
||||
public void ShowUser(int userId) => ShowUser(new User { Id = userId });
|
||||
|
||||
public void ShowUser(User user, bool fetchOnline = true)
|
||||
@ -72,12 +77,6 @@ namespace osu.Game.Overlays
|
||||
Origin = Anchor.TopCentre,
|
||||
};
|
||||
|
||||
Add(new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background6
|
||||
});
|
||||
|
||||
Add(sectionsContainer = new ProfileSectionsContainer
|
||||
{
|
||||
ExpandableHeader = Header,
|
||||
|
@ -49,6 +49,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
[Cached]
|
||||
protected OnlinePlayBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; }
|
||||
|
||||
protected IBindable<BeatmapAvailability> BeatmapAvailability => BeatmapAvailablilityTracker.Availability;
|
||||
|
||||
protected RoomSubScreen()
|
||||
{
|
||||
AddInternal(BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker
|
||||
|
@ -267,6 +267,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
base.LoadComplete();
|
||||
|
||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
||||
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
|
||||
UserMods.BindValueChanged(onUserModsChanged);
|
||||
|
||||
client.LoadRequested += onLoadRequested;
|
||||
@ -321,6 +322,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
client.ChangeUserMods(mods.NewValue);
|
||||
}
|
||||
|
||||
private void updateBeatmapAvailability(ValueChangedEvent<BeatmapAvailability> availability)
|
||||
{
|
||||
if (client.Room == null)
|
||||
return;
|
||||
|
||||
client.ChangeBeatmapAvailability(availability.NewValue);
|
||||
|
||||
// while this flow is handled server-side, this covers the edge case of the local user being in a ready state and then deleting the current beatmap.
|
||||
if (availability.NewValue != Online.Rooms.BeatmapAvailability.LocallyAvailable()
|
||||
&& client.LocalUser?.State == MultiplayerUserState.Ready)
|
||||
client.ChangeState(MultiplayerUserState.Idle);
|
||||
}
|
||||
|
||||
private void onReadyClick()
|
||||
{
|
||||
Debug.Assert(readyClickOperation == null);
|
||||
|
@ -162,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
||||
|
||||
const double fade_time = 50;
|
||||
|
||||
userStateDisplay.Status = User.State;
|
||||
userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability);
|
||||
|
||||
if (Room.Host?.Equals(User) == true)
|
||||
crown.FadeIn(fade_time);
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -8,83 +10,94 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
||||
{
|
||||
public class StateDisplay : CompositeDrawable
|
||||
{
|
||||
private const double fade_time = 50;
|
||||
|
||||
private SpriteIcon icon;
|
||||
private OsuSpriteText text;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
public StateDisplay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Alpha = 0;
|
||||
}
|
||||
|
||||
private MultiplayerUserState status;
|
||||
|
||||
private OsuSpriteText text;
|
||||
private SpriteIcon icon;
|
||||
|
||||
private const double fade_time = 50;
|
||||
|
||||
public MultiplayerUserState Status
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value == status)
|
||||
return;
|
||||
|
||||
status = value;
|
||||
|
||||
if (IsLoaded)
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
this.colours = colours;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 12),
|
||||
Colour = Color4Extensions.FromHex("#DDFFFF")
|
||||
},
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Icon = FontAwesome.Solid.CheckCircle,
|
||||
Size = new Vector2(12),
|
||||
}
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
progressBar = new ProgressBar(false)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
BackgroundColour = Color4.Black.Opacity(0.4f),
|
||||
FillColour = colours.Blue,
|
||||
Alpha = 0f,
|
||||
},
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Padding = new MarginPadding { Horizontal = 5f, Vertical = 1f },
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 12),
|
||||
Colour = Color4Extensions.FromHex("#DDFFFF")
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
updateStatus();
|
||||
}
|
||||
private OsuColour colours;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
private void updateStatus()
|
||||
public void UpdateStatus(MultiplayerUserState state, BeatmapAvailability availability)
|
||||
{
|
||||
switch (status)
|
||||
// the only case where the progress bar is used does its own local fade in.
|
||||
// starting by fading out is a sane default.
|
||||
progressBar.FadeOut(fade_time);
|
||||
this.FadeIn(fade_time);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
this.FadeOut(fade_time);
|
||||
return;
|
||||
case MultiplayerUserState.Idle:
|
||||
showBeatmapAvailability(availability);
|
||||
break;
|
||||
|
||||
case MultiplayerUserState.Ready:
|
||||
text.Text = "ready";
|
||||
@ -121,9 +134,43 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
||||
icon.Icon = FontAwesome.Solid.ArrowAltCircleUp;
|
||||
icon.Colour = colours.BlueLighter;
|
||||
break;
|
||||
}
|
||||
|
||||
this.FadeIn(fade_time);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(state), state, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void showBeatmapAvailability(BeatmapAvailability availability)
|
||||
{
|
||||
switch (availability.State)
|
||||
{
|
||||
default:
|
||||
this.FadeOut(fade_time);
|
||||
break;
|
||||
|
||||
case DownloadState.NotDownloaded:
|
||||
text.Text = "no map";
|
||||
icon.Icon = FontAwesome.Solid.MinusCircle;
|
||||
icon.Colour = colours.RedLight;
|
||||
break;
|
||||
|
||||
case DownloadState.Downloading:
|
||||
Debug.Assert(availability.DownloadProgress != null);
|
||||
|
||||
progressBar.FadeIn(fade_time);
|
||||
progressBar.CurrentTime = availability.DownloadProgress.Value;
|
||||
|
||||
text.Text = "downloading map";
|
||||
icon.Icon = FontAwesome.Solid.ArrowAltCircleDown;
|
||||
icon.Colour = colours.Blue;
|
||||
break;
|
||||
|
||||
case DownloadState.Importing:
|
||||
text.Text = "importing map";
|
||||
icon.Icon = FontAwesome.Solid.ArrowAltCircleDown;
|
||||
icon.Colour = colours.Yellow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user