From cbd6de58ee0350ebb2eac877e5e5e434071a23bf Mon Sep 17 00:00:00 2001 From: Flutterish Date: Mon, 18 Apr 2022 20:25:46 +0200 Subject: [PATCH] add autonavigation for autonavigated tabs --- .../API/APIUser.cs | 24 +++++++ .../API/ListingEntry.cs | 21 ++++-- .../API/UserProfile.cs | 32 +++++++++ .../CategorisedTabControlOverlayHeader.cs | 72 +++++++++---------- .../UI/Overlay/RurusettoOverlayBackground.cs | 6 +- .../UI/Overlay/RurusettoOverlayHeader.cs | 54 ++++++++++++-- .../UI/Users/DrawableRurusettoUser.cs | 25 +++---- 7 files changed, 174 insertions(+), 60 deletions(-) diff --git a/osu.Game.Rulesets.RurusettoAddon/API/APIUser.cs b/osu.Game.Rulesets.RurusettoAddon/API/APIUser.cs index c492028..785e73c 100644 --- a/osu.Game.Rulesets.RurusettoAddon/API/APIUser.cs +++ b/osu.Game.Rulesets.RurusettoAddon/API/APIUser.cs @@ -58,6 +58,30 @@ void requestDefault ( Exception? e = null ) { } } + public void RequestDarkCover ( Action success, Action? failure = null ) { + void requestDefault ( Exception? e = null ) { + if ( API != null ) { + API.RequestImage( StaticAPIResource.DefaultCover, success, failure: e => { + failure?.Invoke( e ); + } ); + } + } + + if ( Source == Source.Web && API != null ) { + RequestDetail( detail => { + if ( !string.IsNullOrWhiteSpace( detail.DarkCover ) ) { + API.RequestImage( detail.DarkCover, success, requestDefault ); + } + else { + requestDefault(); + } + }, failure: requestDefault ); + } + else { + requestDefault(); + } + } + public override string ToString () => Source is Source.Web ? $"User with ID = {ID}" : $"Local user"; } \ No newline at end of file diff --git a/osu.Game.Rulesets.RurusettoAddon/API/ListingEntry.cs b/osu.Game.Rulesets.RurusettoAddon/API/ListingEntry.cs index c2fd975..573b32c 100644 --- a/osu.Game.Rulesets.RurusettoAddon/API/ListingEntry.cs +++ b/osu.Game.Rulesets.RurusettoAddon/API/ListingEntry.cs @@ -3,7 +3,7 @@ #nullable disable namespace osu.Game.Rulesets.RurusettoAddon.API; -public record ListingEntry { +public record ShortListingEntry { /// The ID of the ruleset in RÅ«rusetto database. [JsonProperty( "id" )] public int ID { get; init; } @@ -11,9 +11,11 @@ public record ListingEntry { /// The name of the ruleset. [JsonProperty( "name" )] public string Name { get; init; } + /// The slug of the ruleset. Use in the URL of the ruleset's wiki page. [JsonProperty( "slug" )] public string Slug { get; init; } + /// The short description of the rulesets. [JsonProperty( "description" )] public string Description { get; init; } @@ -21,20 +23,23 @@ public record ListingEntry { /// The URL of the ruleset icon that use in website's default theme (dark theme). [JsonProperty( "icon" )] public string DarkIcon { get; init; } + /// The URL of the ruleset icon that use in website's light theme. [JsonProperty( "light_icon" )] public string LightIcon { get; init; } - /// The user_detail of the ruleset's current owner. - [JsonProperty( "owner_detail" )] - public UserDetail Owner { get; init; } - /// True if the wiki maintainer has verified that the the owner is the real owner of this ruleset. [JsonProperty( "verified" )] public bool IsVerified { get; init; } + + /// True if the rulesets is stop update or archived by rulesets creator. + [JsonProperty( "archive" )] + public bool IsArchived { get; init; } + /// URL for download the latest release of ruleset from GitHub [JsonProperty( "direct_download_link" )] public string Download { get; init; } + /// /// True if website can render the direct download link from the source and github_download_filename /// so user can download directly from direct_download_link. @@ -45,4 +50,10 @@ public record ListingEntry { /// The status of the ruleset. [JsonProperty( "status" )] public Status Status { get; init; } +} + +public record ListingEntry : ShortListingEntry { + /// The user_detail of the ruleset's current owner. + [JsonProperty( "owner_detail" )] + public UserDetail Owner { get; init; } } \ No newline at end of file diff --git a/osu.Game.Rulesets.RurusettoAddon/API/UserProfile.cs b/osu.Game.Rulesets.RurusettoAddon/API/UserProfile.cs index 92a7425..0eded7b 100644 --- a/osu.Game.Rulesets.RurusettoAddon/API/UserProfile.cs +++ b/osu.Game.Rulesets.RurusettoAddon/API/UserProfile.cs @@ -7,20 +7,26 @@ public record UserProfile { /// The ID of the user. Use in URL path to target user's profile page. [JsonProperty( "id" )] public int? ID { get; init; } + [JsonProperty( "user" )] private UserInfo info { get; init; } + /// URL of the user's profile picture. [JsonProperty( "image" )] public string ProfilePicture { get; init; } + /// URL of the user's cover picture in website's default theme (Dark theme). [JsonProperty( "cover" )] public string DarkCover { get; init; } + /// URL of the user's cover picture in website's light theme. [JsonProperty( "cover_light" )] public string LightCover { get; init; } + /// User's introduction text on profile page. [JsonProperty( "about_me" )] public string Bio { get; init; } + /// osu! account username of target user (Can be blank) [JsonProperty( "osu_username" )] public string OsuUsername { get; init; } @@ -30,4 +36,30 @@ public record UserProfile { /// public string Email => info?.Email; + + /// List of tag that user has. Will be [] if no tags found in this user. + [JsonProperty( "tags" )] + public List Tags { get; init; } + + /// List of ruleset that user created. Will be [] if no created rulesets found from this user. + [JsonProperty( "created_rulesets" )] + public List CreatedRulesets { get; init; } +} + +public record UserTag { + /// The name of the tag. + [JsonProperty( "name" )] + public string Text { get; init; } + + /// The background color of the tag pills that show in profile. Will return in hex color (e.g. #FFFFFF). + [JsonProperty( "pills_color" )] + public string BackgroundColor { get; init; } + + /// The font color of the tag pills that show in profile. Will return in hex color (e.g. #FFFFFF). + [JsonProperty( "font_color" )] + public string ForegroundColor { get; init; } + + /// The description of the tag. + [JsonProperty( "description" )] + public string Description { get; init; } } \ No newline at end of file diff --git a/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/CategorisedTabControlOverlayHeader.cs b/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/CategorisedTabControlOverlayHeader.cs index 0e0f451..0035d5d 100644 --- a/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/CategorisedTabControlOverlayHeader.cs +++ b/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/CategorisedTabControlOverlayHeader.cs @@ -49,47 +49,47 @@ protected CategorisedTabControlOverlayHeader () { Direction = FillDirection.Vertical, Children = new Drawable[] { - new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Depth = -1, - Children = new Drawable[] { - categoryControlBackground = new Box + new Container { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Depth = -1, + Children = new Drawable[] { + categoryControlBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + categoryControlContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = ContentSidePadding }, + Child = CategoryControl = CreateCategoryControl().With(control => { - RelativeSizeAxes = Axes.Both, - }, - categoryControlContainer = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = ContentSidePadding }, - Child = CategoryControl = CreateCategoryControl().With(control => - { - control.Current = CurrentCategory; - }) - } + control.Current = CurrentCategory; + }) } - }, - new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] { - controlBackground = new Box - { - RelativeSizeAxes = Axes.Both, - }, - tabControlContainer = new Container + } + }, + new Container { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { + controlBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + tabControlContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = ContentSidePadding }, + Child = TabControl = CreateTabControl().With(control => { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = ContentSidePadding }, - Child = TabControl = CreateTabControl().With(control => - { - control.Current = Current; - }) - } + control.Current = Current; + }) } } + } } } ); } diff --git a/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/RurusettoOverlayBackground.cs b/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/RurusettoOverlayBackground.cs index babff06..66d372b 100644 --- a/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/RurusettoOverlayBackground.cs +++ b/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/RurusettoOverlayBackground.cs @@ -17,10 +17,10 @@ public RurusettoOverlayBackground () { [BackgroundDependencyLoader] private void load ( GameHost host, TextureStore textures, RurusettoAddonRuleset ruleset ) { - SetCover( defaultCover = ruleset.GetTexture( host, textures, TextureNames.HeaderBackground ) ); + SetCover( defaultCover = ruleset.GetTexture( host, textures, TextureNames.HeaderBackground ), expanded: true ); } - public void SetCover ( Texture? cover ) { + public void SetCover ( Texture? cover, bool expanded ) { cover ??= defaultCover; if ( !covers.TryGetValue( cover, out var sprite ) ) { @@ -41,7 +41,7 @@ public void SetCover ( Texture? cover ) { currentCover = sprite; - if ( currentCover.Texture == defaultCover ) { + if ( expanded ) { this.FadeIn().ResizeHeightTo( 80, 400, Easing.Out ); } else { diff --git a/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/RurusettoOverlayHeader.cs b/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/RurusettoOverlayHeader.cs index 2e1a925..6adcccf 100644 --- a/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/RurusettoOverlayHeader.cs +++ b/osu.Game.Rulesets.RurusettoAddon/UI/Overlay/RurusettoOverlayHeader.cs @@ -26,26 +26,36 @@ public class RurusettoOverlayHeader : CategorisedTabControlOverlayHeader { CategoryControl.Current.Value = v.NewValue.Category; + if ( userNaviaged && v.NewValue.Tab is null ) { + if ( navigationDirection == 1 ) + NavigateForward(); + else + NavigateBack(); + } switch ( v.NewValue ) { case { Tab: APIRuleset ruleset }: ruleset.RequestDarkCover( texture => { - background.SetCover( texture ); + background.SetCover( texture, expanded: false ); } ); break; case { Tab: APIUser user }: - + user.RequestDarkCover( cover => { + background.SetCover( cover, expanded: true ); + } ); break; default: - background.SetCover( null ); + background.SetCover( null, expanded: true ); break; } }; @@ -72,7 +82,7 @@ public void NavigateTo ( RurusettoTabItem tab, bool perserveCategories = false ) return; RurusettoTabItem item = new() { - Tab = tab, + Tab = tab.Tab, Title = tab.Title, Category = tab.Category }; @@ -83,10 +93,15 @@ public void NavigateTo ( RurusettoTabItem tab, bool perserveCategories = false ) clearHistoryAfterCurrent(); TabControl.AddItem( item ); + userNaviaged = false; TabControl.Current.Value = item; + userNaviaged = true; } public void NavigateTo ( object tab, LocalisableString title, bool perserveCategories = false ) { + if ( tab == Current.Value.Tab ) + return; + var category = tab switch { APIRuleset => ListingTab, APIUser => UsersTab, @@ -104,7 +119,9 @@ public void NavigateTo ( object tab, LocalisableString title, bool perserveCateg clearHistoryAfterCurrent(); TabControl.AddItem( item ); + userNaviaged = false; TabControl.Current.Value = item; + userNaviaged = true; } private void clearHistoryAfterCurrent () { @@ -117,7 +134,11 @@ public bool NavigateBack () { if ( TabControl.Items[0] == Current.Value ) return false; + var prevDir = navigationDirection; + + navigationDirection = -1; TabControl.SwitchTab( -1, wrap: false ); + navigationDirection = prevDir; return true; } @@ -125,7 +146,11 @@ public bool NavigateForward () { if ( TabControl.Items[^1] == Current.Value ) return false; + var prevDir = navigationDirection; + + navigationDirection = 1; TabControl.SwitchTab( 1, wrap: false ); + navigationDirection = prevDir; return true; } @@ -171,6 +196,10 @@ public OverlayHeaderBreadcrumbControl () { }; } + protected override Dropdown CreateDropdown () { + return new ControlDropdown(); + } + [BackgroundDependencyLoader] private void load ( OverlayColourProvider colourProvider ) { AccentColour = colourProvider.Light2; @@ -180,6 +209,11 @@ private void load ( OverlayColourProvider colourProvider ) { AccentColour = AccentColour, }; + private class ControlDropdown : OsuTabDropdown { + protected override LocalisableString GenerateItemText ( RurusettoTabItem item ) + => item.Title; + } + private class ControlTabItem : BreadcrumbTabItem { protected override float ChevronSize => 8; @@ -191,6 +225,7 @@ public ControlTabItem ( RurusettoTabItem value ) Text.Origin = Anchor.CentreLeft; Chevron.Y = 1; Bar.Height = 0; + AlwaysPresent = true; } protected override void LoadComplete () { @@ -199,6 +234,17 @@ protected override void LoadComplete () { Text.Text = Value.Title; } + protected override void Update () { + base.Update(); + if ( Alpha == 0 ) { + AutoSizeAxes = Axes.None; + Width = 0; + } + else { + AutoSizeAxes = Axes.X; + } + } + // base OsuTabItem makes font bold on activation, we don't want that here protected override void OnActivated () => FadeHovered(); diff --git a/osu.Game.Rulesets.RurusettoAddon/UI/Users/DrawableRurusettoUser.cs b/osu.Game.Rulesets.RurusettoAddon/UI/Users/DrawableRurusettoUser.cs index 5fb314d..a79f282 100644 --- a/osu.Game.Rulesets.RurusettoAddon/UI/Users/DrawableRurusettoUser.cs +++ b/osu.Game.Rulesets.RurusettoAddon/UI/Users/DrawableRurusettoUser.cs @@ -170,20 +170,21 @@ protected override void LoadComplete () { public LocalisableString TooltipText => usernameText; protected override bool OnClick ( ClickEvent e ) { - if ( !string.IsNullOrWhiteSpace( profile?.OsuUsername ) && ProfileOverlay != null && OnlineAPI != null ) { - var request = new GetUserRequest( profile.OsuUsername ); - request.Success += v => { - ProfileOverlay.ShowUser( v ); - }; - request.Failure += v => { - // :( - }; - OnlineAPI.PerformAsync( request ); + //if ( !string.IsNullOrWhiteSpace( profile?.OsuUsername ) && ProfileOverlay != null && OnlineAPI != null ) { + // var request = new GetUserRequest( profile.OsuUsername ); + // request.Success += v => { + // ProfileOverlay.ShowUser( v ); + // }; + // request.Failure += v => { + // // :( + // }; + // OnlineAPI.PerformAsync( request ); + //} + + if ( user.HasProfile ) { + user.RequestDetail( profile => Overlay.Header.NavigateTo( user, profile.Username, perserveCategories: true ) ); } - //if ( user.HasProfile ) - // Overlay.Header.SelectedInfo.Value = user; - return true; } } \ No newline at end of file