diff --git a/osu.Game/Screens/SelectV2/BeatmapPanel.cs b/osu.Game/Screens/SelectV2/BeatmapPanel.cs index c36a23e51f..bd4cf6d7cf 100644 --- a/osu.Game/Screens/SelectV2/BeatmapPanel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapPanel.cs @@ -6,13 +6,9 @@ using System.Diagnostics; using System.Threading; 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.Effects; using osu.Framework.Graphics.Pooling; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -25,7 +21,6 @@ using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { @@ -33,36 +28,23 @@ namespace osu.Game.Screens.SelectV2 { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT; - private const float colour_box_width = 30; - private const float corner_radius = 10; - // todo: this should be replaced with information from CarouselItem about how deep is BeatmapPanel in the carousel // (i.e. whether it's under a beatmap set that's under a group, or just under a top-level beatmap set). private const float difficulty_x_offset = 100f; // constant X offset for beatmap difficulty panels specifically. - private const float preselected_x_offset = 25f; - private const float selected_x_offset = 50f; - private const float duration = 500; - [Resolved] - private BeatmapCarousel? carousel { get; set; } - - [Resolved] - private IBindable ruleset { get; set; } = null!; - - [Resolved] - private IBindable> mods { get; set; } = null!; - - private Container panel = null!; + private CarouselPanelPiece panel = null!; private StarCounter starCounter = null!; - private ConstrainedIconContainer iconContainer = null!; - private Box hoverLayer = null!; - private Box activationFlash = null!; - - private Box backgroundBorder = null!; - + private ConstrainedIconContainer difficultyIcon = null!; + private OsuSpriteText keyCountText = null!; private StarRatingDisplay starRatingDisplay = null!; + private TopLocalRankV2 difficultyRank = null!; + private OsuSpriteText difficultyText = null!; + private OsuSpriteText authorText = null!; + + private IBindable? starDifficultyBindable; + private CancellationTokenSource? starDifficultyCancellationSource; [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -73,16 +55,24 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } = null!; - private OsuSpriteText keyCountText = null!; + [Resolved] + private BeatmapCarousel? carousel { get; set; } - private IBindable? starDifficultyBindable; - private CancellationTokenSource? starDifficultyCancellationSource; + [Resolved] + private IBindable ruleset { get; set; } = null!; - private Container rightContainer = null!; - private Box starRatingGradient = null!; - private TopLocalRankV2 difficultyRank = null!; - private OsuSpriteText difficultyText = null!; - private OsuSpriteText authorText = null!; + [Resolved] + private IBindable> mods { get; set; } = null!; + + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) + { + var inputRectangle = panel.TopLevelContent.DrawRectangle; + + // Cover the gaps introduced by the spacing between BeatmapPanels. + inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING / 2f }); + + return inputRectangle.Contains(panel.TopLevelContent.ToLocalSpace(screenSpacePos)); + } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -94,67 +84,21 @@ namespace osu.Game.Screens.SelectV2 Width = 1f; Height = HEIGHT; - InternalChild = panel = new Container + InternalChild = panel = new CarouselPanelPiece(difficulty_x_offset) { - Masking = true, - CornerRadius = corner_radius, - RelativeSizeAxes = Axes.Both, - X = corner_radius, - EdgeEffect = new EdgeEffectParameters + Icon = difficultyIcon = new ConstrainedIconContainer { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(1f), - Radius = 10, + Size = new Vector2(20), + Margin = new MarginPadding { Horizontal = 5f }, + Colour = colourProvider.Background5, }, - Children = new Drawable[] + Children = new[] { - new BufferedContainer - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - backgroundBorder = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.ForStarDifficulty(0), - EdgeSmoothness = new Vector2(2, 0), - }, - rightContainer = new Container - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Masking = true, - CornerRadius = corner_radius, - RelativeSizeAxes = Axes.X, - Height = HEIGHT, - X = colour_box_width, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(colourProvider.Background3, colourProvider.Background4), - }, - starRatingGradient = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - }, - }, - } - }, - iconContainer = new ConstrainedIconContainer - { - X = colour_box_width / 2, - Origin = Anchor.Centre, - Anchor = Anchor.CentreLeft, - Size = new Vector2(20), - Colour = colourProvider.Background5, - }, new FillFlowContainer { - Padding = new MarginPadding { Top = 8, Left = colour_box_width + corner_radius }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Padding = new MarginPadding { Left = 10f }, Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Both, Children = new Drawable[] @@ -216,34 +160,10 @@ namespace osu.Game.Screens.SelectV2 } } }, - hoverLayer = new Box - { - Colour = colours.Blue.Opacity(0.1f), - Alpha = 0, - Blending = BlendingParameters.Additive, - RelativeSizeAxes = Axes.Both, - }, - activationFlash = new Box - { - Blending = BlendingParameters.Additive, - Alpha = 0f, - RelativeSizeAxes = Axes.Both, - }, - new HoverSounds(), } }; } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) - { - var inputRectangle = panel.DrawRectangle; - - // Cover the gaps introduced by the spacing between BeatmapPanels. - inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING / 2f }); - - return inputRectangle.Contains(panel.ToLocalSpace(screenSpacePos)); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -260,8 +180,8 @@ namespace osu.Game.Screens.SelectV2 updateKeyCount(); }, true); - Selected.BindValueChanged(_ => updateSelectionDisplay(), true); - KeyboardSelected.BindValueChanged(_ => updateKeyboardSelectedDisplay(), true); + Selected.BindValueChanged(s => panel.Active.Value = s.NewValue, true); + KeyboardSelected.BindValueChanged(k => panel.KeyboardActive.Value = k.NewValue, true); } protected override void PrepareForUse() @@ -271,63 +191,25 @@ namespace osu.Game.Screens.SelectV2 Debug.Assert(Item != null); var beatmap = (BeatmapInfo)Item.Model; - iconContainer.Icon = beatmap.Ruleset.CreateInstance().CreateIcon(); + difficultyIcon.Icon = beatmap.Ruleset.CreateInstance().CreateIcon(); difficultyRank.Beatmap = beatmap; difficultyText.Text = beatmap.DifficultyName; authorText.Text = BeatmapsetsStrings.ShowDetailsMappedBy(beatmap.Metadata.Author.Username); - starDifficultyBindable = null; - computeStarRating(); updateKeyCount(); - updateSelectionDisplay(); FinishTransforms(true); - this.FadeInFromZero(duration, Easing.OutQuint); - - // todo: only do this when visible. - // starCounter.ReplayAnimation(); } - private void updateSelectionDisplay() + protected override void FreeAfterUse() { - bool selected = Selected.Value; + base.FreeAfterUse(); - rightContainer.ResizeHeightTo(selected ? HEIGHT - 4 : HEIGHT, duration, Easing.OutQuint); - - updatePanelPosition(); - updateEdgeEffectColour(); - } - - private void updateKeyboardSelectedDisplay() - { - updatePanelPosition(); - updateHover(); - } - - private void updatePanelPosition() - { - float x = difficulty_x_offset + selected_x_offset + preselected_x_offset; - - if (Selected.Value) - x -= selected_x_offset; - - if (KeyboardSelected.Value) - x -= preselected_x_offset; - - this.TransformTo(nameof(Padding), new MarginPadding { Left = x }, duration, Easing.OutQuint); - } - - private void updateHover() - { - bool hovered = IsHovered || KeyboardSelected.Value; - - if (hovered) - hoverLayer.FadeIn(100, Easing.OutQuint); - else - hoverLayer.FadeOut(1000, Easing.OutQuint); + difficultyRank.Beatmap = null; + starDifficultyBindable = null; } private void computeStarRating() @@ -341,34 +223,7 @@ namespace osu.Game.Screens.SelectV2 var beatmap = (BeatmapInfo)Item.Model; starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token); - starDifficultyBindable.BindValueChanged(d => - { - var value = d.NewValue ?? default; - - starRatingDisplay.Current.Value = value; - starCounter.Current = (float)value.Stars; - - iconContainer.FadeColour(value.Stars > 6.5f ? colours.Orange1 : colourProvider.Background5, duration, Easing.OutQuint); - - var starRatingColour = colours.ForStarDifficulty(value.Stars); - - backgroundBorder.FadeColour(starRatingColour, duration, Easing.OutQuint); - starCounter.FadeColour(starRatingColour, duration, Easing.OutQuint); - starRatingGradient.FadeColour(ColourInfo.GradientHorizontal(starRatingColour.Opacity(0.25f), starRatingColour.Opacity(0)), duration, Easing.OutQuint); - starRatingGradient.FadeIn(duration, Easing.OutQuint); - - // todo: this doesn't work for dark star rating colours, still not sure how to fix. - activationFlash.FadeColour(starRatingColour, duration, Easing.OutQuint); - - updateEdgeEffectColour(); - }, true); - } - - private void updateEdgeEffectColour() - { - panel.FadeEdgeEffectTo(Selected.Value - ? colours.ForStarDifficulty(starDifficultyBindable?.Value?.Stars ?? 0f).Opacity(0.5f) - : Color4.Black.Opacity(0.4f), duration, Easing.OutQuint); + starDifficultyBindable.BindValueChanged(_ => updateDisplay(), true); } private void updateKeyCount() @@ -392,16 +247,18 @@ namespace osu.Game.Screens.SelectV2 keyCountText.Alpha = 0; } - protected override bool OnHover(HoverEvent e) + private void updateDisplay() { - updateHover(); - return true; - } + var starDifficulty = starDifficultyBindable?.Value ?? default; - protected override void OnHoverLost(HoverLostEvent e) - { - updateHover(); - base.OnHoverLost(e); + starRatingDisplay.Current.Value = starDifficulty; + starCounter.Current = (float)starDifficulty.Stars; + + difficultyIcon.FadeColour(starDifficulty.Stars > 6.5f ? colours.Orange1 : colourProvider.Background5, duration, Easing.OutQuint); + + var starRatingColour = colours.ForStarDifficulty(starDifficulty.Stars); + starCounter.FadeColour(starRatingColour, duration, Easing.OutQuint); + panel.AccentColour = starRatingColour; } protected override bool OnClick(ClickEvent e) @@ -430,7 +287,7 @@ namespace osu.Game.Screens.SelectV2 public void Activated() { - activationFlash.FadeOutFromOne(500, Easing.OutQuint); + panel.Flash(); } #endregion diff --git a/osu.Game/Screens/SelectV2/BeatmapSetPanel.cs b/osu.Game/Screens/SelectV2/BeatmapSetPanel.cs index aabc39f27f..f5d7e0594b 100644 --- a/osu.Game/Screens/SelectV2/BeatmapSetPanel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapSetPanel.cs @@ -6,12 +6,9 @@ using System.Diagnostics; using System.Linq; 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.Effects; using osu.Framework.Graphics.Pooling; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; @@ -19,10 +16,8 @@ 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.Overlays; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { @@ -30,18 +25,22 @@ namespace osu.Game.Screens.SelectV2 { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.6f; - private const float arrow_container_width = 20; - private const float corner_radius = 10; - // todo: this should be replaced with information from CarouselItem about how deep is BeatmapPanel in the carousel // (i.e. whether it's under a beatmap set that's under a group, or just under a top-level beatmap set). private const float set_x_offset = 20f; // constant X offset for beatmap set/standalone panels specifically. - private const float preselected_x_offset = 25f; - private const float expanded_x_offset = 50f; - private const float duration = 500; + private CarouselPanelPiece panel = null!; + private BeatmapSetPanelBackground background = null!; + + private OsuSpriteText titleText = null!; + private OsuSpriteText artistText = null!; + private Drawable chevronIcon = null!; + private UpdateBeatmapSetButtonV2 updateButton = null!; + private BeatmapSetOnlineStatusPill statusPill = null!; + private DifficultySpectrumDisplay difficultiesDisplay = null!; + [Resolved] private BeatmapCarousel? carousel { get; set; } @@ -51,22 +50,15 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private BeatmapManager beatmaps { get; set; } = null!; - [Resolved] - private OsuColour colours { get; set; } = null!; + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) + { + var inputRectangle = panel.TopLevelContent.DrawRectangle; - private Container panel = null!; - private Box backgroundBorder = null!; - private BeatmapSetPanelBackground background = null!; - private Container backgroundContainer = null!; - private FillFlowContainer mainFlowContainer = null!; - private SpriteIcon chevronIcon = null!; - private Box hoverLayer = null!; + // Cover a gap introduced by the spacing between a BeatmapSetPanel and a BeatmapPanel either above it or below it. + inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING / 2f }); - private OsuSpriteText titleText = null!; - private OsuSpriteText artistText = null!; - private UpdateBeatmapSetButtonV2 updateButton = null!; - private BeatmapSetOnlineStatusPill statusPill = null!; - private DifficultySpectrumDisplay difficultiesDisplay = null!; + return inputRectangle.Contains(panel.TopLevelContent.ToLocalSpace(screenSpacePos)); + } [BackgroundDependencyLoader] private void load() @@ -76,137 +68,89 @@ namespace osu.Game.Screens.SelectV2 RelativeSizeAxes = Axes.X; Height = HEIGHT; - InternalChild = panel = new Container + InternalChild = panel = new CarouselPanelPiece(set_x_offset) { - Masking = true, - CornerRadius = corner_radius, - RelativeSizeAxes = Axes.Both, - X = corner_radius, - EdgeEffect = new EdgeEffectParameters + Icon = chevronIcon = new Container { - Type = EdgeEffectType.Shadow, - Radius = 10, - }, - Children = new Drawable[] - { - new BufferedContainer + Size = new Vector2(22), + Child = new SpriteIcon { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - backgroundBorder = new Box - { - RelativeSizeAxes = Axes.Y, - Alpha = 0, - EdgeSmoothness = new Vector2(2, 0), - }, - backgroundContainer = new Container - { - Masking = true, - CornerRadius = corner_radius, - RelativeSizeAxes = Axes.X, - MaskingSmoothness = 2, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Children = new Drawable[] - { - background = new BeatmapSetPanelBackground - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }, - }, - }, - } - }, - chevronIcon = new SpriteIcon - { - X = arrow_container_width / 2, + Anchor = Anchor.Centre, Origin = Anchor.Centre, - Anchor = Anchor.CentreLeft, Icon = FontAwesome.Solid.ChevronRight, Size = new Vector2(12), + X = 1f, Colour = colourProvider.Background5, }, - mainFlowContainer = new FillFlowContainer + }, + Background = background = new BeatmapSetPanelBackground + { + RelativeSizeAxes = Axes.Both, + }, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Top = 7.5f, Left = 15, Bottom = 5 }, + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Top = 7.5f, Left = 15, Bottom = 5 }, - Children = new Drawable[] + titleText = new OsuSpriteText { - titleText = new OsuSpriteText + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22, italics: true), + }, + artistText = new OsuSpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 17, italics: true), + }, + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 5f }, + Children = new Drawable[] { - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22, italics: true), - Shadow = true, - }, - artistText = new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 17, italics: true), - Shadow = true, - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 5f }, - Children = new Drawable[] + updateButton = new UpdateBeatmapSetButtonV2 { - updateButton = new UpdateBeatmapSetButtonV2 - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Right = 5f, Top = -2f }, - }, - statusPill = new BeatmapSetOnlineStatusPill - { - AutoSizeAxes = Axes.Both, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - TextSize = 11, - TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, - Margin = new MarginPadding { Right = 5f }, - }, - difficultiesDisplay = new DifficultySpectrumDisplay - { - DotSize = new Vector2(5, 10), - DotSpacing = 2, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Right = 5f, Top = -2f }, }, - } + statusPill = new BeatmapSetOnlineStatusPill + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + TextSize = 11, + TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, + Margin = new MarginPadding { Right = 5f }, + }, + difficultiesDisplay = new DifficultySpectrumDisplay + { + DotSize = new Vector2(5, 10), + DotSpacing = 2, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + }, } - }, - hoverLayer = new Box - { - Colour = colours.Blue.Opacity(0.1f), - Alpha = 0, - Blending = BlendingParameters.Additive, - RelativeSizeAxes = Axes.Both, - }, - new HoverSounds(), + } } }; } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) - { - var inputRectangle = panel.DrawRectangle; - - // Cover a gap introduced by the spacing between a BeatmapSetPanel and a BeatmapPanel either above it or below it. - inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING / 2f }); - - return inputRectangle.Contains(panel.ToLocalSpace(screenSpacePos)); - } - protected override void LoadComplete() { base.LoadComplete(); - Expanded.BindValueChanged(_ => updateExpandedDisplay(), true); - KeyboardSelected.BindValueChanged(_ => updateKeyboardSelectedDisplay(), true); + Expanded.BindValueChanged(_ => onExpanded(), true); + KeyboardSelected.BindValueChanged(k => panel.KeyboardActive.Value = k.NewValue, true); + } + + private void onExpanded() + { + panel.Active.Value = Expanded.Value; + chevronIcon.ResizeWidthTo(Expanded.Value ? 22 : 0f, duration, Easing.OutQuint); + chevronIcon.FadeTo(Expanded.Value ? 1f : 0f, duration, Easing.OutQuint); } protected override void PrepareForUse() @@ -226,9 +170,7 @@ namespace osu.Game.Screens.SelectV2 statusPill.Status = beatmapSet.Status; difficultiesDisplay.BeatmapSet = beatmapSet; - updateExpandedDisplay(); FinishTransforms(true); - this.FadeInFromZero(duration, Easing.OutQuint); } @@ -241,70 +183,6 @@ namespace osu.Game.Screens.SelectV2 difficultiesDisplay.BeatmapSet = null; } - private void updateExpandedDisplay() - { - if (Item == null) - return; - - updatePanelPosition(); - - backgroundBorder.RelativeSizeAxes = Expanded.Value ? Axes.Both : Axes.Y; - backgroundBorder.Width = Expanded.Value ? 1 : arrow_container_width + corner_radius; - backgroundBorder.FadeTo(Expanded.Value ? 1 : 0, duration, Easing.OutQuint); - chevronIcon.FadeTo(Expanded.Value ? 1 : 0, duration, Easing.OutQuint); - - backgroundContainer.ResizeHeightTo(Expanded.Value ? HEIGHT - 4 : HEIGHT, duration, Easing.OutQuint); - backgroundContainer.MoveToX(Expanded.Value ? arrow_container_width : 0, duration, Easing.OutQuint); - mainFlowContainer.MoveToX(Expanded.Value ? arrow_container_width : 0, duration, Easing.OutQuint); - - panel.EdgeEffect = panel.EdgeEffect with { Radius = Expanded.Value ? 15 : 10 }; - - panel.FadeEdgeEffectTo(Expanded.Value - ? Color4Extensions.FromHex(@"4EBFFF").Opacity(0.5f) - : Color4.Black.Opacity(0.4f), duration, Easing.OutQuint); - } - - private void updateKeyboardSelectedDisplay() - { - updatePanelPosition(); - updateHover(); - } - - private void updatePanelPosition() - { - float x = set_x_offset + expanded_x_offset + preselected_x_offset; - - if (Expanded.Value) - x -= expanded_x_offset; - - if (KeyboardSelected.Value) - x -= preselected_x_offset; - - this.TransformTo(nameof(Padding), new MarginPadding { Left = x }, duration, Easing.OutQuint); - } - - private void updateHover() - { - bool hovered = IsHovered || KeyboardSelected.Value; - - if (hovered) - hoverLayer.FadeIn(100, Easing.OutQuint); - else - hoverLayer.FadeOut(1000, Easing.OutQuint); - } - - protected override bool OnHover(HoverEvent e) - { - updateHover(); - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateHover(); - base.OnHoverLost(e); - } - protected override bool OnClick(ClickEvent e) { if (carousel != null) diff --git a/osu.Game/Screens/SelectV2/BeatmapStandalonePanel.cs b/osu.Game/Screens/SelectV2/BeatmapStandalonePanel.cs index c0a5f828f4..a8fa2224d7 100644 --- a/osu.Game/Screens/SelectV2/BeatmapStandalonePanel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapStandalonePanel.cs @@ -8,12 +8,9 @@ using System.Linq; using System.Threading; 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.Effects; using osu.Framework.Graphics.Pooling; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; @@ -21,13 +18,11 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { @@ -35,15 +30,9 @@ namespace osu.Game.Screens.SelectV2 { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.6f; - private const float difficulty_icon_container_width = 30; - private const float corner_radius = 10; - // todo: this should be replaced with information from CarouselItem about how deep is BeatmapPanel in the carousel // (i.e. whether it's under a beatmap set that's under a group, or just under a top-level beatmap set). - private const float set_x_offset = 20f; // constant X offset for beatmap set/standalone panels specifically. - - private const float preselected_x_offset = 25f; - private const float selected_x_offset = 50f; + private const float standalone_x_offset = 20f; // constant X offset for beatmap set/standalone panels specifically. private const float duration = 500; @@ -71,12 +60,8 @@ namespace osu.Game.Screens.SelectV2 private IBindable? starDifficultyBindable; private CancellationTokenSource? starDifficultyCancellationSource; - private Container panel = null!; - private Box backgroundBorder = null!; + private CarouselPanelPiece panel = null!; private BeatmapSetPanelBackground background = null!; - private Container backgroundContainer = null!; - private FillFlowContainer mainFlowContainer = null!; - private Box hoverLayer = null!; private OsuSpriteText titleText = null!; private OsuSpriteText artistText = null!; @@ -91,6 +76,16 @@ namespace osu.Game.Screens.SelectV2 private OsuSpriteText difficultyName = null!; private OsuSpriteText difficultyAuthor = null!; + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) + { + var inputRectangle = panel.TopLevelContent.DrawRectangle; + + // Cover a gap introduced by the spacing between a BeatmapSetPanel and a BeatmapPanel either above it or below it. + inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING / 2f }); + + return inputRectangle.Contains(panel.TopLevelContent.ToLocalSpace(screenSpacePos)); + } + [BackgroundDependencyLoader] private void load() { @@ -100,167 +95,109 @@ namespace osu.Game.Screens.SelectV2 Width = 1f; Height = HEIGHT; - InternalChild = panel = new Container + InternalChild = panel = new CarouselPanelPiece(standalone_x_offset) { - Masking = true, - CornerRadius = corner_radius, - RelativeSizeAxes = Axes.Both, - X = corner_radius, - EdgeEffect = new EdgeEffectParameters + Icon = difficultyIcon = new ConstrainedIconContainer { - Type = EdgeEffectType.Shadow, - Radius = 10, + Size = new Vector2(20), + Margin = new MarginPadding { Horizontal = 5f }, + Colour = colourProvider.Background5, }, - Children = new Drawable[] + Background = background = new BeatmapSetPanelBackground { - new BufferedContainer + RelativeSizeAxes = Axes.Both, + }, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Top = 7.5f, Left = 15, Bottom = 5 }, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + titleText = new OsuSpriteText { - backgroundBorder = new Box + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22, italics: true), + Shadow = true, + }, + artistText = new OsuSpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 17, italics: true), + Shadow = true, + }, + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 5f }, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Y, - Alpha = 0, - EdgeSmoothness = new Vector2(2, 0), - }, - backgroundContainer = new Container - { - Masking = true, - CornerRadius = corner_radius, - RelativeSizeAxes = Axes.X, - MaskingSmoothness = 2, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Children = new Drawable[] + updateButton = new UpdateBeatmapSetButtonV2 { - background = new BeatmapSetPanelBackground - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Right = 5f, Top = -2f }, }, - }, - } - }, - difficultyIcon = new ConstrainedIconContainer - { - X = difficulty_icon_container_width / 2, - Origin = Anchor.Centre, - Anchor = Anchor.CentreLeft, - Size = new Vector2(20), - }, - mainFlowContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Top = 7.5f, Left = 15, Bottom = 5 }, - Children = new Drawable[] - { - titleText = new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22, italics: true), - Shadow = true, - }, - artistText = new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 17, italics: true), - Shadow = true, - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 5f }, - Children = new Drawable[] + statusPill = new BeatmapSetOnlineStatusPill { - updateButton = new UpdateBeatmapSetButtonV2 + AutoSizeAxes = Axes.Both, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + TextSize = 11, + TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, + Margin = new MarginPadding { Right = 5f }, + }, + difficultyLine = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Right = 5f, Top = -2f }, - }, - statusPill = new BeatmapSetOnlineStatusPill - { - AutoSizeAxes = Axes.Both, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - TextSize = 11, - TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, - Margin = new MarginPadding { Right = 5f }, - }, - difficultyLine = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + difficultyStarRating = new StarRatingDisplay(default, StarRatingDisplaySize.Small) { - difficultyStarRating = new StarRatingDisplay(default, StarRatingDisplaySize.Small) - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Scale = new Vector2(8f / 9f), - Margin = new MarginPadding { Right = 5f }, - }, - difficultyRank = new TopLocalRankV2 - { - Scale = new Vector2(8f / 11), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Margin = new MarginPadding { Right = 5f }, - }, - difficultyKeyCountText = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 18, weight: FontWeight.SemiBold), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Alpha = 0, - Margin = new MarginPadding { Bottom = 2f }, - }, - difficultyName = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 18, weight: FontWeight.SemiBold), - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Margin = new MarginPadding { Right = 5f, Bottom = 2f }, - }, - difficultyAuthor = new OsuSpriteText - { - Colour = colourProvider.Content2, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold), - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Margin = new MarginPadding { Right = 5f, Bottom = 2f }, - } + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Scale = new Vector2(8f / 9f), + Margin = new MarginPadding { Right = 5f }, + }, + difficultyRank = new TopLocalRankV2 + { + Scale = new Vector2(8f / 11), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Margin = new MarginPadding { Right = 5f }, + }, + difficultyKeyCountText = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 18, weight: FontWeight.SemiBold), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Alpha = 0, + Margin = new MarginPadding { Bottom = 2f }, + }, + difficultyName = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 18, weight: FontWeight.SemiBold), + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Margin = new MarginPadding { Right = 5f, Bottom = 2f }, + }, + difficultyAuthor = new OsuSpriteText + { + Colour = colourProvider.Content2, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold), + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Margin = new MarginPadding { Right = 5f, Bottom = 2f }, } - }, + } }, - } + }, } - }, - hoverLayer = new Box - { - Colour = colours.Blue.Opacity(0.1f), - Alpha = 0, - Blending = BlendingParameters.Additive, - RelativeSizeAxes = Axes.Both, - }, - new HoverSounds(), - } + } + }, }; } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) - { - var inputRectangle = panel.DrawRectangle; - - // Cover a gap introduced by the spacing between a BeatmapSetPanel and a BeatmapPanel either above it or below it. - inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING / 2f }); - - return inputRectangle.Contains(panel.ToLocalSpace(screenSpacePos)); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -277,8 +214,8 @@ namespace osu.Game.Screens.SelectV2 updateKeyCount(); }, true); - Selected.BindValueChanged(_ => updateSelectedDisplay(), true); - KeyboardSelected.BindValueChanged(_ => updateKeyboardSelectedDisplay(), true); + Selected.BindValueChanged(s => panel.Active.Value = s.NewValue, true); + KeyboardSelected.BindValueChanged(k => panel.KeyboardActive.Value = k.NewValue, true); } protected override void PrepareForUse() @@ -308,7 +245,6 @@ namespace osu.Game.Screens.SelectV2 computeStarRating(); - updateSelectedDisplay(); FinishTransforms(true); this.FadeInFromZero(duration, Easing.OutQuint); @@ -324,55 +260,6 @@ namespace osu.Game.Screens.SelectV2 starDifficultyBindable = null; } - private void updateSelectedDisplay() - { - if (Item == null) - return; - - updatePanelPosition(); - - backgroundBorder.RelativeSizeAxes = Selected.Value ? Axes.Both : Axes.Y; - backgroundBorder.Width = Selected.Value ? 1 : difficulty_icon_container_width + corner_radius; - backgroundBorder.FadeTo(Selected.Value ? 1 : 0, duration, Easing.OutQuint); - difficultyIcon.FadeTo(Selected.Value ? 1 : 0, duration, Easing.OutQuint); - - backgroundContainer.ResizeHeightTo(Selected.Value ? HEIGHT - 4 : HEIGHT, duration, Easing.OutQuint); - backgroundContainer.MoveToX(Selected.Value ? difficulty_icon_container_width : 0, duration, Easing.OutQuint); - mainFlowContainer.MoveToX(Selected.Value ? difficulty_icon_container_width : 0, duration, Easing.OutQuint); - - panel.EdgeEffect = panel.EdgeEffect with { Radius = Selected.Value ? 15 : 10 }; - updateEdgeEffectColour(); - } - - private void updateKeyboardSelectedDisplay() - { - updatePanelPosition(); - updateHover(); - } - - private void updatePanelPosition() - { - float x = set_x_offset + selected_x_offset + preselected_x_offset; - - if (Selected.Value) - x -= selected_x_offset; - - if (KeyboardSelected.Value) - x -= preselected_x_offset; - - this.TransformTo(nameof(Padding), new MarginPadding { Left = x }, duration, Easing.OutQuint); - } - - private void updateHover() - { - bool hovered = IsHovered || KeyboardSelected.Value; - - if (hovered) - hoverLayer.FadeIn(100, Easing.OutQuint); - else - hoverLayer.FadeOut(1000, Easing.OutQuint); - } - private void computeStarRating() { starDifficultyCancellationSource?.Cancel(); @@ -384,23 +271,7 @@ namespace osu.Game.Screens.SelectV2 var beatmap = (BeatmapInfo)Item.Model; starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token); - starDifficultyBindable.BindValueChanged(d => - { - var value = d.NewValue ?? default; - - backgroundBorder.FadeColour(colours.ForStarDifficulty(value.Stars), duration, Easing.OutQuint); - difficultyIcon.FadeColour(value.Stars > 6.5f ? colours.Orange1 : colourProvider.Background5, duration, Easing.OutQuint); - difficultyStarRating.Current.Value = value; - - updateEdgeEffectColour(); - }, true); - } - - private void updateEdgeEffectColour() - { - panel.FadeEdgeEffectTo(Selected.Value - ? colours.ForStarDifficulty(starDifficultyBindable?.Value?.Stars ?? 0f).Opacity(0.5f) - : Color4.Black.Opacity(0.4f), duration, Easing.OutQuint); + starDifficultyBindable.BindValueChanged(_ => updateDisplay(), true); } private void updateKeyCount() @@ -424,16 +295,13 @@ namespace osu.Game.Screens.SelectV2 difficultyKeyCountText.Alpha = 0; } - protected override bool OnHover(HoverEvent e) + private void updateDisplay() { - updateHover(); - return true; - } + var starDifficulty = starDifficultyBindable?.Value ?? default; - protected override void OnHoverLost(HoverLostEvent e) - { - updateHover(); - base.OnHoverLost(e); + panel.AccentColour = colours.ForStarDifficulty(starDifficulty.Stars); + difficultyIcon.FadeColour(starDifficulty.Stars > 6.5f ? colours.Orange1 : colourProvider.Background5, duration, Easing.OutQuint); + difficultyStarRating.Current.Value = starDifficulty; } protected override bool OnClick(ClickEvent e) diff --git a/osu.Game/Screens/SelectV2/CarouselPanelPiece.cs b/osu.Game/Screens/SelectV2/CarouselPanelPiece.cs new file mode 100644 index 0000000000..a7f2b3a163 --- /dev/null +++ b/osu.Game/Screens/SelectV2/CarouselPanelPiece.cs @@ -0,0 +1,240 @@ +// 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.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.SelectV2 +{ + public partial class CarouselPanelPiece : Container + { + private const float corner_radius = 10; + + private const float left_edge_x_offset = 20f; + private const float keyboard_active_x_offset = 25f; + private const float active_x_offset = 50f; + + private const float duration = 500; + + private readonly float panelXOffset; + + private readonly Box backgroundBorder; + private readonly Box backgroundGradient; + private readonly Box backgroundAccentGradient; + private readonly Container backgroundLayer; + private readonly Container backgroundLayerHorizontalPadding; + private readonly Container backgroundContainer; + private readonly Container iconContainer; + private readonly Box activationFlash; + private readonly Box hoverLayer; + + public Container TopLevelContent { get; } + + protected override Container Content { get; } + + public Drawable Background + { + set => backgroundContainer.Child = value; + } + + public Drawable Icon + { + set => iconContainer.Child = value; + } + + private Color4? accentColour; + + public Color4? AccentColour + { + get => accentColour; + set + { + accentColour = value; + updateDisplay(); + } + } + + public readonly BindableBool Active = new BindableBool(); + public readonly BindableBool KeyboardActive = new BindableBool(); + + public CarouselPanelPiece(float panelXOffset) + { + this.panelXOffset = panelXOffset; + + RelativeSizeAxes = Axes.Both; + + InternalChild = TopLevelContent = new Container + { + Masking = true, + CornerRadius = corner_radius, + RelativeSizeAxes = Axes.Both, + X = corner_radius, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(1f), + Radius = 10, + }, + Children = new Drawable[] + { + new BufferedContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + backgroundBorder = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + backgroundLayerHorizontalPadding = new Container + { + RelativeSizeAxes = Axes.Both, + Child = backgroundLayer = new Container + { + RelativeSizeAxes = Axes.Both, + Child = new Container + { + Masking = true, + CornerRadius = corner_radius, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + backgroundGradient = new Box + { + RelativeSizeAxes = Axes.Both, + }, + backgroundAccentGradient = new Box + { + RelativeSizeAxes = Axes.Both, + }, + backgroundContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + } + }, + }, + } + }, + }, + iconContainer = new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + }, + Content = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = panelXOffset + corner_radius }, + }, + hoverLayer = new Box + { + Alpha = 0, + Blending = BlendingParameters.Additive, + RelativeSizeAxes = Axes.Both, + }, + activationFlash = new Box + { + Colour = Color4.White.Opacity(0.4f), + Blending = BlendingParameters.Additive, + Alpha = 0f, + RelativeSizeAxes = Axes.Both, + }, + new HoverSounds(), + } + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider, OsuColour colours) + { + hoverLayer.Colour = colours.Blue.Opacity(0.1f); + backgroundGradient.Colour = ColourInfo.GradientHorizontal(colourProvider.Background3, colourProvider.Background4); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Active.BindValueChanged(_ => updateDisplay()); + KeyboardActive.BindValueChanged(_ => updateDisplay(), true); + } + + public void Flash() + { + activationFlash.FadeOutFromOne(500, Easing.OutQuint); + } + + private void updateDisplay() + { + backgroundLayer.TransformTo(nameof(Padding), backgroundLayer.Padding with { Vertical = Active.Value ? 2f : 0f }, duration, Easing.OutQuint); + + var backgroundColour = accentColour ?? Color4.White; + var edgeEffectColour = accentColour ?? Color4Extensions.FromHex(@"4EBFFF"); + + backgroundAccentGradient.FadeColour(ColourInfo.GradientHorizontal(backgroundColour.Opacity(0.25f), backgroundColour.Opacity(0f)), duration, Easing.OutQuint); + backgroundBorder.FadeColour(backgroundColour, duration, Easing.OutQuint); + + TopLevelContent.FadeEdgeEffectTo(Active.Value ? edgeEffectColour.Opacity(0.5f) : Color4.Black.Opacity(0.4f), duration, Easing.OutQuint); + + updateXOffset(); + updateHover(); + } + + private void updateXOffset() + { + float x = panelXOffset + active_x_offset + keyboard_active_x_offset + left_edge_x_offset; + + if (Active.Value) + x -= active_x_offset; + + if (KeyboardActive.Value) + x -= keyboard_active_x_offset; + + this.TransformTo(nameof(Padding), new MarginPadding { Left = x }, duration, Easing.OutQuint); + } + + private void updateHover() + { + bool hovered = IsHovered || KeyboardActive.Value; + + if (hovered) + hoverLayer.FadeIn(100, Easing.OutQuint); + else + hoverLayer.FadeOut(1000, Easing.OutQuint); + } + + protected override bool OnHover(HoverEvent e) + { + updateDisplay(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateDisplay(); + base.OnHoverLost(e); + } + + protected override void Update() + { + base.Update(); + Content.Padding = Content.Padding with { Left = iconContainer.DrawWidth }; + backgroundLayerHorizontalPadding.Padding = new MarginPadding { Left = iconContainer.DrawWidth }; + } + } +} diff --git a/osu.Game/Screens/SelectV2/GroupPanel.cs b/osu.Game/Screens/SelectV2/GroupPanel.cs index b5fa338f82..12c4df830c 100644 --- a/osu.Game/Screens/SelectV2/GroupPanel.cs +++ b/osu.Game/Screens/SelectV2/GroupPanel.cs @@ -10,10 +10,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; @@ -24,137 +24,83 @@ namespace osu.Game.Screens.SelectV2 { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT; - private const float corner_radius = 10; - - private const float glow_offset = 10f; // extra space for any edge effect to not be cutoff by the right edge of the carousel. - private const float preselected_x_offset = 25f; - private const float selected_x_offset = 50f; - private const float duration = 500; [Resolved] private BeatmapCarousel? carousel { get; set; } - private Container panel = null!; - private Box activationFlash = null!; + private CarouselPanelPiece panel = null!; + private Drawable chevronIcon = null!; private OsuSpriteText titleText = null!; - private Box hoverLayer = null!; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) { - var inputRectangle = panel.DrawRectangle; + var inputRectangle = panel.TopLevelContent.DrawRectangle; - // Cover a gap introduced by the spacing between a GroupPanel and a BeatmapPanel either below/above it. + // Cover a gap introduced by the spacing between a GroupPanel and other panel types either below/above it. inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING / 2f }); - return inputRectangle.Contains(panel.ToLocalSpace(screenSpacePos)); + return inputRectangle.Contains(panel.TopLevelContent.ToLocalSpace(screenSpacePos)); } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, OsuColour colours) + private void load(OverlayColourProvider colourProvider) { Anchor = Anchor.TopRight; Origin = Anchor.TopRight; RelativeSizeAxes = Axes.X; Height = HEIGHT; - InternalChild = panel = new Container + InternalChild = panel = new CarouselPanelPiece(0) { - RelativeSizeAxes = Axes.Both, - CornerRadius = corner_radius, - Masking = true, - X = corner_radius, + Icon = chevronIcon = new SpriteIcon + { + AlwaysPresent = true, + Icon = FontAwesome.Solid.ChevronDown, + Size = new Vector2(12), + Margin = new MarginPadding { Horizontal = 5f }, + X = 2f, + Colour = colourProvider.Background3, + }, + Background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Dark1, + }, + AccentColour = colourProvider.Highlight1, Children = new Drawable[] { - new Container + titleText = new OsuSpriteText { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 10f }, - Child = new Container + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + X = 10f, + }, + new CircularContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Size = new Vector2(50f, 14f), + Margin = new MarginPadding { Right = 20f }, + Masking = true, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - CornerRadius = corner_radius, - Masking = true, - Children = new Drawable[] + new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background6, - }, - } - } - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background3, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 10f }, - Child = new Container - { - RelativeSizeAxes = Axes.Both, - CornerRadius = corner_radius, - Masking = true, - Children = new Drawable[] + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.7f), + }, + new OsuSpriteText { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background5, - }, - titleText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - X = 10f, - }, - new CircularContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Size = new Vector2(50f, 14f), - Margin = new MarginPadding { Right = 30f }, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.7f), - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), - // TODO: requires Carousel/CarouselItem-side implementation - Text = "43", - UseFullGlyphHeight = false, - } - }, - }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), + // TODO: requires Carousel/CarouselItem-side implementation + Text = "43", + UseFullGlyphHeight = false, } - } - }, - activationFlash = new Box - { - Colour = Color4.White, - Blending = BlendingParameters.Additive, - Alpha = 0, - RelativeSizeAxes = Axes.Both, - }, - hoverLayer = new Box - { - Colour = colours.Blue.Opacity(0.1f), - Alpha = 0, - Blending = BlendingParameters.Additive, - RelativeSizeAxes = Axes.Both, - }, - new HoverSounds(), + }, + } } }; } @@ -163,17 +109,17 @@ namespace osu.Game.Screens.SelectV2 { base.LoadComplete(); - Expanded.BindValueChanged(_ => updateExpandedDisplay(), true); - KeyboardSelected.BindValueChanged(_ => updateKeyboardSelectedDisplay(), true); + Expanded.BindValueChanged(_ => onExpanded(), true); + KeyboardSelected.BindValueChanged(k => panel.KeyboardActive.Value = k.NewValue, true); } - private void updateExpandedDisplay() + private void onExpanded() { - updatePanelPosition(); + panel.Active.Value = Expanded.Value; + panel.Flash(); - // todo: figma shares no extra visual feedback on this. - - activationFlash.FadeTo(0.2f).FadeTo(0f, 500, Easing.OutQuint); + chevronIcon.ResizeWidthTo(Expanded.Value ? 12f : 0f, duration, Easing.OutQuint); + chevronIcon.FadeTo(Expanded.Value ? 1f : 0f, duration, Easing.OutQuint); } protected override void PrepareForUse() @@ -186,6 +132,7 @@ namespace osu.Game.Screens.SelectV2 titleText.Text = group.Title; + FinishTransforms(true); this.FadeInFromZero(500, Easing.OutQuint); } @@ -197,47 +144,6 @@ namespace osu.Game.Screens.SelectV2 return true; } - private void updateKeyboardSelectedDisplay() - { - updatePanelPosition(); - updateHover(); - } - - private void updatePanelPosition() - { - float x = glow_offset + selected_x_offset + preselected_x_offset; - - if (Expanded.Value) - x -= selected_x_offset; - - if (KeyboardSelected.Value) - x -= preselected_x_offset; - - this.TransformTo(nameof(Padding), new MarginPadding { Left = x }, duration, Easing.OutQuint); - } - - private void updateHover() - { - bool hovered = IsHovered || KeyboardSelected.Value; - - if (hovered) - hoverLayer.FadeIn(100, Easing.OutQuint); - else - hoverLayer.FadeOut(1000, Easing.OutQuint); - } - - protected override bool OnHover(HoverEvent e) - { - updateHover(); - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateHover(); - base.OnHoverLost(e); - } - #region ICarouselPanel public CarouselItem? Item { get; set; } @@ -249,7 +155,7 @@ namespace osu.Game.Screens.SelectV2 public void Activated() { - // sets should never be activated. + // groups should never be activated. throw new InvalidOperationException(); } diff --git a/osu.Game/Screens/SelectV2/StarsGroupPanel.cs b/osu.Game/Screens/SelectV2/StarsGroupPanel.cs index 76e3da2500..8e179ec5c1 100644 --- a/osu.Game/Screens/SelectV2/StarsGroupPanel.cs +++ b/osu.Game/Screens/SelectV2/StarsGroupPanel.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -26,10 +27,6 @@ namespace osu.Game.Screens.SelectV2 { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT; - private const float glow_offset = 10f; // extra space for the edge effect to not be cutoff by the right edge of the carousel. - private const float preselected_x_offset = 25f; - private const float expanded_x_offset = 50f; - private const float duration = 500; [Resolved] @@ -41,20 +38,20 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - private Box activationFlash = null!; - private Box outerLayer = null!; + private CarouselPanelPiece panel = null!; + private Drawable chevronIcon = null!; + private Box contentBackground = null!; private StarRatingDisplay starRatingDisplay = null!; private StarCounter starCounter = null!; - private Box hoverLayer = null!; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) { - var inputRectangle = DrawRectangle; + var inputRectangle = panel.TopLevelContent.DrawRectangle; - // Cover a gap introduced by the spacing between a GroupPanel and a BeatmapPanel either below/above it. + // Cover a gap introduced by the spacing between a GroupPanel and other panel types either below/above it. inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING / 2f }); - return inputRectangle.Contains(ToLocalSpace(screenSpacePos)); + return inputRectangle.Contains(panel.TopLevelContent.ToLocalSpace(screenSpacePos)); } [BackgroundDependencyLoader] @@ -65,118 +62,71 @@ namespace osu.Game.Screens.SelectV2 RelativeSizeAxes = Axes.X; Height = HEIGHT; - InternalChild = new Container + InternalChild = panel = new CarouselPanelPiece(0) { - RelativeSizeAxes = Axes.Both, - CornerRadius = 10f, - Masking = true, + Icon = chevronIcon = new SpriteIcon + { + AlwaysPresent = true, + Icon = FontAwesome.Solid.ChevronDown, + Size = new Vector2(12), + Margin = new MarginPadding { Horizontal = 5f }, + X = 2f, + }, + Background = contentBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Dark1, + }, + AccentColour = colourProvider.Highlight1, Children = new Drawable[] { - new Container + new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 10f }, - Child = new Container + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10f, 0f), + Margin = new MarginPadding { Left = 10f }, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - CornerRadius = 10f, - Masking = true, - Children = new Drawable[] + starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small) { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background6, - }, - } + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + starCounter = new StarCounter + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(8f / 20f), + }, } }, - outerLayer = new Box + new CircularContainer { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background3, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 10f }, - Child = new Container + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Size = new Vector2(50f, 14f), + Margin = new MarginPadding { Right = 20f }, + Masking = true, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - CornerRadius = 10f, - Masking = true, - Children = new Drawable[] + new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.2f), - }, - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10f, 0f), - Margin = new MarginPadding { Left = 10f }, - Children = new Drawable[] - { - starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - starCounter = new StarCounter - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(8f / 20f), - }, - } - }, - new CircularContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Size = new Vector2(50f, 14f), - Margin = new MarginPadding { Right = 30f }, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.7f), - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), - // TODO: requires Carousel/CarouselItem-side implementation - Text = "43", - UseFullGlyphHeight = false, - } - }, - }, + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.7f), + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), + // TODO: requires Carousel/CarouselItem-side implementation + Text = "43", + UseFullGlyphHeight = false, } - } - }, - activationFlash = new Box - { - Colour = Color4.White, - Blending = BlendingParameters.Additive, - Alpha = 0, - RelativeSizeAxes = Axes.Both, - }, - hoverLayer = new Box - { - Colour = colours.Blue.Opacity(0.1f), - Alpha = 0, - Blending = BlendingParameters.Additive, - RelativeSizeAxes = Axes.Both, - }, - new HoverSounds(), + }, + } } }; } @@ -185,17 +135,17 @@ namespace osu.Game.Screens.SelectV2 { base.LoadComplete(); - Expanded.BindValueChanged(_ => updateExpandedDisplay(), true); - KeyboardSelected.BindValueChanged(_ => updateKeyboardSelectedDisplay(), true); + Expanded.BindValueChanged(_ => onExpanded(), true); + KeyboardSelected.BindValueChanged(k => panel.KeyboardActive.Value = k.NewValue, true); } - private void updateExpandedDisplay() + private void onExpanded() { - updatePanelPosition(); + panel.Active.Value = Expanded.Value; + panel.Flash(); - // todo: figma shares no extra visual feedback on this. - - activationFlash.FadeTo(0.2f).FadeTo(0f, 500, Easing.OutQuint); + chevronIcon.ResizeWidthTo(Expanded.Value ? 12f : 0f, duration, Easing.OutQuint); + chevronIcon.FadeTo(Expanded.Value ? 1f : 0f, duration, Easing.OutQuint); } protected override void PrepareForUse() @@ -209,12 +159,15 @@ namespace osu.Game.Screens.SelectV2 Color4 colour = group.StarNumber >= 9 ? OsuColour.Gray(0.2f) : colours.ForStarDifficulty(group.StarNumber); Color4 contentColour = group.StarNumber >= 7 ? colours.Orange1 : colourProvider.Background5; - outerLayer.Colour = colour; - starCounter.Colour = contentColour; + panel.AccentColour = colour; + contentBackground.Colour = colour.Darken(0.3f); starRatingDisplay.Current.Value = new StarDifficulty(group.StarNumber, 0); starCounter.Current = group.StarNumber; + chevronIcon.Colour = contentColour; + starCounter.Colour = contentColour; + this.FadeInFromZero(500, Easing.OutQuint); } @@ -226,47 +179,6 @@ namespace osu.Game.Screens.SelectV2 return true; } - private void updateKeyboardSelectedDisplay() - { - updatePanelPosition(); - updateHover(); - } - - private void updatePanelPosition() - { - float x = glow_offset + expanded_x_offset + preselected_x_offset; - - if (Expanded.Value) - x -= expanded_x_offset; - - if (KeyboardSelected.Value) - x -= preselected_x_offset; - - this.TransformTo(nameof(Padding), new MarginPadding { Left = x }, duration, Easing.OutQuint); - } - - private void updateHover() - { - bool hovered = IsHovered || KeyboardSelected.Value; - - if (hovered) - hoverLayer.FadeIn(100, Easing.OutQuint); - else - hoverLayer.FadeOut(1000, Easing.OutQuint); - } - - protected override bool OnHover(HoverEvent e) - { - updateHover(); - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateHover(); - base.OnHoverLost(e); - } - #region ICarouselPanel public CarouselItem? Item { get; set; }