From 81d95f8584b21f8b656ab522107a130acbe29941 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 18 Jul 2020 20:24:38 +0300 Subject: [PATCH 1/5] Implement UserBrickPanel component --- .../Visual/Online/TestSceneSocialOverlay.cs | 84 ------ .../Visual/Online/TestSceneUserPanel.cs | 13 + .../Dashboard/Friends/FriendDisplay.cs | 3 + .../OverlayPanelDisplayStyleControl.cs | 7 +- .../Sections/General/LoginSettings.cs | 2 +- osu.Game/Overlays/SocialOverlay.cs | 242 ------------------ osu.Game/Users/ExtendedUserPanel.cs | 148 +++++++++++ osu.Game/Users/UserBrickPanel.cs | 65 +++++ osu.Game/Users/UserGridPanel.cs | 2 +- osu.Game/Users/UserListPanel.cs | 2 +- osu.Game/Users/UserPanel.cs | 130 +--------- 11 files changed, 241 insertions(+), 457 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs delete mode 100644 osu.Game/Overlays/SocialOverlay.cs create mode 100644 osu.Game/Users/ExtendedUserPanel.cs create mode 100644 osu.Game/Users/UserBrickPanel.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs deleted file mode 100644 index 77e77d90c1..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Game.Overlays; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual.Online -{ - [TestFixture] - public class TestSceneSocialOverlay : OsuTestScene - { - protected override bool UseOnlineAPI => true; - - public TestSceneSocialOverlay() - { - SocialOverlay s = new SocialOverlay - { - Users = new[] - { - new User - { - Username = @"flyte", - Id = 3103765, - Country = new Country { FlagName = @"JP" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", - }, - new User - { - Username = @"Cookiezi", - Id = 124493, - Country = new Country { FlagName = @"KR" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg", - }, - new User - { - Username = @"Angelsim", - Id = 1777162, - Country = new Country { FlagName = @"KR" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", - }, - new User - { - Username = @"Rafis", - Id = 2558286, - Country = new Country { FlagName = @"PL" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg", - }, - new User - { - Username = @"hvick225", - Id = 50265, - Country = new Country { FlagName = @"TW" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c5.jpg", - }, - new User - { - Username = @"peppy", - Id = 2, - Country = new Country { FlagName = @"AU" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - }, - new User - { - Username = @"filsdelama", - Id = 2831793, - Country = new Country { FlagName = @"FR" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c7.jpg" - }, - new User - { - Username = @"_index", - Id = 652457, - Country = new Country { FlagName = @"RU" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c8.jpg" - }, - }, - }; - Add(s); - - AddStep(@"toggle", s.ToggleVisibility); - } - } -} diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index f763e50067..c2e9945c99 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -42,6 +42,19 @@ namespace osu.Game.Tests.Visual.Online Spacing = new Vector2(10f), Children = new Drawable[] { + new UserBrickPanel(new User + { + Username = @"flyte", + Id = 3103765, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + }), + new UserBrickPanel(new User + { + Username = @"peppy", + Id = 2, + Colour = "99EB47", + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }), flyte = new UserGridPanel(new User { Username = @"flyte", diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index 79fda99c73..41b25ee1a5 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -225,6 +225,9 @@ namespace osu.Game.Overlays.Dashboard.Friends case OverlayPanelDisplayStyle.List: return new UserListPanel(user); + + case OverlayPanelDisplayStyle.Brick: + return new UserBrickPanel(user); } } diff --git a/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs b/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs index 7269007b41..87b9d89d4d 100644 --- a/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs +++ b/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs @@ -34,6 +34,10 @@ namespace osu.Game.Overlays { Icon = FontAwesome.Solid.Bars }); + AddTabItem(new PanelDisplayTabItem(OverlayPanelDisplayStyle.Brick) + { + Icon = FontAwesome.Solid.Th + }); } protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer @@ -96,6 +100,7 @@ namespace osu.Game.Overlays public enum OverlayPanelDisplayStyle { Card, - List + List, + Brick } } diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 52b712a40e..34e5da4ef4 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Settings.Sections.General [Resolved] private OsuColour colours { get; set; } - private UserPanel panel; + private UserGridPanel panel; private UserDropdown dropdown; /// diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs deleted file mode 100644 index 1b05142192..0000000000 --- a/osu.Game/Overlays/SocialOverlay.cs +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Bindables; -using osuTK; -using osuTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.SearchableList; -using osu.Game.Overlays.Social; -using osu.Game.Users; -using System.Threading; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Threading; - -namespace osu.Game.Overlays -{ - public class SocialOverlay : SearchableListOverlay - { - private readonly LoadingSpinner loading; - private FillFlowContainer panels; - - protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"60284b"); - protected override Color4 TrianglesColourLight => Color4Extensions.FromHex(@"672b51"); - protected override Color4 TrianglesColourDark => Color4Extensions.FromHex(@"5c2648"); - - protected override SearchableListHeader CreateHeader() => new Header(); - protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); - - private User[] users = Array.Empty(); - - public User[] Users - { - get => users; - set - { - if (users == value) - return; - - users = value ?? Array.Empty(); - - if (LoadState >= LoadState.Ready) - recreatePanels(); - } - } - - public SocialOverlay() - : base(OverlayColourScheme.Pink) - { - Add(loading = new LoadingSpinner()); - - Filter.Search.Current.ValueChanged += text => - { - if (!string.IsNullOrEmpty(text.NewValue)) - { - // force searching in players until searching for friends is supported - Header.Tabs.Current.Value = SocialTab.AllPlayers; - - if (Filter.Tabs.Current.Value != SocialSortCriteria.Rank) - Filter.Tabs.Current.Value = SocialSortCriteria.Rank; - } - }; - - Header.Tabs.Current.ValueChanged += _ => queueUpdate(); - Filter.Tabs.Current.ValueChanged += _ => onFilterUpdate(); - - Filter.DisplayStyleControl.DisplayStyle.ValueChanged += _ => recreatePanels(); - Filter.Dropdown.Current.ValueChanged += _ => recreatePanels(); - - currentQuery.BindTo(Filter.Search.Current); - currentQuery.ValueChanged += query => - { - queryChangedDebounce?.Cancel(); - - if (string.IsNullOrEmpty(query.NewValue)) - queueUpdate(); - else - queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500); - }; - } - - [BackgroundDependencyLoader] - private void load() - { - recreatePanels(); - } - - private APIRequest getUsersRequest; - - private readonly Bindable currentQuery = new Bindable(); - - private ScheduledDelegate queryChangedDebounce; - - private void queueUpdate() => Scheduler.AddOnce(updateSearch); - - private CancellationTokenSource loadCancellation; - - private void updateSearch() - { - queryChangedDebounce?.Cancel(); - - if (!IsLoaded) - return; - - Users = null; - clearPanels(); - getUsersRequest?.Cancel(); - - if (API?.IsLoggedIn != true) - return; - - switch (Header.Tabs.Current.Value) - { - case SocialTab.Friends: - var friendRequest = new GetFriendsRequest(); // TODO filter arguments? - friendRequest.Success += users => Users = users.ToArray(); - API.Queue(getUsersRequest = friendRequest); - break; - - default: - var userRequest = new GetUsersRequest(); // TODO filter arguments! - userRequest.Success += res => Users = res.Users.Select(r => r.User).ToArray(); - API.Queue(getUsersRequest = userRequest); - break; - } - } - - private void recreatePanels() - { - clearPanels(); - - if (Users == null) - { - loading.Hide(); - return; - } - - IEnumerable sortedUsers = Users; - - switch (Filter.Tabs.Current.Value) - { - case SocialSortCriteria.Location: - sortedUsers = sortedUsers.OrderBy(u => u.Country.FullName); - break; - - case SocialSortCriteria.Name: - sortedUsers = sortedUsers.OrderBy(u => u.Username); - break; - } - - if (Filter.Dropdown.Current.Value == SortDirection.Descending) - sortedUsers = sortedUsers.Reverse(); - - var newPanels = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10f), - Margin = new MarginPadding { Top = 10 }, - ChildrenEnumerable = sortedUsers.Select(u => - { - UserPanel panel; - - switch (Filter.DisplayStyleControl.DisplayStyle.Value) - { - case PanelDisplayStyle.Grid: - panel = new UserGridPanel(u) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Width = 290, - }; - break; - - default: - panel = new UserListPanel(u); - break; - } - - panel.Status.BindTo(u.Status); - panel.Activity.BindTo(u.Activity); - return panel; - }) - }; - - LoadComponentAsync(newPanels, f => - { - if (panels != null) - ScrollFlow.Remove(panels); - - loading.Hide(); - ScrollFlow.Add(panels = newPanels); - }, (loadCancellation = new CancellationTokenSource()).Token); - } - - private void onFilterUpdate() - { - if (Filter.Tabs.Current.Value == SocialSortCriteria.Rank) - { - queueUpdate(); - return; - } - - recreatePanels(); - } - - private void clearPanels() - { - loading.Show(); - - loadCancellation?.Cancel(); - - if (panels != null) - { - panels.Expire(); - panels = null; - } - } - - public override void APIStateChanged(IAPIProvider api, APIState state) - { - switch (state) - { - case APIState.Online: - queueUpdate(); - break; - - default: - Users = null; - clearPanels(); - break; - } - } - } -} diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs new file mode 100644 index 0000000000..5bd98b3fb7 --- /dev/null +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -0,0 +1,148 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK; +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.Sprites; +using osu.Framework.Graphics.Sprites; +using osu.Game.Users.Drawables; +using osu.Framework.Input.Events; + +namespace osu.Game.Users +{ + public abstract class ExtendedUserPanel : UserPanel + { + public readonly Bindable Status = new Bindable(); + + public readonly IBindable Activity = new Bindable(); + + protected TextFlowContainer LastVisitMessage { get; private set; } + + private SpriteIcon statusIcon; + private OsuSpriteText statusMessage; + + public ExtendedUserPanel(User user) + : base(user) + { + } + + [BackgroundDependencyLoader] + private void load() + { + BorderColour = ColourProvider?.Light1 ?? Colours.GreyVioletLighter; + + Status.ValueChanged += status => displayStatus(status.NewValue, Activity.Value); + Activity.ValueChanged += activity => displayStatus(Status.Value, activity.NewValue); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Status.TriggerChange(); + + // Colour should be applied immediately on first load. + statusIcon.FinishTransforms(); + } + + protected UpdateableAvatar CreateAvatar() => new UpdateableAvatar + { + User = User, + OpenOnClick = { Value = false } + }; + + protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.Country) + { + Size = new Vector2(39, 26) + }; + + protected SpriteIcon CreateStatusIcon() => statusIcon = new SpriteIcon + { + Icon = FontAwesome.Regular.Circle, + Size = new Vector2(25) + }; + + protected FillFlowContainer CreateStatusMessage(bool rightAlignedChildren) + { + var statusContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical + }; + + var alignment = rightAlignedChildren ? Anchor.CentreRight : Anchor.CentreLeft; + + statusContainer.Add(LastVisitMessage = new TextFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)).With(text => + { + text.Anchor = alignment; + text.Origin = alignment; + text.AutoSizeAxes = Axes.Both; + text.Alpha = 0; + + if (User.LastVisit.HasValue) + { + text.AddText(@"Last seen "); + text.AddText(new DrawableDate(User.LastVisit.Value, italic: false) + { + Shadow = false + }); + } + })); + + statusContainer.Add(statusMessage = new OsuSpriteText + { + Anchor = alignment, + Origin = alignment, + Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold) + }); + + return statusContainer; + } + + private void displayStatus(UserStatus status, UserActivity activity = null) + { + if (status != null) + { + LastVisitMessage.FadeTo(status is UserStatusOffline && User.LastVisit.HasValue ? 1 : 0); + + // Set status message based on activity (if we have one) and status is not offline + if (activity != null && !(status is UserStatusOffline)) + { + statusMessage.Text = activity.Status; + statusIcon.FadeColour(activity.GetAppropriateColour(Colours), 500, Easing.OutQuint); + return; + } + + // Otherwise use only status + statusMessage.Text = status.Message; + statusIcon.FadeColour(status.GetAppropriateColour(Colours), 500, Easing.OutQuint); + + return; + } + + // Fallback to web status if local one is null + if (User.IsOnline) + { + Status.Value = new UserStatusOnline(); + return; + } + + Status.Value = new UserStatusOffline(); + } + + protected override bool OnHover(HoverEvent e) + { + BorderThickness = 2; + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + BorderThickness = 0; + base.OnHoverLost(e); + } + } +} diff --git a/osu.Game/Users/UserBrickPanel.cs b/osu.Game/Users/UserBrickPanel.cs new file mode 100644 index 0000000000..f6eabc3b75 --- /dev/null +++ b/osu.Game/Users/UserBrickPanel.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK; + +namespace osu.Game.Users +{ + public class UserBrickPanel : UserPanel + { + public UserBrickPanel(User user) + : base(user) + { + AutoSizeAxes = Axes.X; + Height = 23; + CornerRadius = 6; + } + + [BackgroundDependencyLoader] + private void load() + { + Background.FadeTo(0.3f); + } + + protected override Drawable CreateLayout() => new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5, 0), + Margin = new MarginPadding + { + Horizontal = 5 + }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new CircularContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Masking = true, + Width = 4, + Height = 13, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = string.IsNullOrEmpty(User.Colour) ? Color4Extensions.FromHex("0087ca") : Color4Extensions.FromHex(User.Colour) + } + }, + CreateUsername().With(u => + { + u.Anchor = Anchor.CentreLeft; + u.Origin = Anchor.CentreLeft; + u.Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold); + }) + } + }; + } +} diff --git a/osu.Game/Users/UserGridPanel.cs b/osu.Game/Users/UserGridPanel.cs index e62a834d6d..44dcbc305d 100644 --- a/osu.Game/Users/UserGridPanel.cs +++ b/osu.Game/Users/UserGridPanel.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Users { - public class UserGridPanel : UserPanel + public class UserGridPanel : ExtendedUserPanel { private const int margin = 10; diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index 1c3ae20577..9c95eff739 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -12,7 +12,7 @@ using osu.Game.Overlays.Profile.Header.Components; namespace osu.Game.Users { - public class UserListPanel : UserPanel + public class UserListPanel : ExtendedUserPanel { public UserListPanel(User user) : base(user) diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 6f59f9e443..94c0c31cfc 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osuTK; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -14,11 +12,8 @@ using osu.Game.Overlays; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Containers; -using osu.Game.Users.Drawables; using JetBrains.Annotations; -using osu.Framework.Input.Events; namespace osu.Game.Users { @@ -26,21 +21,12 @@ namespace osu.Game.Users { public readonly User User; - public readonly Bindable Status = new Bindable(); - - public readonly IBindable Activity = new Bindable(); - public new Action Action; protected Action ViewProfile { get; private set; } protected DelayedLoadUnloadWrapper Background { get; private set; } - protected TextFlowContainer LastVisitMessage { get; private set; } - - private SpriteIcon statusIcon; - private OsuSpriteText statusMessage; - protected UserPanel(User user) { if (user == null) @@ -53,23 +39,22 @@ namespace osu.Game.Users private UserProfileOverlay profileOverlay { get; set; } [Resolved(canBeNull: true)] - private OverlayColourProvider colourProvider { get; set; } + protected OverlayColourProvider ColourProvider { get; private set; } [Resolved] - private OsuColour colours { get; set; } + protected OsuColour Colours { get; private set; } [BackgroundDependencyLoader] private void load() { Masking = true; - BorderColour = colourProvider?.Light1 ?? colours.GreyVioletLighter; AddRange(new[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = colourProvider?.Background5 ?? colours.Gray1 + Colour = ColourProvider?.Background5 ?? Colours.Gray1 }, Background = new DelayedLoadUnloadWrapper(() => new UserCoverBackground { @@ -86,9 +71,6 @@ namespace osu.Game.Users CreateLayout() }); - Status.ValueChanged += status => displayStatus(status.NewValue, Activity.Value); - Activity.ValueChanged += activity => displayStatus(Status.Value, activity.NewValue); - base.Action = ViewProfile = () => { Action?.Invoke(); @@ -96,41 +78,9 @@ namespace osu.Game.Users }; } - protected override void LoadComplete() - { - base.LoadComplete(); - Status.TriggerChange(); - - // Colour should be applied immediately on first load. - statusIcon.FinishTransforms(); - } - - protected override bool OnHover(HoverEvent e) - { - BorderThickness = 2; - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - BorderThickness = 0; - base.OnHoverLost(e); - } - [NotNull] protected abstract Drawable CreateLayout(); - protected UpdateableAvatar CreateAvatar() => new UpdateableAvatar - { - User = User, - OpenOnClick = { Value = false } - }; - - protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.Country) - { - Size = new Vector2(39, 26) - }; - protected OsuSpriteText CreateUsername() => new OsuSpriteText { Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), @@ -138,80 +88,6 @@ namespace osu.Game.Users Text = User.Username, }; - protected SpriteIcon CreateStatusIcon() => statusIcon = new SpriteIcon - { - Icon = FontAwesome.Regular.Circle, - Size = new Vector2(25) - }; - - protected FillFlowContainer CreateStatusMessage(bool rightAlignedChildren) - { - var statusContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical - }; - - var alignment = rightAlignedChildren ? Anchor.CentreRight : Anchor.CentreLeft; - - statusContainer.Add(LastVisitMessage = new TextFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)).With(text => - { - text.Anchor = alignment; - text.Origin = alignment; - text.AutoSizeAxes = Axes.Both; - text.Alpha = 0; - - if (User.LastVisit.HasValue) - { - text.AddText(@"Last seen "); - text.AddText(new DrawableDate(User.LastVisit.Value, italic: false) - { - Shadow = false - }); - } - })); - - statusContainer.Add(statusMessage = new OsuSpriteText - { - Anchor = alignment, - Origin = alignment, - Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold) - }); - - return statusContainer; - } - - private void displayStatus(UserStatus status, UserActivity activity = null) - { - if (status != null) - { - LastVisitMessage.FadeTo(status is UserStatusOffline && User.LastVisit.HasValue ? 1 : 0); - - // Set status message based on activity (if we have one) and status is not offline - if (activity != null && !(status is UserStatusOffline)) - { - statusMessage.Text = activity.Status; - statusIcon.FadeColour(activity.GetAppropriateColour(colours), 500, Easing.OutQuint); - return; - } - - // Otherwise use only status - statusMessage.Text = status.Message; - statusIcon.FadeColour(status.GetAppropriateColour(colours), 500, Easing.OutQuint); - - return; - } - - // Fallback to web status if local one is null - if (User.IsOnline) - { - Status.Value = new UserStatusOnline(); - return; - } - - Status.Value = new UserStatusOffline(); - } - public MenuItem[] ContextMenuItems => new MenuItem[] { new OsuMenuItem("View Profile", MenuItemType.Highlighted, ViewProfile), From 753b1f3401757cb6cd71b1cece3b2499a59f1328 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 18 Jul 2020 20:26:15 +0300 Subject: [PATCH 2/5] Make ctor protected --- osu.Game/Users/ExtendedUserPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index 5bd98b3fb7..2604815751 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -25,7 +25,7 @@ namespace osu.Game.Users private SpriteIcon statusIcon; private OsuSpriteText statusMessage; - public ExtendedUserPanel(User user) + protected ExtendedUserPanel(User user) : base(user) { } From 2025e5418c841a386ba5599164c3c9d017c7814c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 19 Jul 2020 04:10:35 +0300 Subject: [PATCH 3/5] Minor visual adjustments --- osu.Game/Users/UserBrickPanel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Users/UserBrickPanel.cs b/osu.Game/Users/UserBrickPanel.cs index f6eabc3b75..9ca7768187 100644 --- a/osu.Game/Users/UserBrickPanel.cs +++ b/osu.Game/Users/UserBrickPanel.cs @@ -16,15 +16,14 @@ namespace osu.Game.Users public UserBrickPanel(User user) : base(user) { - AutoSizeAxes = Axes.X; - Height = 23; + AutoSizeAxes = Axes.Both; CornerRadius = 6; } [BackgroundDependencyLoader] private void load() { - Background.FadeTo(0.3f); + Background.FadeTo(0.2f); } protected override Drawable CreateLayout() => new FillFlowContainer @@ -34,7 +33,8 @@ namespace osu.Game.Users Spacing = new Vector2(5, 0), Margin = new MarginPadding { - Horizontal = 5 + Horizontal = 10, + Vertical = 3, }, Anchor = Anchor.Centre, Origin = Anchor.Centre, From f044c06d089319841abc78a59c68096fd0a5a330 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Jul 2020 22:26:58 +0900 Subject: [PATCH 4/5] Fix hold notes accepting presses during release lenience --- .../TestSceneHoldNoteInput.cs | 65 +++++++++++++++++-- .../Objects/Drawables/DrawableHoldNote.cs | 6 ++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 0d13b85901..95072cf4f8 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Screens; using osu.Game.Beatmaps; @@ -10,6 +11,8 @@ using osu.Game.Replays; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -236,6 +239,53 @@ namespace osu.Game.Rulesets.Mania.Tests assertTailJudgement(HitResult.Meh); } + [Test] + public void TestMissReleaseAndHitSecondRelease() + { + var windows = new ManiaHitWindows(); + windows.SetDifficulty(10); + + var beatmap = new Beatmap + { + HitObjects = + { + new HoldNote + { + StartTime = 1000, + Duration = 500, + Column = 0, + }, + new HoldNote + { + StartTime = 1000 + 500 + windows.WindowFor(HitResult.Miss) + 10, + Duration = 500, + Column = 0, + }, + }, + BeatmapInfo = + { + BaseDifficulty = new BeatmapDifficulty + { + SliderTickRate = 4, + OverallDifficulty = 10, + }, + Ruleset = new ManiaRuleset().RulesetInfo + }, + }; + + performTest(new List + { + new ManiaReplayFrame(beatmap.HitObjects[1].StartTime, ManiaAction.Key1), + new ManiaReplayFrame(beatmap.HitObjects[1].GetEndTime()), + }, beatmap); + + AddAssert("first hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[0].NestedHitObjects.Contains(j.HitObject)) + .All(j => j.Type == HitResult.Miss)); + + AddAssert("second hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[1].NestedHitObjects.Contains(j.HitObject)) + .All(j => j.Type == HitResult.Perfect)); + } + private void assertHeadJudgement(HitResult result) => AddAssert($"head judged as {result}", () => judgementResults[0].Type == result); @@ -250,11 +300,11 @@ namespace osu.Game.Rulesets.Mania.Tests private ScoreAccessibleReplayPlayer currentPlayer; - private void performTest(List frames) + private void performTest(List frames, Beatmap beatmap = null) { - AddStep("load player", () => + if (beatmap == null) { - Beatmap.Value = CreateWorkingBeatmap(new Beatmap + beatmap = new Beatmap { HitObjects = { @@ -270,9 +320,14 @@ namespace osu.Game.Rulesets.Mania.Tests BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 4 }, Ruleset = new ManiaRuleset().RulesetInfo }, - }); + }; - Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f }); + beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f }); + } + + AddStep("load player", () => + { + Beatmap.Value = CreateWorkingBeatmap(beatmap); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 2262bd2b7d..0c5289efe1 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -167,6 +167,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (action != Action.Value) return false; + // The tail has a lenience applied to it which is factored into the miss window (i.e. the miss judgement will be delayed). + // But the hold cannot ever be started within the late-lenience window, so we should skip trying to begin the hold during that time. + // Note: Unlike below, we use the tail's start time to determine the time offset. + if (Time.Current > Tail.HitObject.StartTime && !Tail.HitObject.HitWindows.CanBeHit(Time.Current - Tail.HitObject.StartTime)) + return false; + beginHoldAt(Time.Current - Head.HitObject.StartTime); Head.UpdateResult(); From f71ed47e6693f59c0bba83433d83d0f43c876833 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 20 Jul 2020 11:52:02 -0700 Subject: [PATCH 5/5] Fix focused textbox absorbing input when unfocused --- osu.Game/Graphics/UserInterface/FocusedTextBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 8977f014b6..f77a3109c9 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -67,6 +67,8 @@ namespace osu.Game.Graphics.UserInterface public bool OnPressed(GlobalAction action) { + if (!HasFocus) return false; + if (action == GlobalAction.Back) { if (Text.Length > 0)