diff --git a/osu-framework b/osu-framework index 6b44a9f807..2d2e2fe698 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 6b44a9f807fadcb3b3f044780d7e27d62ffe80ac +Subproject commit 2d2e2fe698ab32d80d5e856f52c8c398ceb35540 diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6bec2cb184..4e99a44a07 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -175,11 +175,11 @@ namespace osu.Game LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(social = new SocialOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); - LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add); + LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); LoadComponentAsync(musicController = new MusicController { - Depth = -2, + Depth = -3, Position = new Vector2(0, Toolbar.HEIGHT), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -187,14 +187,14 @@ namespace osu.Game LoadComponentAsync(notificationManager = new NotificationManager { - Depth = -2, + Depth = -3, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); LoadComponentAsync(dialogOverlay = new DialogOverlay { - Depth = -4, + Depth = -5, }, overlayContent.Add); Logger.NewEntry += entry => @@ -221,7 +221,7 @@ namespace osu.Game LoadComponentAsync(Toolbar = new Toolbar { - Depth = -3, + Depth = -4, OnHome = delegate { intro?.ChildScreen?.MakeCurrent(); }, }, overlayContent.Add); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 939c4a4915..7a2d91d733 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -133,9 +133,27 @@ namespace osu.Game Token = LocalConfig.Get(OsuSetting.Token) }); + Beatmap.ValueChanged += b => + { + // compare to last baetmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) + if (lastBeatmap?.Track != b.Track) + { + // this disposal is done to stop the audio track. + // it may not be exactly what we want for cases beatmaps are reused, as it will + // trigger a fresh load of contained resources. + lastBeatmap?.Dispose(); + + Audio.Track.AddItem(b.Track); + } + + lastBeatmap = b; + }; + API.Register(this); } + private WorkingBeatmap lastBeatmap; + public void APIStateChanged(APIAccess api, APIState state) { switch (state) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 198a2a9419..c08e62428b 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -11,6 +12,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics.Effects; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; +using osu.Game.Users; namespace osu.Game.Overlays.Chat { @@ -62,6 +64,8 @@ namespace osu.Game.Overlays.Chat private const float message_padding = 200; private const float text_size = 20; + private Action loadProfile; + private Color4 customUsernameColour; public ChatLine(Message message) @@ -75,9 +79,10 @@ namespace osu.Game.Overlays.Chat } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, UserProfileOverlay profile) { customUsernameColour = colours.ChatBlue; + loadProfile = u => profile?.ShowUser(u); } protected override void LoadComplete() @@ -87,8 +92,6 @@ namespace osu.Game.Overlays.Chat bool hasBackground = !string.IsNullOrEmpty(Message.Sender.Colour); Drawable username = new OsuSpriteText { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, Font = @"Exo2.0-BoldItalic", Text = $@"{Message.Sender.Username}" + (hasBackground ? "" : ":"), Colour = hasBackground ? customUsernameColour : username_colours[Message.UserId % username_colours.Length], @@ -132,7 +135,7 @@ namespace osu.Game.Overlays.Chat new Container { Size = new Vector2(message_padding, text_size), - Children = new[] + Children = new Drawable[] { new OsuSpriteText { @@ -144,7 +147,14 @@ namespace osu.Game.Overlays.Chat TextSize = text_size * 0.75f, Alpha = 0.4f, }, - username + new ClickableContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Child = username, + Action = () => loadProfile(Message.Sender), + }, } }, new Container diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 87c0afebf7..1e4c3c5ff6 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -15,7 +13,6 @@ using osu.Game.Database; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; -using osu.Framework.Extensions; using osu.Framework.Input; using osu.Framework.Graphics.Shapes; @@ -30,7 +27,6 @@ namespace osu.Game.Overlays.Music private FilterControl filter; private PlaylistList list; - private TrackManager trackManager; private BeatmapDatabase beatmaps; private readonly Bindable beatmapBacking = new Bindable(); @@ -43,7 +39,6 @@ namespace osu.Game.Overlays.Music { this.inputManager = inputManager; this.beatmaps = beatmaps; - trackManager = game.Audio.Track; Children = new Drawable[] { @@ -154,13 +149,7 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - - Task.Run(() => - { - var track = beatmapBacking.Value.Track; - trackManager.SetExclusive(track); - track.Start(); - }).ContinueWith(task => Schedule(task.ThrowIfFaulted), TaskContinuationOptions.OnlyOnFaulted); + beatmapBacking.Value.Track.Start(); } } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 17493d5078..21bfd5afd6 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -12,16 +12,20 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Users; +using System.Diagnostics; +using System.Globalization; namespace osu.Game.Overlays.Profile { public class ProfileHeader : Container { - private readonly OsuTextFlowContainer infoTextLeft, infoTextRight; + private readonly OsuTextFlowContainer infoTextLeft; + private readonly LinkFlowContainer infoTextRight; private readonly FillFlowContainer scoreText, scoreNumberText; private readonly Container coverContainer, chartContainer, supporterTag; @@ -29,6 +33,7 @@ namespace osu.Game.Overlays.Profile private readonly SpriteText levelText; private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA; private readonly Box colourBar; + private readonly DrawableFlag countryFlag; private const float cover_height = 350; private const float info_height = 150; @@ -114,16 +119,17 @@ namespace osu.Game.Overlays.Profile } } }, - new OsuSpriteText + new LinkFlowContainer.LinkText { Text = user.Username, + Url = $@"https://osu.ppy.sh/users/{user.Id}", TextSize = 30, Font = @"Exo2.0-RegularItalic", Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Y = -48 }, - new DrawableFlag(user.Country?.FlagName ?? "__") + countryFlag = new DrawableFlag(user.Country?.FlagName ?? "__") { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -158,7 +164,7 @@ namespace osu.Game.Overlays.Profile ParagraphSpacing = 0.8f, LineSpacing = 0.2f }, - infoTextRight = new OsuTextFlowContainer(t => + infoTextRight = new LinkFlowContainer(t => { t.TextSize = 14; t.Font = @"Exo2.0-RegularItalic"; @@ -350,6 +356,7 @@ namespace osu.Game.Overlays.Profile { infoTextLeft.AddText("from "); infoTextLeft.AddText(user.Country.FullName, boldItalic); + countryFlag.FlagName = user.Country.FlagName; } infoTextLeft.NewParagraph(); @@ -373,14 +380,22 @@ namespace osu.Game.Overlays.Profile infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); } + string websiteWithoutProtcol = user.Website; + if (!string.IsNullOrEmpty(websiteWithoutProtcol)) + { + int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal); + if (protocolIndex >= 0) + websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2); + } + tryAddInfoRightLine(FontAwesome.fa_map_marker, user.Location); tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Intrerests); tryAddInfoRightLine(FontAwesome.fa_suitcase, user.Occupation); infoTextRight.NewParagraph(); if (!string.IsNullOrEmpty(user.Twitter)) - tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter); - tryAddInfoRightLine(FontAwesome.fa_globe, user.Website); - tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype); + tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); + tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website); + tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); if (user.Statistics != null) { @@ -390,7 +405,7 @@ namespace osu.Game.Overlays.Profile scoreText.Add(createScoreText("Ranked Score")); scoreNumberText.Add(createScoreNumberText(user.Statistics.RankedScore.ToString(@"#,0"))); scoreText.Add(createScoreText("Accuracy")); - scoreNumberText.Add(createScoreNumberText($"{user.Statistics.Accuracy}%")); + scoreNumberText.Add(createScoreNumberText($"{user.Statistics.Accuracy.ToString("0.##", CultureInfo.CurrentCulture)}%")); scoreText.Add(createScoreText("Play Count")); scoreNumberText.Add(createScoreNumberText(user.Statistics.PlayCount.ToString(@"#,0"))); scoreText.Add(createScoreText("Total Score")); @@ -433,12 +448,12 @@ namespace osu.Game.Overlays.Profile Text = text }; - private void tryAddInfoRightLine(FontAwesome icon, string str) + private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null) { if (string.IsNullOrEmpty(str)) return; infoTextRight.AddIcon(icon); - infoTextRight.AddText(" " + str); + infoTextRight.AddLink(" " + str, url); infoTextRight.NewLine(); } @@ -479,5 +494,51 @@ namespace osu.Game.Overlays.Profile badge.Texture = textures.Get($"Grades/{grade}"); } } + + private class LinkFlowContainer : OsuTextFlowContainer + { + public override bool HandleInput => true; + + public LinkFlowContainer(Action defaultCreationParameters = null) : base(defaultCreationParameters) + { + } + + protected override SpriteText CreateSpriteText() => new LinkText(); + + public void AddLink(string text, string url) => AddText(text, link => ((LinkText)link).Url = url); + + public class LinkText : OsuSpriteText + { + public override bool HandleInput => Url != null; + + public string Url; + + private Color4 hoverColour; + + protected override bool OnHover(InputState state) + { + FadeColour(hoverColour, 500, EasingTypes.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + FadeColour(Color4.White, 500, EasingTypes.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + Process.Start(Url); + return true; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverColour = colours.Yellow; + } + } + } } } diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index aeb5282666..6e42a1bae9 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -72,8 +72,6 @@ namespace osu.Game.Screens.Menu menuVoice = config.GetBindable(OsuSetting.MenuVoice); menuMusic = config.GetBindable(OsuSetting.MenuMusic); - var trackManager = audio.Track; - BeatmapSetInfo setInfo = null; if (!menuMusic) @@ -106,7 +104,6 @@ namespace osu.Game.Screens.Menu Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); track = Beatmap.Value.Track; - trackManager.SetExclusive(track); welcome = audio.Sample.Get(@"welcome"); seeya = audio.Sample.Get(@"seeya"); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 40f24a77c9..4bbb47d419 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -119,10 +119,7 @@ namespace osu.Game.Screens.Play Track track = Beatmap.Value.Track; if (track != null) - { - audio.Track.SetExclusive(track); adjustableSourceClock = track; - } adjustableSourceClock = (IAdjustableClock)track ?? new StopwatchClock(); diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index ddb808c5e4..33252f78f8 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Linq; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; @@ -31,7 +32,7 @@ namespace osu.Game.Screens.Select public Action OnBack; public Action OnStart; - private readonly FillFlowContainer buttons; + private readonly FillFlowContainer buttons; public OsuLogo StartButton; @@ -43,29 +44,21 @@ namespace osu.Game.Screens.Select /// Higher depth to be put on the left, and lower to be put on the right. /// Notice this is different to ! /// - public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0) + public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0) => buttons.Add(new FooterButton { - var button = new FooterButton - { - Text = text, - Height = play_song_select_button_height, - Width = play_song_select_button_width, - Depth = depth, - SelectedColour = colour, - DeselectedColour = colour.Opacity(0.5f), - Hotkey = hotkey, - }; + Text = text, + Height = play_song_select_button_height, + Width = play_song_select_button_width, + Depth = depth, + SelectedColour = colour, + DeselectedColour = colour.Opacity(0.5f), + Hotkey = hotkey, + Hovered = updateModeLight, + HoverLost = updateModeLight, + Action = action, + }); - button.Hovered = () => updateModeLight(button); - button.HoverLost = () => updateModeLight(); - button.Action = action; - buttons.Add(button); - } - - private void updateModeLight(FooterButton button = null) - { - modeLight.FadeColour(button?.SelectedColour ?? Color4.Transparent, TRANSITION_LENGTH, EasingTypes.OutQuint); - } + private void updateModeLight() => modeLight.FadeColour(buttons.FirstOrDefault(b => b.IsHovered)?.SelectedColour ?? Color4.Transparent, TRANSITION_LENGTH, EasingTypes.OutQuint); public Footer() { @@ -111,7 +104,7 @@ namespace osu.Game.Screens.Select Spacing = new Vector2(padding, 0), Children = new Drawable[] { - buttons = new FillFlowContainer + buttons = new FillFlowContainer { Direction = FillDirection.Horizontal, Spacing = new Vector2(0.2f, 0), diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4ecbd91b11..861c244024 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -32,7 +32,6 @@ namespace osu.Game.Screens.Select protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(); private readonly BeatmapCarousel carousel; - private TrackManager trackManager; private DialogOverlay dialogOverlay; private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245); @@ -174,7 +173,6 @@ namespace osu.Game.Screens.Select database.BeatmapSetAdded += onBeatmapSetAdded; database.BeatmapSetRemoved += onBeatmapSetRemoved; - trackManager = audio.Track; dialogOverlay = dialog; sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty"); @@ -360,10 +358,11 @@ namespace osu.Game.Screens.Select { Track track = Beatmap.Value.Track; - trackManager.SetExclusive(track); - - if (preview) track.Seek(Beatmap.Value.Metadata.PreviewTime); - track.Start(); + if (!track.IsRunning) + { + if (preview) track.Seek(Beatmap.Value.Metadata.PreviewTime); + track.Start(); + } } private void removeBeatmapSet(BeatmapSetInfo beatmapSet)