1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-06 07:02:54 +08:00

Merge branch 'master' into lead-in-fixes

This commit is contained in:
Dan Balasescu 2019-11-22 13:48:55 +09:00 committed by GitHub
commit 5d2a608be0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 832 additions and 167 deletions

View File

@ -14,12 +14,13 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{ {
public class PathControlPointPiece : BlueprintPiece<Slider> public class PathControlPointPiece : BlueprintPiece<Slider>
{ {
public Action<int> RequestSelection; public Action<int, MouseButtonEvent> RequestSelection;
public Action<Vector2[]> ControlPointsChanged; public Action<Vector2[]> ControlPointsChanged;
public readonly BindableBool IsSelected = new BindableBool(); public readonly BindableBool IsSelected = new BindableBool();
@ -129,10 +130,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
protected override bool OnMouseDown(MouseDownEvent e) protected override bool OnMouseDown(MouseDownEvent e)
{ {
if (RequestSelection != null) if (RequestSelection == null)
return false;
switch (e.Button)
{ {
RequestSelection.Invoke(Index); case MouseButton.Left:
return true; RequestSelection.Invoke(Index, e);
return true;
case MouseButton.Right:
if (!IsSelected.Value)
RequestSelection.Invoke(Index, e);
return false; // Allow context menu to show
} }
return false; return false;
@ -142,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
protected override bool OnClick(ClickEvent e) => RequestSelection != null; protected override bool OnClick(ClickEvent e) => RequestSelection != null;
protected override bool OnDragStart(DragStartEvent e) => true; protected override bool OnDragStart(DragStartEvent e) => e.Button == MouseButton.Left;
protected override bool OnDrag(DragEvent e) protected override bool OnDrag(DragEvent e)
{ {

View File

@ -3,19 +3,25 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Humanizer;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose;
using osuTK; using osuTK;
using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{ {
public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction> public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
{ {
public Action<Vector2[]> ControlPointsChanged; public Action<Vector2[]> ControlPointsChanged;
@ -73,9 +79,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false; return false;
} }
private void selectPiece(int index) public bool OnPressed(PlatformAction action)
{ {
if (inputManager.CurrentState.Keyboard.ControlPressed) switch (action.ActionMethod)
{
case PlatformActionMethod.Delete:
return deleteSelected();
}
return false;
}
public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete;
private void selectPiece(int index, MouseButtonEvent e)
{
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
Pieces[index].IsSelected.Toggle(); Pieces[index].IsSelected.Toggle();
else else
{ {
@ -84,50 +103,60 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
} }
} }
public bool OnPressed(PlatformAction action) private bool deleteSelected()
{ {
switch (action.ActionMethod) var newControlPoints = new List<Vector2>();
foreach (var piece in Pieces)
{ {
case PlatformActionMethod.Delete: if (!piece.IsSelected.Value)
var newControlPoints = new List<Vector2>(); newControlPoints.Add(slider.Path.ControlPoints[piece.Index]);
foreach (var piece in Pieces)
{
if (!piece.IsSelected.Value)
newControlPoints.Add(slider.Path.ControlPoints[piece.Index]);
}
// Ensure that there are any points to be deleted
if (newControlPoints.Count == slider.Path.ControlPoints.Length)
return false;
// If there are 0 remaining control points, treat the slider as being deleted
if (newControlPoints.Count == 0)
{
placementHandler?.Delete(slider);
return true;
}
// Make control points relative
Vector2 first = newControlPoints[0];
for (int i = 0; i < newControlPoints.Count; i++)
newControlPoints[i] = newControlPoints[i] - first;
// The slider's position defines the position of the first control point, and all further control points are relative to that point
slider.Position = slider.Position + first;
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
foreach (var piece in Pieces)
piece.IsSelected.Value = false;
ControlPointsChanged?.Invoke(newControlPoints.ToArray());
return true;
} }
return false; // Ensure that there are any points to be deleted
if (newControlPoints.Count == slider.Path.ControlPoints.Length)
return false;
// If there are 0 remaining control points, treat the slider as being deleted
if (newControlPoints.Count == 0)
{
placementHandler?.Delete(slider);
return true;
}
// Make control points relative
Vector2 first = newControlPoints[0];
for (int i = 0; i < newControlPoints.Count; i++)
newControlPoints[i] = newControlPoints[i] - first;
// The slider's position defines the position of the first control point, and all further control points are relative to that point
slider.Position = slider.Position + first;
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
foreach (var piece in Pieces)
piece.IsSelected.Value = false;
ControlPointsChanged?.Invoke(newControlPoints.ToArray());
return true;
} }
public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete; public MenuItem[] ContextMenuItems
{
get
{
if (!Pieces.Any(p => p.IsHovered))
return null;
int selectedPoints = Pieces.Count(p => p.IsSelected.Value);
if (selectedPoints == 0)
return null;
return new MenuItem[]
{
new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints)}", MenuItemType.Destructive, () => deleteSelected())
};
}
}
} }
} }

View File

@ -4,8 +4,8 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mods;
using osu.Framework.Timing; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osuTK; using osuTK;
using osuTK.Input; using osuTK.Input;
@ -18,40 +18,38 @@ namespace osu.Game.Tests.Visual.Gameplay
private SkipOverlay skip; private SkipOverlay skip;
private int requestCount; private int requestCount;
private FramedOffsetClock offsetClock;
private StopwatchClock stopwatchClock;
private double increment; private double increment;
private GameplayClockContainer gameplayClockContainer;
private GameplayClock gameplayClock;
private const double skip_time = 6000;
[SetUp] [SetUp]
public void SetUp() => Schedule(() => public void SetUp() => Schedule(() =>
{ {
requestCount = 0; requestCount = 0;
increment = 6000; increment = skip_time;
Child = new Container Child = gameplayClockContainer = new GameplayClockContainer(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new Mod[] { }, 0)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Clock = offsetClock = new FramedOffsetClock(stopwatchClock = new StopwatchClock(true)),
Children = new Drawable[] Children = new Drawable[]
{ {
skip = new SkipOverlay(6000) skip = new SkipOverlay(skip_time)
{ {
RequestSkip = () => RequestSkip = () =>
{ {
requestCount++; requestCount++;
offsetClock.Offset += increment; gameplayClockContainer.Seek(gameplayClock.CurrentTime + increment);
} }
} }
}, },
}; };
});
protected override void Update() gameplayClockContainer.Start();
{ gameplayClock = gameplayClockContainer.GameplayClock;
if (stopwatchClock != null) });
stopwatchClock.Rate = Clock.Rate;
}
[Test] [Test]
public void TestFadeOnIdle() public void TestFadeOnIdle()
@ -80,7 +78,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
AddStep("click", () => AddStep("click", () =>
{ {
increment = 6000 - offsetClock.CurrentTime - GameplayClockContainer.MINIMUM_SKIP_TIME / 2; increment = skip_time - gameplayClock.CurrentTime - GameplayClockContainer.MINIMUM_SKIP_TIME / 2;
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });
AddStep("click", () => InputManager.Click(MouseButton.Left)); AddStep("click", () => InputManager.Click(MouseButton.Left));
@ -106,7 +104,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre)); AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
AddStep("button down", () => InputManager.PressButton(MouseButton.Left)); AddStep("button down", () => InputManager.PressButton(MouseButton.Left));
AddUntilStep("wait for overlay disapper", () => !skip.IsAlive); AddUntilStep("wait for overlay disappear", () => !skip.IsPresent);
AddAssert("ensure button didn't disappear", () => skip.Children.First().Alpha > 0); AddAssert("ensure button didn't disappear", () => skip.Children.First().Alpha > 0);
AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left)); AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left));
checkRequestCount(0); checkRequestCount(0);

View File

@ -0,0 +1,77 @@
// 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.Game.Overlays.BeatmapSet;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Taiko;
using osu.Game.Rulesets.Catch;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Bindables;
using osu.Game.Rulesets;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneLeaderboardModSelector : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(LeaderboardModSelector),
};
public TestSceneLeaderboardModSelector()
{
LeaderboardModSelector modSelector;
FillFlowContainer<SpriteText> selectedMods;
var ruleset = new Bindable<RulesetInfo>();
Add(selectedMods = new FillFlowContainer<SpriteText>
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
});
Add(modSelector = new LeaderboardModSelector
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Ruleset = { BindTarget = ruleset }
});
modSelector.SelectedMods.ItemsAdded += mods =>
{
mods.ForEach(mod => selectedMods.Add(new SpriteText
{
Text = mod.Acronym,
}));
};
modSelector.SelectedMods.ItemsRemoved += mods =>
{
mods.ForEach(mod =>
{
foreach (var selected in selectedMods)
{
if (selected.Text == mod.Acronym)
{
selectedMods.Remove(selected);
break;
}
}
});
};
AddStep("osu ruleset", () => ruleset.Value = new OsuRuleset().RulesetInfo);
AddStep("mania ruleset", () => ruleset.Value = new ManiaRuleset().RulesetInfo);
AddStep("taiko ruleset", () => ruleset.Value = new TaikoRuleset().RulesetInfo);
AddStep("catch ruleset", () => ruleset.Value = new CatchRuleset().RulesetInfo);
AddStep("Deselect all", () => modSelector.DeselectAll());
AddStep("null ruleset", () => ruleset.Value = null);
}
}
}

View File

@ -0,0 +1,23 @@
// 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.Game.Overlays;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneNewsOverlay : OsuTestScene
{
private NewsOverlay news;
protected override void LoadComplete()
{
base.LoadComplete();
Add(news = new NewsOverlay());
AddStep(@"Show", news.Show);
AddStep(@"Hide", news.Hide);
AddStep(@"Show front page", () => news.ShowFrontPage());
AddStep(@"Custom article", () => news.Current.Value = "Test Article 101");
}
}
}

View File

@ -0,0 +1,36 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Game.Overlays.Comments;
using osu.Framework.MathUtils;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneTotalCommentsCounter : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TotalCommentsCounter),
};
public TestSceneTotalCommentsCounter()
{
var count = new BindableInt();
Add(new TotalCommentsCounter
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Current = { BindTarget = count }
});
AddStep(@"Set 100", () => count.Value = 100);
AddStep(@"Set 0", () => count.Value = 0);
AddStep(@"Set random", () => count.Value = RNG.Next(0, int.MaxValue));
}
}
}

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual
private IReadOnlyList<Type> requiredGameDependencies => new[] private IReadOnlyList<Type> requiredGameDependencies => new[]
{ {
typeof(OsuGame), typeof(OsuGame),
typeof(RavenLogger), typeof(SentryLogger),
typeof(OsuLogo), typeof(OsuLogo),
typeof(IdleTracker), typeof(IdleTracker),
typeof(OnScreenDisplay), typeof(OnScreenDisplay),

View File

@ -0,0 +1,11 @@
// 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.Configuration
{
public enum BackgroundSource
{
Skin,
Beatmap
}
}

View File

@ -117,6 +117,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.UIHoldActivationDelay, 200f, 0f, 500f, 50f); Set(OsuSetting.UIHoldActivationDelay, 200f, 0f, 500f, 50f);
Set(OsuSetting.IntroSequence, IntroSequence.Triangles); Set(OsuSetting.IntroSequence, IntroSequence.Triangles);
Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin);
} }
public OsuConfigManager(Storage storage) public OsuConfigManager(Storage storage)
@ -186,6 +188,7 @@ namespace osu.Game.Configuration
UIScale, UIScale,
IntroSequence, IntroSequence,
UIHoldActivationDelay, UIHoldActivationDelay,
HitLighting HitLighting,
MenuBackgroundSource
} }
} }

View File

@ -0,0 +1,28 @@
// 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.Graphics.Textures;
using osu.Game.Beatmaps;
namespace osu.Game.Graphics.Backgrounds
{
public class BeatmapBackground : Background
{
public readonly WorkingBeatmap Beatmap;
private readonly string fallbackTextureName;
public BeatmapBackground(WorkingBeatmap beatmap, string fallbackTextureName = @"Backgrounds/bg1")
{
Beatmap = beatmap;
this.fallbackTextureName = fallbackTextureName;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName);
}
}
}

View File

@ -71,7 +71,7 @@ namespace osu.Game
[Cached] [Cached]
private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); private readonly ScreenshotManager screenshotManager = new ScreenshotManager();
protected RavenLogger RavenLogger; protected SentryLogger SentryLogger;
public virtual Storage GetStorageForStableInstall() => null; public virtual Storage GetStorageForStableInstall() => null;
@ -110,7 +110,7 @@ namespace osu.Game
forwardLoggedErrorsToNotifications(); forwardLoggedErrorsToNotifications();
RavenLogger = new RavenLogger(this); SentryLogger = new SentryLogger(this);
} }
private void updateBlockingOverlayFade() => private void updateBlockingOverlayFade() =>
@ -166,7 +166,7 @@ namespace osu.Game
dependencies.CacheAs(this); dependencies.CacheAs(this);
dependencies.Cache(RavenLogger); dependencies.Cache(SentryLogger);
dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 }); dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 });
@ -486,7 +486,7 @@ namespace osu.Game
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
RavenLogger.Dispose(); SentryLogger.Dispose();
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -0,0 +1,144 @@
// 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.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Framework.Bindables;
using osu.Game.Rulesets;
using osuTK;
using osu.Game.Rulesets.UI;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
using System;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
namespace osu.Game.Overlays.BeatmapSet
{
public class LeaderboardModSelector : CompositeDrawable
{
public readonly BindableList<Mod> SelectedMods = new BindableList<Mod>();
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private readonly FillFlowContainer<ModButton> modsContainer;
public LeaderboardModSelector()
{
AutoSizeAxes = Axes.Both;
InternalChild = modsContainer = new FillFlowContainer<ModButton>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Full,
Spacing = new Vector2(4),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Ruleset.BindValueChanged(onRulesetChanged, true);
}
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
{
SelectedMods.Clear();
modsContainer.Clear();
if (ruleset.NewValue == null)
return;
modsContainer.Add(new ModButton(new ModNoMod()));
modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m)));
modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged);
}
protected override bool OnHover(HoverEvent e)
{
updateHighlighted();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
updateHighlighted();
}
private void selectionChanged(Mod mod, bool selected)
{
if (selected)
SelectedMods.Add(mod);
else
SelectedMods.Remove(mod);
updateHighlighted();
}
private void updateHighlighted()
{
if (SelectedMods.Any())
return;
modsContainer.Children.Where(button => !button.IsHovered).ForEach(button => button.Highlighted.Value = !IsHovered);
}
public void DeselectAll() => modsContainer.ForEach(mod => mod.Selected.Value = false);
private class ModButton : ModIcon
{
private const int duration = 200;
public readonly BindableBool Highlighted = new BindableBool();
public Action<Mod, bool> OnSelectionChanged;
public ModButton(Mod mod)
: base(mod)
{
Scale = new Vector2(0.4f);
Add(new HoverClickSounds());
}
protected override void LoadComplete()
{
base.LoadComplete();
Highlighted.BindValueChanged(highlighted =>
{
if (Selected.Value)
return;
this.FadeColour(highlighted.NewValue ? Color4.White : Color4.DimGray, duration, Easing.OutQuint);
}, true);
Selected.BindValueChanged(selected =>
{
OnSelectionChanged?.Invoke(Mod, selected.NewValue);
Highlighted.TriggerChange();
}, true);
}
protected override bool OnClick(ClickEvent e)
{
Selected.Toggle();
return true;
}
protected override bool OnHover(HoverEvent e)
{
Highlighted.Value = true;
return false;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
Highlighted.Value = false;
}
}
}
}

View File

@ -0,0 +1,80 @@
// 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.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Framework.Graphics.Sprites;
using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
namespace osu.Game.Overlays.Comments
{
public class TotalCommentsCounter : CompositeDrawable
{
public readonly BindableInt Current = new BindableInt();
private readonly SpriteText counter;
public TotalCommentsCounter()
{
RelativeSizeAxes = Axes.X;
Height = 50;
AddInternal(new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = 50 },
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
new SpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 20, italics: true),
Text = @"Comments"
},
new CircularContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.05f)
},
counter = new SpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = 10, Vertical = 5 },
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)
}
},
}
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
counter.Colour = colours.BlueLighter;
}
protected override void LoadComplete()
{
Current.BindValueChanged(value => counter.Text = value.NewValue.ToString("N0"), true);
base.LoadComplete();
}
}
}

View File

@ -241,7 +241,7 @@ namespace osu.Game.Overlays.Dialog
protected override void PopOut() protected override void PopOut()
{ {
if (!actionInvoked) if (!actionInvoked && content.IsPresent)
// In the case a user did not choose an action before a hide was triggered, press the last button. // In the case a user did not choose an action before a hide was triggered, press the last button.
// This is presumed to always be a sane default "cancel" action. // This is presumed to always be a sane default "cancel" action.
buttonsContainer.Last().Click(); buttonsContainer.Last().Click();

View File

@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Mods
} }
} }
foregroundIcon.Highlighted.Value = Selected; foregroundIcon.Selected.Value = Selected;
SelectionChanged?.Invoke(SelectedMod); SelectionChanged?.Invoke(SelectedMod);
return true; return true;

View File

@ -0,0 +1,19 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Overlays.News
{
public abstract class NewsContent : FillFlowContainer
{
protected NewsContent()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Padding = new MarginPadding { Bottom = 100, Top = 20, Horizontal = 50 };
}
}
}

View File

@ -0,0 +1,109 @@
// 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.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using System;
namespace osu.Game.Overlays.News
{
public class NewsHeader : OverlayHeader
{
private const string front_page_string = "Front Page";
private NewsHeaderTitle title;
public readonly Bindable<string> Current = new Bindable<string>(null);
public Action ShowFrontPage;
public NewsHeader()
{
TabControl.AddItem(front_page_string);
TabControl.Current.ValueChanged += e =>
{
if (e.NewValue == front_page_string)
ShowFrontPage?.Invoke();
};
Current.ValueChanged += showArticle;
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
TabControl.AccentColour = colour.Violet;
}
private void showArticle(ValueChangedEvent<string> e)
{
if (e.OldValue != null)
TabControl.RemoveItem(e.OldValue);
if (e.NewValue != null)
{
TabControl.AddItem(e.NewValue);
TabControl.Current.Value = e.NewValue;
title.IsReadingArticle = true;
}
else
{
TabControl.Current.Value = front_page_string;
title.IsReadingArticle = false;
}
}
protected override Drawable CreateBackground() => new NewsHeaderBackground();
protected override Drawable CreateContent() => new Container();
protected override ScreenTitle CreateTitle() => title = new NewsHeaderTitle();
private class NewsHeaderBackground : Sprite
{
public NewsHeaderBackground()
{
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fill;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(@"Headers/news");
}
}
private class NewsHeaderTitle : ScreenTitle
{
private const string article_string = "Article";
public bool IsReadingArticle
{
set => Section = value ? article_string : front_page_string;
}
public NewsHeaderTitle()
{
Title = "News";
IsReadingArticle = false;
}
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/news");
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Violet;
}
}
}
}

View File

@ -0,0 +1,68 @@
// 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.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.News;
namespace osu.Game.Overlays
{
public class NewsOverlay : FullscreenOverlay
{
private NewsHeader header;
//ReSharper disable NotAccessedField.Local
private Container<NewsContent> content;
public readonly Bindable<string> Current = new Bindable<string>(null);
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.PurpleDarkAlternative
},
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
header = new NewsHeader
{
ShowFrontPage = ShowFrontPage
},
content = new Container<NewsContent>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
}
},
},
},
};
header.Current.BindTo(Current);
Current.TriggerChange();
}
public void ShowFrontPage()
{
Current.Value = null;
Show();
}
}
}

View File

@ -60,7 +60,10 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
Children = new Drawable[] Children = new Drawable[]
{ {
dropdown = new AudioDeviceSettingsDropdown() dropdown = new AudioDeviceSettingsDropdown
{
Keywords = new[] { "speaker", "headphone", "output" }
}
}; };
updateItems(); updateItems();

View File

@ -34,6 +34,12 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
Bindable = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence), Bindable = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence),
Items = Enum.GetValues(typeof(IntroSequence)).Cast<IntroSequence>() Items = Enum.GetValues(typeof(IntroSequence)).Cast<IntroSequence>()
}, },
new SettingsDropdown<BackgroundSource>
{
LabelText = "Background source",
Bindable = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource),
Items = Enum.GetValues(typeof(BackgroundSource)).Cast<BackgroundSource>()
}
}; };
} }
} }

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays.Settings.Sections.Audio; using osu.Game.Overlays.Settings.Sections.Audio;
@ -10,6 +12,9 @@ namespace osu.Game.Overlays.Settings.Sections
public class AudioSection : SettingsSection public class AudioSection : SettingsSection
{ {
public override string Header => "Audio"; public override string Header => "Audio";
public override IEnumerable<string> FilterTerms => base.FilterTerms.Concat(new[] { "sound" });
public override IconUsage Icon => FontAwesome.Solid.VolumeUp; public override IconUsage Icon => FontAwesome.Solid.VolumeUp;
public AudioSection() public AudioSection()

View File

@ -38,6 +38,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{ {
LabelText = "Show health display even when you can't fail", LabelText = "Show health display even when you can't fail",
Bindable = config.GetBindable<bool>(OsuSetting.ShowHealthDisplayWhenCantFail), Bindable = config.GetBindable<bool>(OsuSetting.ShowHealthDisplayWhenCantFail),
Keywords = new[] { "hp", "bar" }
}, },
new SettingsCheckbox new SettingsCheckbox
{ {

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Configuration; using osu.Game.Configuration;
@ -10,6 +12,8 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{ {
protected override string Header => "Mods"; protected override string Header => "Mods";
public override IEnumerable<string> FilterTerms => base.FilterTerms.Concat(new[] { "mod" });
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
@ -18,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
new SettingsCheckbox new SettingsCheckbox
{ {
LabelText = "Increase visibility of first object when visual impairment mods are enabled", LabelText = "Increase visibility of first object when visual impairment mods are enabled",
Bindable = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility) Bindable = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility),
}, },
}; };
} }

View File

@ -31,13 +31,15 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{ {
LabelText = "Display beatmaps from", LabelText = "Display beatmaps from",
Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMinimum), Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMinimum),
KeyboardStep = 0.1f KeyboardStep = 0.1f,
Keywords = new[] { "star", "difficulty" }
}, },
new SettingsSlider<double, StarSlider> new SettingsSlider<double, StarSlider>
{ {
LabelText = "up to", LabelText = "up to",
Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMaximum), Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMaximum),
KeyboardStep = 0.1f KeyboardStep = 0.1f,
Keywords = new[] { "star", "difficulty" }
}, },
new SettingsEnumDropdown<RandomSelectAlgorithm> new SettingsEnumDropdown<RandomSelectAlgorithm>
{ {

View File

@ -75,12 +75,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
LabelText = "UI Scaling", LabelText = "UI Scaling",
TransferValueOnCommit = true, TransferValueOnCommit = true,
Bindable = osuConfig.GetBindable<float>(OsuSetting.UIScale), Bindable = osuConfig.GetBindable<float>(OsuSetting.UIScale),
KeyboardStep = 0.01f KeyboardStep = 0.01f,
Keywords = new[] { "scale", "letterbox" },
}, },
new SettingsEnumDropdown<ScalingMode> new SettingsEnumDropdown<ScalingMode>
{ {
LabelText = "Screen Scaling", LabelText = "Screen Scaling",
Bindable = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling), Bindable = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling),
Keywords = new[] { "scale", "letterbox" },
}, },
scalingSettings = new FillFlowContainer<SettingsSlider<float>> scalingSettings = new FillFlowContainer<SettingsSlider<float>>
{ {

View File

@ -76,7 +76,9 @@ namespace osu.Game.Overlays.Settings
} }
} }
public virtual IEnumerable<string> FilterTerms => new[] { LabelText }; public virtual IEnumerable<string> FilterTerms => Keywords == null ? new[] { LabelText } : new List<string>(Keywords) { LabelText }.ToArray();
public IEnumerable<string> Keywords { get; set; }
public bool MatchingFilter public bool MatchingFilter
{ {

View File

@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings
public abstract string Header { get; } public abstract string Header { get; }
public IEnumerable<IFilterable> FilterableChildren => Children.OfType<IFilterable>(); public IEnumerable<IFilterable> FilterableChildren => Children.OfType<IFilterable>();
public IEnumerable<string> FilterTerms => new[] { Header }; public virtual IEnumerable<string> FilterTerms => new[] { Header };
private const int header_size = 26; private const int header_size = 26;
private const int header_margin = 25; private const int header_margin = 25;

View File

@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings
protected abstract string Header { get; } protected abstract string Header { get; }
public IEnumerable<IFilterable> FilterableChildren => Children.OfType<IFilterable>(); public IEnumerable<IFilterable> FilterableChildren => Children.OfType<IFilterable>();
public IEnumerable<string> FilterTerms => new[] { Header }; public virtual IEnumerable<string> FilterTerms => new[] { Header };
public bool MatchingFilter public bool MatchingFilter
{ {

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
/// <summary> /// <summary>
@ -11,5 +13,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "No Mod"; public override string Name => "No Mod";
public override string Acronym => "NM"; public override string Acronym => "NM";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
public override IconUsage Icon => FontAwesome.Solid.Ban;
public override ModType Type => ModType.System;
} }
} }

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.UI
{ {
public class ModIcon : Container, IHasTooltip public class ModIcon : Container, IHasTooltip
{ {
public readonly BindableBool Highlighted = new BindableBool(); public readonly BindableBool Selected = new BindableBool();
private readonly SpriteIcon modIcon; private readonly SpriteIcon modIcon;
private readonly SpriteIcon background; private readonly SpriteIcon background;
@ -34,9 +34,11 @@ namespace osu.Game.Rulesets.UI
public virtual string TooltipText { get; } public virtual string TooltipText { get; }
protected Mod Mod { get; private set; }
public ModIcon(Mod mod) public ModIcon(Mod mod)
{ {
if (mod == null) throw new ArgumentNullException(nameof(mod)); Mod = mod ?? throw new ArgumentNullException(nameof(mod));
type = mod.Type; type = mod.Type;
@ -98,13 +100,19 @@ namespace osu.Game.Rulesets.UI
backgroundColour = colours.Pink; backgroundColour = colours.Pink;
highlightedColour = colours.PinkLight; highlightedColour = colours.PinkLight;
break; break;
case ModType.System:
backgroundColour = colours.Gray6;
highlightedColour = colours.Gray7;
modIcon.Colour = colours.Yellow;
break;
} }
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
Highlighted.BindValueChanged(highlighted => background.Colour = highlighted.NewValue ? highlightedColour : backgroundColour, true); Selected.BindValueChanged(selected => background.Colour = selected.NewValue ? highlightedColour : backgroundColour, true);
} }
} }
} }

View File

@ -6,7 +6,6 @@ using System.Threading;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
@ -107,22 +106,6 @@ namespace osu.Game.Screens.Backgrounds
return base.Equals(other) && beatmap == otherBeatmapBackground.Beatmap; return base.Equals(other) && beatmap == otherBeatmapBackground.Beatmap;
} }
protected class BeatmapBackground : Background
{
public readonly WorkingBeatmap Beatmap;
public BeatmapBackground(WorkingBeatmap beatmap)
{
Beatmap = beatmap;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Sprite.Texture = Beatmap?.Background ?? textures.Get(@"Backgrounds/bg1");
}
}
public class DimmableBackground : UserDimContainer public class DimmableBackground : UserDimContainer
{ {
/// <summary> /// <summary>

View File

@ -6,6 +6,8 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -24,6 +26,10 @@ namespace osu.Game.Screens.Backgrounds
private Bindable<User> user; private Bindable<User> user;
private Bindable<Skin> skin; private Bindable<Skin> skin;
private Bindable<BackgroundSource> mode;
[Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; }
public BackgroundScreenDefault(bool animateOnEnter = true) public BackgroundScreenDefault(bool animateOnEnter = true)
: base(animateOnEnter) : base(animateOnEnter)
@ -31,13 +37,16 @@ namespace osu.Game.Screens.Backgrounds
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(IAPIProvider api, SkinManager skinManager) private void load(IAPIProvider api, SkinManager skinManager, OsuConfigManager config)
{ {
user = api.LocalUser.GetBoundCopy(); user = api.LocalUser.GetBoundCopy();
skin = skinManager.CurrentSkin.GetBoundCopy(); skin = skinManager.CurrentSkin.GetBoundCopy();
mode = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource);
user.ValueChanged += _ => Next(); user.ValueChanged += _ => Next();
skin.ValueChanged += _ => Next(); skin.ValueChanged += _ => Next();
mode.ValueChanged += _ => Next();
beatmap.ValueChanged += _ => Next();
currentDisplay = RNG.Next(0, background_count); currentDisplay = RNG.Next(0, background_count);
@ -66,7 +75,18 @@ namespace osu.Game.Screens.Backgrounds
Background newBackground; Background newBackground;
if (user.Value?.IsSupporter ?? false) if (user.Value?.IsSupporter ?? false)
newBackground = new SkinnedBackground(skin.Value, backgroundName); {
switch (mode.Value)
{
case BackgroundSource.Beatmap:
newBackground = new BeatmapBackground(beatmap.Value, backgroundName);
break;
default:
newBackground = new SkinnedBackground(skin.Value, backgroundName);
break;
}
}
else else
newBackground = new Background(backgroundName); newBackground = new Background(backgroundName);

View File

@ -313,14 +313,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// Attempts to select any hovered blueprints. /// Attempts to select any hovered blueprints.
/// </summary> /// </summary>
/// <param name="e">The input event that triggered this selection.</param> /// <param name="e">The input event that triggered this selection.</param>
private void beginClickSelection(UIEvent e) private void beginClickSelection(MouseButtonEvent e)
{ {
Debug.Assert(!clickSelectionBegan); Debug.Assert(!clickSelectionBegan);
// If a select blueprint is already hovered, disallow changes in selection. // Deselections are only allowed for control + left clicks
// Exception is made when holding control, as deselection should still be allowed. bool allowDeselection = e.ControlPressed && e.Button == MouseButton.Left;
if (!e.CurrentState.Keyboard.ControlPressed &&
selectionHandler.SelectedBlueprints.Any(s => s.IsHovered)) // Todo: This is probably incorrectly disallowing multiple selections on stacked objects
if (!allowDeselection && selectionHandler.SelectedBlueprints.Any(s => s.IsHovered))
return; return;
foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints) foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints)

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
@ -170,8 +169,6 @@ namespace osu.Game.Screens.Menu
track.Start(); track.Start();
} }
} }
Beatmap.ValueChanged += beatmap_ValueChanged;
} }
private bool exitConfirmed; private bool exitConfirmed;
@ -220,14 +217,6 @@ namespace osu.Game.Screens.Menu
seq.OnAbort(_ => buttons.SetOsuLogo(null)); seq.OnAbort(_ => buttons.SetOsuLogo(null));
} }
private void beatmap_ValueChanged(ValueChangedEvent<WorkingBeatmap> e)
{
if (!this.IsCurrentScreen())
return;
((BackgroundScreenDefault)Background).Next();
}
public override void OnSuspending(IScreen next) public override void OnSuspending(IScreen next)
{ {
base.OnSuspending(next); base.OnSuspending(next);

View File

@ -19,6 +19,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.MathUtils;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
@ -35,6 +36,9 @@ namespace osu.Game.Screens.Play
private FadeContainer fadeContainer; private FadeContainer fadeContainer;
private double displayTime; private double displayTime;
[Resolved]
private GameplayClock gameplayClock { get; set; }
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
/// <summary> /// <summary>
@ -45,8 +49,6 @@ namespace osu.Game.Screens.Play
{ {
this.startTime = startTime; this.startTime = startTime;
Show();
RelativePositionAxes = Axes.Both; RelativePositionAxes = Axes.Both;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -57,13 +59,8 @@ namespace osu.Game.Screens.Play
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(OsuColour colours, GameplayClock clock) private void load(OsuColour colours)
{ {
var baseClock = Clock;
if (clock != null)
Clock = clock;
Children = new Drawable[] Children = new Drawable[]
{ {
fadeContainer = new FadeContainer fadeContainer = new FadeContainer
@ -73,7 +70,6 @@ namespace osu.Game.Screens.Play
{ {
button = new Button button = new Button
{ {
Clock = baseClock,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
}, },
@ -92,40 +88,40 @@ namespace osu.Game.Screens.Play
private const double fade_time = 300; private const double fade_time = 300;
private double beginFadeTime => startTime - fade_time; private double fadeOutBeginTime => startTime - GameplayClockContainer.MINIMUM_SKIP_TIME;
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
// skip is not required if there is no extra "empty" time to skip. // skip is not required if there is no extra "empty" time to skip.
if (Clock.CurrentTime > beginFadeTime - GameplayClockContainer.MINIMUM_SKIP_TIME) // we may need to remove this if rewinding before the initial player load position becomes a thing.
if (fadeOutBeginTime < gameplayClock.CurrentTime)
{ {
Alpha = 0;
Expire(); Expire();
return; return;
} }
this.FadeInFromZero(fade_time);
using (BeginAbsoluteSequence(beginFadeTime))
this.FadeOut(fade_time);
button.Action = () => RequestSkip?.Invoke(); button.Action = () => RequestSkip?.Invoke();
displayTime = gameplayClock.CurrentTime;
displayTime = Time.Current; Show();
Expire();
} }
protected override void PopIn() => this.FadeIn(); protected override void PopIn() => this.FadeIn(fade_time);
protected override void PopOut() => this.FadeOut(); protected override void PopOut() => this.FadeOut(fade_time);
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint);
button.Enabled.Value = Time.Current < startTime - GameplayClockContainer.MINIMUM_SKIP_TIME; var progress = Math.Max(0, 1 - (gameplayClock.CurrentTime - displayTime) / (fadeOutBeginTime - displayTime));
remainingTimeBox.Width = (float)Interpolation.Lerp(remainingTimeBox.Width, progress, Math.Clamp(Time.Elapsed / 40, 0, 1));
button.Enabled.Value = progress > 0;
State.Value = progress > 0 ? Visibility.Visible : Visibility.Hidden;
} }
protected override bool OnMouseMove(MouseMoveEvent e) protected override bool OnMouseMove(MouseMoveEvent e)

View File

@ -640,10 +640,19 @@ namespace osu.Game.Screens.Select
itemsCache.Validate(); itemsCache.Validate();
} }
private bool firstScroll = true;
private void updateScrollPosition() private void updateScrollPosition()
{ {
if (scrollTarget != null) if (scrollTarget != null)
{ {
if (firstScroll)
{
// reduce movement when first displaying the carousel.
scroll.ScrollTo(scrollTarget.Value - 200, false);
firstScroll = false;
}
scroll.ScrollTo(scrollTarget.Value); scroll.ScrollTo(scrollTarget.Value);
scrollPositionCache.Validate(); scrollPositionCache.Validate();
} }

View File

@ -2,31 +2,34 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading.Tasks;
using osu.Framework.Logging; using osu.Framework.Logging;
using SharpRaven; using Sentry;
using SharpRaven.Data;
namespace osu.Game.Utils namespace osu.Game.Utils
{ {
/// <summary> /// <summary>
/// Report errors to sentry. /// Report errors to sentry.
/// </summary> /// </summary>
public class RavenLogger : IDisposable public class SentryLogger : IDisposable
{ {
private readonly RavenClient raven = new RavenClient("https://5e342cd55f294edebdc9ad604d28bbd3@sentry.io/1255255"); private SentryClient sentry;
private Scope sentryScope;
private readonly List<Task> tasks = new List<Task>(); public SentryLogger(OsuGame game)
public RavenLogger(OsuGame game)
{ {
raven.Release = game.Version;
if (!game.IsDeployedBuild) return; if (!game.IsDeployedBuild) return;
var options = new SentryOptions
{
Dsn = new Dsn("https://5e342cd55f294edebdc9ad604d28bbd3@sentry.io/1255255"),
Release = game.Version
};
sentry = new SentryClient(options);
sentryScope = new Scope(options);
Exception lastException = null; Exception lastException = null;
Logger.NewEntry += entry => Logger.NewEntry += entry =>
@ -46,10 +49,10 @@ namespace osu.Game.Utils
return; return;
lastException = exception; lastException = exception;
queuePendingTask(raven.CaptureAsync(new SentryEvent(exception) { Message = entry.Message })); sentry.CaptureEvent(new SentryEvent(exception) { Message = entry.Message }, sentryScope);
} }
else else
raven.AddTrail(new Breadcrumb(entry.Target.ToString(), BreadcrumbType.Navigation) { Message = entry.Message }); sentryScope.AddBreadcrumb(DateTimeOffset.Now, entry.Message, entry.Target.ToString(), "navigation");
}; };
} }
@ -81,19 +84,9 @@ namespace osu.Game.Utils
return true; return true;
} }
private void queuePendingTask(Task<string> task)
{
lock (tasks) tasks.Add(task);
task.ContinueWith(_ =>
{
lock (tasks)
tasks.Remove(task);
});
}
#region Disposal #region Disposal
~RavenLogger() ~SentryLogger()
{ {
Dispose(false); Dispose(false);
} }
@ -112,7 +105,9 @@ namespace osu.Game.Utils
return; return;
isDisposed = true; isDisposed = true;
lock (tasks) Task.WaitAll(tasks.ToArray(), 5000); sentry?.Dispose();
sentry = null;
sentryScope = null;
} }
#endregion #endregion

View File

@ -22,9 +22,9 @@
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1121.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.1121.0" />
<PackageReference Include="Sentry" Version="1.2.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.6.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="4.6.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>