mirror of
https://github.com/ppy/osu.git
synced 2025-02-20 05:25:36 +08:00
Merge branch 'master' into remove-current-room
This commit is contained in:
commit
ceb07826d7
@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -61,10 +62,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
));
|
||||
AddStep("scroll up", () => triggerUserScroll(1));
|
||||
AddStep("scroll down", () => triggerUserScroll(-1));
|
||||
AddStep("scroll up a bit", () => triggerUserScroll(0.1f));
|
||||
AddStep("scroll down a bit", () => triggerUserScroll(-0.1f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCorrectSectionSelected()
|
||||
public void TestCorrectSelectionAndVisibleTop()
|
||||
{
|
||||
const int sections_count = 11;
|
||||
float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f };
|
||||
@ -79,6 +82,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
AddStep($"scroll to section {scrollIndex + 1}", () => container.ScrollTo(container.Children[scrollIndex]));
|
||||
AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]);
|
||||
AddUntilStep("section top is visible", () =>
|
||||
{
|
||||
float scrollPosition = container.ChildrenOfType<UserTrackingScrollContainer>().First().Current;
|
||||
float sectionTop = container.Children[scrollIndex].BoundingBox.Top;
|
||||
return scrollPosition < sectionTop;
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 1; i < sections_count; i++)
|
||||
|
@ -365,6 +365,10 @@ namespace osu.Game.Beatmaps
|
||||
queryable = beatmaps.BeatmapSetsOverview;
|
||||
break;
|
||||
|
||||
case IncludedDetails.AllButRuleset:
|
||||
queryable = beatmaps.BeatmapSetsWithoutRuleset;
|
||||
break;
|
||||
|
||||
case IncludedDetails.AllButFiles:
|
||||
queryable = beatmaps.BeatmapSetsWithoutFiles;
|
||||
break;
|
||||
@ -384,8 +388,33 @@ namespace osu.Game.Beatmaps
|
||||
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <param name="includes">The level of detail to include in the returned objects.</param>
|
||||
/// <returns>Results from the provided query.</returns>
|
||||
public IEnumerable<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query) => beatmaps.ConsumableItems.AsNoTracking().Where(query);
|
||||
public IEnumerable<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query, IncludedDetails includes = IncludedDetails.All)
|
||||
{
|
||||
IQueryable<BeatmapSetInfo> queryable;
|
||||
|
||||
switch (includes)
|
||||
{
|
||||
case IncludedDetails.Minimal:
|
||||
queryable = beatmaps.BeatmapSetsOverview;
|
||||
break;
|
||||
|
||||
case IncludedDetails.AllButRuleset:
|
||||
queryable = beatmaps.BeatmapSetsWithoutRuleset;
|
||||
break;
|
||||
|
||||
case IncludedDetails.AllButFiles:
|
||||
queryable = beatmaps.BeatmapSetsWithoutFiles;
|
||||
break;
|
||||
|
||||
default:
|
||||
queryable = beatmaps.ConsumableItems;
|
||||
break;
|
||||
}
|
||||
|
||||
return queryable.AsNoTracking().Where(query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
|
||||
@ -554,6 +583,11 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
AllButFiles,
|
||||
|
||||
/// <summary>
|
||||
/// Include everything except ruleset. Used for cases where we aren't sure the ruleset is present but still want to consume the beatmap.
|
||||
/// </summary>
|
||||
AllButRuleset,
|
||||
|
||||
/// <summary>
|
||||
/// Include everything.
|
||||
/// </summary>
|
||||
|
@ -92,6 +92,13 @@ namespace osu.Game.Beatmaps
|
||||
.Include(s => s.Beatmaps)
|
||||
.AsNoTracking();
|
||||
|
||||
public IQueryable<BeatmapSetInfo> BeatmapSetsWithoutRuleset => ContextFactory.Get().BeatmapSetInfo
|
||||
.Include(s => s.Metadata)
|
||||
.Include(s => s.Files).ThenInclude(f => f.FileInfo)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
|
||||
.AsNoTracking();
|
||||
|
||||
public IQueryable<BeatmapSetInfo> BeatmapSetsWithoutFiles => ContextFactory.Get().BeatmapSetInfo
|
||||
.Include(s => s.Metadata)
|
||||
.Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset)
|
||||
|
@ -22,7 +22,8 @@ namespace osu.Game.Graphics.Containers
|
||||
where T : Drawable
|
||||
{
|
||||
public Bindable<T> SelectedSection { get; } = new Bindable<T>();
|
||||
private Drawable lastClickedSection;
|
||||
|
||||
private T lastClickedSection;
|
||||
|
||||
public Drawable ExpandableHeader
|
||||
{
|
||||
@ -144,10 +145,25 @@ namespace osu.Game.Graphics.Containers
|
||||
footerHeight = null;
|
||||
}
|
||||
|
||||
public void ScrollTo(Drawable section)
|
||||
public void ScrollTo(Drawable target)
|
||||
{
|
||||
lastClickedSection = section;
|
||||
scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - scrollContainer.DisplayableContent * scroll_y_centre - (FixedHeader?.BoundingBox.Height ?? 0));
|
||||
lastKnownScroll = null;
|
||||
|
||||
float fixedHeaderSize = FixedHeader?.BoundingBox.Height ?? 0;
|
||||
|
||||
// implementation similar to ScrollIntoView but a bit more nuanced.
|
||||
float top = scrollContainer.GetChildPosInContent(target);
|
||||
|
||||
float bottomScrollExtent = scrollContainer.ScrollableExtent - fixedHeaderSize;
|
||||
float scrollTarget = top - fixedHeaderSize - scrollContainer.DisplayableContent * scroll_y_centre;
|
||||
|
||||
if (scrollTarget > bottomScrollExtent)
|
||||
scrollContainer.ScrollToEnd();
|
||||
else
|
||||
scrollContainer.ScrollTo(scrollTarget);
|
||||
|
||||
if (target is T section)
|
||||
lastClickedSection = section;
|
||||
}
|
||||
|
||||
public void ScrollToTop() => scrollContainer.ScrollTo(0);
|
||||
@ -170,13 +186,22 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
if (source == InvalidationSource.Child && (invalidation & Invalidation.DrawSize) != 0)
|
||||
{
|
||||
lastKnownScroll = null;
|
||||
InvalidateScrollPosition();
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void InvalidateScrollPosition()
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
lastKnownScroll = null;
|
||||
lastClickedSection = null;
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
@ -224,15 +249,19 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
float scrollCentre = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_y_centre + selectionLenienceAboveSection;
|
||||
|
||||
if (Precision.AlmostBigger(0, scrollContainer.Current))
|
||||
SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault();
|
||||
var presentChildren = Children.Where(c => c.IsPresent);
|
||||
|
||||
if (lastClickedSection != null)
|
||||
SelectedSection.Value = lastClickedSection;
|
||||
else if (Precision.AlmostBigger(0, scrollContainer.Current))
|
||||
SelectedSection.Value = presentChildren.FirstOrDefault();
|
||||
else if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent))
|
||||
SelectedSection.Value = lastClickedSection as T ?? Children.LastOrDefault();
|
||||
SelectedSection.Value = presentChildren.LastOrDefault();
|
||||
else
|
||||
{
|
||||
SelectedSection.Value = Children
|
||||
SelectedSection.Value = presentChildren
|
||||
.TakeWhile(section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollCentre <= 0)
|
||||
.LastOrDefault() ?? Children.FirstOrDefault();
|
||||
.LastOrDefault() ?? presentChildren.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,12 @@ namespace osu.Game.Graphics.Containers
|
||||
base.OnUserScroll(value, animated, distanceDecay);
|
||||
}
|
||||
|
||||
public new void ScrollIntoView(Drawable target, bool animated = true)
|
||||
{
|
||||
UserScrolling = false;
|
||||
base.ScrollIntoView(target, animated);
|
||||
}
|
||||
|
||||
public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null)
|
||||
{
|
||||
UserScrolling = false;
|
||||
|
@ -4,9 +4,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -19,6 +21,10 @@ namespace osu.Game.Overlays.Settings
|
||||
protected FillFlowContainer FlowContent;
|
||||
protected override Container<Drawable> Content => FlowContent;
|
||||
|
||||
private IBindable<SettingsSection> selectedSection;
|
||||
|
||||
private OsuSpriteText header;
|
||||
|
||||
public abstract Drawable CreateIcon();
|
||||
public abstract LocalisableString Header { get; }
|
||||
|
||||
@ -36,6 +42,9 @@ namespace osu.Game.Overlays.Settings
|
||||
|
||||
public bool FilteringActive { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private SettingsPanel settingsPanel { get; set; }
|
||||
|
||||
protected SettingsSection()
|
||||
{
|
||||
Margin = new MarginPadding { Top = margin };
|
||||
@ -61,6 +70,7 @@ namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "separator",
|
||||
Colour = new Color4(0, 0, 0, 255),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = border_size,
|
||||
@ -76,7 +86,7 @@ namespace osu.Game.Overlays.Settings
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
header = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: header_size),
|
||||
Text = Header,
|
||||
@ -91,6 +101,51 @@ namespace osu.Game.Overlays.Settings
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
selectedSection = settingsPanel.CurrentSection.GetBoundCopy();
|
||||
selectedSection.BindValueChanged(_ => updateContentFade(), true);
|
||||
}
|
||||
|
||||
private bool isCurrentSection => selectedSection.Value == this;
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateContentFade();
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
updateContentFade();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if (!isCurrentSection)
|
||||
settingsPanel.SectionsContainer.ScrollTo(this);
|
||||
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
protected override bool ShouldBeConsideredForInput(Drawable child) =>
|
||||
// only the current section should accept input.
|
||||
// this provides the behaviour of the first click scrolling the target section to the centre of the screen.
|
||||
isCurrentSection;
|
||||
|
||||
private void updateContentFade()
|
||||
{
|
||||
float contentFade = 1;
|
||||
float headerFade = 1;
|
||||
|
||||
if (!isCurrentSection)
|
||||
{
|
||||
contentFade = 0.25f;
|
||||
headerFade = IsHovered ? 0.5f : 0.25f;
|
||||
}
|
||||
|
||||
header.FadeTo(headerFade, 500, Easing.OutQuint);
|
||||
FlowContent.FadeTo(contentFade, 500, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -21,6 +22,7 @@ using osu.Game.Overlays.Settings;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
[Cached]
|
||||
public abstract class SettingsPanel : OsuFocusedOverlayContainer
|
||||
{
|
||||
public const float CONTENT_MARGINS = 15;
|
||||
@ -46,7 +48,7 @@ namespace osu.Game.Overlays
|
||||
protected Sidebar Sidebar;
|
||||
private SidebarButton selectedSidebarButton;
|
||||
|
||||
protected SettingsSectionsContainer SectionsContainer;
|
||||
public SettingsSectionsContainer SectionsContainer { get; private set; }
|
||||
|
||||
private SeekLimitedSearchTextBox searchTextBox;
|
||||
|
||||
@ -65,6 +67,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
private Task sectionsLoadingTask;
|
||||
|
||||
public IBindable<SettingsSection> CurrentSection = new Bindable<SettingsSection>();
|
||||
|
||||
protected SettingsPanel(bool showSidebar)
|
||||
{
|
||||
this.showSidebar = showSidebar;
|
||||
@ -105,6 +109,7 @@ namespace osu.Game.Overlays
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ExpandableHeader = CreateHeader(),
|
||||
SelectedSection = { BindTarget = CurrentSection },
|
||||
FixedHeader = searchTextBox = new SeekLimitedSearchTextBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -238,8 +243,10 @@ namespace osu.Game.Overlays
|
||||
if (selectedSidebarButton != null)
|
||||
selectedSidebarButton.Selected = false;
|
||||
|
||||
selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue);
|
||||
selectedSidebarButton.Selected = true;
|
||||
selectedSidebarButton = Sidebar.Children.FirstOrDefault(b => b.Section == section.NewValue);
|
||||
|
||||
if (selectedSidebarButton != null)
|
||||
selectedSidebarButton.Selected = true;
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
@ -273,6 +280,16 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
public SearchContainer<SettingsSection> SearchContainer;
|
||||
|
||||
public string SearchTerm
|
||||
{
|
||||
get => SearchContainer.SearchTerm;
|
||||
set
|
||||
{
|
||||
SearchContainer.SearchTerm = value;
|
||||
InvalidateScrollPosition();
|
||||
}
|
||||
}
|
||||
|
||||
protected override FlowContainer<SettingsSection> CreateScrollContentContainer()
|
||||
=> SearchContainer = new SearchContainer<SettingsSection>
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
@ -109,7 +110,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
bool loadThemedIntro()
|
||||
{
|
||||
setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == BeatmapHash);
|
||||
setInfo = beatmaps.QueryBeatmapSets(b => b.Hash == BeatmapHash, IncludedDetails.AllButRuleset).FirstOrDefault();
|
||||
|
||||
if (setInfo == null)
|
||||
return false;
|
||||
|
@ -171,8 +171,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
|
||||
sampleJoin?.Play();
|
||||
lounge?.Join(Room, null);
|
||||
|
||||
return base.OnClick(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
public class PasswordEntryPopover : OsuPopover
|
||||
|
Loading…
Reference in New Issue
Block a user