From 5fcd7363320485cf35d25715edaf057cf646f6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Sep 2023 13:52:45 +0200 Subject: [PATCH] Redo nano beatmap card design to fit needs better Wanting to use this inside notification, it turns out that the original design did not work very well at such narrow widths, and additionally the typical button setup borrowed from elsewhere resulted in teeny tiny action buttons. To that end, slim down the design (get rid of thumbnail, audio preview, make expandable right side slimmer), as well as change the entire panel so that it has only one action associated with it at all times, and clicking the panel in any place triggers that action. --- .../Drawables/Cards/BeatmapCardNano.cs | 37 +-- .../Cards/CollapsibleButtonContainerSlim.cs | 246 ++++++++++++++++++ 2 files changed, 267 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainerSlim.cs diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNano.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNano.cs index ba2142d28f..2f46bc51d6 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNano.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNano.cs @@ -20,15 +20,25 @@ namespace osu.Game.Beatmaps.Drawables.Cards protected override Drawable IdleContent => idleBottomContent; protected override Drawable DownloadInProgressContent => downloadProgressBar; + public override float Width + { + get => base.Width; + set + { + base.Width = value; + + if (LoadState >= LoadState.Ready) + buttonContainer.Width = value; + } + } + private const float height = 60; private const float width = 300; - private const float cover_width = 80; [Cached] private readonly BeatmapCardContent content; - private BeatmapCardThumbnail thumbnail = null!; - private CollapsibleButtonContainer buttonContainer = null!; + private CollapsibleButtonContainerSlim buttonContainer = null!; private FillFlowContainer idleBottomContent = null!; private BeatmapCardDownloadProgressBar downloadProgressBar = null!; @@ -52,22 +62,16 @@ namespace osu.Game.Beatmaps.Drawables.Cards { c.MainContent = new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + Height = height, Children = new Drawable[] { - thumbnail = new BeatmapCardThumbnail(BeatmapSet) + buttonContainer = new CollapsibleButtonContainerSlim(BeatmapSet) { - Name = @"Left (icon) area", - Size = new Vector2(cover_width, height), - Padding = new MarginPadding { Right = CORNER_RADIUS }, - }, - buttonContainer = new CollapsibleButtonContainer(BeatmapSet) - { - X = cover_width - CORNER_RADIUS, - Width = width - cover_width + CORNER_RADIUS, + Width = Width, FavouriteState = { BindTarget = FavouriteState }, - ButtonsCollapsedWidth = CORNER_RADIUS, - ButtonsExpandedWidth = 30, + ButtonsCollapsedWidth = 5, + ButtonsExpandedWidth = 20, Children = new Drawable[] { new FillFlowContainer @@ -145,6 +149,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards }; c.Expanded.BindTarget = Expanded; }); + + Action = () => buttonContainer.TriggerClick(); } private LocalisableString createArtistText() @@ -160,7 +166,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards bool showDetails = IsHovered; buttonContainer.ShowDetails.Value = showDetails; - thumbnail.Dimmed.Value = showDetails; } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainerSlim.cs b/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainerSlim.cs new file mode 100644 index 0000000000..d17ff0d759 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainerSlim.cs @@ -0,0 +1,246 @@ +// 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.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Resources.Localisation.Web; +using osuTK; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public partial class CollapsibleButtonContainerSlim : OsuClickableContainer + { + public Bindable ShowDetails = new Bindable(); + public Bindable FavouriteState = new Bindable(); + + private readonly BeatmapDownloadTracker downloadTracker; + + private float buttonsExpandedWidth; + + public float ButtonsExpandedWidth + { + get => buttonsExpandedWidth; + set + { + buttonsExpandedWidth = value; + buttonArea.Width = value; + if (IsLoaded) + updateState(); + } + } + + private float buttonsCollapsedWidth; + + public float ButtonsCollapsedWidth + { + get => buttonsCollapsedWidth; + set + { + buttonsCollapsedWidth = value; + if (IsLoaded) + updateState(); + } + } + + protected override Container Content => mainContent; + + private readonly APIBeatmapSet beatmapSet; + + private readonly Container background; + + private readonly Container buttonArea; + + private readonly Container mainArea; + private readonly Container mainContent; + + private readonly Container icons; + private readonly SpriteIcon downloadIcon; + private readonly LoadingSpinner spinner; + private readonly SpriteIcon goToBeatmapIcon; + + private const int icon_size = 12; + + private Bindable preferNoVideo = null!; + + [Resolved] + private BeatmapModelDownloader beatmaps { get; set; } = null!; + + [Resolved] + private OsuGame? game { get; set; } + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + public CollapsibleButtonContainerSlim(APIBeatmapSet beatmapSet) + { + this.beatmapSet = beatmapSet; + + downloadTracker = new BeatmapDownloadTracker(beatmapSet); + + RelativeSizeAxes = Axes.Y; + Masking = true; + CornerRadius = BeatmapCard.CORNER_RADIUS; + + base.Content.AddRange(new Drawable[] + { + downloadTracker, + background = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White + }, + }, + buttonArea = new Container + { + Name = @"Right (button) area", + RelativeSizeAxes = Axes.Y, + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Child = icons = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + downloadIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(icon_size), + Icon = FontAwesome.Solid.Download + }, + spinner = new LoadingSpinner + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(icon_size) + }, + goToBeatmapIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(icon_size), + Icon = FontAwesome.Solid.AngleDoubleRight + }, + } + } + }, + mainArea = new Container + { + Name = @"Main content", + RelativeSizeAxes = Axes.Y, + CornerRadius = BeatmapCard.CORNER_RADIUS, + Masking = true, + Children = new Drawable[] + { + new BeatmapCardContentBackground(beatmapSet) + { + RelativeSizeAxes = Axes.Both, + Dimmed = { BindTarget = ShowDetails } + }, + mainContent = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = 10, + Vertical = 4 + }, + } + } + } + }); + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + preferNoVideo = config.GetBindable(OsuSetting.PreferNoVideo); + + downloadIcon.Colour = spinner.Colour = colourProvider.Content1; + goToBeatmapIcon.Colour = colourProvider.Foreground1; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + preferNoVideo.BindValueChanged(_ => updateState()); + downloadTracker.State.BindValueChanged(_ => updateState()); + ShowDetails.BindValueChanged(_ => updateState(), true); + FinishTransforms(true); + } + + private void updateState() + { + float targetWidth = Width - (ShowDetails.Value ? ButtonsExpandedWidth : ButtonsCollapsedWidth); + + mainArea.ResizeWidthTo(targetWidth, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + + var backgroundColour = downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3; + if (ShowDetails.Value) + backgroundColour = backgroundColour.Lighten(0.2f); + + background.FadeColour(backgroundColour, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + icons.FadeTo(ShowDetails.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + + if (beatmapSet.Availability.DownloadDisabled) + { + Enabled.Value = false; + TooltipText = BeatmapsetsStrings.AvailabilityDisabled; + return; + } + + switch (downloadTracker.State.Value) + { + case DownloadState.NotDownloaded: + Action = () => beatmaps.Download(beatmapSet, preferNoVideo.Value); + break; + + case DownloadState.LocallyAvailable: + Action = () => game?.PresentBeatmap(beatmapSet); + break; + + default: + Action = null; + break; + } + + downloadIcon.FadeTo(downloadTracker.State.Value == DownloadState.NotDownloaded ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + spinner.FadeTo(downloadTracker.State.Value == DownloadState.Downloading || downloadTracker.State.Value == DownloadState.Importing ? 1 : 0, + BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + goToBeatmapIcon.FadeTo(downloadTracker.State.Value == DownloadState.LocallyAvailable ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + + if (downloadTracker.State.Value == DownloadState.NotDownloaded) + { + if (!beatmapSet.HasVideo) + TooltipText = BeatmapsetsStrings.PanelDownloadAll; + else + TooltipText = preferNoVideo.Value ? BeatmapsetsStrings.PanelDownloadNoVideo : BeatmapsetsStrings.PanelDownloadVideo; + } + else + { + TooltipText = default; + } + } + } +}