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

Merge branch 'master' into fix-lounge-loading

This commit is contained in:
Salman Ahmed 2021-08-19 06:26:15 +03:00
commit b984780d6a
20 changed files with 460 additions and 315 deletions

View File

@ -1,65 +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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Screens.Ranking.Expanded;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneStarRatingDisplay : OsuTestScene
{
[Test]
public void TestDisplay()
{
AddStep("load displays", () => Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
ChildrenEnumerable = new[]
{
1.23,
2.34,
3.45,
4.56,
5.67,
6.78,
10.11,
}.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
})
});
}
[Test]
public void TestChangingStarRatingDisplay()
{
StarRatingDisplay starRating = null;
AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(3f),
});
AddRepeatStep("set random value", () =>
{
starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
}, 10);
AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
{
if (starRating != null)
starRating.Current.Value = new StarDifficulty(d, 1);
});
}
}
}

View File

@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Settings
[SetUpSteps] [SetUpSteps]
public void SetUpSteps() public void SetUpSteps()
{ {
AddUntilStep("wait for load", () => panel.ChildrenOfType<GlobalKeyBindingsSection>().Any());
AddStep("Scroll to top", () => panel.ChildrenOfType<SettingsPanel.SettingsSectionsContainer>().First().ScrollToTop()); AddStep("Scroll to top", () => panel.ChildrenOfType<SettingsPanel.SettingsSectionsContainer>().First().ScrollToTop());
AddWaitStep("wait for scroll", 5); AddWaitStep("wait for scroll", 5);
} }

View File

@ -4,6 +4,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Overlays; using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Settings namespace osu.Game.Tests.Visual.Settings
@ -11,27 +12,39 @@ namespace osu.Game.Tests.Visual.Settings
[TestFixture] [TestFixture]
public class TestSceneSettingsPanel : OsuTestScene public class TestSceneSettingsPanel : OsuTestScene
{ {
private readonly SettingsPanel settings; private SettingsPanel settings;
private readonly DialogOverlay dialogOverlay; private DialogOverlay dialogOverlay;
public TestSceneSettingsPanel() [SetUpSteps]
public void SetUpSteps()
{ {
settings = new SettingsOverlay AddStep("create settings", () =>
{ {
State = { Value = Visibility.Visible } settings?.Expire();
};
Add(dialogOverlay = new DialogOverlay Add(settings = new SettingsOverlay
{ {
Depth = -1 State = { Value = Visibility.Visible }
});
}); });
} }
[Test]
public void ToggleVisibility()
{
AddWaitStep("wait some", 5);
AddToggleStep("toggle editor visibility", visible => settings.ToggleVisibility());
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Dependencies.Cache(dialogOverlay); Add(dialogOverlay = new DialogOverlay
{
Depth = -1
});
Add(settings); Dependencies.Cache(dialogOverlay);
} }
} }
} }

View File

@ -0,0 +1,97 @@
// 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 NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneStarRatingDisplay : OsuTestScene
{
[TestCase(StarRatingDisplaySize.Regular)]
[TestCase(StarRatingDisplaySize.Small)]
public void TestDisplay(StarRatingDisplaySize size)
{
AddStep("load displays", () =>
{
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(2f),
Direction = FillDirection.Horizontal,
ChildrenEnumerable = Enumerable.Range(0, 15).Select(i => new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(2f),
Direction = FillDirection.Vertical,
ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i * (i >= 11 ? 25f : 1f) + j * 0.1f, 0), size)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}),
})
};
});
}
[Test]
public void TestSpectrum()
{
StarRatingDisplay starRating = null;
BindableDouble starRatingNumeric;
AddStep("load display", () =>
{
Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(3f),
};
});
AddStep("transform over spectrum", () =>
{
starRatingNumeric = new BindableDouble();
starRatingNumeric.BindValueChanged(val => starRating.Current.Value = new StarDifficulty(val.NewValue, 1));
this.TransformBindableTo(starRatingNumeric, 10, 10000, Easing.OutQuint);
});
}
[Test]
public void TestChangingStarRatingDisplay()
{
StarRatingDisplay starRating = null;
AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(3f),
});
AddRepeatStep("set random value", () =>
{
starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
}, 10);
AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
{
if (starRating != null)
starRating.Current.Value = new StarDifficulty(d, 1);
});
}
}
}

View File

@ -0,0 +1,145 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
/// <summary>
/// A pill that displays the star rating of a beatmap.
/// </summary>
public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue<StarDifficulty>
{
private readonly Box background;
private readonly SpriteIcon starIcon;
private readonly OsuSpriteText starsText;
private readonly BindableWithCurrent<StarDifficulty> current = new BindableWithCurrent<StarDifficulty>();
public Bindable<StarDifficulty> Current
{
get => current.Current;
set => current.Current = value;
}
[Resolved]
private OsuColour colours { get; set; }
[Resolved(canBeNull: true)]
private OverlayColourProvider colourProvider { get; set; }
/// <summary>
/// Creates a new <see cref="StarRatingDisplay"/> using an already computed <see cref="StarDifficulty"/>.
/// </summary>
/// <param name="starDifficulty">The already computed <see cref="StarDifficulty"/> to display.</param>
/// <param name="size">The size of the star rating display.</param>
public StarRatingDisplay(StarDifficulty starDifficulty, StarRatingDisplaySize size = StarRatingDisplaySize.Regular)
{
Current.Value = starDifficulty;
AutoSizeAxes = Axes.Both;
MarginPadding margin = default;
switch (size)
{
case StarRatingDisplaySize.Small:
margin = new MarginPadding { Horizontal = 7f };
break;
case StarRatingDisplaySize.Range:
margin = new MarginPadding { Horizontal = 8f };
break;
case StarRatingDisplaySize.Regular:
margin = new MarginPadding { Horizontal = 8f, Vertical = 2f };
break;
}
InternalChild = new CircularContainer
{
Masking = true,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
new GridContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Margin = margin,
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Absolute, 3f),
new Dimension(GridSizeMode.AutoSize, minSize: 25f),
},
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
Content = new[]
{
new[]
{
starIcon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.Solid.Star,
Size = new Vector2(8f),
},
Empty(),
starsText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Bottom = 1.5f },
// todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f
// see https://github.com/ppy/osu-framework/issues/3271.
Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold),
Shadow = false,
}
}
}
},
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(c =>
{
starsText.Text = c.NewValue.Stars.ToString("0.00");
background.Colour = colours.ForStarDifficulty(c.NewValue.Stars);
starIcon.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47");
starsText.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f);
}, true);
}
}
public enum StarRatingDisplaySize
{
Small,
Range,
Regular,
}
}

View File

@ -22,7 +22,10 @@ namespace osu.Game.Graphics.UserInterface
public void TakeFocus() public void TakeFocus()
{ {
if (allowImmediateFocus) GetContainingInputManager().ChangeFocus(this); if (!allowImmediateFocus)
return;
Scheduler.Add(() => GetContainingInputManager().ChangeFocus(this), false);
} }
public bool HoldFocus public bool HoldFocus

View File

@ -6,6 +6,7 @@ using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
@ -57,18 +58,13 @@ namespace osu.Game.Graphics.UserInterface
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
Colour = GlowColour, Colour = GlowColour.Opacity(0),
Type = EdgeEffectType.Glow, Type = EdgeEffectType.Glow,
Radius = 10, Radius = 10,
Roundness = 8, Roundness = 8,
}; };
} }
protected override void LoadComplete()
{
FadeEdgeEffectTo(0);
}
private bool glowing; private bool glowing;
public bool Glowing public bool Glowing

View File

@ -36,6 +36,7 @@ namespace osu.Game.Graphics.UserInterface
public Color4 BackgroundColour public Color4 BackgroundColour
{ {
get => backgroundColour ?? Color4.White;
set set
{ {
backgroundColour = value; backgroundColour = value;

View File

@ -69,6 +69,7 @@ namespace osu.Game.Graphics.UserInterface
BackgroundColour = Color4.Black.Opacity(0.5f); BackgroundColour = Color4.Black.Opacity(0.5f);
MaskingContainer.CornerRadius = corner_radius; MaskingContainer.CornerRadius = corner_radius;
Alpha = 0;
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
ItemsContainer.Padding = new MarginPadding(5); ItemsContainer.Padding = new MarginPadding(5);
@ -94,9 +95,11 @@ namespace osu.Game.Graphics.UserInterface
protected override void AnimateClose() protected override void AnimateClose()
{ {
this.FadeOut(300, Easing.OutQuint);
if (wasOpened) if (wasOpened)
{
this.FadeOut(300, Easing.OutQuint);
sampleClose?.Play(); sampleClose?.Play();
}
} }
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring

View File

@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
@ -38,12 +39,12 @@ namespace osu.Game.Overlays
current.ValueChanged += _ => UpdateState(); current.ValueChanged += _ => UpdateState();
current.DefaultChanged += _ => UpdateState(); current.DefaultChanged += _ => UpdateState();
current.DisabledChanged += _ => UpdateState(); current.DisabledChanged += _ => UpdateState();
UpdateState();
if (IsLoaded)
UpdateState();
} }
} }
private Color4 buttonColour;
private bool hovering; private bool hovering;
public RestoreDefaultValueButton() public RestoreDefaultValueButton()
@ -58,12 +59,11 @@ namespace osu.Game.Overlays
private void load(OsuColour colour) private void load(OsuColour colour)
{ {
BackgroundColour = colour.Yellow; BackgroundColour = colour.Yellow;
buttonColour = colour.Yellow;
Content.Width = 0.33f; Content.Width = 0.33f;
Content.CornerRadius = 3; Content.CornerRadius = 3;
Content.EdgeEffect = new EdgeEffectParameters Content.EdgeEffect = new EdgeEffectParameters
{ {
Colour = buttonColour.Opacity(0.1f), Colour = BackgroundColour.Opacity(0.1f),
Type = EdgeEffectType.Glow, Type = EdgeEffectType.Glow,
Radius = 2, Radius = 2,
}; };
@ -81,7 +81,10 @@ namespace osu.Game.Overlays
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
UpdateState();
// avoid unnecessary transforms on first display.
Alpha = currentAlpha;
Background.Colour = currentColour;
} }
public LocalisableString TooltipText => "revert to default"; public LocalisableString TooltipText => "revert to default";
@ -101,14 +104,16 @@ namespace osu.Game.Overlays
public void UpdateState() => Scheduler.AddOnce(updateState); public void UpdateState() => Scheduler.AddOnce(updateState);
private float currentAlpha => current.IsDefault ? 0f : hovering && !current.Disabled ? 1f : 0.65f;
private ColourInfo currentColour => current.Disabled ? Color4.Gray : BackgroundColour;
private void updateState() private void updateState()
{ {
if (current == null) if (current == null)
return; return;
this.FadeTo(current.IsDefault ? 0f : this.FadeTo(currentAlpha, 200, Easing.OutQuint);
hovering && !current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); Background.FadeColour(currentColour, 200, Easing.OutQuint);
this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint);
} }
} }
} }

View File

@ -21,17 +21,26 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
private SettingsDropdown<string> dropdown; private SettingsDropdown<string> dropdown;
protected override void Dispose(bool isDisposing) [BackgroundDependencyLoader]
private void load()
{ {
base.Dispose(isDisposing); Children = new Drawable[]
if (audio != null)
{ {
audio.OnNewDevice -= onDeviceChanged; dropdown = new AudioDeviceSettingsDropdown
audio.OnLostDevice -= onDeviceChanged; {
} Keywords = new[] { "speaker", "headphone", "output" }
}
};
updateItems();
audio.OnNewDevice += onDeviceChanged;
audio.OnLostDevice += onDeviceChanged;
dropdown.Current = audio.AudioDevice;
} }
private void onDeviceChanged(string name) => updateItems();
private void updateItems() private void updateItems()
{ {
var deviceItems = new List<string> { string.Empty }; var deviceItems = new List<string> { string.Empty };
@ -50,26 +59,15 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
dropdown.Items = deviceItems.Distinct().ToList(); dropdown.Items = deviceItems.Distinct().ToList();
} }
private void onDeviceChanged(string name) => updateItems(); protected override void Dispose(bool isDisposing)
protected override void LoadComplete()
{ {
base.LoadComplete(); base.Dispose(isDisposing);
Children = new Drawable[] if (audio != null)
{ {
dropdown = new AudioDeviceSettingsDropdown audio.OnNewDevice -= onDeviceChanged;
{ audio.OnLostDevice -= onDeviceChanged;
Keywords = new[] { "speaker", "headphone", "output" } }
}
};
updateItems();
dropdown.Current = audio.AudioDevice;
audio.OnNewDevice += onDeviceChanged;
audio.OnLostDevice += onDeviceChanged;
} }
private class AudioDeviceSettingsDropdown : SettingsDropdown<string> private class AudioDeviceSettingsDropdown : SettingsDropdown<string>

View File

@ -98,8 +98,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
AutoSizeDuration = transition_duration,
AutoSizeEasing = Easing.OutQuint,
Masking = true, Masking = true,
Children = new[] Children = new[]
{ {
@ -176,13 +174,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
scalingMode.BindValueChanged(mode => scalingMode.BindValueChanged(mode =>
{ {
scalingSettings.ClearTransforms(); scalingSettings.ClearTransforms();
scalingSettings.AutoSizeAxes = mode.NewValue != ScalingMode.Off ? Axes.Y : Axes.None; scalingSettings.AutoSizeDuration = transition_duration;
scalingSettings.AutoSizeEasing = Easing.OutQuint;
if (mode.NewValue == ScalingMode.Off) updateScalingModeVisibility();
scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); });
scalingSettings.ForEach(s => s.TransferValueOnCommit = mode.NewValue == ScalingMode.Everything); // initial update bypasses transforms
}, true); updateScalingModeVisibility();
void updateResolutionDropdown() void updateResolutionDropdown()
{ {
@ -191,6 +190,15 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
else else
resolutionDropdown.Hide(); resolutionDropdown.Hide();
} }
void updateScalingModeVisibility()
{
if (scalingMode.Value == ScalingMode.Off)
scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
scalingSettings.AutoSizeAxes = scalingMode.Value != ScalingMode.Off ? Axes.Y : Axes.None;
scalingSettings.ForEach(s => s.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything);
}
} }
private void bindPreviewEvent(Bindable<float> bindable) private void bindPreviewEvent(Bindable<float> bindable)

View File

@ -93,15 +93,13 @@ namespace osu.Game.Overlays.Settings
public bool MatchingFilter public bool MatchingFilter
{ {
set => this.FadeTo(value ? 1 : 0); set => Alpha = value ? 1 : 0;
} }
public bool FilteringActive { get; set; } public bool FilteringActive { get; set; }
public event Action SettingChanged; public event Action SettingChanged;
private readonly RestoreDefaultValueButton<T> restoreDefaultButton;
protected SettingsItem() protected SettingsItem()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -110,7 +108,6 @@ namespace osu.Game.Overlays.Settings
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
restoreDefaultButton = new RestoreDefaultValueButton<T>(),
FlowContent = new FillFlowContainer FlowContent = new FillFlowContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -123,7 +120,7 @@ namespace osu.Game.Overlays.Settings
}, },
}; };
// all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is // IMPORTANT: all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is
// never loaded, but requires bindable storage. // never loaded, but requires bindable storage.
if (controlWithCurrent == null) if (controlWithCurrent == null)
throw new ArgumentException(@$"Control created via {nameof(CreateControl)} must implement {nameof(IHasCurrentValue<T>)}"); throw new ArgumentException(@$"Control created via {nameof(CreateControl)} must implement {nameof(IHasCurrentValue<T>)}");
@ -132,12 +129,17 @@ namespace osu.Game.Overlays.Settings
controlWithCurrent.Current.DisabledChanged += _ => updateDisabled(); controlWithCurrent.Current.DisabledChanged += _ => updateDisabled();
} }
protected override void LoadComplete() [BackgroundDependencyLoader]
private void load()
{ {
base.LoadComplete(); // intentionally done before LoadComplete to avoid overhead.
if (ShowsDefaultIndicator) if (ShowsDefaultIndicator)
restoreDefaultButton.Current = controlWithCurrent.Current; {
AddInternal(new RestoreDefaultValueButton<T>
{
Current = controlWithCurrent.Current,
});
}
} }
private void updateDisabled() private void updateDisabled()

View File

@ -22,6 +22,9 @@ namespace osu.Game.Overlays.Settings
private readonly Box selectionIndicator; private readonly Box selectionIndicator;
private readonly Container text; private readonly Container text;
// always consider as part of flow, even when not visible (for the sake of the initial animation).
public override bool IsPresent => true;
private SettingsSection section; private SettingsSection section;
public SettingsSection Section public SettingsSection Section

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -58,6 +59,12 @@ namespace osu.Game.Overlays
private readonly bool showSidebar; private readonly bool showSidebar;
private LoadingLayer loading;
private readonly List<SettingsSection> loadableSections = new List<SettingsSection>();
private Task sectionsLoadingTask;
protected SettingsPanel(bool showSidebar) protected SettingsPanel(bool showSidebar)
{ {
this.showSidebar = showSidebar; this.showSidebar = showSidebar;
@ -86,69 +93,48 @@ namespace osu.Game.Overlays
Colour = OsuColour.Gray(0.05f), Colour = OsuColour.Gray(0.05f),
Alpha = 1, Alpha = 1,
}, },
SectionsContainer = new SettingsSectionsContainer loading = new LoadingLayer
{ {
Masking = true, State = { Value = Visibility.Visible }
RelativeSizeAxes = Axes.Both, }
ExpandableHeader = CreateHeader(),
FixedHeader = searchTextBox = new SeekLimitedSearchTextBox
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Width = 0.95f,
Margin = new MarginPadding
{
Top = 20,
Bottom = 20
},
},
Footer = CreateFooter()
},
} }
}; };
Add(SectionsContainer = new SettingsSectionsContainer
{
Masking = true,
RelativeSizeAxes = Axes.Both,
ExpandableHeader = CreateHeader(),
FixedHeader = searchTextBox = new SeekLimitedSearchTextBox
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Width = 0.95f,
Margin = new MarginPadding
{
Top = 20,
Bottom = 20
},
},
Footer = CreateFooter().With(f => f.Alpha = 0)
});
if (showSidebar) if (showSidebar)
{ {
AddInternal(Sidebar = new Sidebar { Width = sidebar_width }); AddInternal(Sidebar = new Sidebar { Width = sidebar_width });
SectionsContainer.SelectedSection.ValueChanged += section =>
{
selectedSidebarButton.Selected = false;
selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue);
selectedSidebarButton.Selected = true;
};
} }
searchTextBox.Current.ValueChanged += term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue;
CreateSections()?.ForEach(AddSection); CreateSections()?.ForEach(AddSection);
} }
protected void AddSection(SettingsSection section) protected void AddSection(SettingsSection section)
{ {
SectionsContainer.Add(section); if (IsLoaded)
// just to keep things simple. can be accommodated for if we ever need it.
throw new InvalidOperationException("All sections must be added before the panel is loaded.");
if (Sidebar != null) loadableSections.Add(section);
{
var button = new SidebarButton
{
Section = section,
Action = () =>
{
SectionsContainer.ScrollTo(section);
Sidebar.State = ExpandedState.Contracted;
},
};
Sidebar.Add(button);
if (selectedSidebarButton == null)
{
selectedSidebarButton = Sidebar.Children.First();
selectedSidebarButton.Selected = true;
}
}
} }
protected virtual Drawable CreateHeader() => new Container(); protected virtual Drawable CreateHeader() => new Container();
@ -161,6 +147,12 @@ namespace osu.Game.Overlays
ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint);
// delay load enough to ensure it doesn't overlap with the initial animation.
// this is done as there is still a brief stutter during load completion which is more visible if the transition is in progress.
// the eventual goal would be to remove the need for this by splitting up load into smaller work pieces, or fixing the remaining
// load complete overheads.
Scheduler.AddDelayed(loadSections, TRANSITION_LENGTH / 3);
Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint);
@ -199,6 +191,78 @@ namespace osu.Game.Overlays
Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
} }
private const double fade_in_duration = 1000;
private void loadSections()
{
if (sectionsLoadingTask != null)
return;
sectionsLoadingTask = LoadComponentsAsync(loadableSections, sections =>
{
SectionsContainer.AddRange(sections);
SectionsContainer.Footer.FadeInFromZero(fade_in_duration, Easing.OutQuint);
SectionsContainer.SearchContainer.FadeInFromZero(fade_in_duration, Easing.OutQuint);
loading.Hide();
searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue, true);
searchTextBox.TakeFocus();
loadSidebarButtons();
});
}
private void loadSidebarButtons()
{
if (Sidebar == null)
return;
LoadComponentsAsync(createSidebarButtons(), buttons =>
{
float delay = 0;
foreach (var button in buttons)
{
Sidebar.Add(button);
button.FadeOut()
.Delay(delay)
.FadeInFromZero(fade_in_duration, Easing.OutQuint);
delay += 40;
}
SectionsContainer.SelectedSection.BindValueChanged(section =>
{
if (selectedSidebarButton != null)
selectedSidebarButton.Selected = false;
selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue);
selectedSidebarButton.Selected = true;
}, true);
});
}
private IEnumerable<SidebarButton> createSidebarButtons()
{
foreach (var section in SectionsContainer)
{
yield return new SidebarButton
{
Section = section,
Action = () =>
{
if (!SectionsContainer.IsLoaded)
return;
SectionsContainer.ScrollTo(section);
Sidebar.State = ExpandedState.Contracted;
},
};
}
}
private class NonMaskedContent : Container<Drawable> private class NonMaskedContent : Container<Drawable>
{ {
// masking breaks the pan-out transform with nested sub-settings panels. // masking breaks the pan-out transform with nested sub-settings panels.

View File

@ -10,8 +10,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Screens.Ranking.Expanded;
using osuTK; using osuTK;
namespace osu.Game.Screens.OnlinePlay.Components namespace osu.Game.Screens.OnlinePlay.Components
@ -64,8 +64,8 @@ namespace osu.Game.Screens.OnlinePlay.Components
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
minDisplay = new StarRatingDisplay(default), minDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Range),
maxDisplay = new StarRatingDisplay(default) maxDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Range)
} }
} }
}; };

View File

@ -10,12 +10,12 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation; using osu.Framework.Localisation;
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.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Ranking.Expanded;
using osuTK; using osuTK;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation; using osu.Framework.Localisation;
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.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;

View File

@ -1,129 +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.Globalization;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking.Expanded
{
/// <summary>
/// A pill that displays the star rating of a <see cref="BeatmapInfo"/>.
/// </summary>
public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue<StarDifficulty>
{
private Box background;
private FillFlowContainer content;
private OsuTextFlowContainer textFlow;
[Resolved]
private OsuColour colours { get; set; }
private readonly BindableWithCurrent<StarDifficulty> current = new BindableWithCurrent<StarDifficulty>();
public Bindable<StarDifficulty> Current
{
get => current.Current;
set => current.Current = value;
}
/// <summary>
/// Creates a new <see cref="StarRatingDisplay"/> using an already computed <see cref="StarDifficulty"/>.
/// </summary>
/// <param name="starDifficulty">The already computed <see cref="StarDifficulty"/> to display the star difficulty of.</param>
public StarRatingDisplay(StarDifficulty starDifficulty)
{
Current.Value = starDifficulty;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache)
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
}
},
content = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 8, Vertical = 4 },
Direction = FillDirection.Horizontal,
Spacing = new Vector2(2, 0),
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(7),
Icon = FontAwesome.Solid.Star,
},
textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
TextAnchor = Anchor.BottomLeft,
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(_ => updateDisplay(), true);
}
private void updateDisplay()
{
var starRatingParts = Current.Value.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.');
string wholePart = starRatingParts[0];
string fractionPart = starRatingParts[1];
string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
var stars = Current.Value.Stars;
background.Colour = colours.ForStarDifficulty(stars);
content.Colour = stars >= 6.5 ? colours.Orange1 : Color4.Black;
textFlow.Clear();
textFlow.AddText($"{wholePart}", s =>
{
s.Font = s.Font.With(size: 14);
s.UseFullGlyphHeight = false;
});
textFlow.AddText($"{separator}{fractionPart}", s =>
{
s.Font = s.Font.With(size: 7);
s.UseFullGlyphHeight = false;
});
}
}
}

View File

@ -28,7 +28,6 @@ using osu.Game.Extensions;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select