mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 07:06:35 +09:00
Merge remote-tracking branch 'refs/remotes/ppy/master' into underscored_link
This commit is contained in:
@ -109,7 +109,7 @@ namespace osu.Game.Overlays
|
||||
break;
|
||||
|
||||
case APIState.Online:
|
||||
State = Visibility.Hidden;
|
||||
Hide();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-";
|
||||
bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToString(@"0.##") ?? "-";
|
||||
|
||||
if (beatmap == null)
|
||||
{
|
||||
@ -60,7 +60,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
}
|
||||
else
|
||||
{
|
||||
length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
|
||||
length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToString(@"m\:ss");
|
||||
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString();
|
||||
sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString();
|
||||
}
|
||||
|
83
osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs
Normal file
83
osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs
Normal file
@ -0,0 +1,83 @@
|
||||
// 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.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class BeatmapAvailability : Container
|
||||
{
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
|
||||
private bool downloadDisabled => BeatmapSet?.OnlineInfo.Availability?.DownloadDisabled ?? false;
|
||||
private bool hasExternalLink => !string.IsNullOrEmpty(BeatmapSet?.OnlineInfo.Availability?.ExternalLink);
|
||||
|
||||
private readonly LinkFlowContainer textContainer;
|
||||
|
||||
public BeatmapAvailability()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Padding = new MarginPadding { Top = 10, Right = 20 };
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.6f),
|
||||
},
|
||||
textContainer = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 14))
|
||||
{
|
||||
Direction = FillDirection.Full,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(10),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get => beatmapSet;
|
||||
|
||||
set
|
||||
{
|
||||
if (value == beatmapSet)
|
||||
return;
|
||||
|
||||
beatmapSet = value;
|
||||
|
||||
if (downloadDisabled || hasExternalLink)
|
||||
{
|
||||
Show();
|
||||
updateText();
|
||||
}
|
||||
else
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateText()
|
||||
{
|
||||
textContainer.Clear();
|
||||
textContainer.AddParagraph(downloadDisabled
|
||||
? "This beatmap is currently not available for download."
|
||||
: "Portions of this beatmap have been removed at the request of the creator or a third-party rights holder.", t => t.Colour = Color4.Orange);
|
||||
|
||||
if (hasExternalLink)
|
||||
{
|
||||
textContainer.NewParagraph();
|
||||
textContainer.NewParagraph();
|
||||
textContainer.AddLink("Check here for more information.", BeatmapSet.OnlineInfo.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Users;
|
||||
@ -19,7 +20,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
{
|
||||
public class DownloadButton : DownloadTrackingComposite, IHasTooltip
|
||||
public class HeaderDownloadButton : BeatmapDownloadTrackingComposite, IHasTooltip
|
||||
{
|
||||
private readonly bool noVideo;
|
||||
|
||||
@ -30,7 +31,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
private ShakeContainer shakeContainer;
|
||||
private HeaderButton button;
|
||||
|
||||
public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
|
||||
public HeaderDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
|
||||
: base(beatmapSet)
|
||||
{
|
||||
this.noVideo = noVideo;
|
@ -16,10 +16,11 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class Details : FillFlowContainer
|
||||
{
|
||||
protected readonly UserRatings Ratings;
|
||||
|
||||
private readonly PreviewButton preview;
|
||||
private readonly BasicStats basic;
|
||||
private readonly AdvancedStats advanced;
|
||||
private readonly UserRatings ratings;
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
|
||||
@ -33,6 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
beatmapSet = value;
|
||||
|
||||
basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,13 +48,12 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
if (value == beatmap) return;
|
||||
|
||||
basic.Beatmap = advanced.Beatmap = beatmap = value;
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
ratings.Metrics = Beatmap?.Metrics;
|
||||
Ratings.Metrics = BeatmapSet?.Metrics;
|
||||
}
|
||||
|
||||
public Details()
|
||||
@ -87,7 +88,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
},
|
||||
new DetailBox
|
||||
{
|
||||
Child = ratings = new UserRatings
|
||||
Child = Ratings = new UserRatings
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 95,
|
||||
|
@ -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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -8,20 +9,19 @@ using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Overlays.BeatmapSet.Buttons;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using DownloadButton = osu.Game.Overlays.BeatmapSet.Buttons.DownloadButton;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class Header : DownloadTrackingComposite
|
||||
public class Header : BeatmapDownloadTrackingComposite
|
||||
{
|
||||
private const float transition_duration = 200;
|
||||
private const float tabs_height = 50;
|
||||
@ -33,19 +33,26 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
private readonly OsuSpriteText title, artist;
|
||||
private readonly AuthorInfo author;
|
||||
private readonly FillFlowContainer downloadButtonsContainer;
|
||||
private readonly BeatmapAvailability beatmapAvailability;
|
||||
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
|
||||
public Details Details;
|
||||
|
||||
public bool DownloadButtonsVisible => downloadButtonsContainer.Any();
|
||||
|
||||
public readonly BeatmapPicker Picker;
|
||||
|
||||
private readonly FavouriteButton favouriteButton;
|
||||
|
||||
private readonly FillFlowContainer fadeContent;
|
||||
|
||||
private readonly LoadingAnimation loading;
|
||||
|
||||
public Header()
|
||||
{
|
||||
ExternalLinkButton externalLink;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 400;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Masking = true;
|
||||
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
@ -72,7 +79,8 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Top = tabs_height },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -84,6 +92,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
cover = new UpdateableBeatmapSetCover
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
@ -94,71 +103,89 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = BeatmapSetOverlay.X_PADDING },
|
||||
Child = new FillFlowContainer
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
Top = 20,
|
||||
Bottom = 30,
|
||||
Left = BeatmapSetOverlay.X_PADDING,
|
||||
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
fadeContent = new FillFlowContainer
|
||||
{
|
||||
new Container
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 113,
|
||||
Child = Picker = new BeatmapPicker(),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
new Container
|
||||
{
|
||||
title = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
externalLink = new ExternalLinkButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font
|
||||
},
|
||||
}
|
||||
},
|
||||
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) },
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
Child = author = new AuthorInfo(),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = buttons_height,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Children = new Drawable[]
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = Picker = new BeatmapPicker(),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
favouriteButton = new FavouriteButton(),
|
||||
downloadButtonsContainer = new FillFlowContainer
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
|
||||
Spacing = new Vector2(buttons_spacing),
|
||||
title = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
externalLink = new ExternalLinkButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font
|
||||
},
|
||||
}
|
||||
},
|
||||
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) },
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
Child = author = new AuthorInfo(),
|
||||
},
|
||||
beatmapAvailability = new BeatmapAvailability(),
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = buttons_height,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
favouriteButton = new FavouriteButton(),
|
||||
downloadButtonsContainer = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
|
||||
Spacing = new Vector2(buttons_spacing),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
loading = new LoadingAnimation
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(1.5f),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING },
|
||||
Margin = new MarginPadding { Top = BeatmapSetOverlay.TOP_PADDING, Right = BeatmapSetOverlay.X_PADDING },
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(10),
|
||||
Children = new Drawable[]
|
||||
@ -177,8 +204,11 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
},
|
||||
};
|
||||
|
||||
Picker.Beatmap.ValueChanged += b => Details.Beatmap = b.NewValue;
|
||||
Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}";
|
||||
Picker.Beatmap.ValueChanged += b =>
|
||||
{
|
||||
Details.Beatmap = b.NewValue;
|
||||
externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}";
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -190,25 +220,36 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
BeatmapSet.BindValueChanged(setInfo =>
|
||||
{
|
||||
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = setInfo.NewValue;
|
||||
|
||||
title.Text = setInfo.NewValue?.Metadata.Title ?? string.Empty;
|
||||
artist.Text = setInfo.NewValue?.Metadata.Artist ?? string.Empty;
|
||||
onlineStatusPill.Status = setInfo.NewValue?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
|
||||
Picker.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue;
|
||||
cover.BeatmapSet = setInfo.NewValue;
|
||||
|
||||
if (setInfo.NewValue != null)
|
||||
{
|
||||
downloadButtonsContainer.FadeIn(transition_duration);
|
||||
favouriteButton.FadeIn(transition_duration);
|
||||
}
|
||||
else
|
||||
if (setInfo.NewValue == null)
|
||||
{
|
||||
onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint);
|
||||
fadeContent.Hide();
|
||||
|
||||
loading.Show();
|
||||
|
||||
downloadButtonsContainer.FadeOut(transition_duration);
|
||||
favouriteButton.FadeOut(transition_duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
fadeContent.FadeIn(500, Easing.OutQuint);
|
||||
|
||||
updateDownloadButtons();
|
||||
loading.Hide();
|
||||
|
||||
title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty;
|
||||
artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty;
|
||||
|
||||
onlineStatusPill.FadeIn(500, Easing.OutQuint);
|
||||
onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status;
|
||||
|
||||
downloadButtonsContainer.FadeIn(transition_duration);
|
||||
favouriteButton.FadeIn(transition_duration);
|
||||
|
||||
updateDownloadButtons();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@ -216,11 +257,17 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
if (BeatmapSet.Value == null) return;
|
||||
|
||||
if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false)
|
||||
{
|
||||
downloadButtonsContainer.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (State.Value)
|
||||
{
|
||||
case DownloadState.LocallyAvailable:
|
||||
// temporary for UX until new design is implemented.
|
||||
downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value)
|
||||
downloadButtonsContainer.Child = new PanelDownloadButton(BeatmapSet.Value)
|
||||
{
|
||||
Width = 50,
|
||||
RelativeSizeAxes = Axes.Y
|
||||
@ -230,13 +277,13 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
case DownloadState.Downloading:
|
||||
case DownloadState.Downloaded:
|
||||
// temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design.
|
||||
downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value);
|
||||
downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value);
|
||||
downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value);
|
||||
if (BeatmapSet.Value.OnlineInfo.HasVideo)
|
||||
downloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true));
|
||||
downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
private Color4 backgroundHoveredColour;
|
||||
|
||||
private readonly Box background;
|
||||
private readonly TopScoreUserSection userSection;
|
||||
private readonly TopScoreStatisticsSection statisticsSection;
|
||||
|
||||
public DrawableTopScore()
|
||||
public DrawableTopScore(ScoreInfo score, int position = 1)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
@ -61,16 +59,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
userSection = new TopScoreUserSection
|
||||
new TopScoreUserSection
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Score = score,
|
||||
ScorePosition = position,
|
||||
},
|
||||
null,
|
||||
statisticsSection = new TopScoreStatisticsSection
|
||||
new TopScoreStatisticsSection
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Score = score,
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -91,18 +92,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
background.Colour = backgroundIdleColour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the score to be displayed.
|
||||
/// </summary>
|
||||
public ScoreInfo Score
|
||||
{
|
||||
set
|
||||
{
|
||||
userSection.Score = value;
|
||||
statisticsSection.Score = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
background.FadeColour(backgroundHoveredColour, fade_duration, Easing.OutQuint);
|
||||
|
@ -13,7 +13,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -59,7 +59,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
Content = null;
|
||||
backgroundFlow.Clear();
|
||||
|
||||
if (value == null || !value.Any())
|
||||
if (value?.Any() != true)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < value.Count; i++)
|
||||
@ -103,7 +103,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
Text = $"#{index + 1}",
|
||||
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
|
||||
},
|
||||
new DrawableRank(score.Rank)
|
||||
new UpdateableRank(score.Rank)
|
||||
{
|
||||
Size = new Vector2(30, 20)
|
||||
},
|
||||
@ -135,7 +135,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
Spacing = new Vector2(5, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DrawableFlag(score.User.Country) { Size = new Vector2(20, 13) },
|
||||
new UpdateableFlag(score.User.Country)
|
||||
{
|
||||
Size = new Vector2(20, 13),
|
||||
ShowPlaceholderOnNull = false,
|
||||
},
|
||||
username
|
||||
}
|
||||
},
|
||||
|
@ -5,15 +5,14 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
using System.Linq;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osuTK;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
{
|
||||
@ -24,18 +23,65 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
|
||||
private readonly Box background;
|
||||
private readonly ScoreTable scoreTable;
|
||||
|
||||
private readonly DrawableTopScore topScore;
|
||||
private readonly FillFlowContainer topScoresContainer;
|
||||
private readonly LoadingAnimation loadingAnimation;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
private GetScoresRequest getScoresRequest;
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get => beatmap;
|
||||
set
|
||||
{
|
||||
if (beatmap == value)
|
||||
return;
|
||||
|
||||
beatmap = value;
|
||||
|
||||
getScores(beatmap);
|
||||
}
|
||||
}
|
||||
|
||||
protected APILegacyScores Scores
|
||||
{
|
||||
set
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
loading = false;
|
||||
|
||||
topScoresContainer.Clear();
|
||||
|
||||
if (value?.Scores.Any() != true)
|
||||
{
|
||||
scoreTable.Scores = null;
|
||||
scoreTable.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
scoreTable.Scores = value.Scores;
|
||||
scoreTable.Show();
|
||||
|
||||
var topScore = value.Scores.First();
|
||||
var userScore = value.UserScore;
|
||||
|
||||
topScoresContainer.Add(new DrawableTopScore(topScore));
|
||||
|
||||
if (userScore != null && userScore.Score.OnlineScoreID != topScore.OnlineScoreID)
|
||||
topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public ScoresContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
@ -54,7 +100,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
Margin = new MarginPadding { Vertical = spacing },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
topScore = new DrawableTopScore(),
|
||||
topScoresContainer = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
},
|
||||
scoreTable = new ScoreTable
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
@ -65,7 +117,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
loadingAnimation = new LoadingAnimation
|
||||
{
|
||||
Alpha = 0,
|
||||
Margin = new MarginPadding(20)
|
||||
Margin = new MarginPadding(20),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -74,7 +126,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
background.Colour = colours.Gray2;
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
private bool loading
|
||||
@ -82,61 +133,23 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
|
||||
}
|
||||
|
||||
private GetScoresRequest getScoresRequest;
|
||||
private IReadOnlyList<ScoreInfo> scores;
|
||||
|
||||
public IReadOnlyList<ScoreInfo> Scores
|
||||
{
|
||||
get => scores;
|
||||
set
|
||||
{
|
||||
getScoresRequest?.Cancel();
|
||||
scores = value;
|
||||
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get => beatmap;
|
||||
set
|
||||
{
|
||||
beatmap = value;
|
||||
|
||||
Scores = null;
|
||||
|
||||
if (beatmap?.OnlineBeatmapID.HasValue != true)
|
||||
return;
|
||||
|
||||
loading = true;
|
||||
|
||||
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
|
||||
getScoresRequest.Success += r => Schedule(() => Scores = r.Scores);
|
||||
api.Queue(getScoresRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
loading = false;
|
||||
|
||||
scoreTable.Scores = scores?.Count > 1 ? scores : new List<ScoreInfo>();
|
||||
|
||||
if (scores?.Any() == true)
|
||||
{
|
||||
topScore.Score = scores.FirstOrDefault();
|
||||
topScore.Show();
|
||||
}
|
||||
else
|
||||
topScore.Hide();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
private void getScores(BeatmapInfo beatmap)
|
||||
{
|
||||
getScoresRequest?.Cancel();
|
||||
getScoresRequest = null;
|
||||
|
||||
Scores = null;
|
||||
|
||||
if (beatmap?.OnlineBeatmapID.HasValue != true)
|
||||
{
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
|
||||
getScoresRequest.Success += scores => Scores = scores;
|
||||
api.Queue(getScoresRequest);
|
||||
loading = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -22,11 +22,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
public class TopScoreUserSection : CompositeDrawable
|
||||
{
|
||||
private readonly SpriteText rankText;
|
||||
private readonly DrawableRank rank;
|
||||
private readonly UpdateableRank rank;
|
||||
private readonly UpdateableAvatar avatar;
|
||||
private readonly LinkFlowContainer usernameText;
|
||||
private readonly SpriteText date;
|
||||
private readonly DrawableFlag flag;
|
||||
private readonly UpdateableFlag flag;
|
||||
|
||||
public TopScoreUserSection()
|
||||
{
|
||||
@ -39,21 +39,30 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
Spacing = new Vector2(10, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
rankText = new OsuSpriteText
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "#1",
|
||||
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true)
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
rankText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Font = OsuFont.GetFont(size: 24, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
rank = new UpdateableRank(ScoreRank.D)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(40),
|
||||
FillMode = FillMode.Fit,
|
||||
},
|
||||
}
|
||||
},
|
||||
rank = new DrawableRank(ScoreRank.F)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(40),
|
||||
FillMode = FillMode.Fit,
|
||||
},
|
||||
avatar = new UpdateableAvatar
|
||||
avatar = new UpdateableAvatar(hideImmediately: true)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -67,6 +76,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
Offset = new Vector2(0, 2),
|
||||
Radius = 1,
|
||||
},
|
||||
ShowGuestOnNull = false,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
@ -89,11 +99,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold)
|
||||
},
|
||||
flag = new DrawableFlag
|
||||
flag = new UpdateableFlag(hideImmediately: true)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = new Vector2(20, 13),
|
||||
ShowPlaceholderOnNull = false,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -107,6 +118,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
rankText.Colour = colours.Yellow;
|
||||
}
|
||||
|
||||
public int ScorePosition
|
||||
{
|
||||
set => rankText.Text = $"#{value}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the score to be displayed.
|
||||
/// </summary>
|
||||
@ -121,7 +137,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
usernameText.Clear();
|
||||
usernameText.AddUserLink(value.User);
|
||||
|
||||
rank.UpdateRank(value.Rank);
|
||||
rank.Rank = value.Rank;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,11 +14,12 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class SuccessRate : Container
|
||||
{
|
||||
protected readonly FailRetryGraph Graph;
|
||||
|
||||
private readonly FillFlowContainer header;
|
||||
private readonly OsuSpriteText successRateLabel, successPercent, graphLabel;
|
||||
private readonly Bar successRate;
|
||||
private readonly Container percentContainer;
|
||||
private readonly FailRetryGraph graph;
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
|
||||
@ -37,15 +38,15 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
int passCount = beatmap?.OnlineInfo.PassCount ?? 0;
|
||||
int playCount = beatmap?.OnlineInfo.PlayCount ?? 0;
|
||||
int passCount = beatmap?.OnlineInfo?.PassCount ?? 0;
|
||||
int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0;
|
||||
|
||||
var rate = playCount != 0 ? (float)passCount / playCount : 0;
|
||||
successPercent.Text = rate.ToString("P0");
|
||||
successRate.Length = rate;
|
||||
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
|
||||
|
||||
graph.Metrics = beatmap?.Metrics;
|
||||
Graph.Metrics = beatmap?.Metrics;
|
||||
}
|
||||
|
||||
public SuccessRate()
|
||||
@ -94,7 +95,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
},
|
||||
},
|
||||
},
|
||||
graph = new FailRetryGraph
|
||||
Graph = new FailRetryGraph
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
@ -117,7 +118,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
graph.Padding = new MarginPadding { Top = header.DrawHeight };
|
||||
Graph.Padding = new MarginPadding { Top = header.DrawHeight };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,17 +21,13 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
public class BeatmapSetOverlay : FullscreenOverlay
|
||||
{
|
||||
private const int fade_duration = 300;
|
||||
|
||||
public const float X_PADDING = 40;
|
||||
public const float TOP_PADDING = 25;
|
||||
public const float RIGHT_WIDTH = 275;
|
||||
|
||||
private readonly Header header;
|
||||
protected readonly Header Header;
|
||||
|
||||
private RulesetStore rulesets;
|
||||
|
||||
private readonly ScrollContainer scroll;
|
||||
|
||||
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
|
||||
|
||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||
@ -39,8 +35,9 @@ namespace osu.Game.Overlays
|
||||
|
||||
public BeatmapSetOverlay()
|
||||
{
|
||||
OsuScrollContainer scroll;
|
||||
Info info;
|
||||
ScoresContainer scores;
|
||||
ScoresContainer scoreContainer;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -49,7 +46,7 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.2f)
|
||||
},
|
||||
scroll = new ScrollContainer
|
||||
scroll = new OsuScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
@ -60,21 +57,23 @@ namespace osu.Game.Overlays
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
header = new Header(),
|
||||
Header = new Header(),
|
||||
info = new Info(),
|
||||
scores = new ScoresContainer(),
|
||||
scoreContainer = new ScoresContainer(),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
header.BeatmapSet.BindTo(beatmapSet);
|
||||
Header.BeatmapSet.BindTo(beatmapSet);
|
||||
info.BeatmapSet.BindTo(beatmapSet);
|
||||
|
||||
header.Picker.Beatmap.ValueChanged += b =>
|
||||
Header.Picker.Beatmap.ValueChanged += b =>
|
||||
{
|
||||
info.Beatmap = b.NewValue;
|
||||
scores.Beatmap = b.NewValue;
|
||||
scoreContainer.Beatmap = b.NewValue;
|
||||
|
||||
scroll.ScrollToStart();
|
||||
};
|
||||
}
|
||||
|
||||
@ -92,37 +91,44 @@ namespace osu.Game.Overlays
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
State = Visibility.Hidden;
|
||||
Hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void FetchAndShowBeatmap(int beatmapId)
|
||||
{
|
||||
beatmapSet.Value = null;
|
||||
|
||||
var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId);
|
||||
req.Success += res =>
|
||||
{
|
||||
beatmapSet.Value = res.ToBeatmapSet(rulesets);
|
||||
header.Picker.Beatmap.Value = header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
|
||||
Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
|
||||
};
|
||||
API.Queue(req);
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
public void FetchAndShowBeatmapSet(int beatmapSetId)
|
||||
{
|
||||
beatmapSet.Value = null;
|
||||
|
||||
var req = new GetBeatmapSetRequest(beatmapSetId);
|
||||
req.Success += res => beatmapSet.Value = res.ToBeatmapSet(rulesets);
|
||||
API.Queue(req);
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show an already fully-populated beatmap set.
|
||||
/// </summary>
|
||||
/// <param name="set">The set to show.</param>
|
||||
public void ShowBeatmapSet(BeatmapSetInfo set)
|
||||
{
|
||||
beatmapSet.Value = set;
|
||||
Show();
|
||||
scroll.ScrollTo(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ using System.Text.RegularExpressions;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Users;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Overlays.Changelog
|
||||
{
|
||||
@ -45,8 +46,12 @@ namespace osu.Game.Overlays.Changelog
|
||||
Direction = FillDirection.Vertical,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var categoryEntries in build.ChangelogEntries.GroupBy(b => b.Category).OrderBy(c => c.Key))
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
foreach (var categoryEntries in Build.ChangelogEntries.GroupBy(b => b.Category).OrderBy(c => c.Key))
|
||||
{
|
||||
ChangelogEntries.Add(new OsuSpriteText
|
||||
{
|
||||
@ -69,34 +74,69 @@ namespace osu.Game.Overlays.Changelog
|
||||
Margin = new MarginPadding { Vertical = 5 },
|
||||
};
|
||||
|
||||
var entryColour = entry.Major ? colours.YellowLight : Color4.White;
|
||||
|
||||
title.AddIcon(FontAwesome.Solid.Check, t =>
|
||||
{
|
||||
t.Font = fontSmall;
|
||||
t.Colour = entryColour;
|
||||
t.Padding = new MarginPadding { Left = -17, Right = 5 };
|
||||
});
|
||||
|
||||
title.AddText(entry.Title, t => { t.Font = fontLarge; });
|
||||
title.AddText(entry.Title, t =>
|
||||
{
|
||||
t.Font = fontLarge;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
|
||||
if (!string.IsNullOrEmpty(entry.Repository))
|
||||
{
|
||||
title.AddText(" (", t => t.Font = fontLarge);
|
||||
title.AddText(" (", t =>
|
||||
{
|
||||
t.Font = fontLarge;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, Online.Chat.LinkAction.External,
|
||||
creationParameters: t => { t.Font = fontLarge; });
|
||||
title.AddText(")", t => t.Font = fontLarge);
|
||||
creationParameters: t =>
|
||||
{
|
||||
t.Font = fontLarge;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
title.AddText(")", t =>
|
||||
{
|
||||
t.Font = fontLarge;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
}
|
||||
|
||||
title.AddText(" by ", t => t.Font = fontMedium);
|
||||
title.AddText(" by ", t =>
|
||||
{
|
||||
t.Font = fontMedium;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
|
||||
if (entry.GithubUser.UserId != null)
|
||||
title.AddUserLink(new User
|
||||
{
|
||||
Username = entry.GithubUser.OsuUsername,
|
||||
Id = entry.GithubUser.UserId.Value
|
||||
}, t => t.Font = fontMedium);
|
||||
}, t =>
|
||||
{
|
||||
t.Font = fontMedium;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
else if (entry.GithubUser.GithubUrl != null)
|
||||
title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t => t.Font = fontMedium);
|
||||
title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t =>
|
||||
{
|
||||
t.Font = fontMedium;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
else
|
||||
title.AddText(entry.GithubUser.DisplayName, t => t.Font = fontSmall);
|
||||
title.AddText(entry.GithubUser.DisplayName, t =>
|
||||
{
|
||||
t.Font = fontSmall;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
|
||||
ChangelogEntries.Add(title);
|
||||
|
||||
|
@ -58,7 +58,11 @@ namespace osu.Game.Overlays.Changelog
|
||||
}
|
||||
|
||||
if (build != null)
|
||||
Child = new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild };
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild },
|
||||
new Comments(build)
|
||||
};
|
||||
}
|
||||
|
||||
public class ChangelogBuildWithNavigation : ChangelogBuild
|
||||
@ -88,24 +92,16 @@ namespace osu.Game.Overlays.Changelog
|
||||
});
|
||||
}
|
||||
|
||||
NavigationIconButton left, right;
|
||||
|
||||
fill.AddRange(new[]
|
||||
fill.Insert(-1, new NavigationIconButton(Build.Versions?.Previous)
|
||||
{
|
||||
left = new NavigationIconButton(Build.Versions?.Previous)
|
||||
{
|
||||
Icon = FontAwesome.Solid.ChevronLeft,
|
||||
SelectBuild = b => SelectBuild(b)
|
||||
},
|
||||
right = new NavigationIconButton(Build.Versions?.Next)
|
||||
{
|
||||
Icon = FontAwesome.Solid.ChevronRight,
|
||||
SelectBuild = b => SelectBuild(b)
|
||||
},
|
||||
Icon = FontAwesome.Solid.ChevronLeft,
|
||||
SelectBuild = b => SelectBuild(b)
|
||||
});
|
||||
fill.Insert(1, new NavigationIconButton(Build.Versions?.Next)
|
||||
{
|
||||
Icon = FontAwesome.Solid.ChevronRight,
|
||||
SelectBuild = b => SelectBuild(b)
|
||||
});
|
||||
|
||||
fill.SetLayoutPosition(left, -1);
|
||||
fill.SetLayoutPosition(right, 1);
|
||||
|
||||
return fill;
|
||||
}
|
||||
|
79
osu.Game/Overlays/Changelog/Comments.cs
Normal file
79
osu.Game/Overlays/Changelog/Comments.cs
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Changelog
|
||||
{
|
||||
public class Comments : CompositeDrawable
|
||||
{
|
||||
private readonly APIChangelogBuild build;
|
||||
|
||||
public Comments(APIChangelogBuild build)
|
||||
{
|
||||
this.build = build;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 50,
|
||||
Vertical = 20,
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
LinkFlowContainer text;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 10,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.GreyVioletDarker
|
||||
},
|
||||
},
|
||||
text = new LinkFlowContainer(t =>
|
||||
{
|
||||
t.Colour = colours.PinkLighter;
|
||||
t.Font = OsuFont.Default.With(size: 14);
|
||||
})
|
||||
{
|
||||
Padding = new MarginPadding(20),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
}
|
||||
};
|
||||
|
||||
text.AddParagraph("Got feedback?", t =>
|
||||
{
|
||||
t.Colour = Color4.White;
|
||||
t.Font = OsuFont.Default.With(italics: true, size: 20);
|
||||
t.Padding = new MarginPadding { Bottom = 20 };
|
||||
});
|
||||
|
||||
text.AddParagraph("We would love to hear what you think of this update! ");
|
||||
text.AddIcon(FontAwesome.Regular.GrinHearts);
|
||||
|
||||
text.AddParagraph("Please visit the ");
|
||||
text.AddLink("web version", $"{build.Url}#comments");
|
||||
text.AddText(" of this changelog to leave any comments.");
|
||||
}
|
||||
}
|
||||
}
|
@ -90,8 +90,8 @@ namespace osu.Game.Overlays.Changelog
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
sampleClick = audio.Sample.Get(@"UI/generic-select-soft");
|
||||
sampleHover = audio.Sample.Get(@"UI/generic-hover-soft");
|
||||
sampleClick = audio.Samples.Get(@"UI/generic-select-soft");
|
||||
sampleHover = audio.Samples.Get(@"UI/generic-hover-soft");
|
||||
}
|
||||
|
||||
protected override void OnActivated() => updateState();
|
||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colour.PurpleDarkAlternative,
|
||||
},
|
||||
new ScrollContainer
|
||||
new OsuScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
@ -76,7 +76,7 @@ namespace osu.Game.Overlays
|
||||
},
|
||||
};
|
||||
|
||||
sampleBack = audio.Sample.Get(@"UI/generic-select-soft");
|
||||
sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
|
||||
|
||||
header.Current.BindTo(Current);
|
||||
|
||||
@ -92,7 +92,7 @@ namespace osu.Game.Overlays
|
||||
public void ShowListing()
|
||||
{
|
||||
Current.Value = null;
|
||||
State = Visibility.Visible;
|
||||
Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -106,7 +106,7 @@ namespace osu.Game.Overlays
|
||||
if (build == null) throw new ArgumentNullException(nameof(build));
|
||||
|
||||
Current.Value = build;
|
||||
State = Visibility.Visible;
|
||||
Show();
|
||||
}
|
||||
|
||||
public void ShowBuild([NotNull] string updateStream, [NotNull] string version)
|
||||
@ -123,7 +123,7 @@ namespace osu.Game.Overlays
|
||||
ShowBuild(build);
|
||||
});
|
||||
|
||||
State = Visibility.Visible;
|
||||
Show();
|
||||
}
|
||||
|
||||
public override bool OnPressed(GlobalAction action)
|
||||
@ -133,7 +133,7 @@ namespace osu.Game.Overlays
|
||||
case GlobalAction.Back:
|
||||
if (Current.Value == null)
|
||||
{
|
||||
State = Visibility.Hidden;
|
||||
Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -14,6 +15,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
@ -72,17 +74,12 @@ namespace osu.Game.Overlays.Chat
|
||||
}
|
||||
}
|
||||
|
||||
private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
customUsernameColour = colours.ChatBlue;
|
||||
}
|
||||
|
||||
private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour);
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
bool hasBackground = senderHasBackground;
|
||||
|
||||
@ -177,6 +174,11 @@ namespace osu.Game.Overlays.Chat
|
||||
};
|
||||
|
||||
updateMessageContent();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
@ -201,6 +203,9 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
private Action startChatAction;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
public MessageSender(User sender)
|
||||
{
|
||||
this.sender = sender;
|
||||
@ -213,11 +218,21 @@ namespace osu.Game.Overlays.Chat
|
||||
startChatAction = () => chatManager?.OpenPrivateChannel(sender);
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
public MenuItem[] ContextMenuItems
|
||||
{
|
||||
new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action),
|
||||
new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction),
|
||||
};
|
||||
get
|
||||
{
|
||||
List<MenuItem> items = new List<MenuItem>
|
||||
{
|
||||
new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action)
|
||||
};
|
||||
|
||||
if (sender.Id != api.LocalUser.Value.Id)
|
||||
items.Add(new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction));
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Color4[] username_colours =
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -17,15 +18,18 @@ namespace osu.Game.Overlays.Chat
|
||||
public class DrawableChannel : Container
|
||||
{
|
||||
public readonly Channel Channel;
|
||||
protected readonly ChatLineContainer ChatLineFlow;
|
||||
private readonly ScrollContainer scroll;
|
||||
protected ChatLineContainer ChatLineFlow;
|
||||
private OsuScrollContainer scroll;
|
||||
|
||||
public DrawableChannel(Channel channel)
|
||||
{
|
||||
Channel = channel;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
scroll = new OsuScrollContainer
|
||||
@ -48,18 +52,17 @@ namespace osu.Game.Overlays.Chat
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
newMessagesArrived(Channel.Messages);
|
||||
|
||||
Channel.NewMessagesArrived += newMessagesArrived;
|
||||
Channel.MessageRemoved += messageRemoved;
|
||||
Channel.PendingMessageResolved += pendingMessageResolved;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
scrollToEnd();
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Chat.Selection
|
||||
private readonly SearchTextBox search;
|
||||
private readonly SearchContainer<ChannelSection> sectionsFlow;
|
||||
|
||||
protected override bool DimMainContent => false;
|
||||
|
||||
public Action<Channel> OnRequestJoin;
|
||||
public Action<Channel> OnRequestLeave;
|
||||
|
||||
|
@ -49,6 +49,8 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
// performTabSort might've made selectorTab's position wonky, fix it
|
||||
TabContainer.SetLayoutPosition(selectorTab, float.MaxValue);
|
||||
|
||||
((ChannelTabItem)item).OnRequestClose += tabCloseRequested;
|
||||
|
||||
base.AddTabItem(item, addToDropdown);
|
||||
}
|
||||
|
||||
@ -57,10 +59,10 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
switch (value.Type)
|
||||
{
|
||||
default:
|
||||
return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested };
|
||||
return new ChannelTabItem(value);
|
||||
|
||||
case ChannelType.PM:
|
||||
return new PrivateChannelTabItem(value) { OnRequestClose = tabCloseRequested };
|
||||
return new PrivateChannelTabItem(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Chat.Tabs
|
||||
@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
if (value.Type != ChannelType.PM)
|
||||
throw new ArgumentException("Argument value needs to have the targettype user!");
|
||||
|
||||
Avatar avatar;
|
||||
DrawableAvatar avatar;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Child = new DelayedLoadWrapper(avatar = new Avatar(value.Users.First())
|
||||
Child = new DelayedLoadWrapper(avatar = new DrawableAvatar(value.Users.First())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
OpenOnClick = { Value = false },
|
||||
|
@ -31,12 +31,13 @@ namespace osu.Game.Overlays
|
||||
|
||||
private ChannelManager channelManager;
|
||||
|
||||
private readonly Container<DrawableChannel> currentChannelContainer;
|
||||
private Container<DrawableChannel> currentChannelContainer;
|
||||
|
||||
private readonly List<DrawableChannel> loadedChannels = new List<DrawableChannel>();
|
||||
|
||||
private readonly LoadingAnimation loading;
|
||||
private LoadingAnimation loading;
|
||||
|
||||
private readonly FocusedTextBox textbox;
|
||||
private FocusedTextBox textbox;
|
||||
|
||||
private const int transition_length = 500;
|
||||
|
||||
@ -44,19 +45,22 @@ namespace osu.Game.Overlays
|
||||
|
||||
public const float TAB_AREA_HEIGHT = 50;
|
||||
|
||||
private readonly ChannelTabControl channelTabControl;
|
||||
protected ChannelTabControl ChannelTabControl;
|
||||
|
||||
private readonly Container chatContainer;
|
||||
private readonly TabsArea tabsArea;
|
||||
private readonly Box chatBackground;
|
||||
private readonly Box tabBackground;
|
||||
protected virtual ChannelTabControl CreateChannelTabControl() => new ChannelTabControl();
|
||||
|
||||
private Container chatContainer;
|
||||
private TabsArea tabsArea;
|
||||
private Box chatBackground;
|
||||
private Box tabBackground;
|
||||
|
||||
public Bindable<double> ChatHeight { get; set; }
|
||||
|
||||
private readonly Container channelSelectionContainer;
|
||||
private readonly ChannelSelectionOverlay channelSelectionOverlay;
|
||||
private Container channelSelectionContainer;
|
||||
protected ChannelSelectionOverlay ChannelSelectionOverlay;
|
||||
|
||||
public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || channelSelectionOverlay.State == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos);
|
||||
public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos)
|
||||
|| (ChannelSelectionOverlay.State.Value == Visibility.Visible && ChannelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos));
|
||||
|
||||
public ChatOverlay()
|
||||
{
|
||||
@ -64,7 +68,11 @@ namespace osu.Game.Overlays
|
||||
RelativePositionAxes = Axes.Both;
|
||||
Anchor = Anchor.BottomLeft;
|
||||
Origin = Anchor.BottomLeft;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, OsuColour colours, ChannelManager channelManager)
|
||||
{
|
||||
const float padding = 5;
|
||||
|
||||
Children = new Drawable[]
|
||||
@ -76,7 +84,7 @@ namespace osu.Game.Overlays
|
||||
Masking = true,
|
||||
Children = new[]
|
||||
{
|
||||
channelSelectionOverlay = new ChannelSelectionOverlay
|
||||
ChannelSelectionOverlay = new ChannelSelectionOverlay
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
@ -130,7 +138,7 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 1,
|
||||
PlaceholderText = "type your message",
|
||||
Exit = () => State = Visibility.Hidden,
|
||||
Exit = Hide,
|
||||
OnCommit = postMessage,
|
||||
ReleaseFocusOnCommit = false,
|
||||
HoldFocus = true,
|
||||
@ -149,33 +157,27 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
channelTabControl = new ChannelTabControl
|
||||
ChannelTabControl = CreateChannelTabControl().With(d =>
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
OnRequestLeave = channel => channelManager.LeaveChannel(channel)
|
||||
},
|
||||
d.Anchor = Anchor.BottomLeft;
|
||||
d.Origin = Anchor.BottomLeft;
|
||||
d.RelativeSizeAxes = Axes.Both;
|
||||
d.OnRequestLeave = channelManager.LeaveChannel;
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
channelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue;
|
||||
channelTabControl.ChannelSelectorActive.ValueChanged += active => channelSelectionOverlay.State = active.NewValue ? Visibility.Visible : Visibility.Hidden;
|
||||
channelSelectionOverlay.StateChanged += state =>
|
||||
ChannelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue;
|
||||
ChannelTabControl.ChannelSelectorActive.ValueChanged += active => ChannelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden;
|
||||
ChannelSelectionOverlay.State.ValueChanged += state =>
|
||||
{
|
||||
if (state == Visibility.Hidden && channelManager.CurrentChannel.Value == null)
|
||||
{
|
||||
channelSelectionOverlay.State = Visibility.Visible;
|
||||
State = Visibility.Hidden;
|
||||
return;
|
||||
}
|
||||
// Propagate the visibility state to ChannelSelectorActive
|
||||
ChannelTabControl.ChannelSelectorActive.Value = state.NewValue == Visibility.Visible;
|
||||
|
||||
channelTabControl.ChannelSelectorActive.Value = state == Visibility.Visible;
|
||||
|
||||
if (state == Visibility.Visible)
|
||||
if (state.NewValue == Visibility.Visible)
|
||||
{
|
||||
textbox.HoldFocus = false;
|
||||
if (1f - ChatHeight.Value < channel_selection_min_height)
|
||||
@ -185,17 +187,53 @@ namespace osu.Game.Overlays
|
||||
textbox.HoldFocus = true;
|
||||
};
|
||||
|
||||
channelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel);
|
||||
channelSelectionOverlay.OnRequestLeave = channel => channelManager.LeaveChannel(channel);
|
||||
ChannelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel);
|
||||
ChannelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel;
|
||||
|
||||
ChatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
|
||||
ChatHeight.ValueChanged += height =>
|
||||
{
|
||||
chatContainer.Height = (float)height.NewValue;
|
||||
channelSelectionContainer.Height = 1f - (float)height.NewValue;
|
||||
tabBackground.FadeTo(height.NewValue == 1 ? 1 : 0.8f, 200);
|
||||
};
|
||||
ChatHeight.TriggerChange();
|
||||
|
||||
chatBackground.Colour = colours.ChatBlue;
|
||||
|
||||
this.channelManager = channelManager;
|
||||
|
||||
loading.Show();
|
||||
|
||||
// This is a relatively expensive (and blocking) operation.
|
||||
// Scheduling it ensures that it won't be performed unless the user decides to open chat.
|
||||
// TODO: Refactor OsuFocusedOverlayContainer / OverlayContainer to support delayed content loading.
|
||||
Schedule(() =>
|
||||
{
|
||||
// TODO: consider scheduling bindable callbacks to not perform when overlay is not present.
|
||||
channelManager.JoinedChannels.ItemsAdded += onChannelAddedToJoinedChannels;
|
||||
channelManager.JoinedChannels.ItemsRemoved += onChannelRemovedFromJoinedChannels;
|
||||
foreach (Channel channel in channelManager.JoinedChannels)
|
||||
ChannelTabControl.AddChannel(channel);
|
||||
|
||||
channelManager.AvailableChannels.ItemsAdded += availableChannelsChanged;
|
||||
channelManager.AvailableChannels.ItemsRemoved += availableChannelsChanged;
|
||||
ChannelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels);
|
||||
|
||||
currentChannel = channelManager.CurrentChannel.GetBoundCopy();
|
||||
currentChannel.BindValueChanged(currentChannelChanged, true);
|
||||
});
|
||||
}
|
||||
|
||||
private Bindable<Channel> currentChannel;
|
||||
|
||||
private void currentChannelChanged(ValueChangedEvent<Channel> e)
|
||||
{
|
||||
if (e.NewValue == null)
|
||||
{
|
||||
textbox.Current.Disabled = true;
|
||||
currentChannelContainer.Clear(false);
|
||||
channelSelectionOverlay.State = Visibility.Visible;
|
||||
ChannelSelectionOverlay.Show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -204,8 +242,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
textbox.Current.Disabled = e.NewValue.ReadOnly;
|
||||
|
||||
if (channelTabControl.Current.Value != e.NewValue)
|
||||
Scheduler.Add(() => channelTabControl.Current.Value = e.NewValue);
|
||||
if (ChannelTabControl.Current.Value != e.NewValue)
|
||||
Scheduler.Add(() => ChannelTabControl.Current.Value = e.NewValue);
|
||||
|
||||
var loaded = loadedChannels.Find(d => d.Channel == e.NewValue);
|
||||
|
||||
@ -253,7 +291,7 @@ namespace osu.Game.Overlays
|
||||
double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y;
|
||||
|
||||
// If the channel selection screen is shown, mind its minimum height
|
||||
if (channelSelectionOverlay.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
|
||||
if (ChannelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
|
||||
targetChatHeight = 1f - channel_selection_min_height;
|
||||
|
||||
ChatHeight.Value = targetChatHeight;
|
||||
@ -270,9 +308,9 @@ namespace osu.Game.Overlays
|
||||
|
||||
private void selectTab(int index)
|
||||
{
|
||||
var channel = channelTabControl.Items.Skip(index).FirstOrDefault();
|
||||
var channel = ChannelTabControl.Items.Skip(index).FirstOrDefault();
|
||||
if (channel != null && !(channel is ChannelSelectorTabItem.ChannelSelectorTabChannel))
|
||||
channelTabControl.Current.Value = channel;
|
||||
ChannelTabControl.Current.Value = channel;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
@ -317,6 +355,7 @@ namespace osu.Game.Overlays
|
||||
this.FadeIn(transition_length, Easing.OutQuint);
|
||||
|
||||
textbox.HoldFocus = true;
|
||||
|
||||
base.PopIn();
|
||||
}
|
||||
|
||||
@ -325,58 +364,29 @@ namespace osu.Game.Overlays
|
||||
this.MoveToY(Height, transition_length, Easing.InSine);
|
||||
this.FadeOut(transition_length, Easing.InSine);
|
||||
|
||||
channelSelectionOverlay.State = Visibility.Hidden;
|
||||
ChannelSelectionOverlay.Hide();
|
||||
|
||||
textbox.HoldFocus = false;
|
||||
base.PopOut();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, OsuColour colours, ChannelManager channelManager)
|
||||
{
|
||||
ChatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
|
||||
ChatHeight.ValueChanged += height =>
|
||||
{
|
||||
chatContainer.Height = (float)height.NewValue;
|
||||
channelSelectionContainer.Height = 1f - (float)height.NewValue;
|
||||
tabBackground.FadeTo(height.NewValue == 1 ? 1 : 0.8f, 200);
|
||||
};
|
||||
ChatHeight.TriggerChange();
|
||||
|
||||
chatBackground.Colour = colours.ChatBlue;
|
||||
|
||||
loading.Show();
|
||||
|
||||
this.channelManager = channelManager;
|
||||
channelManager.CurrentChannel.ValueChanged += currentChannelChanged;
|
||||
channelManager.JoinedChannels.ItemsAdded += onChannelAddedToJoinedChannels;
|
||||
channelManager.JoinedChannels.ItemsRemoved += onChannelRemovedFromJoinedChannels;
|
||||
channelManager.AvailableChannels.ItemsAdded += availableChannelsChanged;
|
||||
channelManager.AvailableChannels.ItemsRemoved += availableChannelsChanged;
|
||||
|
||||
//for the case that channelmanager was faster at fetching the channels than our attachment to CollectionChanged.
|
||||
channelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels);
|
||||
foreach (Channel channel in channelManager.JoinedChannels)
|
||||
channelTabControl.AddChannel(channel);
|
||||
}
|
||||
|
||||
private void onChannelAddedToJoinedChannels(IEnumerable<Channel> channels)
|
||||
{
|
||||
foreach (Channel channel in channels)
|
||||
channelTabControl.AddChannel(channel);
|
||||
ChannelTabControl.AddChannel(channel);
|
||||
}
|
||||
|
||||
private void onChannelRemovedFromJoinedChannels(IEnumerable<Channel> channels)
|
||||
{
|
||||
foreach (Channel channel in channels)
|
||||
{
|
||||
channelTabControl.RemoveChannel(channel);
|
||||
ChannelTabControl.RemoveChannel(channel);
|
||||
loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel));
|
||||
}
|
||||
}
|
||||
|
||||
private void availableChannelsChanged(IEnumerable<Channel> channels)
|
||||
=> channelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels);
|
||||
=> ChannelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels);
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
|
@ -134,9 +134,9 @@ namespace osu.Game.Overlays.Dialog
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Position = new Vector2(0f, -50f),
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0f, 10f),
|
||||
Padding = new MarginPadding { Bottom = 10 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
@ -144,10 +144,6 @@ namespace osu.Game.Overlays.Dialog
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Size = ringSize,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Bottom = 30,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
ring = new CircularContainer
|
||||
@ -181,15 +177,15 @@ namespace osu.Game.Overlays.Dialog
|
||||
Anchor = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(15),
|
||||
TextAnchor = Anchor.TopCentre,
|
||||
},
|
||||
body = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 18))
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
TextAnchor = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(15),
|
||||
TextAnchor = Anchor.TopCentre,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -37,8 +37,8 @@ namespace osu.Game.Overlays
|
||||
dialogContainer.Add(currentDialog);
|
||||
|
||||
currentDialog.Show();
|
||||
currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state);
|
||||
State = Visibility.Visible;
|
||||
currentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue);
|
||||
Show();
|
||||
}
|
||||
|
||||
protected override bool PlaySamplesOnStateChange => false;
|
||||
@ -53,7 +53,7 @@ namespace osu.Game.Overlays
|
||||
dialog.Delay(PopupDialog.EXIT_DURATION).Expire();
|
||||
|
||||
if (dialog == currentDialog)
|
||||
State = Visibility.Hidden;
|
||||
Hide();
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
@ -66,7 +66,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
if (currentDialog?.State == Visibility.Visible)
|
||||
if (currentDialog?.State.Value == Visibility.Visible)
|
||||
{
|
||||
currentDialog.Hide();
|
||||
return;
|
||||
|
19
osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs
Normal file
19
osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public abstract class BeatmapDownloadTrackingComposite : DownloadTrackingComposite<BeatmapSetInfo, BeatmapManager>
|
||||
{
|
||||
public Bindable<BeatmapSetInfo> BeatmapSet => Model;
|
||||
|
||||
protected BeatmapDownloadTrackingComposite(BeatmapSetInfo set = null)
|
||||
: base(set)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -155,7 +155,7 @@ namespace osu.Game.Overlays.Direct
|
||||
},
|
||||
},
|
||||
},
|
||||
new DownloadButton(SetInfo)
|
||||
new PanelDownloadButton(SetInfo)
|
||||
{
|
||||
Size = new Vector2(50, 30),
|
||||
Margin = new MarginPadding(horizontal_padding),
|
||||
|
@ -27,6 +27,7 @@ namespace osu.Game.Overlays.Direct
|
||||
private const float height = 70;
|
||||
|
||||
private FillFlowContainer statusContainer;
|
||||
protected PanelDownloadButton DownloadButton;
|
||||
private PlayButton playButton;
|
||||
private Box progressBar;
|
||||
|
||||
@ -149,7 +150,7 @@ namespace osu.Game.Overlays.Direct
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Child = new DownloadButton(SetInfo)
|
||||
Child = DownloadButton = new PanelDownloadButton(SetInfo)
|
||||
{
|
||||
Size = new Vector2(height - vertical_padding * 3),
|
||||
Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding },
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -34,6 +35,7 @@ namespace osu.Game.Overlays.Direct
|
||||
|
||||
public PreviewTrack Preview => PlayButton.Preview;
|
||||
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
|
||||
|
||||
protected abstract PlayButton PlayButton { get; }
|
||||
protected abstract Box PreviewBar { get; }
|
||||
|
||||
@ -43,6 +45,8 @@ namespace osu.Game.Overlays.Direct
|
||||
|
||||
protected DirectPanel(BeatmapSetInfo setInfo)
|
||||
{
|
||||
Debug.Assert(setInfo.OnlineBeatmapSetID != null);
|
||||
|
||||
SetInfo = setInfo;
|
||||
}
|
||||
|
||||
@ -117,12 +121,11 @@ namespace osu.Game.Overlays.Direct
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
ShowInformation();
|
||||
Debug.Assert(SetInfo.OnlineBeatmapSetID != null);
|
||||
beatmapSetOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
88
osu.Game/Overlays/Direct/DirectRulesetSelector.cs
Normal file
88
osu.Game/Overlays/Direct/DirectRulesetSelector.cs
Normal file
@ -0,0 +1,88 @@
|
||||
// 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.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class DirectRulesetSelector : RulesetSelector
|
||||
{
|
||||
public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput;
|
||||
|
||||
public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput;
|
||||
|
||||
public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree;
|
||||
|
||||
public DirectRulesetSelector()
|
||||
{
|
||||
TabContainer.Masking = false;
|
||||
TabContainer.Spacing = new Vector2(10, 0);
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Current.DisabledChanged += value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new DirectRulesetTabItem(value);
|
||||
|
||||
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
private class DirectRulesetTabItem : TabItem<RulesetInfo>
|
||||
{
|
||||
private readonly ConstrainedIconContainer iconContainer;
|
||||
|
||||
public DirectRulesetTabItem(RulesetInfo value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
iconContainer = new ConstrainedIconContainer
|
||||
{
|
||||
Icon = value.CreateInstance().CreateIcon(),
|
||||
Size = new Vector2(32),
|
||||
},
|
||||
new HoverClickSounds()
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override void OnActivated() => updateState();
|
||||
|
||||
protected override void OnDeactivated() => updateState();
|
||||
|
||||
private void updateState() => iconContainer.FadeColour(IsHovered || Active.Value ? Color4.White : Color4.Gray, 120, Easing.InQuad);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class DownloadButton : DownloadTrackingComposite
|
||||
{
|
||||
private readonly bool noVideo;
|
||||
private readonly SpriteIcon icon;
|
||||
private readonly SpriteIcon checkmark;
|
||||
private readonly Box background;
|
||||
|
||||
private OsuColour colours;
|
||||
|
||||
private readonly ShakeContainer shakeContainer;
|
||||
|
||||
private readonly OsuAnimatedButton button;
|
||||
|
||||
public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
|
||||
: base(beatmapSet)
|
||||
{
|
||||
this.noVideo = noVideo;
|
||||
|
||||
InternalChild = shakeContainer = new ShakeContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = button = new OsuAnimatedButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Depth = float.MaxValue
|
||||
},
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(13),
|
||||
Icon = FontAwesome.Solid.Download,
|
||||
},
|
||||
checkmark = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
X = 8,
|
||||
Size = Vector2.Zero,
|
||||
Icon = FontAwesome.Solid.Check,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
State.BindValueChanged(state => updateState(state.NewValue), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuColour colours, OsuGame game, BeatmapManager beatmaps)
|
||||
{
|
||||
this.colours = colours;
|
||||
|
||||
button.Action = () =>
|
||||
{
|
||||
switch (State.Value)
|
||||
{
|
||||
case DownloadState.Downloading:
|
||||
case DownloadState.Downloaded:
|
||||
shakeContainer.Shake();
|
||||
break;
|
||||
|
||||
case DownloadState.LocallyAvailable:
|
||||
game.PresentBeatmap(BeatmapSet.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
beatmaps.Download(BeatmapSet.Value, noVideo);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void updateState(DownloadState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case DownloadState.NotDownloaded:
|
||||
background.FadeColour(colours.Gray4, 500, Easing.InOutExpo);
|
||||
icon.MoveToX(0, 500, Easing.InOutExpo);
|
||||
checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo);
|
||||
break;
|
||||
|
||||
case DownloadState.Downloading:
|
||||
background.FadeColour(colours.Blue, 500, Easing.InOutExpo);
|
||||
icon.MoveToX(0, 500, Easing.InOutExpo);
|
||||
checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo);
|
||||
break;
|
||||
|
||||
case DownloadState.Downloaded:
|
||||
background.FadeColour(colours.Yellow, 500, Easing.InOutExpo);
|
||||
break;
|
||||
|
||||
case DownloadState.LocallyAvailable:
|
||||
background.FadeColour(colours.Green, 500, Easing.InOutExpo);
|
||||
icon.MoveToX(-8, 500, Easing.InOutExpo);
|
||||
checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,11 +7,12 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class DownloadProgressBar : DownloadTrackingComposite
|
||||
public class DownloadProgressBar : BeatmapDownloadTrackingComposite
|
||||
{
|
||||
private readonly ProgressBar progressBar;
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public enum DownloadState
|
||||
{
|
||||
NotDownloaded,
|
||||
Downloading,
|
||||
Downloaded,
|
||||
LocallyAvailable
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API.Requests;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
/// <summary>
|
||||
/// A component which tracks a beatmap through potential download/import/deletion.
|
||||
/// </summary>
|
||||
public abstract class DownloadTrackingComposite : CompositeDrawable
|
||||
{
|
||||
public readonly Bindable<BeatmapSetInfo> BeatmapSet = new Bindable<BeatmapSetInfo>();
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
|
||||
/// </summary>
|
||||
protected readonly Bindable<DownloadState> State = new Bindable<DownloadState>();
|
||||
|
||||
protected readonly Bindable<double> Progress = new Bindable<double>();
|
||||
|
||||
protected DownloadTrackingComposite(BeatmapSetInfo beatmapSet = null)
|
||||
{
|
||||
BeatmapSet.Value = beatmapSet;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(BeatmapManager beatmaps)
|
||||
{
|
||||
this.beatmaps = beatmaps;
|
||||
|
||||
BeatmapSet.BindValueChanged(setInfo =>
|
||||
{
|
||||
if (setInfo.NewValue == null)
|
||||
attachDownload(null);
|
||||
else if (beatmaps.GetAllUsableBeatmapSetsEnumerable().Any(s => s.OnlineBeatmapSetID == setInfo.NewValue.OnlineBeatmapSetID))
|
||||
State.Value = DownloadState.LocallyAvailable;
|
||||
else
|
||||
attachDownload(beatmaps.GetExistingDownload(setInfo.NewValue));
|
||||
}, true);
|
||||
|
||||
beatmaps.BeatmapDownloadBegan += download =>
|
||||
{
|
||||
if (download.BeatmapSet.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID)
|
||||
attachDownload(download);
|
||||
};
|
||||
|
||||
beatmaps.ItemAdded += setAdded;
|
||||
beatmaps.ItemRemoved += setRemoved;
|
||||
}
|
||||
|
||||
#region Disposal
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (beatmaps != null)
|
||||
{
|
||||
beatmaps.BeatmapDownloadBegan -= attachDownload;
|
||||
beatmaps.ItemAdded -= setAdded;
|
||||
}
|
||||
|
||||
State.UnbindAll();
|
||||
|
||||
attachDownload(null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private DownloadBeatmapSetRequest attachedRequest;
|
||||
|
||||
private void attachDownload(DownloadBeatmapSetRequest request)
|
||||
{
|
||||
if (attachedRequest != null)
|
||||
{
|
||||
attachedRequest.Failure -= onRequestFailure;
|
||||
attachedRequest.DownloadProgressed -= onRequestProgress;
|
||||
attachedRequest.Success -= onRequestSuccess;
|
||||
}
|
||||
|
||||
attachedRequest = request;
|
||||
|
||||
if (attachedRequest != null)
|
||||
{
|
||||
if (attachedRequest.Progress == 1)
|
||||
{
|
||||
State.Value = DownloadState.Downloaded;
|
||||
Progress.Value = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
State.Value = DownloadState.Downloading;
|
||||
Progress.Value = attachedRequest.Progress;
|
||||
|
||||
attachedRequest.Failure += onRequestFailure;
|
||||
attachedRequest.DownloadProgressed += onRequestProgress;
|
||||
attachedRequest.Success += onRequestSuccess;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
State.Value = DownloadState.NotDownloaded;
|
||||
}
|
||||
}
|
||||
|
||||
private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Downloaded);
|
||||
|
||||
private void onRequestProgress(float progress) => Schedule(() => Progress.Value = progress);
|
||||
|
||||
private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
|
||||
|
||||
private void setAdded(BeatmapSetInfo s, bool existing) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable);
|
||||
|
||||
private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded);
|
||||
|
||||
private void setDownloadStateFromManager(BeatmapSetInfo s, DownloadState state) => Schedule(() =>
|
||||
{
|
||||
if (s.OnlineBeatmapSetID != BeatmapSet.Value?.OnlineBeatmapSetID)
|
||||
return;
|
||||
|
||||
State.Value = state;
|
||||
});
|
||||
}
|
||||
}
|
@ -4,105 +4,30 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.SearchableList;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class FilterControl : SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory>
|
||||
{
|
||||
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
private FillFlowContainer<RulesetToggleButton> modeButtons;
|
||||
private DirectRulesetSelector rulesetSelector;
|
||||
|
||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552");
|
||||
protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked;
|
||||
|
||||
protected override Drawable CreateSupplementaryControls()
|
||||
{
|
||||
modeButtons = new FillFlowContainer<RulesetToggleButton>
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(10f, 0f),
|
||||
};
|
||||
protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector();
|
||||
|
||||
return modeButtons;
|
||||
}
|
||||
public Bindable<RulesetInfo> Ruleset => rulesetSelector.Current;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(RulesetStore rulesets, OsuColour colours, Bindable<RulesetInfo> ruleset)
|
||||
private void load(OsuColour colours, Bindable<RulesetInfo> ruleset)
|
||||
{
|
||||
DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark;
|
||||
|
||||
Ruleset.Value = ruleset.Value ?? rulesets.GetRuleset(0);
|
||||
foreach (var r in rulesets.AvailableRulesets)
|
||||
modeButtons.Add(new RulesetToggleButton(Ruleset, r));
|
||||
}
|
||||
|
||||
private class RulesetToggleButton : OsuClickableContainer
|
||||
{
|
||||
private Drawable icon
|
||||
{
|
||||
get => iconContainer.Icon;
|
||||
set => iconContainer.Icon = value;
|
||||
}
|
||||
|
||||
private RulesetInfo ruleset;
|
||||
|
||||
public RulesetInfo Ruleset
|
||||
{
|
||||
get => ruleset;
|
||||
set
|
||||
{
|
||||
ruleset = value;
|
||||
icon = Ruleset.CreateInstance().CreateIcon();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Bindable<RulesetInfo> bindable;
|
||||
|
||||
private readonly ConstrainedIconContainer iconContainer;
|
||||
|
||||
private void Bindable_ValueChanged(ValueChangedEvent<RulesetInfo> e)
|
||||
{
|
||||
iconContainer.FadeTo(Ruleset.ID == e.NewValue?.ID ? 1f : 0.5f, 100);
|
||||
}
|
||||
|
||||
public override bool HandleNonPositionalInput => !bindable.Disabled && base.HandleNonPositionalInput;
|
||||
public override bool HandlePositionalInput => !bindable.Disabled && base.HandlePositionalInput;
|
||||
|
||||
public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset)
|
||||
{
|
||||
this.bindable = bindable;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
iconContainer = new ConstrainedIconContainer
|
||||
{
|
||||
Origin = Anchor.TopLeft,
|
||||
Anchor = Anchor.TopLeft,
|
||||
Size = new Vector2(32),
|
||||
}
|
||||
};
|
||||
|
||||
Ruleset = ruleset;
|
||||
bindable.ValueChanged += Bindable_ValueChanged;
|
||||
Bindable_ValueChanged(new ValueChangedEvent<RulesetInfo>(bindable.Value, bindable.Value));
|
||||
Action = () => bindable.Value = Ruleset;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (bindable != null)
|
||||
bindable.ValueChanged -= Bindable_ValueChanged;
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
rulesetSelector.Current.BindTo(ruleset);
|
||||
}
|
||||
}
|
||||
|
||||
|
75
osu.Game/Overlays/Direct/PanelDownloadButton.cs
Normal file
75
osu.Game/Overlays/Direct/PanelDownloadButton.cs
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class PanelDownloadButton : BeatmapDownloadTrackingComposite
|
||||
{
|
||||
protected bool DownloadEnabled => button.Enabled.Value;
|
||||
|
||||
private readonly bool noVideo;
|
||||
|
||||
private readonly ShakeContainer shakeContainer;
|
||||
private readonly DownloadButton button;
|
||||
|
||||
public PanelDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
|
||||
: base(beatmapSet)
|
||||
{
|
||||
this.noVideo = noVideo;
|
||||
|
||||
InternalChild = shakeContainer = new ShakeContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = button = new DownloadButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
button.State.BindTo(State);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuGame game, BeatmapManager beatmaps)
|
||||
{
|
||||
if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false)
|
||||
{
|
||||
button.Enabled.Value = false;
|
||||
button.TooltipText = "This beatmap is currently not available for download.";
|
||||
return;
|
||||
}
|
||||
|
||||
button.Action = () =>
|
||||
{
|
||||
switch (State.Value)
|
||||
{
|
||||
case DownloadState.Downloading:
|
||||
case DownloadState.Downloaded:
|
||||
shakeContainer.Shake();
|
||||
break;
|
||||
|
||||
case DownloadState.LocallyAvailable:
|
||||
game.PresentBeatmap(BeatmapSet.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
beatmaps.Download(BeatmapSet.Value, noVideo);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -252,7 +252,7 @@ namespace osu.Game.Overlays
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
if (State == Visibility.Hidden)
|
||||
if (State.Value == Visibility.Hidden)
|
||||
return;
|
||||
|
||||
if (API == null)
|
||||
|
@ -4,6 +4,7 @@
|
||||
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.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -40,6 +41,19 @@ namespace osu.Game.Overlays
|
||||
};
|
||||
}
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
if (State.Value == Visibility.Visible)
|
||||
{
|
||||
// re-trigger the state changed so we can potentially surface to front
|
||||
State.TriggerChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Show();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
@ -2,6 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -17,6 +19,11 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
private Box overlay;
|
||||
|
||||
private readonly BindableDouble audioVolume = new BindableDouble(1);
|
||||
|
||||
[Resolved]
|
||||
private AudioManager audio { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -33,7 +40,19 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
};
|
||||
|
||||
Progress.ValueChanged += p => overlay.Alpha = (float)p.NewValue;
|
||||
Progress.ValueChanged += p =>
|
||||
{
|
||||
audioVolume.Value = 1 - p.NewValue;
|
||||
overlay.Alpha = (float)p.NewValue;
|
||||
};
|
||||
|
||||
audio.Tracks.AddAdjustment(AdjustableProperty.Volume, audioVolume);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, audioVolume);
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,10 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -47,7 +48,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
public bool FilteringActive { get; set; }
|
||||
|
||||
private OsuSpriteText text;
|
||||
private OsuTextFlowContainer pressAKey;
|
||||
private Drawable pressAKey;
|
||||
|
||||
private FillFlowContainer<KeyButton> buttons;
|
||||
|
||||
@ -80,7 +81,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
Hollow = true,
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
@ -99,15 +100,19 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight
|
||||
},
|
||||
pressAKey = new OsuTextFlowContainer
|
||||
pressAKey = new FillFlowContainer
|
||||
{
|
||||
Text = "Press a key to change binding, Shift+Delete to delete, Escape to cancel.",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding(padding),
|
||||
Padding = new MarginPadding { Top = height },
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(padding) { Top = height + padding * 2 },
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Alpha = 0,
|
||||
Colour = colours.YellowDark
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new CancelButton { Action = finalise },
|
||||
new ClearButton { Action = clear },
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -205,21 +210,6 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
if (!HasFocus)
|
||||
return false;
|
||||
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Delete:
|
||||
{
|
||||
if (e.ShiftPressed)
|
||||
{
|
||||
bindTarget.UpdateKeyCombination(InputKey.None);
|
||||
finalise();
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState));
|
||||
if (!isModifier(e.Key)) finalise();
|
||||
|
||||
@ -254,6 +244,12 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
return true;
|
||||
}
|
||||
|
||||
private void clear()
|
||||
{
|
||||
bindTarget.UpdateKeyCombination(InputKey.None);
|
||||
finalise();
|
||||
}
|
||||
|
||||
private void finalise()
|
||||
{
|
||||
if (bindTarget != null)
|
||||
@ -300,6 +296,41 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
if (bindTarget != null) bindTarget.IsBinding = true;
|
||||
}
|
||||
|
||||
private class CancelButton : TriangleButton
|
||||
{
|
||||
public CancelButton()
|
||||
{
|
||||
Text = "Cancel";
|
||||
Size = new Vector2(80, 20);
|
||||
}
|
||||
}
|
||||
|
||||
private class ClearButton : TriangleButton
|
||||
{
|
||||
public ClearButton()
|
||||
{
|
||||
Text = "Clear";
|
||||
Size = new Vector2(80, 20);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
base.OnMouseUp(e);
|
||||
|
||||
// without this, the mouse up triggers a finalise (and deselection) of the current binding target.
|
||||
return true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
BackgroundColour = colours.Pink;
|
||||
|
||||
Triangles.ColourDark = colours.PinkDark;
|
||||
Triangles.ColourLight = colours.PinkLight;
|
||||
}
|
||||
}
|
||||
|
||||
private class KeyButton : Container
|
||||
{
|
||||
public readonly Framework.Input.Bindings.KeyBinding KeyBinding;
|
||||
|
@ -60,7 +60,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Text = "Reset";
|
||||
Text = "Reset all bindings in section";
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Margin = new MarginPadding { Top = 5 };
|
||||
Height = 20;
|
||||
|
@ -145,7 +145,7 @@ namespace osu.Game.Overlays
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, TextureStore textures, AudioManager audio)
|
||||
{
|
||||
getSample = audio.Sample.Get(@"MedalSplash/medal-get");
|
||||
getSample = audio.Samples.Get(@"MedalSplash/medal-get");
|
||||
innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin");
|
||||
|
||||
disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters
|
||||
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
@ -33,6 +34,13 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public IEnumerable<Mod> SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null);
|
||||
|
||||
private CancellationTokenSource modsLoadCts;
|
||||
|
||||
/// <summary>
|
||||
/// True when all mod icons have completed loading.
|
||||
/// </summary>
|
||||
public bool ModIconsLoaded { get; private set; } = true;
|
||||
|
||||
public IEnumerable<Mod> Mods
|
||||
{
|
||||
set
|
||||
@ -48,8 +56,28 @@ namespace osu.Game.Overlays.Mods
|
||||
};
|
||||
}).ToArray();
|
||||
|
||||
ButtonsContainer.Children = modContainers;
|
||||
modsLoadCts?.Cancel();
|
||||
ModIconsLoaded = false;
|
||||
|
||||
LoadComponentsAsync(modContainers, c =>
|
||||
{
|
||||
ModIconsLoaded = true;
|
||||
ButtonsContainer.ChildrenEnumerable = c;
|
||||
}, (modsLoadCts = new CancellationTokenSource()).Token);
|
||||
|
||||
buttons = modContainers.OfType<ModButton>().ToArray();
|
||||
|
||||
if (value.Any())
|
||||
{
|
||||
headerLabel.FadeIn(200);
|
||||
this.FadeIn(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
// transition here looks weird as mods instantly disappear.
|
||||
headerLabel.Hide();
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,40 +1,40 @@
|
||||
// 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 osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Mods.Sections;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public class ModSelectOverlay : WaveOverlayContainer
|
||||
{
|
||||
private const float content_width = 0.8f;
|
||||
|
||||
protected Color4 LowMultiplierColour, HighMultiplierColour;
|
||||
|
||||
protected readonly TriangleButton DeselectAllButton;
|
||||
protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel;
|
||||
private readonly FillFlowContainer footerContainer;
|
||||
protected readonly TriangleButton CloseButton;
|
||||
|
||||
protected readonly OsuSpriteText MultiplierLabel;
|
||||
protected readonly OsuSpriteText UnrankedLabel;
|
||||
|
||||
protected override bool BlockNonPositionalInput => false;
|
||||
|
||||
@ -46,154 +46,14 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
protected readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio, Bindable<IReadOnlyList<Mod>> mods)
|
||||
{
|
||||
LowMultiplierColour = colours.Red;
|
||||
HighMultiplierColour = colours.Green;
|
||||
UnrankedLabel.Colour = colours.Blue;
|
||||
protected Color4 LowMultiplierColour;
|
||||
protected Color4 HighMultiplierColour;
|
||||
|
||||
Ruleset.BindTo(ruleset);
|
||||
if (mods != null) SelectedMods.BindTo(mods);
|
||||
|
||||
sampleOn = audio.Sample.Get(@"UI/check-on");
|
||||
sampleOff = audio.Sample.Get(@"UI/check-off");
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Ruleset.BindValueChanged(rulesetChanged, true);
|
||||
SelectedMods.BindValueChanged(selectedModsChanged, true);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
Ruleset.UnbindAll();
|
||||
SelectedMods.UnbindAll();
|
||||
}
|
||||
|
||||
private void rulesetChanged(ValueChangedEvent<RulesetInfo> e)
|
||||
{
|
||||
if (e.NewValue == null) return;
|
||||
|
||||
var instance = e.NewValue.CreateInstance();
|
||||
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
section.Mods = instance.GetModsFor(section.ModType);
|
||||
|
||||
// attempt to re-select any already selected mods.
|
||||
// this may be the first time we are receiving the ruleset, in which case they will still match.
|
||||
selectedModsChanged(new ValueChangedEvent<IReadOnlyList<Mod>>(SelectedMods.Value, SelectedMods.Value));
|
||||
|
||||
// write the mods back to the SelectedMods bindable in the case a change was not applicable.
|
||||
// this generally isn't required as the previous line will perform deselection; just here for safety.
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
private void selectedModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> e)
|
||||
{
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList());
|
||||
|
||||
updateMods();
|
||||
}
|
||||
|
||||
private void updateMods()
|
||||
{
|
||||
double multiplier = 1.0;
|
||||
bool ranked = true;
|
||||
|
||||
foreach (Mod mod in SelectedMods.Value)
|
||||
{
|
||||
multiplier *= mod.ScoreMultiplier;
|
||||
ranked &= mod.Ranked;
|
||||
}
|
||||
|
||||
MultiplierLabel.Text = $"{multiplier:N2}x";
|
||||
if (multiplier > 1.0)
|
||||
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
|
||||
else if (multiplier < 1.0)
|
||||
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
|
||||
else
|
||||
MultiplierLabel.FadeColour(Color4.White, 200);
|
||||
|
||||
UnrankedLabel.FadeTo(ranked ? 0 : 1, 200);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
{
|
||||
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
{
|
||||
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeselectAll()
|
||||
{
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
section.DeselectAll();
|
||||
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deselect one or more mods.
|
||||
/// </summary>
|
||||
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
|
||||
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
|
||||
public void DeselectTypes(Type[] modTypes, bool immediate = false)
|
||||
{
|
||||
if (modTypes.Length == 0) return;
|
||||
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
section.DeselectTypes(modTypes, immediate);
|
||||
}
|
||||
private const float content_width = 0.8f;
|
||||
private readonly FillFlowContainer footerContainer;
|
||||
|
||||
private SampleChannel sampleOn, sampleOff;
|
||||
|
||||
private void modButtonPressed(Mod selectedMod)
|
||||
{
|
||||
if (selectedMod != null)
|
||||
{
|
||||
if (State == Visibility.Visible) sampleOn?.Play();
|
||||
DeselectTypes(selectedMod.IncompatibleMods, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (State == Visibility.Visible) sampleOff?.Play();
|
||||
}
|
||||
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
|
||||
|
||||
public ModSelectOverlay()
|
||||
{
|
||||
Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2");
|
||||
@ -312,6 +172,8 @@ namespace osu.Game.Overlays.Mods
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0f, 10f),
|
||||
Width = content_width,
|
||||
LayoutDuration = 200,
|
||||
LayoutEasing = Easing.OutQuint,
|
||||
Children = new ModSection[]
|
||||
{
|
||||
new DifficultyReductionSection { Action = modButtonPressed },
|
||||
@ -364,6 +226,16 @@ namespace osu.Game.Overlays.Mods
|
||||
Right = 20
|
||||
}
|
||||
},
|
||||
CloseButton = new TriangleButton
|
||||
{
|
||||
Width = 180,
|
||||
Text = "Close",
|
||||
Action = Hide,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Right = 20
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = @"Score Multiplier:",
|
||||
@ -401,5 +273,171 @@ namespace osu.Game.Overlays.Mods
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio, Bindable<IReadOnlyList<Mod>> mods)
|
||||
{
|
||||
LowMultiplierColour = colours.Red;
|
||||
HighMultiplierColour = colours.Green;
|
||||
UnrankedLabel.Colour = colours.Blue;
|
||||
|
||||
Ruleset.BindTo(ruleset);
|
||||
if (mods != null) SelectedMods.BindTo(mods);
|
||||
|
||||
sampleOn = audio.Samples.Get(@"UI/check-on");
|
||||
sampleOff = audio.Samples.Get(@"UI/check-off");
|
||||
}
|
||||
|
||||
public void DeselectAll()
|
||||
{
|
||||
foreach (var section in ModSectionsContainer.Children)
|
||||
section.DeselectAll();
|
||||
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deselect one or more mods.
|
||||
/// </summary>
|
||||
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
|
||||
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
|
||||
public void DeselectTypes(Type[] modTypes, bool immediate = false)
|
||||
{
|
||||
if (modTypes.Length == 0) return;
|
||||
|
||||
foreach (var section in ModSectionsContainer.Children)
|
||||
section.DeselectTypes(modTypes, immediate);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Ruleset.BindValueChanged(rulesetChanged, true);
|
||||
SelectedMods.BindValueChanged(selectedModsChanged, true);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
|
||||
foreach (var section in ModSectionsContainer.Children)
|
||||
{
|
||||
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
|
||||
foreach (var section in ModSectionsContainer.Children)
|
||||
{
|
||||
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Number1:
|
||||
DeselectAllButton.Click();
|
||||
return true;
|
||||
|
||||
case Key.Number2:
|
||||
CloseButton.Click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
private void rulesetChanged(ValueChangedEvent<RulesetInfo> e)
|
||||
{
|
||||
if (e.NewValue == null) return;
|
||||
|
||||
var instance = e.NewValue.CreateInstance();
|
||||
|
||||
foreach (var section in ModSectionsContainer.Children)
|
||||
section.Mods = instance.GetModsFor(section.ModType);
|
||||
|
||||
// attempt to re-select any already selected mods.
|
||||
// this may be the first time we are receiving the ruleset, in which case they will still match.
|
||||
selectedModsChanged(new ValueChangedEvent<IReadOnlyList<Mod>>(SelectedMods.Value, SelectedMods.Value));
|
||||
|
||||
// write the mods back to the SelectedMods bindable in the case a change was not applicable.
|
||||
// this generally isn't required as the previous line will perform deselection; just here for safety.
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
private void selectedModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> e)
|
||||
{
|
||||
foreach (var section in ModSectionsContainer.Children)
|
||||
section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList());
|
||||
|
||||
updateMods();
|
||||
}
|
||||
|
||||
private void updateMods()
|
||||
{
|
||||
var multiplier = 1.0;
|
||||
var ranked = true;
|
||||
|
||||
foreach (var mod in SelectedMods.Value)
|
||||
{
|
||||
multiplier *= mod.ScoreMultiplier;
|
||||
ranked &= mod.Ranked;
|
||||
}
|
||||
|
||||
MultiplierLabel.Text = $"{multiplier:N2}x";
|
||||
if (multiplier > 1.0)
|
||||
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
|
||||
else if (multiplier < 1.0)
|
||||
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
|
||||
else
|
||||
MultiplierLabel.FadeColour(Color4.White, 200);
|
||||
|
||||
UnrankedLabel.FadeTo(ranked ? 0 : 1, 200);
|
||||
}
|
||||
|
||||
private void modButtonPressed(Mod selectedMod)
|
||||
{
|
||||
if (selectedMod != null)
|
||||
{
|
||||
if (State.Value == Visibility.Visible) sampleOn?.Play();
|
||||
DeselectTypes(selectedMod.IncompatibleMods, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (State.Value == Visibility.Visible) sampleOff?.Play();
|
||||
}
|
||||
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
|
||||
|
||||
#region Disposal
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
Ruleset.UnbindAll();
|
||||
SelectedMods.UnbindAll();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps, IBindable<WorkingBeatmap> beatmap)
|
||||
{
|
||||
beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false));
|
||||
beatmaps.GetAllUsableBeatmapSets().ForEach(addBeatmapSet);
|
||||
beatmaps.ItemAdded += addBeatmapSet;
|
||||
beatmaps.ItemRemoved += removeBeatmapSet;
|
||||
|
||||
@ -83,15 +83,9 @@ namespace osu.Game.Overlays.Music
|
||||
beatmapBacking.ValueChanged += _ => updateSelectedSet();
|
||||
}
|
||||
|
||||
private void addBeatmapSet(BeatmapSetInfo obj, bool existing) => Schedule(() =>
|
||||
private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() =>
|
||||
{
|
||||
if (existing)
|
||||
return;
|
||||
|
||||
var newItem = new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) };
|
||||
|
||||
items.Add(newItem);
|
||||
items.SetLayoutPosition(newItem, items.Count - 1);
|
||||
items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) });
|
||||
});
|
||||
|
||||
private void removeBeatmapSet(BeatmapSetInfo obj) => Schedule(() =>
|
||||
|
@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Music
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ExitRequested = () => State = Visibility.Hidden,
|
||||
ExitRequested = Hide,
|
||||
FilterChanged = search => list.Filter(search),
|
||||
Padding = new MarginPadding(10),
|
||||
},
|
||||
|
@ -55,6 +55,8 @@ namespace osu.Game.Overlays
|
||||
private Container dragContainer;
|
||||
private Container playerContainer;
|
||||
|
||||
public bool IsUserPaused { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private Bindable<WorkingBeatmap> beatmap { get; set; }
|
||||
|
||||
@ -70,9 +72,6 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
Width = 400;
|
||||
Margin = new MarginPadding(10);
|
||||
|
||||
// required to let MusicController handle beatmap cycling.
|
||||
AlwaysPresent = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -160,7 +159,7 @@ namespace osu.Game.Overlays
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(1.4f),
|
||||
IconScale = new Vector2(1.4f),
|
||||
Action = play,
|
||||
Action = togglePause,
|
||||
Icon = FontAwesome.Regular.PlayCircle,
|
||||
},
|
||||
nextButton = new MusicIconButton
|
||||
@ -200,7 +199,7 @@ namespace osu.Game.Overlays
|
||||
beatmaps.ItemAdded += handleBeatmapAdded;
|
||||
beatmaps.ItemRemoved += handleBeatmapRemoved;
|
||||
|
||||
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
||||
playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private ScheduledDelegate seekDelegate;
|
||||
@ -221,15 +220,9 @@ namespace osu.Game.Overlays
|
||||
beatmapSets.Insert(index, beatmapSetInfo);
|
||||
}
|
||||
|
||||
private void handleBeatmapAdded(BeatmapSetInfo obj, bool existing)
|
||||
{
|
||||
if (existing)
|
||||
return;
|
||||
private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set));
|
||||
|
||||
Schedule(() => beatmapSets.Add(obj));
|
||||
}
|
||||
|
||||
private void handleBeatmapRemoved(BeatmapSetInfo obj) => Schedule(() => beatmapSets.RemoveAll(s => s.ID == obj.ID));
|
||||
private void handleBeatmapRemoved(BeatmapSetInfo set) => Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID));
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
@ -262,6 +255,12 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (pendingBeatmapSwitch != null)
|
||||
{
|
||||
pendingBeatmapSwitch();
|
||||
pendingBeatmapSwitch = null;
|
||||
}
|
||||
|
||||
var track = current?.TrackLoaded ?? false ? current.Track : null;
|
||||
|
||||
if (track?.IsDummyDevice == false)
|
||||
@ -279,7 +278,7 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
}
|
||||
|
||||
private void play()
|
||||
private void togglePause()
|
||||
{
|
||||
var track = current?.Track;
|
||||
|
||||
@ -291,9 +290,15 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
if (track.IsRunning)
|
||||
{
|
||||
IsUserPaused = true;
|
||||
track.Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
track.Start();
|
||||
IsUserPaused = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void prev()
|
||||
@ -349,18 +354,11 @@ namespace osu.Game.Overlays
|
||||
|
||||
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
|
||||
}
|
||||
|
||||
current.Track.Completed -= currentTrackCompleted;
|
||||
}
|
||||
|
||||
current = beatmap.NewValue;
|
||||
|
||||
if (current != null)
|
||||
current.Track.Completed += currentTrackCompleted;
|
||||
|
||||
progressBar.CurrentTime = 0;
|
||||
|
||||
updateDisplay(current, direction);
|
||||
updateDisplay(current = beatmap.NewValue, direction);
|
||||
updateAudioAdjustments();
|
||||
|
||||
queuedDirection = null;
|
||||
@ -378,21 +376,12 @@ namespace osu.Game.Overlays
|
||||
mod.ApplyToClock(track);
|
||||
}
|
||||
|
||||
private void currentTrackCompleted() => Schedule(() =>
|
||||
{
|
||||
if (!current.Track.Looping && !beatmap.Disabled && beatmapSets.Any())
|
||||
next();
|
||||
});
|
||||
|
||||
private ScheduledDelegate pendingBeatmapSwitch;
|
||||
private Action pendingBeatmapSwitch;
|
||||
|
||||
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
|
||||
{
|
||||
//we might be off-screen when this update comes in.
|
||||
//rather than Scheduling, manually handle this to avoid possible memory contention.
|
||||
pendingBeatmapSwitch?.Cancel();
|
||||
|
||||
pendingBeatmapSwitch = Schedule(delegate
|
||||
// avoid using scheduler as our scheduler may not be run for a long time, holding references to beatmaps.
|
||||
pendingBeatmapSwitch = delegate
|
||||
{
|
||||
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
|
||||
Task.Run(() =>
|
||||
@ -432,7 +421,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
playerContainer.Add(newBackground);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
@ -447,10 +436,6 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
// This is here mostly as a performance fix.
|
||||
// If the playlist is not hidden it will update children even when the music controller is hidden (due to AlwaysPresent).
|
||||
playlist.State = Visibility.Hidden;
|
||||
|
||||
this.FadeOut(transition_length, Easing.OutQuint);
|
||||
dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
|
||||
}
|
||||
@ -464,12 +449,26 @@ namespace osu.Game.Overlays
|
||||
|
||||
private class MusicIconButton : IconButton
|
||||
{
|
||||
public MusicIconButton()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
HoverColour = colours.YellowDark.Opacity(0.6f);
|
||||
FlashColour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
// works with AutoSizeAxes above to make buttons autosize with the scale animation.
|
||||
Content.AutoSizeAxes = Axes.None;
|
||||
Content.Size = new Vector2(DEFAULT_BUTTON_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
private class Background : BufferedContainer
|
||||
@ -534,5 +533,10 @@ namespace osu.Game.Overlays
|
||||
return base.OnDragEnd(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the next random or playlist track.
|
||||
/// </summary>
|
||||
public void NextTrack() => next();
|
||||
}
|
||||
}
|
||||
|
@ -35,8 +35,6 @@ namespace osu.Game.Overlays
|
||||
Width = width;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
|
||||
AlwaysPresent = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -58,16 +56,12 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Children = new[]
|
||||
{
|
||||
new NotificationSection
|
||||
new NotificationSection(@"Notifications", @"Clear All")
|
||||
{
|
||||
Title = @"Notifications",
|
||||
ClearText = @"Clear All",
|
||||
AcceptTypes = new[] { typeof(SimpleNotification) }
|
||||
},
|
||||
new NotificationSection
|
||||
new NotificationSection(@"Running Tasks", @"Cancel All")
|
||||
{
|
||||
Title = @"Running Tasks",
|
||||
ClearText = @"Cancel All",
|
||||
AcceptTypes = new[] { typeof(ProgressNotification) }
|
||||
}
|
||||
}
|
||||
@ -81,13 +75,13 @@ namespace osu.Game.Overlays
|
||||
|
||||
private void updateProcessingMode()
|
||||
{
|
||||
bool enabled = OverlayActivationMode.Value == OverlayActivation.All || State == Visibility.Visible;
|
||||
bool enabled = OverlayActivationMode.Value == OverlayActivation.All || State.Value == Visibility.Visible;
|
||||
|
||||
notificationsEnabler?.Cancel();
|
||||
|
||||
if (enabled)
|
||||
// we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
|
||||
notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State == Visibility.Visible ? 0 : 1000);
|
||||
notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State.Value == Visibility.Visible ? 0 : 1000);
|
||||
else
|
||||
processingPosts = false;
|
||||
}
|
||||
@ -96,13 +90,10 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
StateChanged += _ => updateProcessingMode();
|
||||
State.ValueChanged += _ => updateProcessingMode();
|
||||
OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true);
|
||||
}
|
||||
|
||||
private int totalCount => sections.Select(c => c.DisplayedCount).Sum();
|
||||
private int unreadCount => sections.Select(c => c.UnreadCount).Sum();
|
||||
|
||||
public readonly BindableInt UnreadCount = new BindableInt();
|
||||
|
||||
private int runningDepth;
|
||||
@ -111,6 +102,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
private readonly Scheduler postScheduler = new Scheduler();
|
||||
|
||||
public override bool IsPresent => base.IsPresent || postScheduler.HasPendingTasks;
|
||||
|
||||
private bool processingPosts = true;
|
||||
|
||||
public void Post(Notification notification) => postScheduler.Add(() =>
|
||||
@ -128,7 +121,7 @@ namespace osu.Game.Overlays
|
||||
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
|
||||
|
||||
if (notification.IsImportant)
|
||||
State = Visibility.Visible;
|
||||
Show();
|
||||
|
||||
updateCounts();
|
||||
});
|
||||
@ -160,7 +153,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
private void updateCounts()
|
||||
{
|
||||
UnreadCount.Value = unreadCount;
|
||||
UnreadCount.Value = sections.Select(c => c.UnreadCount).Sum();
|
||||
}
|
||||
|
||||
private void markAllRead()
|
||||
|
@ -17,10 +17,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
public class NotificationSection : AlwaysUpdateFillFlowContainer<Drawable>
|
||||
{
|
||||
private OsuSpriteText titleText;
|
||||
private OsuSpriteText countText;
|
||||
|
||||
private ClearAllButton clearButton;
|
||||
private OsuSpriteText countDrawable;
|
||||
|
||||
private FlowContainer<Notification> notifications;
|
||||
|
||||
@ -29,34 +26,19 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
public void Add(Notification notification, float position)
|
||||
{
|
||||
notifications.Add(notification);
|
||||
notifications.SetLayoutPosition(notification, position);
|
||||
notifications.Insert((int)position, notification);
|
||||
}
|
||||
|
||||
public IEnumerable<Type> AcceptTypes;
|
||||
|
||||
private string clearText;
|
||||
private readonly string clearButtonText;
|
||||
|
||||
public string ClearText
|
||||
private readonly string titleText;
|
||||
|
||||
public NotificationSection(string title, string clearButtonText)
|
||||
{
|
||||
get => clearText;
|
||||
set
|
||||
{
|
||||
clearText = value;
|
||||
if (clearButton != null) clearButton.Text = clearText;
|
||||
}
|
||||
}
|
||||
|
||||
private string title;
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => title;
|
||||
set
|
||||
{
|
||||
title = value;
|
||||
if (titleText != null) titleText.Text = title.ToUpperInvariant();
|
||||
}
|
||||
this.clearButtonText = clearButtonText;
|
||||
titleText = title;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -82,9 +64,9 @@ namespace osu.Game.Overlays.Notifications
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
clearButton = new ClearAllButton
|
||||
new ClearAllButton
|
||||
{
|
||||
Text = clearText,
|
||||
Text = clearButtonText,
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Action = clearAll
|
||||
@ -99,12 +81,12 @@ namespace osu.Game.Overlays.Notifications
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
titleText = new OsuSpriteText
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = title.ToUpperInvariant(),
|
||||
Text = titleText.ToUpperInvariant(),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Black)
|
||||
},
|
||||
countText = new OsuSpriteText
|
||||
countDrawable = new OsuSpriteText
|
||||
{
|
||||
Text = "3",
|
||||
Colour = colours.Yellow,
|
||||
@ -134,7 +116,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
base.Update();
|
||||
|
||||
countText.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString();
|
||||
countDrawable.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString();
|
||||
}
|
||||
|
||||
private class ClearAllButton : OsuClickableContainer
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -22,10 +23,16 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
public string CompletionText { get; set; } = "Task has completed!";
|
||||
|
||||
private float progress;
|
||||
|
||||
public float Progress
|
||||
{
|
||||
get => progressBar.Progress;
|
||||
set => Schedule(() => progressBar.Progress = value);
|
||||
get => progress;
|
||||
set
|
||||
{
|
||||
progress = value;
|
||||
Scheduler.AddOnce(() => progressBar.Progress = progress);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -33,53 +40,56 @@ namespace osu.Game.Overlays.Notifications
|
||||
base.LoadComplete();
|
||||
|
||||
//we may have received changes before we were displayed.
|
||||
State = state;
|
||||
updateState();
|
||||
}
|
||||
|
||||
public virtual ProgressNotificationState State
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
public CancellationToken CancellationToken => cancellationTokenSource.Token;
|
||||
|
||||
public ProgressNotificationState State
|
||||
{
|
||||
get => state;
|
||||
set =>
|
||||
Schedule(() =>
|
||||
{
|
||||
bool stateChanged = state != value;
|
||||
state = value;
|
||||
set
|
||||
{
|
||||
if (state == value) return;
|
||||
|
||||
if (IsLoaded)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ProgressNotificationState.Queued:
|
||||
Light.Colour = colourQueued;
|
||||
Light.Pulsate = false;
|
||||
progressBar.Active = false;
|
||||
break;
|
||||
state = value;
|
||||
|
||||
case ProgressNotificationState.Active:
|
||||
Light.Colour = colourActive;
|
||||
Light.Pulsate = true;
|
||||
progressBar.Active = true;
|
||||
break;
|
||||
if (IsLoaded)
|
||||
Schedule(updateState);
|
||||
}
|
||||
}
|
||||
|
||||
case ProgressNotificationState.Cancelled:
|
||||
Light.Colour = colourCancelled;
|
||||
Light.Pulsate = false;
|
||||
progressBar.Active = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void updateState()
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ProgressNotificationState.Queued:
|
||||
Light.Colour = colourQueued;
|
||||
Light.Pulsate = false;
|
||||
progressBar.Active = false;
|
||||
break;
|
||||
|
||||
if (stateChanged)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ProgressNotificationState.Completed:
|
||||
NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint);
|
||||
this.FadeOut(200).Finally(d => Completed());
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
case ProgressNotificationState.Active:
|
||||
Light.Colour = colourActive;
|
||||
Light.Pulsate = true;
|
||||
progressBar.Active = true;
|
||||
break;
|
||||
|
||||
case ProgressNotificationState.Cancelled:
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
Light.Colour = colourCancelled;
|
||||
Light.Pulsate = false;
|
||||
progressBar.Active = false;
|
||||
break;
|
||||
|
||||
case ProgressNotificationState.Completed:
|
||||
NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint);
|
||||
this.FadeOut(200).Finally(d => Completed());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private ProgressNotificationState state;
|
||||
|
@ -1,153 +1,23 @@
|
||||
// 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.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class OverlayHeaderTabControl : TabControl<string>
|
||||
public class OverlayHeaderTabControl : OverlayTabControl<string>
|
||||
{
|
||||
private readonly Box bar;
|
||||
|
||||
private Color4 accentColour = Color4.White;
|
||||
|
||||
public Color4 AccentColour
|
||||
protected override TabItem<string> CreateTabItem(string value) => new OverlayHeaderTabItem(value)
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
bar.Colour = value;
|
||||
|
||||
foreach (TabItem<string> tabItem in TabContainer)
|
||||
{
|
||||
((HeaderTabItem)tabItem).AccentColour = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new MarginPadding Padding
|
||||
{
|
||||
get => TabContainer.Padding;
|
||||
set => TabContainer.Padding = value;
|
||||
}
|
||||
|
||||
public OverlayHeaderTabControl()
|
||||
{
|
||||
TabContainer.Masking = false;
|
||||
TabContainer.Spacing = new Vector2(15, 0);
|
||||
|
||||
AddInternal(bar = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 2,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
});
|
||||
}
|
||||
|
||||
protected override Dropdown<string> CreateDropdown() => null;
|
||||
|
||||
protected override TabItem<string> CreateTabItem(string value) => new HeaderTabItem(value)
|
||||
{
|
||||
AccentColour = AccentColour
|
||||
AccentColour = AccentColour,
|
||||
};
|
||||
|
||||
private class HeaderTabItem : TabItem<string>
|
||||
private class OverlayHeaderTabItem : OverlayTabItem<string>
|
||||
{
|
||||
private readonly OsuSpriteText text;
|
||||
private readonly ExpandingBar bar;
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
bar.Colour = value;
|
||||
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
public HeaderTabItem(string value)
|
||||
public OverlayHeaderTabItem(string value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.X;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding { Bottom = 10 },
|
||||
Origin = Anchor.BottomLeft,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Text = value,
|
||||
Font = OsuFont.GetFont()
|
||||
},
|
||||
bar = new ExpandingBar
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
ExpandedSize = 7.5f,
|
||||
CollapsedSize = 0
|
||||
},
|
||||
new HoverClickSounds()
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
|
||||
updateState();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override void OnActivated() => updateState();
|
||||
|
||||
protected override void OnDeactivated() => updateState();
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
if (Active.Value || IsHovered)
|
||||
{
|
||||
text.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||
bar.Expand();
|
||||
|
||||
if (Active.Value)
|
||||
text.Font = text.Font.With(weight: FontWeight.Bold);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||
bar.Collapse();
|
||||
text.Font = text.Font.With(weight: FontWeight.Medium);
|
||||
}
|
||||
Text.Text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
163
osu.Game/Overlays/OverlayTabControl.cs
Normal file
163
osu.Game/Overlays/OverlayTabControl.cs
Normal file
@ -0,0 +1,163 @@
|
||||
// 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.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public abstract class OverlayTabControl<T> : TabControl<T>
|
||||
{
|
||||
private readonly Box bar;
|
||||
|
||||
private Color4 accentColour = Color4.White;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
bar.Colour = value;
|
||||
|
||||
foreach (TabItem<T> tabItem in TabContainer)
|
||||
{
|
||||
((OverlayTabItem<T>)tabItem).AccentColour = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new MarginPadding Padding
|
||||
{
|
||||
get => TabContainer.Padding;
|
||||
set => TabContainer.Padding = value;
|
||||
}
|
||||
|
||||
protected OverlayTabControl()
|
||||
{
|
||||
TabContainer.Masking = false;
|
||||
TabContainer.Spacing = new Vector2(15, 0);
|
||||
|
||||
AddInternal(bar = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 2,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
});
|
||||
}
|
||||
|
||||
protected override Dropdown<T> CreateDropdown() => null;
|
||||
|
||||
protected override TabItem<T> CreateTabItem(T value) => new OverlayTabItem<T>(value);
|
||||
|
||||
protected class OverlayTabItem<U> : TabItem<U>
|
||||
{
|
||||
private readonly ExpandingBar bar;
|
||||
|
||||
protected readonly OsuSpriteText Text;
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
bar.Colour = value;
|
||||
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
public OverlayTabItem(U value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.X;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Text = new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding { Bottom = 10 },
|
||||
Origin = Anchor.BottomLeft,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Font = OsuFont.GetFont(),
|
||||
},
|
||||
bar = new ExpandingBar
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
ExpandedSize = 7.5f,
|
||||
CollapsedSize = 0
|
||||
},
|
||||
new HoverClickSounds()
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
|
||||
if (!Active.Value)
|
||||
HoverAction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
|
||||
if (!Active.Value)
|
||||
UnhoverAction();
|
||||
}
|
||||
|
||||
protected override void OnActivated()
|
||||
{
|
||||
HoverAction();
|
||||
Text.Font = Text.Font.With(weight: FontWeight.Bold);
|
||||
}
|
||||
|
||||
protected override void OnDeactivated()
|
||||
{
|
||||
UnhoverAction();
|
||||
Text.Font = Text.Font.With(weight: FontWeight.Medium);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
if (Active.Value)
|
||||
OnActivated();
|
||||
else
|
||||
OnDeactivated();
|
||||
}
|
||||
|
||||
protected virtual void HoverAction()
|
||||
{
|
||||
bar.Expand();
|
||||
Text.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||
}
|
||||
|
||||
protected virtual void UnhoverAction()
|
||||
{
|
||||
bar.Collapse();
|
||||
Text.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -87,7 +87,12 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
|
||||
addSpacer(topLinkContainer);
|
||||
|
||||
if (user.LastVisit.HasValue)
|
||||
if (user.IsOnline)
|
||||
{
|
||||
topLinkContainer.AddText("Currently online");
|
||||
addSpacer(topLinkContainer);
|
||||
}
|
||||
else if (user.LastVisit.HasValue)
|
||||
{
|
||||
topLinkContainer.AddText("Last seen ");
|
||||
topLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), embolden);
|
||||
|
@ -0,0 +1,66 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
public class ProfileRulesetSelector : RulesetSelector
|
||||
{
|
||||
private Color4 accentColour = Color4.White;
|
||||
|
||||
public ProfileRulesetSelector()
|
||||
{
|
||||
TabContainer.Masking = false;
|
||||
TabContainer.Spacing = new Vector2(10, 0);
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
accentColour = colours.Seafoam;
|
||||
|
||||
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||
((ProfileRulesetTabItem)tabItem).AccentColour = accentColour;
|
||||
}
|
||||
|
||||
public void SetDefaultRuleset(RulesetInfo ruleset)
|
||||
{
|
||||
// Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value
|
||||
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||
((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
|
||||
}
|
||||
|
||||
public void SelectDefaultRuleset()
|
||||
{
|
||||
// Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value
|
||||
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||
{
|
||||
if (((ProfileRulesetTabItem)tabItem).IsDefault)
|
||||
{
|
||||
Current.Value = ((ProfileRulesetTabItem)tabItem).Value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value)
|
||||
{
|
||||
AccentColour = accentColour
|
||||
};
|
||||
|
||||
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
public class ProfileRulesetTabItem : TabItem<RulesetInfo>, IHasAccentColour
|
||||
{
|
||||
private readonly OsuSpriteText text;
|
||||
private readonly SpriteIcon icon;
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
private bool isDefault;
|
||||
|
||||
public bool IsDefault
|
||||
{
|
||||
get => isDefault;
|
||||
set
|
||||
{
|
||||
if (isDefault == value)
|
||||
return;
|
||||
|
||||
isDefault = value;
|
||||
|
||||
icon.FadeTo(isDefault ? 1 : 0, 200, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
public ProfileRulesetTabItem(RulesetInfo value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(3, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Text = value.Name,
|
||||
},
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
Icon = FontAwesome.Solid.Star,
|
||||
Size = new Vector2(12),
|
||||
},
|
||||
}
|
||||
},
|
||||
new HoverClickSounds()
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override void OnActivated() => updateState();
|
||||
|
||||
protected override void OnDeactivated() => updateState();
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium);
|
||||
|
||||
if (IsHovered || Active.Value)
|
||||
{
|
||||
text.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||
icon.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||
icon.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -116,6 +116,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5),
|
||||
Children = new[]
|
||||
{
|
||||
scoreRankInfos[ScoreRank.XH] = new ScoreRankInfo(ScoreRank.XH),
|
||||
|
@ -78,10 +78,8 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
int displayIndex = index;
|
||||
LoadComponentAsync(new DrawableBadge(badges[index]), asyncBadge =>
|
||||
{
|
||||
badgeFlowContainer.Add(asyncBadge);
|
||||
|
||||
// load in stable order regardless of async load order.
|
||||
badgeFlowContainer.SetLayoutPosition(asyncBadge, displayIndex);
|
||||
badgeFlowContainer.Insert(displayIndex, asyncBadge);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Profile.Header.Components;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header
|
||||
@ -27,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
private OsuSpriteText usernameText;
|
||||
private ExternalLinkButton openUserExternally;
|
||||
private OsuSpriteText titleText;
|
||||
private DrawableFlag userFlag;
|
||||
private UpdateableFlag userFlag;
|
||||
private OsuSpriteText userCountryText;
|
||||
private FillFlowContainer userStats;
|
||||
|
||||
@ -51,7 +52,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
AutoSizeAxes = Axes.X,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Children = new[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
avatar = new UpdateableAvatar
|
||||
{
|
||||
@ -59,6 +60,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
Masking = true,
|
||||
CornerRadius = avatar_size * 0.25f,
|
||||
OpenOnClick = { Value = false },
|
||||
ShowGuestOnNull = false,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
@ -70,18 +72,30 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
usernameText = new OsuSpriteText
|
||||
new FillFlowContainer
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 24, weight: FontWeight.Regular)
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
usernameText = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 24, weight: FontWeight.Regular)
|
||||
},
|
||||
openUserExternally = new ExternalLinkButton
|
||||
{
|
||||
Margin = new MarginPadding { Left = 5 },
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
}
|
||||
},
|
||||
openUserExternally = new ExternalLinkButton
|
||||
titleText = new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding { Left = 5 },
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Regular)
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -93,10 +107,6 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
titleText = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Regular)
|
||||
},
|
||||
supporterTag = new SupporterIcon
|
||||
{
|
||||
Height = 20,
|
||||
@ -109,20 +119,22 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Colour = colours.GreySeafoamLighter,
|
||||
},
|
||||
new Container
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
userFlag = new DrawableFlag
|
||||
userFlag = new UpdateableFlag
|
||||
{
|
||||
Size = new Vector2(30, 20)
|
||||
Size = new Vector2(30, 20),
|
||||
ShowPlaceholderOnNull = false,
|
||||
},
|
||||
userCountryText = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 17.5f, weight: FontWeight.Regular),
|
||||
Margin = new MarginPadding { Left = 40 },
|
||||
Margin = new MarginPadding { Left = 10 },
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Colour = colours.GreySeafoamLighter,
|
||||
|
@ -1,25 +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.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile
|
||||
{
|
||||
public abstract class ProfileSection : FillFlowContainer
|
||||
public abstract class ProfileSection : Container
|
||||
{
|
||||
public abstract string Title { get; }
|
||||
|
||||
public abstract string Identifier { get; }
|
||||
|
||||
private readonly FillFlowContainer content;
|
||||
private readonly Box background;
|
||||
private readonly Box underscore;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
@ -27,50 +31,109 @@ namespace osu.Game.Overlays.Profile
|
||||
|
||||
protected ProfileSection()
|
||||
{
|
||||
Direction = FillDirection.Vertical;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
background = new Box
|
||||
{
|
||||
Text = Title,
|
||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular, italics: true),
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
||||
Vertical = 10
|
||||
}
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
new SectionTriangles
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
||||
Bottom = 20
|
||||
}
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 1,
|
||||
Colour = OsuColour.Gray(34),
|
||||
EdgeSmoothness = new Vector2(1)
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
||||
Top = 15,
|
||||
Bottom = 10,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = Title,
|
||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
|
||||
},
|
||||
underscore = new Box
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Margin = new MarginPadding { Top = 4 },
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 2,
|
||||
}
|
||||
}
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
||||
Bottom = 20
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// placeholder
|
||||
Add(new OsuSpriteText
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
background.Colour = colours.GreySeafoamDarker;
|
||||
underscore.Colour = colours.Seafoam;
|
||||
}
|
||||
|
||||
private class SectionTriangles : Container
|
||||
{
|
||||
private readonly Triangles triangles;
|
||||
private readonly Box foreground;
|
||||
|
||||
public SectionTriangles()
|
||||
{
|
||||
Text = @"coming soon!",
|
||||
Colour = Color4.Gray,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Margin = new MarginPadding { Top = 100, Bottom = 100 }
|
||||
});
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 100;
|
||||
Masking = true;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
triangles = new Triangles
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
TriangleScale = 3,
|
||||
},
|
||||
foreground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
triangles.ColourLight = colours.GreySeafoamDark;
|
||||
triangles.ColourDark = colours.GreySeafoamDarker.Darken(0.2f);
|
||||
foreground.Colour = ColourInfo.GradientVertical(colours.GreySeafoamDarker, colours.GreySeafoamDarker.Opacity(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,13 +29,11 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
|
||||
|
||||
protected override void ShowMore()
|
||||
{
|
||||
base.ShowMore();
|
||||
|
||||
request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
|
||||
request.Success += sets => Schedule(() =>
|
||||
{
|
||||
ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
|
||||
ShowMoreLoading.Hide();
|
||||
MoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
|
||||
MoreButton.IsLoading = false;
|
||||
|
||||
if (!sets.Any() && VisiblePages == 1)
|
||||
{
|
||||
|
@ -24,13 +24,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
|
||||
|
||||
protected override void ShowMore()
|
||||
{
|
||||
base.ShowMore();
|
||||
|
||||
request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
|
||||
request.Success += beatmaps => Schedule(() =>
|
||||
{
|
||||
ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0);
|
||||
ShowMoreLoading.Hide();
|
||||
MoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0);
|
||||
MoreButton.IsLoading = false;
|
||||
|
||||
if (!beatmaps.Any() && VisiblePages == 1)
|
||||
{
|
||||
|
@ -7,20 +7,17 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections
|
||||
{
|
||||
public class PaginatedContainer : FillFlowContainer
|
||||
public abstract class PaginatedContainer : FillFlowContainer
|
||||
{
|
||||
protected readonly FillFlowContainer ItemsContainer;
|
||||
protected readonly OsuHoverContainer ShowMoreButton;
|
||||
protected readonly LoadingAnimation ShowMoreLoading;
|
||||
protected readonly ShowMoreButton MoreButton;
|
||||
protected readonly OsuSpriteText MissingText;
|
||||
|
||||
protected int VisiblePages;
|
||||
@ -32,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections
|
||||
protected APIRequest RetrievalRequest;
|
||||
protected RulesetStore Rulesets;
|
||||
|
||||
public PaginatedContainer(Bindable<User> user, string header, string missing)
|
||||
protected PaginatedContainer(Bindable<User> user, string header, string missing)
|
||||
{
|
||||
User.BindTo(user);
|
||||
|
||||
@ -45,38 +42,26 @@ namespace osu.Game.Overlays.Profile.Sections
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = header,
|
||||
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Regular, italics: true),
|
||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
|
||||
Margin = new MarginPadding { Top = 10, Bottom = 10 },
|
||||
},
|
||||
ItemsContainer = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Margin = new MarginPadding { Bottom = 10 }
|
||||
Spacing = new Vector2(0, 2),
|
||||
},
|
||||
ShowMoreButton = new OsuHoverContainer
|
||||
MoreButton = new ShowMoreButton
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Alpha = 0,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Action = ShowMore,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Child = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 14),
|
||||
Text = "show more",
|
||||
Padding = new MarginPadding { Vertical = 10, Horizontal = 15 },
|
||||
}
|
||||
},
|
||||
ShowMoreLoading = new LoadingAnimation
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Size = new Vector2(14),
|
||||
},
|
||||
MissingText = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 14),
|
||||
Font = OsuFont.GetFont(size: 15),
|
||||
Text = missing,
|
||||
Alpha = 0,
|
||||
},
|
||||
@ -97,16 +82,11 @@ namespace osu.Game.Overlays.Profile.Sections
|
||||
{
|
||||
VisiblePages = 0;
|
||||
ItemsContainer.Clear();
|
||||
ShowMoreButton.Hide();
|
||||
|
||||
if (e.NewValue != null)
|
||||
ShowMore();
|
||||
}
|
||||
|
||||
protected virtual void ShowMore()
|
||||
{
|
||||
ShowMoreLoading.Show();
|
||||
ShowMoreButton.Hide();
|
||||
}
|
||||
protected abstract void ShowMore();
|
||||
}
|
||||
}
|
||||
|
@ -49,8 +49,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
Font = OsuFont.GetFont(size: 11, weight: FontWeight.Regular, italics: true)
|
||||
};
|
||||
|
||||
RightFlowContainer.Add(text);
|
||||
RightFlowContainer.SetLayoutPosition(text, 1);
|
||||
RightFlowContainer.Insert(1, text);
|
||||
|
||||
LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap));
|
||||
LeftFlowContainer.Add(new DrawableDate(Score.Date));
|
||||
@ -59,7 +58,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) });
|
||||
}
|
||||
|
||||
protected override Drawable CreateLeftVisual() => new DrawableRank(Score.Rank)
|
||||
protected override Drawable CreateLeftVisual() => new UpdateableRank(Score.Rank)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 60,
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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;
|
||||
using osu.Game.Users;
|
||||
@ -9,6 +8,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
{
|
||||
@ -31,8 +31,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
|
||||
protected override void ShowMore()
|
||||
{
|
||||
base.ShowMore();
|
||||
|
||||
request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
|
||||
request.Success += scores => Schedule(() =>
|
||||
{
|
||||
@ -41,8 +39,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
|
||||
if (!scores.Any() && VisiblePages == 1)
|
||||
{
|
||||
ShowMoreButton.Hide();
|
||||
ShowMoreLoading.Hide();
|
||||
MoreButton.Hide();
|
||||
MoreButton.IsLoading = false;
|
||||
MissingText.Show();
|
||||
return;
|
||||
}
|
||||
@ -63,8 +61,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
LoadComponentsAsync(drawableScores, s =>
|
||||
{
|
||||
MissingText.Hide();
|
||||
ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0);
|
||||
ShowMoreLoading.Hide();
|
||||
MoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0);
|
||||
MoreButton.IsLoading = false;
|
||||
|
||||
ItemsContainer.AddRange(s);
|
||||
});
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
switch (activity.Type)
|
||||
{
|
||||
case RecentActivityType.Rank:
|
||||
return new DrawableRank(activity.ScoreRank)
|
||||
return new UpdateableRank(activity.ScoreRank)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 60,
|
||||
|
@ -22,13 +22,11 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
|
||||
protected override void ShowMore()
|
||||
{
|
||||
base.ShowMore();
|
||||
|
||||
request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
|
||||
request.Success += activities => Schedule(() =>
|
||||
{
|
||||
ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0);
|
||||
ShowMoreLoading.Hide();
|
||||
MoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0);
|
||||
MoreButton.IsLoading = false;
|
||||
|
||||
if (!activities.Any() && VisiblePages == 1)
|
||||
{
|
||||
|
146
osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs
Normal file
146
osu.Game/Overlays/Profile/Sections/ShowMoreButton.cs
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections
|
||||
{
|
||||
public class ShowMoreButton : OsuHoverContainer
|
||||
{
|
||||
private const float fade_duration = 200;
|
||||
|
||||
private readonly Box background;
|
||||
private readonly LoadingAnimation loading;
|
||||
private readonly FillFlowContainer content;
|
||||
|
||||
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
|
||||
|
||||
private bool isLoading;
|
||||
|
||||
public bool IsLoading
|
||||
{
|
||||
get => isLoading;
|
||||
set
|
||||
{
|
||||
if (isLoading == value)
|
||||
return;
|
||||
|
||||
isLoading = value;
|
||||
|
||||
Enabled.Value = !isLoading;
|
||||
|
||||
if (value)
|
||||
{
|
||||
loading.FadeIn(fade_duration, Easing.OutQuint);
|
||||
content.FadeOut(fade_duration, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
loading.FadeOut(fade_duration, Easing.OutQuint);
|
||||
content.FadeIn(fade_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ShowMoreButton()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
Size = new Vector2(140, 30),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(7),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new ChevronIcon(),
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
|
||||
Text = "show more".ToUpper(),
|
||||
},
|
||||
new ChevronIcon(),
|
||||
}
|
||||
},
|
||||
loading = new LoadingAnimation
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(12)
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colors)
|
||||
{
|
||||
IdleColour = colors.GreySeafoamDark;
|
||||
HoverColour = colors.GreySeafoam;
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if (!Enabled.Value)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
return base.OnClick(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// run afterwards as this will disable this button.
|
||||
IsLoading = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class ChevronIcon : SpriteIcon
|
||||
{
|
||||
private const int bottom_margin = 2;
|
||||
private const int icon_size = 8;
|
||||
|
||||
public ChevronIcon()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
Margin = new MarginPadding { Bottom = bottom_margin };
|
||||
Size = new Vector2(icon_size);
|
||||
Icon = FontAwesome.Solid.ChevronDown;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colors)
|
||||
{
|
||||
Colour = colors.Yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
|
||||
private class AudioDeviceSettingsDropdown : SettingsDropdown<string>
|
||||
{
|
||||
protected override OsuDropdown<string> CreateDropdown() => new AudioDeviceDropdownControl { Items = Items };
|
||||
protected override OsuDropdown<string> CreateDropdown() => new AudioDeviceDropdownControl();
|
||||
|
||||
private class AudioDeviceDropdownControl : DropdownControl
|
||||
{
|
||||
|
@ -2,9 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Runtime;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
@ -14,37 +12,17 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
|
||||
{
|
||||
protected override string Header => "Garbage Collector";
|
||||
|
||||
private readonly Bindable<LatencyMode> latencyMode = new Bindable<LatencyMode>();
|
||||
private Bindable<GCLatencyMode> configLatencyMode;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(FrameworkDebugConfigManager config)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsEnumDropdown<LatencyMode>
|
||||
{
|
||||
LabelText = "Active mode",
|
||||
Bindable = latencyMode
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Text = "Force garbage collection",
|
||||
Action = GC.Collect
|
||||
},
|
||||
};
|
||||
|
||||
configLatencyMode = config.GetBindable<GCLatencyMode>(DebugSetting.ActiveGCMode);
|
||||
configLatencyMode.BindValueChanged(mode => latencyMode.Value = (LatencyMode)mode.NewValue, true);
|
||||
latencyMode.BindValueChanged(mode => configLatencyMode.Value = (GCLatencyMode)mode.NewValue);
|
||||
}
|
||||
|
||||
private enum LatencyMode
|
||||
{
|
||||
Batch = GCLatencyMode.Batch,
|
||||
Interactive = GCLatencyMode.Interactive,
|
||||
LowLatency = GCLatencyMode.LowLatency,
|
||||
SustainedLowLatency = GCLatencyMode.SustainedLowLatency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,11 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
|
||||
LabelText = "Bypass caching (slow)",
|
||||
Bindable = config.GetBindable<bool>(DebugSetting.BypassCaching)
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Bypass front-to-back render pass",
|
||||
Bindable = config.GetBindable<bool>(DebugSetting.BypassFrontToBackPass)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowInterface)
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Show health display even when you can't fail",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowHealthDisplayWhenCantFail),
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Always show key overlay",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.KeyOverlay)
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Increase visibility of first object with \"Hidden\" mod",
|
||||
LabelText = "Increase visibility of first object when visual impairment mods are enabled",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility)
|
||||
},
|
||||
};
|
||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
{
|
||||
new LoadingAnimation
|
||||
{
|
||||
State = Visibility.Visible,
|
||||
State = { Value = Visibility.Visible },
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
@ -154,8 +154,9 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
};
|
||||
|
||||
panel.Status.BindTo(api.LocalUser.Value.Status);
|
||||
panel.Activity.BindTo(api.LocalUser.Value.Activity);
|
||||
|
||||
dropdown.Current.ValueChanged += action =>
|
||||
dropdown.Current.BindValueChanged(action =>
|
||||
{
|
||||
switch (action.NewValue)
|
||||
{
|
||||
@ -178,9 +179,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
api.Logout();
|
||||
break;
|
||||
}
|
||||
};
|
||||
dropdown.Current.TriggerChange();
|
||||
|
||||
}, true);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -320,7 +319,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
BackgroundColour = colours.Gray3;
|
||||
}
|
||||
|
||||
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableUserDropdownMenuItem(item);
|
||||
protected override DrawableDropdownMenuItem CreateDrawableDropdownMenuItem(MenuItem item) => new DrawableUserDropdownMenuItem(item);
|
||||
|
||||
private class DrawableUserDropdownMenuItem : DrawableOsuDropdownMenuItem
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
|
||||
private Bindable<ScalingMode> scalingMode;
|
||||
private Bindable<Size> sizeFullscreen;
|
||||
private readonly BindableList<WindowMode> windowModes = new BindableList<WindowMode>();
|
||||
private readonly IBindableList<WindowMode> windowModes = new BindableList<WindowMode>();
|
||||
|
||||
private OsuGameBase game;
|
||||
private SettingsDropdown<Size> resolutionDropdown;
|
||||
@ -237,7 +237,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
|
||||
private class ResolutionSettingsDropdown : SettingsDropdown<Size>
|
||||
{
|
||||
protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl { Items = Items };
|
||||
protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl();
|
||||
|
||||
private class ResolutionDropdownControl : DropdownControl
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
@ -16,14 +17,16 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
protected override string Header => "General";
|
||||
|
||||
private TriangleButton importBeatmapsButton;
|
||||
private TriangleButton importScoresButton;
|
||||
private TriangleButton importSkinsButton;
|
||||
private TriangleButton deleteSkinsButton;
|
||||
private TriangleButton deleteBeatmapsButton;
|
||||
private TriangleButton deleteScoresButton;
|
||||
private TriangleButton deleteSkinsButton;
|
||||
private TriangleButton restoreButton;
|
||||
private TriangleButton undeleteButton;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay)
|
||||
private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, DialogOverlay dialogOverlay)
|
||||
{
|
||||
if (beatmaps.SupportsImportFromStable)
|
||||
{
|
||||
@ -51,6 +54,32 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
}
|
||||
});
|
||||
|
||||
if (scores.SupportsImportFromStable)
|
||||
{
|
||||
Add(importScoresButton = new SettingsButton
|
||||
{
|
||||
Text = "Import scores from stable",
|
||||
Action = () =>
|
||||
{
|
||||
importScoresButton.Enabled.Value = false;
|
||||
scores.ImportFromStableAsync().ContinueWith(t => Schedule(() => importScoresButton.Enabled.Value = true));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Add(deleteScoresButton = new DangerousSettingsButton
|
||||
{
|
||||
Text = "Delete ALL scores",
|
||||
Action = () =>
|
||||
{
|
||||
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
|
||||
{
|
||||
deleteScoresButton.Enabled.Value = false;
|
||||
Task.Run(() => scores.Delete(scores.GetAllUsableScores())).ContinueWith(t => Schedule(() => deleteScoresButton.Enabled.Value = true));
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
if (skins.SupportsImportFromStable)
|
||||
{
|
||||
Add(importSkinsButton = new SettingsButton
|
||||
|
@ -82,13 +82,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
|
||||
private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray());
|
||||
|
||||
private void itemAdded(SkinInfo s, bool existing)
|
||||
{
|
||||
if (existing)
|
||||
return;
|
||||
|
||||
Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(s).ToArray());
|
||||
}
|
||||
private void itemAdded(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(s).ToArray());
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
@ -108,7 +102,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
|
||||
private class SkinSettingsDropdown : SettingsDropdown<SkinInfo>
|
||||
{
|
||||
protected override OsuDropdown<SkinInfo> CreateDropdown() => new SkinDropdownControl { Items = Items };
|
||||
protected override OsuDropdown<SkinInfo> CreateDropdown() => new SkinDropdownControl();
|
||||
|
||||
private class SkinDropdownControl : DropdownControl
|
||||
{
|
||||
|
@ -10,12 +10,14 @@ namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
private OsuCheckbox checkbox;
|
||||
|
||||
private string labelText;
|
||||
|
||||
protected override Drawable CreateControl() => checkbox = new OsuCheckbox();
|
||||
|
||||
public override string LabelText
|
||||
{
|
||||
get => checkbox.LabelText;
|
||||
set => checkbox.LabelText = value;
|
||||
get => labelText;
|
||||
set => checkbox.LabelText = labelText = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,39 +13,23 @@ namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
protected new OsuDropdown<T> Control => (OsuDropdown<T>)base.Control;
|
||||
|
||||
private IEnumerable<T> items = Enumerable.Empty<T>();
|
||||
|
||||
public IEnumerable<T> Items
|
||||
{
|
||||
get => items;
|
||||
set
|
||||
{
|
||||
items = value;
|
||||
|
||||
if (Control != null)
|
||||
Control.Items = value;
|
||||
}
|
||||
get => Control.Items;
|
||||
set => Control.Items = value;
|
||||
}
|
||||
|
||||
private IBindableList<T> itemSource;
|
||||
|
||||
public IBindableList<T> ItemSource
|
||||
{
|
||||
get => itemSource;
|
||||
set
|
||||
{
|
||||
itemSource = value;
|
||||
|
||||
if (Control != null)
|
||||
Control.ItemSource = value;
|
||||
}
|
||||
get => Control.ItemSource;
|
||||
set => Control.ItemSource = value;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> FilterTerms => base.FilterTerms.Concat(Control.Items.Select(i => i.ToString()));
|
||||
|
||||
protected sealed override Drawable CreateControl() => CreateDropdown();
|
||||
|
||||
protected virtual OsuDropdown<T> CreateDropdown() => new DropdownControl { Items = Items, ItemSource = ItemSource };
|
||||
protected virtual OsuDropdown<T> CreateDropdown() => new DropdownControl();
|
||||
|
||||
protected class DropdownControl : OsuDropdown<T>
|
||||
{
|
||||
|
@ -3,15 +3,16 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Development;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using DebugUtils = osu.Game.Utils.DebugUtils;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
@ -58,15 +59,49 @@ namespace osu.Game.Overlays.Settings
|
||||
Text = game.Name,
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold),
|
||||
},
|
||||
new OsuSpriteText
|
||||
new BuildDisplay(game.Version, DebugUtils.IsDebugBuild)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Font = OsuFont.GetFont(size: 14),
|
||||
Text = game.Version,
|
||||
Colour = DebugUtils.IsDebug ? colours.Red : Color4.White,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class BuildDisplay : OsuAnimatedButton
|
||||
{
|
||||
private readonly string version;
|
||||
private readonly bool isDebug;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
public BuildDisplay(string version, bool isDebug)
|
||||
{
|
||||
this.version = version;
|
||||
this.isDebug = isDebug;
|
||||
|
||||
Content.RelativeSizeAxes = Axes.Y;
|
||||
Content.AutoSizeAxes = AutoSizeAxes = Axes.X;
|
||||
Height = 20;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(ChangelogOverlay changelog)
|
||||
{
|
||||
if (!isDebug)
|
||||
Action = () => changelog?.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
|
||||
|
||||
Add(new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 16),
|
||||
|
||||
Text = version,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Padding = new MarginPadding(5),
|
||||
Colour = isDebug ? colours.Red : Color4.White,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,7 @@ namespace osu.Game.Overlays.Settings
|
||||
if (text == null)
|
||||
{
|
||||
// construct lazily for cases where the label is not needed (may be provided by the Control).
|
||||
Add(text = new OsuSpriteText());
|
||||
FlowContent.SetLayoutPosition(text, -1);
|
||||
FlowContent.Insert(-1, text = new OsuSpriteText());
|
||||
}
|
||||
|
||||
text.Text = value;
|
||||
@ -63,6 +62,9 @@ namespace osu.Game.Overlays.Settings
|
||||
|
||||
set
|
||||
{
|
||||
if (bindable != null)
|
||||
controlWithCurrent?.Current.UnbindFrom(bindable);
|
||||
|
||||
bindable = value;
|
||||
controlWithCurrent?.Current.BindTo(bindable);
|
||||
|
||||
|
17
osu.Game/Overlays/Settings/SettingsNumberBox.cs
Normal file
17
osu.Game/Overlays/Settings/SettingsNumberBox.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// 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.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
public class SettingsNumberBox : SettingsItem<string>
|
||||
{
|
||||
protected override Drawable CreateControl() => new OsuNumberBox
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
RelativeSizeAxes = Axes.X,
|
||||
};
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays.Toolbar;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
@ -76,7 +77,7 @@ namespace osu.Game.Overlays.Settings
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
private class SidebarScrollContainer : ScrollContainer
|
||||
private class SidebarScrollContainer : OsuScrollContainer
|
||||
{
|
||||
public SidebarScrollContainer()
|
||||
{
|
||||
|
@ -9,6 +9,7 @@ using osu.Game.Overlays.Settings.Sections;
|
||||
using osuTK.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -37,23 +38,23 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
}
|
||||
|
||||
public override bool AcceptsFocus => subPanels.All(s => s.State != Visibility.Visible);
|
||||
public override bool AcceptsFocus => subPanels.All(s => s.State.Value != Visibility.Visible);
|
||||
|
||||
private T createSubPanel<T>(T subPanel)
|
||||
where T : SettingsSubPanel
|
||||
{
|
||||
subPanel.Depth = 1;
|
||||
subPanel.Anchor = Anchor.TopRight;
|
||||
subPanel.StateChanged += subPanelStateChanged;
|
||||
subPanel.State.ValueChanged += subPanelStateChanged;
|
||||
|
||||
subPanels.Add(subPanel);
|
||||
|
||||
return subPanel;
|
||||
}
|
||||
|
||||
private void subPanelStateChanged(Visibility visibility)
|
||||
private void subPanelStateChanged(ValueChangedEvent<Visibility> state)
|
||||
{
|
||||
switch (visibility)
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case Visibility.Visible:
|
||||
Background.FadeTo(0.9f, 300, Easing.OutQuint);
|
||||
@ -73,7 +74,7 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
}
|
||||
|
||||
protected override float ExpandedPosition => subPanels.Any(s => s.State == Visibility.Visible) ? -WIDTH : base.ExpandedPosition;
|
||||
protected override float ExpandedPosition => subPanels.Any(s => s.State.Value == Visibility.Visible) ? -WIDTH : base.ExpandedPosition;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -34,6 +34,8 @@ namespace osu.Game.Overlays
|
||||
});
|
||||
}
|
||||
|
||||
protected override bool DimMainContent => false; // dimming is handled by main overlay
|
||||
|
||||
private class BackButton : OsuClickableContainer, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private AspectContainer aspect;
|
||||
|
@ -66,24 +66,64 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
};
|
||||
|
||||
Header.Tabs.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch);
|
||||
Header.Tabs.Current.ValueChanged += _ => queueUpdate();
|
||||
|
||||
Filter.Tabs.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch);
|
||||
Filter.Tabs.Current.ValueChanged += _ => queueUpdate();
|
||||
|
||||
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue);
|
||||
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch);
|
||||
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => queueUpdate();
|
||||
|
||||
currentQuery.BindTo(Filter.Search.Current);
|
||||
currentQuery.ValueChanged += query =>
|
||||
{
|
||||
queryChangedDebounce?.Cancel();
|
||||
|
||||
if (string.IsNullOrEmpty(query.NewValue))
|
||||
Scheduler.AddOnce(updateSearch);
|
||||
queueUpdate();
|
||||
else
|
||||
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500);
|
||||
};
|
||||
}
|
||||
|
||||
currentQuery.BindTo(Filter.Search.Current);
|
||||
private APIRequest getUsersRequest;
|
||||
|
||||
private readonly Bindable<string> currentQuery = new Bindable<string>();
|
||||
|
||||
private ScheduledDelegate queryChangedDebounce;
|
||||
|
||||
private void queueUpdate() => Scheduler.AddOnce(updateSearch);
|
||||
|
||||
private void updateSearch()
|
||||
{
|
||||
queryChangedDebounce?.Cancel();
|
||||
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
Users = null;
|
||||
clearPanels();
|
||||
loading.Hide();
|
||||
getUsersRequest?.Cancel();
|
||||
|
||||
if (API?.IsLoggedIn != true)
|
||||
return;
|
||||
|
||||
switch (Header.Tabs.Current.Value)
|
||||
{
|
||||
case SocialTab.Friends:
|
||||
var friendRequest = new GetFriendsRequest(); // TODO filter arguments?
|
||||
friendRequest.Success += updateUsers;
|
||||
API.Queue(getUsersRequest = friendRequest);
|
||||
break;
|
||||
|
||||
default:
|
||||
var userRequest = new GetUsersRequest(); // TODO filter arguments!
|
||||
userRequest.Success += response => updateUsers(response.Select(r => r.User));
|
||||
API.Queue(getUsersRequest = userRequest);
|
||||
break;
|
||||
}
|
||||
|
||||
loading.Show();
|
||||
}
|
||||
|
||||
private void recreatePanels(PanelDisplayStyle displayStyle)
|
||||
@ -119,6 +159,7 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
panel.Status.BindTo(u.Status);
|
||||
panel.Activity.BindTo(u.Activity);
|
||||
return panel;
|
||||
})
|
||||
};
|
||||
@ -132,45 +173,6 @@ namespace osu.Game.Overlays
|
||||
});
|
||||
}
|
||||
|
||||
private APIRequest getUsersRequest;
|
||||
|
||||
private readonly Bindable<string> currentQuery = new Bindable<string>();
|
||||
|
||||
private ScheduledDelegate queryChangedDebounce;
|
||||
|
||||
private void updateSearch()
|
||||
{
|
||||
queryChangedDebounce?.Cancel();
|
||||
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
Users = null;
|
||||
clearPanels();
|
||||
loading.Hide();
|
||||
getUsersRequest?.Cancel();
|
||||
|
||||
if (API?.IsLoggedIn != true)
|
||||
return;
|
||||
|
||||
switch (Header.Tabs.Current.Value)
|
||||
{
|
||||
case SocialTab.Friends:
|
||||
var friendRequest = new GetFriendsRequest(); // TODO filter arguments?
|
||||
friendRequest.Success += updateUsers;
|
||||
API.Queue(getUsersRequest = friendRequest);
|
||||
break;
|
||||
|
||||
default:
|
||||
var userRequest = new GetUsersRequest(); // TODO filter arguments!
|
||||
userRequest.Success += response => updateUsers(response.Select(r => r.User));
|
||||
API.Queue(getUsersRequest = userRequest);
|
||||
break;
|
||||
}
|
||||
|
||||
loading.Show();
|
||||
}
|
||||
|
||||
private void updateUsers(IEnumerable<User> newUsers)
|
||||
{
|
||||
Users = newUsers;
|
||||
@ -192,7 +194,7 @@ namespace osu.Game.Overlays
|
||||
switch (state)
|
||||
{
|
||||
case APIState.Online:
|
||||
Scheduler.AddOnce(updateSearch);
|
||||
queueUpdate();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
@ -23,6 +24,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
public Action OnHome;
|
||||
|
||||
private ToolbarUserButton userButton;
|
||||
private ToolbarRulesetSelector rulesetSelector;
|
||||
|
||||
protected override bool BlockPositionalInput => false;
|
||||
|
||||
@ -40,7 +42,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuGame osuGame)
|
||||
private void load(OsuGame osuGame, Bindable<RulesetInfo> parentRuleset)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -57,7 +59,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
Action = () => OnHome?.Invoke()
|
||||
},
|
||||
new ToolbarRulesetSelector()
|
||||
rulesetSelector = new ToolbarRulesetSelector()
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
@ -84,10 +86,13 @@ namespace osu.Game.Overlays.Toolbar
|
||||
}
|
||||
};
|
||||
|
||||
StateChanged += visibility =>
|
||||
// Bound after the selector is added to the hierarchy to give it a chance to load the available rulesets
|
||||
rulesetSelector.Current.BindTo(parentRuleset);
|
||||
|
||||
State.ValueChanged += visibility =>
|
||||
{
|
||||
if (overlayActivationMode.Value == OverlayActivation.Disabled)
|
||||
State = Visibility.Hidden;
|
||||
Hide();
|
||||
};
|
||||
|
||||
if (osuGame != null)
|
||||
|
@ -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 osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -15,6 +16,8 @@ namespace osu.Game.Overlays.Toolbar
|
||||
|
||||
private OverlayContainer stateContainer;
|
||||
|
||||
private readonly Bindable<Visibility> overlayState = new Bindable<Visibility>();
|
||||
|
||||
public OverlayContainer StateContainer
|
||||
{
|
||||
get => stateContainer;
|
||||
@ -22,10 +25,12 @@ namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
stateContainer = value;
|
||||
|
||||
overlayState.UnbindBindings();
|
||||
|
||||
if (stateContainer != null)
|
||||
{
|
||||
Action = stateContainer.ToggleVisibility;
|
||||
stateContainer.StateChanged += stateChanged;
|
||||
overlayState.BindTo(stateContainer.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,18 +45,13 @@ namespace osu.Game.Overlays.Toolbar
|
||||
Depth = 2,
|
||||
Alpha = 0,
|
||||
});
|
||||
|
||||
overlayState.ValueChanged += stateChanged;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
private void stateChanged(ValueChangedEvent<Visibility> state)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
if (stateContainer != null)
|
||||
stateContainer.StateChanged -= stateChanged;
|
||||
}
|
||||
|
||||
private void stateChanged(Visibility state)
|
||||
{
|
||||
switch (state)
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case Visibility.Hidden:
|
||||
stateBackground.FadeOut(200);
|
||||
|
@ -1,58 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
public class ToolbarRulesetButton : ToolbarButton
|
||||
{
|
||||
private RulesetInfo ruleset;
|
||||
|
||||
public RulesetInfo Ruleset
|
||||
{
|
||||
get => ruleset;
|
||||
set
|
||||
{
|
||||
ruleset = value;
|
||||
|
||||
var rInstance = ruleset.CreateInstance();
|
||||
|
||||
TooltipMain = rInstance.Description;
|
||||
TooltipSub = $"Play some {rInstance.Description}";
|
||||
SetIcon(rInstance.CreateIcon());
|
||||
}
|
||||
}
|
||||
|
||||
public bool Active
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
IconContainer.Colour = Color4.White;
|
||||
IconContainer.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = new Color4(255, 194, 224, 100),
|
||||
Radius = 15,
|
||||
Roundness = 15,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
IconContainer.Colour = new Color4(255, 194, 224, 255);
|
||||
IconContainer.EdgeEffect = new EdgeEffectParameters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
IconContainer.Scale *= 1.4f;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +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 System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osuTK.Input;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
public class ToolbarRulesetSelector : Container
|
||||
public class ToolbarRulesetSelector : RulesetSelector
|
||||
{
|
||||
private const float padding = 10;
|
||||
|
||||
private readonly FillFlowContainer modeButtons;
|
||||
private readonly Drawable modeButtonLine;
|
||||
private ToolbarRulesetButton activeButton;
|
||||
|
||||
private RulesetStore rulesets;
|
||||
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
protected Drawable ModeButtonLine { get; private set; }
|
||||
|
||||
public ToolbarRulesetSelector()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
AutoSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
Children = new[]
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddRangeInternal(new[]
|
||||
{
|
||||
new OpaqueBackground(),
|
||||
modeButtons = new FillFlowContainer
|
||||
new OpaqueBackground
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Padding = new MarginPadding { Left = padding, Right = padding },
|
||||
Depth = 1,
|
||||
},
|
||||
modeButtonLine = new Container
|
||||
ModeButtonLine = new Container
|
||||
{
|
||||
Size = new Vector2(padding * 2 + ToolbarButton.WIDTH, 3),
|
||||
Anchor = Anchor.BottomLeft,
|
||||
@ -57,35 +50,54 @@ namespace osu.Game.Overlays.Toolbar
|
||||
Radius = 15,
|
||||
Roundness = 15,
|
||||
},
|
||||
Children = new[]
|
||||
Child = new Box
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets, Bindable<RulesetInfo> parentRuleset)
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
base.LoadComplete();
|
||||
|
||||
foreach (var r in rulesets.AvailableRulesets)
|
||||
Current.BindDisabledChanged(disabled => this.FadeColour(disabled ? Color4.Gray : Color4.White, 300), true);
|
||||
Current.BindValueChanged(_ => moveLineToCurrent(), true);
|
||||
}
|
||||
|
||||
private bool hasInitialPosition;
|
||||
|
||||
// Scheduled to allow the flow layout to be computed before the line position is updated
|
||||
private void moveLineToCurrent() => ScheduleAfterChildren(() =>
|
||||
{
|
||||
foreach (var tabItem in TabContainer)
|
||||
{
|
||||
modeButtons.Add(new ToolbarRulesetButton
|
||||
if (tabItem.Value == Current.Value)
|
||||
{
|
||||
Ruleset = r,
|
||||
Action = delegate { ruleset.Value = r; }
|
||||
});
|
||||
ModeButtonLine.MoveToX(tabItem.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ruleset.ValueChanged += rulesetChanged;
|
||||
ruleset.DisabledChanged += disabledChanged;
|
||||
ruleset.BindTo(parentRuleset);
|
||||
}
|
||||
hasInitialPosition = true;
|
||||
});
|
||||
|
||||
public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput;
|
||||
|
||||
public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput;
|
||||
|
||||
public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree;
|
||||
|
||||
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ToolbarRulesetTabButton(value);
|
||||
|
||||
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = padding, Right = padding },
|
||||
};
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
@ -95,52 +107,13 @@ namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
int requested = e.Key - Key.Number1;
|
||||
|
||||
RulesetInfo found = rulesets.AvailableRulesets.Skip(requested).FirstOrDefault();
|
||||
RulesetInfo found = Rulesets.AvailableRulesets.Skip(requested).FirstOrDefault();
|
||||
if (found != null)
|
||||
ruleset.Value = found;
|
||||
Current.Value = found;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
|
||||
public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
|
||||
|
||||
public override bool PropagatePositionalInputSubTree => !ruleset.Disabled && base.PropagatePositionalInputSubTree;
|
||||
|
||||
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
Size = new Vector2(modeButtons.DrawSize.X, 1);
|
||||
}
|
||||
|
||||
private void rulesetChanged(ValueChangedEvent<RulesetInfo> e)
|
||||
{
|
||||
foreach (ToolbarRulesetButton m in modeButtons.Children.Cast<ToolbarRulesetButton>())
|
||||
{
|
||||
bool isActive = m.Ruleset.ID == e.NewValue.ID;
|
||||
m.Active = isActive;
|
||||
if (isActive)
|
||||
activeButton = m;
|
||||
}
|
||||
|
||||
activeMode.Invalidate();
|
||||
}
|
||||
|
||||
private Cached activeMode = new Cached();
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
if (!activeMode.IsValid)
|
||||
{
|
||||
modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, Easing.OutQuint);
|
||||
activeMode.Validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
76
osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
Normal file
76
osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
Normal file
@ -0,0 +1,76 @@
|
||||
// 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.Effects;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
public class ToolbarRulesetTabButton : TabItem<RulesetInfo>
|
||||
{
|
||||
private readonly RulesetButton ruleset;
|
||||
|
||||
public ToolbarRulesetTabButton(RulesetInfo value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.X;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Child = ruleset = new RulesetButton
|
||||
{
|
||||
Active = false,
|
||||
};
|
||||
|
||||
var rInstance = value.CreateInstance();
|
||||
|
||||
ruleset.TooltipMain = rInstance.Description;
|
||||
ruleset.TooltipSub = $"Play some {rInstance.Description}";
|
||||
ruleset.SetIcon(rInstance.CreateIcon());
|
||||
}
|
||||
|
||||
protected override void OnActivated() => ruleset.Active = true;
|
||||
|
||||
protected override void OnDeactivated() => ruleset.Active = false;
|
||||
|
||||
private class RulesetButton : ToolbarButton
|
||||
{
|
||||
public bool Active
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
IconContainer.Colour = Color4.White;
|
||||
IconContainer.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = new Color4(255, 194, 224, 100),
|
||||
Radius = 15,
|
||||
Roundness = 15,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
IconContainer.Colour = new Color4(255, 194, 224, 255);
|
||||
IconContainer.EdgeEffect = new EdgeEffectParameters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
Parent.Click();
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
IconContainer.Scale *= 1.4f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
|
@ -4,11 +4,11 @@
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Profile;
|
||||
using osu.Game.Overlays.Profile.Sections;
|
||||
@ -23,7 +23,7 @@ namespace osu.Game.Overlays
|
||||
private ProfileSection[] sections;
|
||||
private GetUserRequest userReq;
|
||||
protected ProfileHeader Header;
|
||||
private SectionsContainer<ProfileSection> sectionsContainer;
|
||||
private ProfileSectionsContainer sectionsContainer;
|
||||
private ProfileTabControl tabs;
|
||||
|
||||
public const float CONTENT_X_MARGIN = 70;
|
||||
@ -32,7 +32,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
public void ShowUser(User user, bool fetchOnline = true)
|
||||
{
|
||||
if (user == User.SYSTEM_USER) return;
|
||||
if (user == User.SYSTEM_USER)
|
||||
return;
|
||||
|
||||
Show();
|
||||
|
||||
@ -65,19 +66,18 @@ namespace osu.Game.Overlays
|
||||
Add(new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.2f)
|
||||
Colour = OsuColour.Gray(0.1f)
|
||||
});
|
||||
|
||||
Add(sectionsContainer = new SectionsContainer<ProfileSection>
|
||||
Add(sectionsContainer = new ProfileSectionsContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ExpandableHeader = Header = new ProfileHeader(),
|
||||
FixedHeader = tabs,
|
||||
HeaderBackground = new Box
|
||||
{
|
||||
Colour = OsuColour.Gray(34),
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
},
|
||||
});
|
||||
sectionsContainer.SelectedSection.ValueChanged += section =>
|
||||
{
|
||||
@ -141,31 +141,28 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
}
|
||||
|
||||
private class ProfileTabControl : PageTabControl<ProfileSection>
|
||||
private class ProfileTabControl : OverlayTabControl<ProfileSection>
|
||||
{
|
||||
private readonly Box bottom;
|
||||
|
||||
public ProfileTabControl()
|
||||
{
|
||||
TabContainer.RelativeSizeAxes &= ~Axes.X;
|
||||
TabContainer.AutoSizeAxes |= Axes.X;
|
||||
TabContainer.Anchor |= Anchor.x1;
|
||||
TabContainer.Origin |= Anchor.x1;
|
||||
AddInternal(bottom = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 1,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
EdgeSmoothness = new Vector2(1)
|
||||
});
|
||||
}
|
||||
|
||||
protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileTabItem(value);
|
||||
protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileTabItem(value)
|
||||
{
|
||||
AccentColour = AccentColour
|
||||
};
|
||||
|
||||
protected override Dropdown<ProfileSection> CreateDropdown() => null;
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
AccentColour = colours.Seafoam;
|
||||
}
|
||||
|
||||
private class ProfileTabItem : PageTabItem
|
||||
private class ProfileTabItem : OverlayTabItem<ProfileSection>
|
||||
{
|
||||
public ProfileTabItem(ProfileSection value)
|
||||
: base(value)
|
||||
@ -173,12 +170,22 @@ namespace osu.Game.Overlays
|
||||
Text.Text = value.Title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private class ProfileSectionsContainer : SectionsContainer<ProfileSection>
|
||||
{
|
||||
public ProfileSectionsContainer()
|
||||
{
|
||||
bottom.Colour = colours.Yellow;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override FlowContainer<ProfileSection> CreateScrollContentContainer() => new FillFlowContainer<ProfileSection>
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Spacing = new Vector2(0, 20),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,15 +100,23 @@ namespace osu.Game.Overlays
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.DecreaseVolume:
|
||||
if (State == Visibility.Hidden)
|
||||
if (State.Value == Visibility.Hidden)
|
||||
Show();
|
||||
else if (volumeMeterMusic.IsHovered)
|
||||
volumeMeterMusic.Decrease(amount, isPrecise);
|
||||
else if (volumeMeterEffect.IsHovered)
|
||||
volumeMeterEffect.Decrease(amount, isPrecise);
|
||||
else
|
||||
volumeMeterMaster.Decrease(amount, isPrecise);
|
||||
return true;
|
||||
|
||||
case GlobalAction.IncreaseVolume:
|
||||
if (State == Visibility.Hidden)
|
||||
if (State.Value == Visibility.Hidden)
|
||||
Show();
|
||||
else if (volumeMeterMusic.IsHovered)
|
||||
volumeMeterMusic.Increase(amount, isPrecise);
|
||||
else if (volumeMeterEffect.IsHovered)
|
||||
volumeMeterEffect.Increase(amount, isPrecise);
|
||||
else
|
||||
volumeMeterMaster.Increase(amount, isPrecise);
|
||||
return true;
|
||||
@ -126,7 +134,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
if (State == Visibility.Visible)
|
||||
if (State.Value == Visibility.Visible)
|
||||
schedulePopOut();
|
||||
|
||||
base.Show();
|
||||
|
@ -14,6 +14,8 @@ namespace osu.Game.Overlays
|
||||
protected override bool BlockNonPositionalInput => true;
|
||||
protected override Container<Drawable> Content => Waves;
|
||||
|
||||
protected override bool StartHidden => true;
|
||||
|
||||
protected WaveOverlayContainer()
|
||||
{
|
||||
AddInternal(Waves = new WaveContainer
|
||||
@ -25,13 +27,17 @@ namespace osu.Game.Overlays
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
Waves.Show();
|
||||
this.FadeIn(100, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
Waves.Hide();
|
||||
this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user