diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index ce80537b93..b8ba1e2945 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Osu public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { - new KeyBinding(InputKey.Z, OsuAction.LeftButton), - new KeyBinding(InputKey.X, OsuAction.RightButton), + new KeyBinding(InputKey.A, OsuAction.LeftButton), + new KeyBinding(InputKey.S, OsuAction.RightButton), new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton), new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), }; diff --git a/osu.Game.Tests/Visual/TestCaseIconButton.cs b/osu.Game.Tests/Visual/TestCaseIconButton.cs index 16363da527..14cba71ec8 100644 --- a/osu.Game.Tests/Visual/TestCaseIconButton.cs +++ b/osu.Game.Tests/Visual/TestCaseIconButton.cs @@ -25,11 +25,7 @@ namespace osu.Game.Tests.Visual Children = new[] { new NamedIconButton("No change", new IconButton()), - new NamedIconButton("Background colours", new IconButton - { - FlashColour = Color4.DarkGreen, - HoverColour = Color4.Green, - }), + new NamedIconButton("Background colours", new ColouredIconButton()), new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }), new NamedIconButton("Unchanging size", new IconButton(), false), new NamedIconButton("Icon colours", new IconButton @@ -41,6 +37,15 @@ namespace osu.Game.Tests.Visual }; } + private class ColouredIconButton : IconButton + { + public ColouredIconButton() + { + FlashColour = Color4.DarkGreen; + HoverColour = Color4.Green; + } + } + private class NamedIconButton : Container { public NamedIconButton(string name, IconButton button, bool allowSizeChange = true) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index fd24e4297b..b439f74ff3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -45,6 +45,11 @@ namespace osu.Game.Beatmaps /// public event Action BeatmapDownloadBegan; + /// + /// Fired when a beatmap download is interrupted, due to user cancellation or other failures. + /// + public event Action BeatmapDownloadFailed; + /// /// A default representation of a WorkingBeatmap to use when no beatmap is available. /// @@ -92,7 +97,7 @@ namespace osu.Game.Beatmaps // by setting the model here, we can update the noline set id below. b.BeatmapSet = model; - fetchAndPopulateOnlineIDs(b); + fetchAndPopulateOnlineIDs(b, model.Beatmaps); } // check if a set already exists with the same online id, delete if it does. @@ -175,6 +180,8 @@ namespace osu.Game.Beatmaps request.Failure += error => { + BeatmapDownloadFailed?.Invoke(request); + if (error is OperationCanceledException) return; downloadNotification.State = ProgressNotificationState.Cancelled; @@ -396,9 +403,10 @@ namespace osu.Game.Beatmaps /// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties. /// /// The beatmap to populate. + /// The other beatmaps contained within this set. /// Whether to re-query if the provided beatmap already has populated values. /// True if population was successful. - private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, bool force = false) + private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable otherBeatmaps, bool force = false) { if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null) return true; @@ -418,6 +426,12 @@ namespace osu.Game.Beatmaps Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database); + if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID)) + { + Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database); + return false; + } + beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID; return true; diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs index 6acb58e165..6f4d4c0d6f 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs @@ -5,6 +5,7 @@ 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 { @@ -19,9 +20,9 @@ namespace osu.Game.Beatmaps.Drawables private BeatmapManager beatmaps; /// - /// Whether the associated beatmap set has been downloading (by this instance or any other instance). + /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded. /// - public readonly BindableBool Downloaded = new BindableBool(); + public readonly Bindable DownloadState = new Bindable(); public BeatmapSetDownloader(BeatmapSetInfo set, bool noVideo = false) { @@ -36,10 +37,16 @@ namespace osu.Game.Beatmaps.Drawables beatmaps.ItemAdded += setAdded; beatmaps.ItemRemoved += setRemoved; + beatmaps.BeatmapDownloadBegan += downloadBegan; + beatmaps.BeatmapDownloadFailed += downloadFailed; // initial value - if (set.OnlineBeatmapSetID != null) - Downloaded.Value = beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any(); + 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) @@ -50,6 +57,8 @@ namespace osu.Game.Beatmaps.Drawables { beatmaps.ItemAdded -= setAdded; beatmaps.ItemRemoved -= setRemoved; + beatmaps.BeatmapDownloadBegan -= downloadBegan; + beatmaps.BeatmapDownloadFailed -= downloadFailed; } } @@ -57,28 +66,45 @@ namespace osu.Game.Beatmaps.Drawables /// Begin downloading the associated beatmap set. /// /// True if downloading began. False if an existing download is active or completed. - public bool Download() + public void Download() { - if (Downloaded.Value) - return false; - - if (beatmaps.GetExistingDownload(set) != null) - return false; + if (DownloadState.Value > DownloadStatus.NotDownloaded) + return; beatmaps.Download(set, noVideo); - return true; + + DownloadState.Value = DownloadStatus.Downloading; } private void setAdded(BeatmapSetInfo s) { if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID) - Downloaded.Value = true; + DownloadState.Value = DownloadStatus.Downloaded; } private void setRemoved(BeatmapSetInfo s) { if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID) - Downloaded.Value = false; + 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, } } } diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs index 025ab0037f..7d00c35862 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs @@ -9,14 +9,14 @@ using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyColouredContainer : Container, IHasAccentColour + public abstract class DifficultyColouredContainer : Container, IHasAccentColour { public Color4 AccentColour { get; set; } private readonly BeatmapInfo beatmap; private OsuColour palette; - public DifficultyColouredContainer(BeatmapInfo beatmap) + protected DifficultyColouredContainer(BeatmapInfo beatmap) { this.beatmap = beatmap; } diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 42c98aef24..24604711d4 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -3,10 +3,14 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { @@ -14,7 +18,8 @@ namespace osu.Game.Beatmaps.Drawables { private readonly BeatmapInfo beatmap; - public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap) + public DifficultyIcon(BeatmapInfo beatmap) + : base(beatmap) { if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); @@ -28,16 +33,29 @@ namespace osu.Game.Beatmaps.Drawables { Children = new Drawable[] { - new SpriteIcon + new CircularContainer { + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.84f), Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = AccentColour, - Icon = FontAwesome.fa_circle + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.08f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = AccentColour, + }, }, new ConstrainedIconContainer { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) Icon = beatmap.Ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o } diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 1d832d1c54..0dc6297ad2 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -22,8 +22,11 @@ namespace osu.Game.Graphics.Containers protected virtual bool PlaySamplesOnStateChange => true; + protected override bool BlockPassThroughKeyboard => true; + private PreviewTrackManager previewTrackManager; + protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -69,10 +72,13 @@ namespace osu.Game.Graphics.Containers public virtual bool OnPressed(GlobalAction action) { - if (action == GlobalAction.Back) + switch (action) { - State = Visibility.Hidden; - return true; + case GlobalAction.Back: + State = Visibility.Hidden; + return true; + case GlobalAction.Select: + return true; } return false; diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs index 5a25fe641d..eca7bf57c8 100644 --- a/osu.Game/Graphics/UserInterface/IconButton.cs +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -3,31 +3,17 @@ using OpenTK; using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input; -using osu.Game.Graphics.Containers; namespace osu.Game.Graphics.UserInterface { - public class IconButton : OsuClickableContainer + public class IconButton : OsuAnimatedButton { public const float BUTTON_SIZE = 30; - private Color4? flashColour; - /// - /// The colour that should be flashed when the is clicked. - /// - public Color4 FlashColour - { - get { return flashColour ?? Color4.White; } - set { flashColour = value; } - } - private Color4? iconColour; + /// /// The icon colour. This does not affect . /// @@ -42,6 +28,7 @@ namespace osu.Game.Graphics.UserInterface } private Color4? iconHoverColour; + /// /// The icon colour while the is hovered. /// @@ -51,20 +38,6 @@ namespace osu.Game.Graphics.UserInterface set { iconHoverColour = value; } } - private Color4? hoverColour; - /// - /// The background colour of the while it is hovered. - /// - public Color4 HoverColour - { - get { return hoverColour ?? Color4.White; } - set - { - hoverColour = value; - hover.Colour = value; - } - } - /// /// The icon. /// @@ -88,93 +61,39 @@ namespace osu.Game.Graphics.UserInterface /// public Vector2 ButtonSize { - get { return content.Size; } - set { content.Size = value; } + get => Content.Size; + set + { + Content.RelativeSizeAxes = Axes.None; + Content.Size = value; + } } - private readonly Container content; private readonly SpriteIcon icon; - private readonly Box hover; public IconButton() { AutoSizeAxes = Axes.Both; + ButtonSize = new Vector2(BUTTON_SIZE); - Children = new Drawable[] + Add(icon = new SpriteIcon { - content = new Container - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(BUTTON_SIZE), - CornerRadius = 5, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.04f), - Type = EdgeEffectType.Shadow, - Radius = 5, - }, - Children = new Drawable[] - { - hover = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - icon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(18), - } - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (hoverColour == null) - HoverColour = colours.Yellow.Opacity(0.6f); - - if (flashColour == null) - FlashColour = colours.Yellow; - - Enabled.ValueChanged += enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint); + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(18), + }); } protected override bool OnHover(InputState state) { - hover.FadeIn(500, Easing.OutQuint); icon.FadeColour(IconHoverColour, 500, Easing.OutQuint); return base.OnHover(state); } protected override void OnHoverLost(InputState state) { - hover.FadeOut(500, Easing.OutQuint); icon.FadeColour(IconColour, 500, Easing.OutQuint); base.OnHoverLost(state); } - - protected override bool OnClick(InputState state) - { - hover.FlashColour(FlashColour, 800, Easing.OutQuint); - return base.OnClick(state); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - content.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - content.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } } } diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs new file mode 100644 index 0000000000..990a2f20a9 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs @@ -0,0 +1,109 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics.Containers; +using OpenTK.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Highlight on hover, bounce on click. + /// + public class OsuAnimatedButton : OsuClickableContainer + { + /// + /// The colour that should be flashed when the is clicked. + /// + protected Color4 FlashColour = Color4.White.Opacity(0.3f); + + private Color4 hoverColour = Color4.White.Opacity(0.1f); + + /// + /// The background colour of the while it is hovered. + /// + protected Color4 HoverColour + { + get => hoverColour; + set + { + hoverColour = value; + hover.Colour = value; + } + } + + protected override Container Content => content; + + private readonly Container content; + private readonly Box hover; + + public OsuAnimatedButton() + { + base.Content.Add(content = new Container + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + CornerRadius = 5, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.04f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Children = new Drawable[] + { + hover = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = HoverColour, + Blending = BlendingMode.Additive, + Alpha = 0, + }, + } + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Enabled.BindValueChanged(enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true); + } + + protected override bool OnHover(InputState state) + { + hover.FadeIn(500, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + hover.FadeOut(500, Easing.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + hover.FlashColour(FlashColour, 800, Easing.OutQuint); + return base.OnClick(state); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + Content.ScaleTo(0.75f, 2000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + Content.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + } +} diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs index 98ce5def08..1f3c015614 100644 --- a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs +++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs @@ -8,7 +8,6 @@ namespace osu.Game.Migrations protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.Sql("DELETE FROM KeyBinding WHERE RulesetID = 1"); - Logger.Log("osu!taiko bindings have been reset due to new defaults", LoggingTarget.Runtime, LogLevel.Important); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b654e5d53f..557b6e4469 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -19,6 +19,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Platform; @@ -322,24 +323,6 @@ namespace osu.Game dependencies.Cache(notifications); dependencies.Cache(dialogOverlay); - // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; - overlays.AddRange(singleDisplayOverlays); - - foreach (var overlay in singleDisplayOverlays) - { - overlay.StateChanged += state => - { - if (state == Visibility.Hidden) return; - - foreach (var c in singleDisplayOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } - }; - } - var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; overlays.AddRange(singleDisplaySideOverlays); @@ -348,12 +331,7 @@ namespace osu.Game overlay.StateChanged += state => { if (state == Visibility.Hidden) return; - - foreach (var c in singleDisplaySideOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } + singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } @@ -366,12 +344,24 @@ namespace osu.Game overlay.StateChanged += state => { if (state == Visibility.Hidden) return; + informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); + }; + } - foreach (var c in informationalOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } + // ensure only one of these overlays are open at once. + var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; + overlays.AddRange(singleDisplayOverlays); + + foreach (var overlay in singleDisplayOverlays) + { + overlay.StateChanged += state => + { + // informational overlays should be dismissed on a show or hide of a full overlay. + informationalOverlays.ForEach(o => o.Hide()); + + if (state == Visibility.Hidden) return; + + singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index 4fce6a49fb..7a227f4bfa 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -61,20 +61,23 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons Action = () => { - if (!downloader.Download()) + 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.Downloaded.ValueChanged += d => + downloader.DownloadState.ValueChanged += d => { - if (d) + if (d == BeatmapSetDownloader.DownloadStatus.Downloaded) this.FadeOut(200); - else + else if (d == BeatmapSetDownloader.DownloadStatus.NotDownloaded) this.FadeIn(200); }; } diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index e286837746..48963a91e8 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -168,11 +168,10 @@ namespace osu.Game.Overlays.Direct }, new DownloadButton(SetInfo) { - Size = new Vector2(30), + Size = new Vector2(50, 30), Margin = new MarginPadding(horizontal_padding), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Colour = colours.Gray5, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, }, }, }, diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 812a0e2073..17c5153dab 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -120,8 +120,8 @@ namespace osu.Game.Overlays.Direct Alpha = 0, Child = new DownloadButton(SetInfo) { - Size = new Vector2(height - vertical_padding * 2), - Margin = new MarginPadding { Left = vertical_padding }, + Size = new Vector2(height - vertical_padding * 3), + Margin = new MarginPadding { Left = vertical_padding, Right = vertical_padding }, }, }, new FillFlowContainer diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index e63c290ce5..76da4b61b0 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -99,6 +99,7 @@ namespace osu.Game.Overlays.Direct attachDownload(downloadRequest); beatmaps.BeatmapDownloadBegan += attachDownload; + beatmaps.ItemAdded += setAdded; } public override bool DisposeOnDeathRemoval => true; @@ -107,6 +108,7 @@ namespace osu.Game.Overlays.Direct { base.Dispose(isDisposing); beatmaps.BeatmapDownloadBegan -= attachDownload; + beatmaps.ItemAdded -= setAdded; } protected override void Update() @@ -171,6 +173,12 @@ namespace osu.Game.Overlays.Direct }; } + private void setAdded(BeatmapSetInfo s) + { + if (s.OnlineBeatmapSetID == SetInfo.OnlineBeatmapSetID) + progressBar.FadeOut(500); + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 1ffa8dbd35..7758e171c5 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -1,76 +1,109 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Input; +using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using OpenTK; namespace osu.Game.Overlays.Direct { - public class DownloadButton : OsuClickableContainer + public class DownloadButton : OsuAnimatedButton { private readonly SpriteIcon icon; + private readonly SpriteIcon checkmark; + private readonly BeatmapSetDownloader downloader; + private readonly Box background; + + private OsuColour colours; public DownloadButton(BeatmapSetInfo set, bool noVideo = false) { - BeatmapSetDownloader downloader; - Children = new Drawable[] + AddRange(new Drawable[] { downloader = new BeatmapSetDownloader(set, noVideo), + background = new Box + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue + }, icon = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(30), - Icon = FontAwesome.fa_osu_chevron_down_o, + Size = new Vector2(13), + Icon = FontAwesome.fa_download, }, - }; + checkmark = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + X = 8, + Size = Vector2.Zero, + Icon = FontAwesome.fa_check, + } + }); Action = () => { - if (!downloader.Download()) + if (downloader.DownloadState == BeatmapSetDownloader.DownloadStatus.Downloading) { + // todo: replace with ShakeContainer after https://github.com/ppy/osu/pull/2909 is merged. Content.MoveToX(-5, 50, Easing.OutSine).Then() .MoveToX(5, 100, Easing.InOutSine).Then() .MoveToX(-5, 100, Easing.InOutSine).Then() .MoveToX(0, 50, Easing.InSine); } - }; - - downloader.Downloaded.ValueChanged += d => - { - if (d) - this.FadeOut(200); + else if (downloader.DownloadState == BeatmapSetDownloader.DownloadStatus.Downloaded) + { + // TODO: Jump to song select with this set when the capability is implemented + } else - this.FadeIn(200); + { + downloader.Download(); + } }; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override void LoadComplete() { - icon.ScaleTo(0.9f, 1000, Easing.Out); - return base.OnMouseDown(state, args); + base.LoadComplete(); + downloader.DownloadState.BindValueChanged(updateState, true); + FinishTransforms(true); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours) { - icon.ScaleTo(1f, 500, Easing.OutElastic); - return base.OnMouseUp(state, args); + this.colours = colours; } - protected override bool OnHover(InputState state) + private void updateState(BeatmapSetDownloader.DownloadStatus state) { - icon.ScaleTo(1.1f, 500, Easing.OutElastic); - return base.OnHover(state); - } + switch (state) + { + case BeatmapSetDownloader.DownloadStatus.NotDownloaded: + background.FadeColour(colours.Gray4, 500, Easing.InOutExpo); + icon.MoveToX(0, 500, Easing.InOutExpo); + checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); + break; - protected override void OnHoverLost(InputState state) - { - icon.ScaleTo(1f, 500, Easing.OutElastic); + case BeatmapSetDownloader.DownloadStatus.Downloading: + background.FadeColour(colours.Blue, 500, Easing.InOutExpo); + icon.MoveToX(0, 500, Easing.InOutExpo); + checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); + break; + + case BeatmapSetDownloader.DownloadStatus.Downloaded: + background.FadeColour(colours.Green, 500, Easing.InOutExpo); + icon.MoveToX(-8, 500, Easing.InOutExpo); + checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo); + break; + } } } } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index a57d5fd183..56af04c498 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -142,14 +142,14 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Children = new[] { - prevButton = new IconButton + prevButton = new MusicIconButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, Action = prev, Icon = FontAwesome.fa_step_backward, }, - playButton = new IconButton + playButton = new MusicIconButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -158,7 +158,7 @@ namespace osu.Game.Overlays Action = play, Icon = FontAwesome.fa_play_circle_o, }, - nextButton = new IconButton + nextButton = new MusicIconButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -167,7 +167,7 @@ namespace osu.Game.Overlays }, } }, - playlistButton = new IconButton + playlistButton = new MusicIconButton { Origin = Anchor.Centre, Anchor = Anchor.CentreRight, @@ -405,6 +405,16 @@ namespace osu.Game.Overlays Prev } + private class MusicIconButton : IconButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + HoverColour = colours.YellowDark.Opacity(0.6f); + FlashColour = colours.Yellow; + } + } + private class Background : BufferedContainer { private readonly Sprite sprite; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index c5510d3d70..9d09836d25 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -158,6 +158,13 @@ namespace osu.Game.Overlays.Profile } } }, + new Box // this is a temporary workaround for incorrect masking behaviour of FillMode.Fill used in UserCoverBackground (see https://github.com/ppy/osu-framework/issues/1675) + { + RelativeSizeAxes = Axes.X, + Height = 1, + Y = cover_height, + Colour = OsuColour.Gray(34), + }, infoTextLeft = new LinkFlowContainer(t => t.TextSize = 14) { X = UserProfileOverlay.CONTENT_X_MARGIN, diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 8850f716e0..0fe41327db 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { LabelText = "Audio Offset", Bindable = config.GetBindable(OsuSetting.AudioOffset), - KeyboardStep = 100f + KeyboardStep = 1f }, new SettingsButton { diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs index 80896c163f..369c751448 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs @@ -17,10 +17,10 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { Children = new Drawable[] { - new SettingsSlider { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.1f }, - new SettingsSlider { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.1f }, - new SettingsSlider { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.1f }, - new SettingsSlider { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.1f }, + new SettingsSlider { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f }, + new SettingsSlider { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.01f }, + new SettingsSlider { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f }, + new SettingsSlider { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f }, }; } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 9c8f5e2643..21d5d452bf 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -21,13 +21,13 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { LabelText = "Background dim", Bindable = config.GetBindable(OsuSetting.DimLevel), - KeyboardStep = 0.1f + KeyboardStep = 0.01f }, new SettingsSlider { LabelText = "Background blur", Bindable = config.GetBindable(OsuSetting.BlurLevel), - KeyboardStep = 0.1f + KeyboardStep = 0.01f }, new SettingsCheckbox { diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index 7893d76fb8..235ff0f319 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -31,13 +31,13 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { LabelText = "Display beatmaps from", Bindable = config.GetBindable(OsuSetting.DisplayStarsMinimum), - KeyboardStep = 1f + KeyboardStep = 0.1f }, new SettingsSlider { LabelText = "up to", Bindable = config.GetBindable(OsuSetting.DisplayStarsMaximum), - KeyboardStep = 1f + KeyboardStep = 0.1f }, new SettingsEnumDropdown { diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 1f87a635de..42028f6bd5 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -50,13 +50,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { LabelText = "Horizontal position", Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionX), - KeyboardStep = 0.1f + KeyboardStep = 0.01f }, new SettingsSlider { LabelText = "Vertical position", Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionY), - KeyboardStep = 0.1f + KeyboardStep = 0.01f }, } }, diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 18a371e904..df8ebaf4aa 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -36,13 +36,13 @@ namespace osu.Game.Overlays.Settings.Sections { LabelText = "Menu cursor size", Bindable = config.GetBindable(OsuSetting.MenuCursorSize), - KeyboardStep = 0.1f + KeyboardStep = 0.01f }, new SettingsSlider { LabelText = "Gameplay cursor size", Bindable = config.GetBindable(OsuSetting.GameplayCursorSize), - KeyboardStep = 0.1f + KeyboardStep = 0.01f }, new SettingsCheckbox { diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs index f46cc7ef9d..5928fbaa1b 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs @@ -27,16 +27,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline public TimelineButton() { - InternalChild = button = new IconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - IconColour = OsuColour.Gray(0.35f), - IconHoverColour = Color4.White, - HoverColour = OsuColour.Gray(0.25f), - FlashColour = OsuColour.Gray(0.5f), - Action = () => Action?.Invoke() - }; + InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() }; button.Enabled.BindTo(Enabled); Width = button.ButtonSize.X; @@ -48,5 +39,18 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight); } + + private class TimelineIconButton : IconButton + { + public TimelineIconButton() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + IconColour = OsuColour.Gray(0.35f); + IconHoverColour = Color4.White; + HoverColour = OsuColour.Gray(0.25f); + FlashColour = OsuColour.Gray(0.5f); + } + } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b406bda411..49a180902b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -169,7 +169,7 @@ namespace osu.Game.Screens.Play OnPause = () => { pauseContainer.Retries = RestartCount; - hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused; + hudOverlay.KeyCounter.IsCounting = !pauseContainer.IsPaused; }, OnResume = () => hudOverlay.KeyCounter.IsCounting = true, Children = new[] @@ -219,9 +219,7 @@ namespace osu.Game.Screens.Play { if (!IsCurrentScreen) return; - //we want to hide the hitrenderer immediately (looks better). - //we may be able to remove this once the mouse cursor trail is improved. - RulesetContainer?.Hide(); + pauseContainer.Hide(); Restart(); }, } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs new file mode 100644 index 0000000000..cdec8c042f --- /dev/null +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -0,0 +1,69 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; + +namespace osu.Game.Storyboards.Drawables +{ + public class DrawableStoryboardSample : Component + { + /// + /// The amount of time allowable beyond the start time of the sample, for the sample to start. + /// + private const double allowable_late_start = 100; + + private readonly StoryboardSample sample; + private SampleChannel channel; + + public override bool RemoveWhenNotAlive => false; + + public DrawableStoryboardSample(StoryboardSample sample) + { + this.sample = sample; + LifetimeStart = sample.Time; + } + + [BackgroundDependencyLoader] + private void load(IBindableBeatmap beatmap) + { + // Try first with the full name, then attempt with no path + channel = beatmap.Value.Skin.GetSample(sample.Path) ?? beatmap.Value.Skin.GetSample(Path.ChangeExtension(sample.Path, null)); + + if (channel != null) + channel.Volume.Value = sample.Volume / 100; + } + + protected override void Update() + { + base.Update(); + + // TODO: this logic will need to be consolidated with other game samples like hitsounds. + if (Time.Current < sample.Time) + { + // We've rewound before the start time of the sample + channel?.Stop(); + + // In the case that the user fast-forwards to a point far beyond the start time of the sample, + // we want to be able to fall into the if-conditional below (therefore we must not have a life time end) + LifetimeStart = sample.Time; + LifetimeEnd = double.MaxValue; + } + else if (Time.Current - Time.Elapsed < sample.Time) + { + // We've passed the start time of the sample. We only play the sample if we're within an allowable range + // from the sample's start, to reduce layering if we've been fast-forwarded far into the future + if (Time.Current - sample.Time < allowable_late_start) + channel?.Play(); + + // In the case that the user rewinds to a point far behind the start time of the sample, + // we want to be able to fall into the if-conditional above (therefore we must not have a life time start) + LifetimeStart = double.MinValue; + LifetimeEnd = sample.Time; + } + } + } +} diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index d0555493a6..c34a39a7bf 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -2,14 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using System; +using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards { public class StoryboardSample : IStoryboardElement { public string Path { get; set; } - public bool IsDrawable => false; + public bool IsDrawable => true; public double Time; public float Volume; @@ -21,9 +21,6 @@ namespace osu.Game.Storyboards Volume = volume; } - public Drawable CreateDrawable() - { - throw new InvalidOperationException(); - } + public Drawable CreateDrawable() => new DrawableStoryboardSample(this); } }