1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 07:22:55 +08:00

Merge pull request #4092 from peppy/download-tracking-component

Add a download tracking component
This commit is contained in:
Dean Herbert 2019-01-31 22:24:32 +09:00 committed by GitHub
commit ccc6968697
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 464 additions and 364 deletions

View File

@ -1,112 +0,0 @@
// 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
namespace osu.Game.Beatmaps.Drawables
{
/// <summary>
/// A component to allow downloading of a beatmap set. Automatically handles state syncing between other instances.
/// </summary>
public class BeatmapSetDownloader : Component
{
private readonly BeatmapSetInfo set;
private readonly bool noVideo;
private BeatmapManager beatmaps;
/// <summary>
/// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
/// </summary>
public readonly Bindable<DownloadStatus> DownloadState = new Bindable<DownloadStatus>();
public BeatmapSetDownloader(BeatmapSetInfo set, bool noVideo = false)
{
this.set = set;
this.noVideo = noVideo;
}
[BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps)
{
this.beatmaps = beatmaps;
beatmaps.ItemAdded += setAdded;
beatmaps.ItemRemoved += setRemoved;
beatmaps.BeatmapDownloadBegan += downloadBegan;
beatmaps.BeatmapDownloadFailed += downloadFailed;
// initial value
if (set.OnlineBeatmapSetID != null && beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any())
DownloadState.Value = DownloadStatus.Downloaded;
else if (beatmaps.GetExistingDownload(set) != null)
DownloadState.Value = DownloadStatus.Downloading;
else
DownloadState.Value = DownloadStatus.NotDownloaded;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (beatmaps != null)
{
beatmaps.ItemAdded -= setAdded;
beatmaps.ItemRemoved -= setRemoved;
beatmaps.BeatmapDownloadBegan -= downloadBegan;
beatmaps.BeatmapDownloadFailed -= downloadFailed;
}
}
/// <summary>
/// Begin downloading the associated beatmap set.
/// </summary>
/// <returns>True if downloading began. False if an existing download is active or completed.</returns>
public void Download()
{
if (DownloadState.Value > DownloadStatus.NotDownloaded)
return;
if (beatmaps.Download(set, noVideo))
{
// Only change state if download can happen
DownloadState.Value = DownloadStatus.Downloading;
}
}
private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => Schedule(() =>
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.Downloaded;
});
private void setRemoved(BeatmapSetInfo s) => Schedule(() =>
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.NotDownloaded;
});
private void downloadBegan(DownloadBeatmapSetRequest d)
{
if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.Downloading;
}
private void downloadFailed(DownloadBeatmapSetRequest d)
{
if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.NotDownloaded;
}
public enum DownloadStatus
{
NotDownloaded,
Downloading,
Downloaded,
}
}
}

View File

@ -355,11 +355,7 @@ namespace osu.Game.Online.API
State = APIState.Offline; State = APIState.Offline;
} }
private static User createGuestUser() => new User private static User createGuestUser() => new GuestUser();
{
Username = @"Guest",
Id = 1,
};
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
@ -370,6 +366,15 @@ namespace osu.Game.Online.API
} }
} }
internal class GuestUser : User
{
public GuestUser()
{
Username = @"Guest";
Id = 1;
}
}
public enum APIState public enum APIState
{ {
/// <summary> /// <summary>

View File

@ -17,7 +17,7 @@ namespace osu.Game.Online.API
return request; return request;
} }
private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total)); private void request_Progress(long current, long total) => API.Schedule(() => Progressed?.Invoke(current, total));
protected APIDownloadRequest() protected APIDownloadRequest()
{ {
@ -29,7 +29,7 @@ namespace osu.Game.Online.API
Success?.Invoke(filename); Success?.Invoke(filename);
} }
public event APIProgressHandler Progress; public event APIProgressHandler Progressed;
public new event APISuccessHandler<string> Success; public new event APISuccessHandler<string> Success;
} }

View File

@ -10,7 +10,9 @@ namespace osu.Game.Online.API.Requests
{ {
public readonly BeatmapSetInfo BeatmapSet; public readonly BeatmapSetInfo BeatmapSet;
public Action<float> DownloadProgressed; public float Progress;
public event Action<float> DownloadProgressed;
private readonly bool noVideo; private readonly bool noVideo;
@ -19,7 +21,7 @@ namespace osu.Game.Online.API.Requests
this.noVideo = noVideo; this.noVideo = noVideo;
BeatmapSet = set; BeatmapSet = set;
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total); Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total);
} }
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}"; protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";

View File

@ -7,42 +7,138 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Overlays.Direct;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Buttons namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class DownloadButton : HeaderButton, IHasTooltip public class DownloadButton : DownloadTrackingComposite, IHasTooltip
{ {
public string TooltipText => "Download this beatmap"; private readonly bool noVideo;
public string TooltipText => button.Enabled ? "Download this beatmap" : "Login to download";
private readonly IBindable<User> localUser = new Bindable<User>(); private readonly IBindable<User> localUser = new Bindable<User>();
public DownloadButton(BeatmapSetInfo set, bool noVideo = false) private ShakeContainer shakeContainer;
{ private HeaderButton button;
Width = 120;
BeatmapSetDownloader downloader; public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
Add(new Container : base(beatmapSet)
{
this.noVideo = noVideo;
Width = 120;
RelativeSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load(APIAccess api, BeatmapManager beatmaps)
{
FillFlowContainer textSprites;
AddRangeInternal(new Drawable[]
{ {
Depth = -1, shakeContainer = new ShakeContainer
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
{ {
downloader = new BeatmapSetDownloader(set, noVideo), Depth = -1,
new FillFlowContainer RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 5,
Children = new Drawable[]
{ {
Anchor = Anchor.CentreLeft, button = new HeaderButton { RelativeSizeAxes = Axes.Both },
Origin = Anchor.CentreLeft, new Container
AutoSizeAxes = Axes.Both, {
Direction = FillDirection.Vertical, // cannot nest inside here due to the structure of button (putting things in its own content).
Children = new[] // requires framework fix.
Padding = new MarginPadding { Horizontal = 10 },
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
textSprites = new FillFlowContainer
{
Depth = -1,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
AutoSizeDuration = 500,
AutoSizeEasing = Easing.OutQuint,
Direction = FillDirection.Vertical,
},
new SpriteIcon
{
Depth = -1,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.fa_download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
}
},
new DownloadProgressBar(BeatmapSet)
{
Depth = -2,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
},
},
},
});
button.Action = () =>
{
if (State.Value != DownloadState.NotDownloaded)
{
shakeContainer.Shake();
return;
}
beatmaps.Download(BeatmapSet, noVideo);
};
localUser.BindTo(api.LocalUser);
localUser.BindValueChanged(userChanged, true);
button.Enabled.BindValueChanged(enabledChanged, true);
State.BindValueChanged(state =>
{
switch (state)
{
case DownloadState.Downloading:
textSprites.Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Downloading...",
TextSize = 13,
Font = @"Exo2.0-Bold",
},
};
break;
case DownloadState.Downloaded:
textSprites.Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Importing...",
TextSize = 13,
Font = @"Exo2.0-Bold",
},
};
break;
case DownloadState.LocallyAvailable:
this.FadeOut(200);
break;
case DownloadState.NotDownloaded:
textSprites.Children = new Drawable[]
{ {
new OsuSpriteText new OsuSpriteText
{ {
@ -52,57 +148,18 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = set.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty, Text = BeatmapSet.Value.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty,
TextSize = 11, TextSize = 11,
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
}, },
}, };
},
new SpriteIcon
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.fa_download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
},
});
Action = () =>
{
if (downloader.DownloadState.Value == BeatmapSetDownloader.DownloadStatus.Downloading)
{
Content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine);
return;
}
downloader.Download();
};
downloader.DownloadState.ValueChanged += state =>
{
switch (state)
{
case BeatmapSetDownloader.DownloadStatus.Downloaded:
this.FadeOut(200);
break;
case BeatmapSetDownloader.DownloadStatus.NotDownloaded:
this.FadeIn(200); this.FadeIn(200);
break; break;
} }
}; }, true);
} }
[BackgroundDependencyLoader] private void userChanged(User user) => button.Enabled.Value = !(user is GuestUser);
private void load(APIAccess api)
{
localUser.BindTo(api.LocalUser);
Enabled.BindValueChanged(enabledChanged, true);
}
private void enabledChanged(bool enabled) => this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); private void enabledChanged(bool enabled) => this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
} }

View File

@ -13,12 +13,14 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Overlays.Direct;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using DownloadButton = osu.Game.Overlays.BeatmapSet.Buttons.DownloadButton;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet
{ {
public class Header : Container public class Header : DownloadTrackingComposite
{ {
private const float transition_duration = 200; private const float transition_duration = 200;
private const float tabs_height = 50; private const float tabs_height = 50;
@ -28,76 +30,23 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Box tabsBg; private readonly Box tabsBg;
private readonly UpdateableBeatmapSetCover cover; private readonly UpdateableBeatmapSetCover cover;
private readonly OsuSpriteText title, artist; private readonly OsuSpriteText title, artist;
private readonly Container noVideoButtons;
private readonly FillFlowContainer videoButtons;
private readonly AuthorInfo author; private readonly AuthorInfo author;
private readonly Container downloadButtonsContainer; private readonly FillFlowContainer downloadButtonsContainer;
private readonly BeatmapSetOnlineStatusPill onlineStatusPill; private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
public Details Details; public Details Details;
public readonly BeatmapPicker Picker; public readonly BeatmapPicker Picker;
private BeatmapSetInfo beatmapSet;
private readonly FavouriteButton favouriteButton; private readonly FavouriteButton favouriteButton;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet;
updateDisplay();
}
}
private void updateDisplay()
{
title.Text = BeatmapSet?.Metadata.Title ?? string.Empty;
artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty;
onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
cover.BeatmapSet = BeatmapSet;
if (BeatmapSet != null)
{
downloadButtonsContainer.FadeIn(transition_duration);
favouriteButton.FadeIn(transition_duration);
if (BeatmapSet.OnlineInfo.HasVideo)
{
videoButtons.Children = new[]
{
new DownloadButton(BeatmapSet),
new DownloadButton(BeatmapSet, true),
};
videoButtons.FadeIn(transition_duration);
noVideoButtons.FadeOut(transition_duration);
}
else
{
noVideoButtons.Child = new DownloadButton(BeatmapSet);
noVideoButtons.FadeIn(transition_duration);
videoButtons.FadeOut(transition_duration);
}
}
else
{
downloadButtonsContainer.FadeOut(transition_duration);
favouriteButton.FadeOut(transition_duration);
}
}
public Header() public Header()
{ {
ExternalLinkButton externalLink; ExternalLinkButton externalLink;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 400; Height = 400;
Masking = true; Masking = true;
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
Colour = Color4.Black.Opacity(0.25f), Colour = Color4.Black.Opacity(0.25f),
@ -105,7 +54,8 @@ namespace osu.Game.Overlays.BeatmapSet
Radius = 3, Radius = 3,
Offset = new Vector2(0f, 1f), Offset = new Vector2(0f, 1f),
}; };
Children = new Drawable[]
InternalChildren = new Drawable[]
{ {
new Container new Container
{ {
@ -196,24 +146,11 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[] Children = new Drawable[]
{ {
favouriteButton = new FavouriteButton(), favouriteButton = new FavouriteButton(),
downloadButtonsContainer = new Container downloadButtonsContainer = new FillFlowContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
Children = new Drawable[] Spacing = new Vector2(buttons_spacing),
{
noVideoButtons = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
},
videoButtons = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Spacing = new Vector2(buttons_spacing),
Alpha = 0f,
},
},
}, },
}, },
}, },
@ -245,14 +182,64 @@ namespace osu.Game.Overlays.BeatmapSet
}; };
Picker.Beatmap.ValueChanged += b => Details.Beatmap = b; Picker.Beatmap.ValueChanged += b => Details.Beatmap = b;
Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}"; Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}";
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
tabsBg.Colour = colours.Gray3; tabsBg.Colour = colours.Gray3;
updateDisplay();
State.BindValueChanged(_ => updateDownloadButtons());
BeatmapSet.BindValueChanged(beatmapSet =>
{
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = beatmapSet;
title.Text = beatmapSet?.Metadata.Title ?? string.Empty;
artist.Text = beatmapSet?.Metadata.Artist ?? string.Empty;
onlineStatusPill.Status = beatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
cover.BeatmapSet = beatmapSet;
if (beatmapSet != null)
{
downloadButtonsContainer.FadeIn(transition_duration);
favouriteButton.FadeIn(transition_duration);
}
else
{
downloadButtonsContainer.FadeOut(transition_duration);
favouriteButton.FadeOut(transition_duration);
}
updateDownloadButtons();
}, true);
}
private void updateDownloadButtons()
{
if (BeatmapSet.Value == null) return;
switch (State.Value)
{
case DownloadState.LocallyAvailable:
// temporary for UX until new design is implemented.
downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet)
{
Width = 50,
RelativeSizeAxes = Axes.Y
};
break;
case DownloadState.Downloading:
case DownloadState.Downloaded:
// temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design.
downloadButtonsContainer.Child = new DownloadButton(BeatmapSet);
break;
default:
downloadButtonsContainer.Child = new DownloadButton(BeatmapSet);
if (BeatmapSet.Value.OnlineInfo.HasVideo)
downloadButtonsContainer.Add(new DownloadButton(BeatmapSet, true));
break;
}
} }
} }
} }

View File

@ -46,7 +46,7 @@ namespace osu.Game.Overlays
if (value == beatmapSet) if (value == beatmapSet)
return; return;
header.BeatmapSet = info.BeatmapSet = beatmapSet = value; header.BeatmapSet.Value = info.BeatmapSet = beatmapSet = value;
} }
} }
@ -140,7 +140,7 @@ namespace osu.Game.Overlays
req.Success += res => req.Success += res =>
{ {
BeatmapSet = res.ToBeatmapSet(rulesets); BeatmapSet = res.ToBeatmapSet(rulesets);
header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); header.Picker.Beatmap.Value = header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
}; };
api.Queue(req); api.Queue(req);
Show(); Show();

View File

@ -16,8 +16,6 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -31,8 +29,6 @@ namespace osu.Game.Overlays.Direct
private Container content; private Container content;
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private BeatmapSetOverlay beatmapSetOverlay; private BeatmapSetOverlay beatmapSetOverlay;
public PreviewTrack Preview => PlayButton.Preview; public PreviewTrack Preview => PlayButton.Preview;
@ -65,14 +61,10 @@ namespace osu.Game.Overlays.Direct
Colour = Color4.Black.Opacity(0.3f), Colour = Color4.Black.Opacity(0.3f),
}; };
private OsuColour colours;
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{ {
this.beatmaps = beatmaps;
this.beatmapSetOverlay = beatmapSetOverlay; this.beatmapSetOverlay = beatmapSetOverlay;
this.colours = colours;
AddInternal(content = new Container AddInternal(content = new Container
{ {
@ -82,33 +74,14 @@ namespace osu.Game.Overlays.Direct
Children = new[] Children = new[]
{ {
CreateBackground(), CreateBackground(),
progressBar = new ProgressBar new DownloadProgressBar(SetInfo)
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Height = 0,
Alpha = 0,
BackgroundColour = Color4.Black.Opacity(0.7f),
FillColour = colours.Blue,
Depth = -1, Depth = -1,
}, },
} }
}); });
var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
if (downloadRequest != null)
attachDownload(downloadRequest);
beatmaps.BeatmapDownloadBegan += attachDownload;
beatmaps.ItemAdded += setAdded;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapDownloadBegan -= attachDownload;
beatmaps.ItemAdded -= setAdded;
} }
protected override void Update() protected override void Update()
@ -149,37 +122,6 @@ namespace osu.Game.Overlays.Direct
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo); protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
private void attachDownload(DownloadBeatmapSetRequest request)
{
if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID)
return;
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
progressBar.Current.Value = 0;
request.Failure += e =>
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
};
request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress);
request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FillColour = colours.Yellow;
};
}
private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => Schedule(() =>
{
if (s.OnlineBeatmapSetID == SetInfo.OnlineBeatmapSetID)
progressBar.FadeOut(500);
});
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();

View File

@ -5,103 +5,114 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK; using osuTK;
namespace osu.Game.Overlays.Direct namespace osu.Game.Overlays.Direct
{ {
public class DownloadButton : OsuAnimatedButton public class DownloadButton : DownloadTrackingComposite
{ {
private readonly BeatmapSetInfo beatmapSet; private readonly bool noVideo;
private readonly SpriteIcon icon; private readonly SpriteIcon icon;
private readonly SpriteIcon checkmark; private readonly SpriteIcon checkmark;
private readonly BeatmapSetDownloader downloader;
private readonly Box background; private readonly Box background;
private OsuColour colours; private OsuColour colours;
public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) private readonly ShakeContainer shakeContainer;
{
this.beatmapSet = beatmapSet;
AddRange(new Drawable[] private readonly OsuAnimatedButton button;
public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
: base(beatmapSet)
{
this.noVideo = noVideo;
InternalChild = shakeContainer = new ShakeContainer
{ {
downloader = new BeatmapSetDownloader(beatmapSet, noVideo), RelativeSizeAxes = Axes.Both,
background = new Box Child = button = new OsuAnimatedButton
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Depth = float.MaxValue Children = new Drawable[]
}, {
icon = new SpriteIcon background = new Box
{ {
Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, Depth = float.MaxValue
Size = new Vector2(13), },
Icon = FontAwesome.fa_download, icon = new SpriteIcon
}, {
checkmark = new SpriteIcon Anchor = Anchor.Centre,
{ Origin = Anchor.Centre,
Anchor = Anchor.Centre, Size = new Vector2(13),
Origin = Anchor.Centre, Icon = FontAwesome.fa_download,
X = 8, },
Size = Vector2.Zero, checkmark = new SpriteIcon
Icon = FontAwesome.fa_check, {
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
X = 8,
Size = Vector2.Zero,
Icon = FontAwesome.fa_check,
}
}
} }
}); };
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
downloader.DownloadState.BindValueChanged(updateState, true);
State.BindValueChanged(updateState, true);
FinishTransforms(true); FinishTransforms(true);
} }
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame game) private void load(OsuColour colours, OsuGame game, BeatmapManager beatmaps)
{ {
this.colours = colours; this.colours = colours;
Action = () => button.Action = () =>
{ {
switch (downloader.DownloadState.Value) switch (State.Value)
{ {
case BeatmapSetDownloader.DownloadStatus.Downloading: case DownloadState.Downloading:
// todo: replace with ShakeContainer after https://github.com/ppy/osu/pull/2909 is merged. case DownloadState.Downloaded:
Content.MoveToX(-5, 50, Easing.OutSine).Then() shakeContainer.Shake();
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine);
break; break;
case BeatmapSetDownloader.DownloadStatus.Downloaded: case DownloadState.LocallyAvailable:
game.PresentBeatmap(beatmapSet); game.PresentBeatmap(BeatmapSet);
break; break;
default: default:
downloader.Download(); beatmaps.Download(BeatmapSet, noVideo);
break; break;
} }
}; };
} }
private void updateState(BeatmapSetDownloader.DownloadStatus state) private void updateState(DownloadState state)
{ {
switch (state) switch (state)
{ {
case BeatmapSetDownloader.DownloadStatus.NotDownloaded: case DownloadState.NotDownloaded:
background.FadeColour(colours.Gray4, 500, Easing.InOutExpo); background.FadeColour(colours.Gray4, 500, Easing.InOutExpo);
icon.MoveToX(0, 500, Easing.InOutExpo); icon.MoveToX(0, 500, Easing.InOutExpo);
checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo);
break; break;
case BeatmapSetDownloader.DownloadStatus.Downloading: case DownloadState.Downloading:
background.FadeColour(colours.Blue, 500, Easing.InOutExpo); background.FadeColour(colours.Blue, 500, Easing.InOutExpo);
icon.MoveToX(0, 500, Easing.InOutExpo); icon.MoveToX(0, 500, Easing.InOutExpo);
checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo);
break; break;
case DownloadState.Downloaded:
case BeatmapSetDownloader.DownloadStatus.Downloaded: background.FadeColour(colours.Yellow, 500, Easing.InOutExpo);
break;
case DownloadState.LocallyAvailable:
background.FadeColour(colours.Green, 500, Easing.InOutExpo); background.FadeColour(colours.Green, 500, Easing.InOutExpo);
icon.MoveToX(-8, 500, Easing.InOutExpo); icon.MoveToX(-8, 500, Easing.InOutExpo);
checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo); checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo);

View File

@ -0,0 +1,64 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Overlays.Direct
{
public class DownloadProgressBar : DownloadTrackingComposite
{
private readonly ProgressBar progressBar;
public DownloadProgressBar(BeatmapSetInfo beatmapSet)
: base(beatmapSet)
{
AddInternal(progressBar = new ProgressBar
{
Height = 0,
Alpha = 0,
});
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours)
{
progressBar.FillColour = colours.Blue;
progressBar.BackgroundColour = Color4.Black.Opacity(0.7f);
progressBar.Current = Progress;
State.BindValueChanged(state =>
{
switch (state)
{
case DownloadState.NotDownloaded:
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
break;
case DownloadState.Downloading:
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
break;
case DownloadState.Downloaded:
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
progressBar.Current.Value = 1;
progressBar.FillColour = colours.Yellow;
break;
case DownloadState.LocallyAvailable:
progressBar.FadeOut(500);
break;
}
}, true);
}
}
}

View File

@ -0,0 +1,13 @@
// 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.
namespace osu.Game.Overlays.Direct
{
public enum DownloadState
{
NotDownloaded,
Downloading,
Downloaded,
LocallyAvailable
}
}

View File

@ -0,0 +1,131 @@
// 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 Microsoft.EntityFrameworkCore.Internal;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Direct
{
public abstract class DownloadTrackingComposite : CompositeDrawable
{
public readonly Bindable<BeatmapSetInfo> BeatmapSet = new Bindable<BeatmapSetInfo>();
private BeatmapManager beatmaps;
/// <summary>
/// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
/// </summary>
protected readonly Bindable<DownloadState> State = new Bindable<DownloadState>();
protected readonly Bindable<double> Progress = new Bindable<double>();
protected DownloadTrackingComposite(BeatmapSetInfo beatmapSet = null)
{
BeatmapSet.Value = beatmapSet;
}
[BackgroundDependencyLoader(true)]
private void load(BeatmapManager beatmaps)
{
this.beatmaps = beatmaps;
BeatmapSet.BindValueChanged(set =>
{
if (set == null)
attachDownload(null);
else if (beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID).Any())
State.Value = DownloadState.LocallyAvailable;
else
attachDownload(beatmaps.GetExistingDownload(set));
}, true);
beatmaps.BeatmapDownloadBegan += download =>
{
if (download.BeatmapSet.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID)
attachDownload(download);
};
beatmaps.ItemAdded += setAdded;
}
#region Disposal
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapDownloadBegan -= attachDownload;
beatmaps.ItemAdded -= setAdded;
State.UnbindAll();
attachDownload(null);
}
#endregion
private DownloadBeatmapSetRequest attachedRequest;
private void attachDownload(DownloadBeatmapSetRequest request)
{
if (attachedRequest != null)
{
attachedRequest.Failure -= onRequestFailure;
attachedRequest.DownloadProgressed -= onRequestProgress;
attachedRequest.Success -= onRequestSuccess;
}
attachedRequest = request;
if (attachedRequest != null)
{
if (attachedRequest.Progress == 1)
{
State.Value = DownloadState.Downloaded;
Progress.Value = 1;
}
else
{
State.Value = DownloadState.Downloading;
Progress.Value = attachedRequest.Progress;
attachedRequest.Failure += onRequestFailure;
attachedRequest.DownloadProgressed += onRequestProgress;
attachedRequest.Success += onRequestSuccess;
}
}
else
{
State.Value = DownloadState.NotDownloaded;
}
}
private void onRequestSuccess(string data)
{
Schedule(() => State.Value = DownloadState.Downloaded);
}
private void onRequestProgress(float progress)
{
Schedule(() => Progress.Value = progress);
}
private void onRequestFailure(Exception e)
{
Schedule(() => attachDownload(null));
}
private void setAdded(BeatmapSetInfo s, bool existing, bool silent)
{
if (s.OnlineBeatmapSetID != BeatmapSet.Value?.OnlineBeatmapSetID)
return;
Schedule(() => State.Value = DownloadState.LocallyAvailable);
}
}
}