1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 07:27:25 +08:00
osu-lazer/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs

381 lines
16 KiB
C#
Raw Normal View History

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.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.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online;
using osu.Game.Online.Chat;
2020-12-25 12:38:11 +08:00
using osu.Game.Online.Rooms;
using osu.Game.Overlays.BeatmapListing.Panels;
2021-01-13 16:33:00 +08:00
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play.HUD;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay
{
public class DrawableRoomPlaylistItem : OsuRearrangeableListItem<PlaylistItem>
{
public Action<PlaylistItem> RequestDeletion;
2020-02-14 15:55:05 +08:00
public readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
private Container maskingContainer;
private Container difficultyIconContainer;
private LinkFlowContainer beatmapText;
private LinkFlowContainer authorText;
2021-01-17 21:40:24 +08:00
private ExplicitContentBeatmapPill explicitContentPill;
private ModDisplay modDisplay;
private readonly Bindable<BeatmapInfo> beatmap = new Bindable<BeatmapInfo>();
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
private readonly BindableList<Mod> requiredMods = new BindableList<Mod>();
public readonly PlaylistItem Item;
private readonly bool allowEdit;
private readonly bool allowSelection;
protected override bool ShouldBeConsideredForInput(Drawable child) => allowEdit || !allowSelection || SelectedItem.Value == Model;
public DrawableRoomPlaylistItem(PlaylistItem item, bool allowEdit, bool allowSelection)
: base(item)
{
Item = item;
// TODO: edit support should be moved out into a derived class
this.allowEdit = allowEdit;
this.allowSelection = allowSelection;
beatmap.BindTo(item.Beatmap);
ruleset.BindTo(item.Ruleset);
requiredMods.BindTo(item.RequiredMods);
2020-09-08 15:36:36 +08:00
ShowDragHandle.Value = allowEdit;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (!allowEdit)
HandleColour = HandleColour.Opacity(0);
maskingContainer.BorderColour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
2020-02-14 20:17:07 +08:00
SelectedItem.BindValueChanged(selected => maskingContainer.BorderThickness = selected.NewValue == Model ? 5 : 0, true);
2020-02-14 15:55:05 +08:00
beatmap.BindValueChanged(_ => scheduleRefresh());
ruleset.BindValueChanged(_ => scheduleRefresh());
requiredMods.CollectionChanged += (_, __) => scheduleRefresh();
refresh();
}
2020-02-14 15:55:05 +08:00
private ScheduledDelegate scheduledRefresh;
private void scheduleRefresh()
{
scheduledRefresh?.Cancel();
scheduledRefresh = Schedule(refresh);
}
private void refresh()
{
difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) };
2020-02-14 15:55:05 +08:00
beatmapText.Clear();
beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text =>
2021-07-22 12:18:37 +08:00
{
text.Truncate = true;
text.RelativeSizeAxes = Axes.X;
});
2020-02-14 15:55:05 +08:00
authorText.Clear();
if (Item.Beatmap?.Value?.Metadata?.Author != null)
2020-02-14 15:55:05 +08:00
{
authorText.AddText("mapped by ");
authorText.AddUserLink(Item.Beatmap.Value?.Metadata.Author);
2020-02-14 15:55:05 +08:00
}
2021-01-13 16:33:00 +08:00
bool hasExplicitContent = Item.Beatmap.Value.BeatmapSet.OnlineInfo?.HasExplicitContent == true;
2021-01-17 21:40:24 +08:00
explicitContentPill.Alpha = hasExplicitContent ? 1 : 0;
2021-01-13 16:33:00 +08:00
2020-02-14 15:55:05 +08:00
modDisplay.Current.Value = requiredMods.ToArray();
}
2021-02-02 16:09:59 +08:00
protected override Drawable CreateContent()
{
2021-02-02 16:09:59 +08:00
Action<SpriteText> fontParameters = s => s.Font = OsuFont.Default.With(weight: FontWeight.SemiBold);
return maskingContainer = new Container
{
2021-02-02 16:09:59 +08:00
RelativeSizeAxes = Axes.X,
Height = 50,
Masking = true,
CornerRadius = 10,
Children = new Drawable[]
{
2021-02-02 16:09:59 +08:00
new Box // A transparent box that forces the border to be drawn if the panel background is opaque
{
2021-02-02 16:09:59 +08:00
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
},
new PanelBackground
{
RelativeSizeAxes = Axes.Both,
Beatmap = { BindTarget = beatmap }
},
2021-07-22 12:18:37 +08:00
new GridContainer
2021-02-02 16:09:59 +08:00
{
RelativeSizeAxes = Axes.Both,
2021-07-22 12:18:37 +08:00
ColumnDimensions = new[]
{
2021-07-22 12:18:37 +08:00
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
{
new Drawable[]
2021-02-02 16:09:59 +08:00
{
2021-07-22 12:18:37 +08:00
difficultyIconContainer = new Container
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Left = 8, Right = 8, },
},
new FillFlowContainer
{
2021-07-22 12:18:37 +08:00
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
2021-07-22 12:18:37 +08:00
beatmapText = new LinkFlowContainer(fontParameters)
2021-01-13 16:33:00 +08:00
{
2021-07-22 12:18:37 +08:00
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0),
Children = new Drawable[]
2021-01-13 16:33:00 +08:00
{
2021-07-22 12:18:37 +08:00
new FillFlowContainer
2021-01-13 16:33:00 +08:00
{
2021-07-22 12:18:37 +08:00
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0),
Children = new Drawable[]
2021-02-02 16:09:59 +08:00
{
2021-07-22 12:18:37 +08:00
authorText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both },
explicitContentPill = new ExplicitContentBeatmapPill
{
Alpha = 0f,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Top = 3f },
}
},
2021-02-02 16:09:59 +08:00
},
2021-07-22 12:18:37 +08:00
new Container
2021-02-02 16:09:59 +08:00
{
2021-07-22 12:18:37 +08:00
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Child = modDisplay = new ModDisplay
{
Scale = new Vector2(0.4f),
ExpansionMode = ExpansionMode.AlwaysExpanded
}
2021-02-02 16:09:59 +08:00
}
2021-01-13 16:33:00 +08:00
}
}
}
2021-07-22 12:18:37 +08:00
},
new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = 8, Right = 10, },
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5),
ChildrenEnumerable = CreateButtons().Select(button => button.With(b =>
{
b.Anchor = Anchor.Centre;
b.Origin = Anchor.Centre;
}))
}
}
}
2021-02-02 16:09:59 +08:00
},
}
2021-02-02 16:09:59 +08:00
};
}
protected virtual IEnumerable<Drawable> CreateButtons() =>
new Drawable[]
{
new PlaylistDownloadButton(Item)
{
Size = new Vector2(50, 30)
},
new PlaylistRemoveButton
{
Size = new Vector2(30, 30),
Alpha = allowEdit ? 1 : 0,
Action = () => RequestDeletion?.Invoke(Model),
},
};
public class PlaylistRemoveButton : GrayButton
{
public PlaylistRemoveButton()
: base(FontAwesome.Solid.MinusSquare)
{
TooltipText = "Remove from playlist";
}
[BackgroundDependencyLoader]
private void load()
{
Icon.Scale = new Vector2(0.8f);
}
}
protected override bool OnClick(ClickEvent e)
{
if (allowSelection)
2020-02-14 15:55:05 +08:00
SelectedItem.Value = Model;
return true;
}
private class PlaylistDownloadButton : BeatmapPanelDownloadButton
{
private readonly PlaylistItem playlistItem;
[Resolved]
private BeatmapManager beatmapManager { get; set; }
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
public PlaylistDownloadButton(PlaylistItem playlistItem)
: base(playlistItem.Beatmap.Value.BeatmapSet)
{
this.playlistItem = playlistItem;
Alpha = 0;
}
protected override void LoadComplete()
{
base.LoadComplete();
DownloadTracker.State.BindValueChanged(stateChanged, true);
FinishTransforms(true);
}
private void stateChanged(ValueChangedEvent<DownloadState> state)
{
switch (state.NewValue)
{
case DownloadState.LocallyAvailable:
// Perform a local query of the beatmap by beatmap checksum, and reset the state if not matching.
if (beatmapManager.QueryBeatmap(b => b.MD5Hash == playlistItem.Beatmap.Value.MD5Hash) == null)
State.Value = DownloadState.NotDownloaded;
else
this.FadeTo(0, 500);
break;
default:
this.FadeTo(1, 500);
break;
}
}
}
// For now, this is the same implementation as in PanelBackground, but supports a beatmap info rather than a working beatmap
private class PanelBackground : Container // todo: should be a buffered container (https://github.com/ppy/osu-framework/issues/3222)
{
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
public PanelBackground()
{
InternalChildren = new Drawable[]
{
new UpdateableBeatmapBackgroundSprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
Beatmap = { BindTarget = Beatmap }
},
new FillFlowContainer
{
Depth = -1,
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
// This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle
Shear = new Vector2(0.8f, 0),
Alpha = 0.5f,
Children = new[]
{
// The left half with no gradient applied
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Width = 0.4f,
},
2021-02-03 05:05:25 +08:00
// Piecewise-linear gradient with 2 segments to make it appear smoother
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.7f)),
Width = 0.4f,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.7f), new Color4(0, 0, 0, 0.4f)),
Width = 0.4f,
},
}
}
};
}
}
}
}