Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion osu.Game/Database/RealmAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ public class RealmAccess : IDisposable
/// 49 2025-06-10 Reset the LegacyOnlineID to -1 for all scores that have it set to 0 (which is semantically the same) for consistency of handling with OnlineID.
/// 50 2025-07-11 Add UserTags to BeatmapMetadata.
/// 51 2025-07-22 Add ScoreInfo.Pauses.
/// 52 2025-12-13 Added SavedBeatmapFilter.
/// </summary>
private const int schema_version = 51;
private const int schema_version = 52;

/// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
Expand Down Expand Up @@ -1326,6 +1327,10 @@ void remapKeyBinding(int oldAction, int newAction)
score.LegacyOnlineID = -1;

break;

case 52:
// SavedBeatmapFilter added.
break;
}

Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms");
Expand Down
22 changes: 17 additions & 5 deletions osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public partial class ShearedSearchTextBox : CompositeDrawable, IHasCurrentValue<

private readonly Box background;
protected readonly InnerSearchTextBox TextBox;
protected readonly Container BackgroundContent;
protected readonly Container RightInterface;

public Bindable<string> Current
{
Expand Down Expand Up @@ -62,6 +64,10 @@ public ShearedSearchTextBox()
{
RelativeSizeAxes = Axes.Both
},
BackgroundContent = new Container
{
RelativeSizeAxes = Axes.Both
},
new GridContainer
{
RelativeSizeAxes = Axes.Both,
Expand All @@ -70,13 +76,19 @@ public ShearedSearchTextBox()
new Drawable[]
{
TextBox = CreateInnerTextBox(),
new SpriteIcon
RightInterface = new Container
{
Icon = FontAwesome.Solid.Search,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(16),
Shear = -Shear
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Child = new SpriteIcon
{
Icon = FontAwesome.Solid.Search,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(16),
Shear = -Shear
}
}
}
},
Expand Down
135 changes: 135 additions & 0 deletions osu.Game/Screens/SelectV2/FilterControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Collections;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
Expand Down Expand Up @@ -111,6 +116,9 @@ private void load(IAPIProvider api)
{
RelativeSizeAxes = Axes.X,
HoldFocus = true,
ApplyFilter = applyFilter,
SaveFilter = saveFilter,
Ruleset = ruleset,
},
},
new GridContainer
Expand Down Expand Up @@ -250,6 +258,51 @@ protected override void Dispose(bool isDisposing)
collectionsSubscription?.Dispose();
}

private void applyFilter(SavedBeatmapFilter filter)
{
searchTextBox.Current.Value = filter.SearchQuery;

sortDropdown.Current.Value = Enum.IsDefined(typeof(SortMode), filter.SortMode)
? (SortMode)filter.SortMode
: SortMode.Title;

groupDropdown.Current.Value = Enum.IsDefined(typeof(GroupMode), filter.GroupMode)
? (GroupMode)filter.GroupMode
: GroupMode.None;

showConvertedBeatmapsButton.Active.Value = filter.ShowConverted;

var lowerBound = (BindableNumber<double>)difficultyRangeSlider.LowerBound;
var upperBound = (BindableNumber<double>)difficultyRangeSlider.UpperBound;

double min = Math.Clamp(filter.MinStars, lowerBound.MinValue, lowerBound.MaxValue);
double max = Math.Clamp(filter.MaxStars, upperBound.MinValue, upperBound.MaxValue);

if (min > max)
min = max;

difficultyRangeSlider.LowerBound.Value = min;
difficultyRangeSlider.UpperBound.Value = max;
}

private void saveFilter(string name)
{
if (string.IsNullOrEmpty(ruleset.Value?.ShortName))
return;

realm.Write(r => r.Add(new SavedBeatmapFilter
{
Name = name.Trim(),
SearchQuery = searchTextBox.Current.Value,
SortMode = (int)sortDropdown.Current.Value,
GroupMode = (int)groupDropdown.Current.Value,
ShowConverted = showConvertedBeatmapsButton.Active.Value,
MinStars = difficultyRangeSlider.LowerBound.Value,
MaxStars = difficultyRangeSlider.UpperBound.Value,
RulesetShortName = ruleset.Value.ShortName
}));
}

/// <summary>
/// Creates a <see cref="FilterCriteria"/> based on the current state of the controls.
/// </summary>
Expand Down Expand Up @@ -311,6 +364,82 @@ protected override void PopOut()

internal partial class SongSelectSearchTextBox : ShearedFilterTextBox
{
public Action<SavedBeatmapFilter>? ApplyFilter { get; set; }
public Action<string>? SaveFilter { get; set; }
public IBindable<RulesetInfo> Ruleset { get; set; } = null!;

private readonly Box hoverBox;
private readonly BindableBool popoverVisible = new BindableBool();

public SongSelectSearchTextBox()
{
var filterButton = new SearchFilterButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};

RightInterface.Clear();
RightInterface.Add(filterButton);

// Create hover box
hoverBox = new Box
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Y,
Width = 55,
Alpha = 0,
};

BackgroundContent.Add(hoverBox);

var popoverTarget = new PopoverTarget
{
Anchor = Anchor.BottomRight,
Origin = Anchor.TopRight,
RelativePositionAxes = Axes.Y,
Y = 1,
Size = Vector2.Zero,
CreatePopover = createPopover
};

AddInternal(popoverTarget);

filterButton.HoverTarget = hoverBox;
filterButton.IsPopoverVisible = () => popoverVisible.Value;
filterButton.SetPopoverVisible = visible => popoverVisible.Value = visible;
filterButton.SetIconShear(-Shear);

popoverVisible.BindValueChanged(visible =>
{
if (visible.NewValue)
popoverTarget.ShowPopover();
else
popoverTarget.HidePopover();
});
}

[Resolved]
private OsuColour colours { get; set; } = null!;

[BackgroundDependencyLoader]
private void load()
{
hoverBox.Colour = colours.Blue;
}

private Popover createPopover()
{
var popover = new SavedFiltersPopover(f => ApplyFilter?.Invoke(f), n => SaveFilter?.Invoke(n), Ruleset.Value);
popover.State.BindValueChanged(state =>
{
if (state.NewValue == Visibility.Hidden)
Schedule(() => popoverVisible.Value = false);
});
return popover;
}

protected override InnerSearchTextBox CreateInnerTextBox() => new InnerTextBox();

private partial class InnerTextBox : InnerFilterTextBox
Expand All @@ -330,6 +459,12 @@ public override bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
return base.OnPressed(e);
}
}

private partial class PopoverTarget : Container, IHasPopover
{
public Func<Popover>? CreatePopover { get; set; }
public Popover GetPopover() => CreatePopover?.Invoke()!;
}
}
}
}
29 changes: 29 additions & 0 deletions osu.Game/Screens/SelectV2/SavedBeatmapFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using osu.Game.Database;
using Realms;

namespace osu.Game.Screens.SelectV2
{
public class SavedBeatmapFilter : RealmObject, IHasGuidPrimaryKey
{
[PrimaryKey]
public Guid ID { get; set; } = Guid.NewGuid();

public string Name { get; set; } = string.Empty;

public string SearchQuery { get; set; } = string.Empty;

public int SortMode { get; set; }
public int GroupMode { get; set; }

public bool ShowConverted { get; set; }

public double MinStars { get; set; }
public double MaxStars { get; set; }

public string RulesetShortName { get; set; } = string.Empty;
}
}
Loading
Loading