Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
144 changes: 144 additions & 0 deletions fluXis/Graphics/UserInterface/Buttons/FluXisFilterButtonsBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Linq;
using fluXis.Graphics.Sprites.Text;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osuTK;

namespace fluXis.Graphics.UserInterface.Buttons;

public abstract partial class FluXisFilterButtonsBase<T> : CompositeDrawable where T : struct
{
private readonly InputManager input;
public Action OnFilterChanged;

protected FillFlowContainer ButtonFlow;
protected FluXisSpriteText Text;

protected abstract T[] Values { get; }
protected abstract string Label { get; }
protected abstract float FontSize { get; set; }
protected abstract List<T> FilterList { get; }
public abstract T[] DefaultFilter { get; set; }
public abstract bool ResetWhenFull { get; set; }

protected FluXisFilterButtonsBase(InputManager input, System.Action onFilterChanged = null)
{
this.input = input;
OnFilterChanged = onFilterChanged;
}

[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X;
Height = 30;
Padding = new MarginPadding { Horizontal = 5 };

InternalChildren = new Drawable[]
{
Text = new FluXisSpriteText
{
Text = Label,
FontSize = FontSize,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
},
ButtonFlow = new FillFlowContainer
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
ChildrenEnumerable = Values.Select(CreateButton)
}
};

if (DefaultFilter.Length != 0)
ResetState();

ButtonFlow.Children.OfType<ISelectableButton<T>>().ForEach(b => b.OnRightClick = ResetAllButtons);
}

protected abstract Drawable CreateButton(T value);

protected void OnValueClick(T value)
{
bool isCtrlPressed = input.CurrentState.Keyboard.ControlPressed;

if (FilterList.Count == Values.Length && DefaultFilter.Length == 0)
{
FilterList.Clear();
FilterList.AddRange(Values);
}

if (isCtrlPressed && (FilterList.Count == 0 || FilterList.Count == Values.Length))
{
FilterList.Clear();
FilterList.AddRange(Values);
}

if (!FilterList.Remove(value))
FilterList.Add(value);

if (FilterList.Count == 0 || (FilterList.Count == Values.Length && ResetWhenFull))
ResetState();

OnFilterChanged?.Invoke();

UpdateAllButtons();
}

protected void ResetAllButtons(ISelectableButton<T> clickedButton)
{
if (FilterList.ToHashSet().SetEquals(DefaultFilter.ToHashSet()))
return;

ResetState();

var buttons = ButtonFlow.Children.OfType<ISelectableButton<T>>().ToList();
int clickedIndex = buttons.IndexOf(clickedButton);

if (clickedIndex == -1)
clickedIndex = buttons.Count / 2;

double lastDelay = 0;

for (int i = 0; i < buttons.Count; i++)
{
int distance = Math.Abs(i - clickedIndex);
double delay = distance * 30;
lastDelay = delay;
int index = i;

Scheduler.AddDelayed(() => buttons[index].Flash(), delay);
}
}

protected void ResetState()
{
FilterList.Clear();
FilterList.AddRange(DefaultFilter);
OnFilterChanged?.Invoke();
UpdateAllButtons();
}

protected void UpdateAllButtons()
{
foreach (var button in ButtonFlow.Children.OfType<ISelectableButton<T>>())
button.UpdateSelection();
}

protected interface ISelectableButton<TValue>
{
void UpdateSelection();
void Flash();
Action<ISelectableButton<TValue>> OnRightClick { get; set; }
}
}
123 changes: 79 additions & 44 deletions fluXis/Overlay/Browse/BrowseFilter.cs
Original file line number Diff line number Diff line change
@@ -1,42 +1,75 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using fluXis.Audio;
using fluXis.Graphics.Sprites.Text;
using fluXis.Graphics.UserInterface.Buttons;
using fluXis.Graphics.UserInterface.Color;
using fluXis.Graphics.UserInterface.Interaction;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osuTK;

namespace fluXis.Overlay.Browse;

public partial class BrowseFilter<T> : FillFlowContainer
public partial class BrowseFilter<T> : FluXisFilterButtonsBase<T> where T : struct
{
public BrowseFilter(LocalisableString title, BindableList<T> selected, IEnumerable<Option> options)
private readonly BindableList<T> selected;
private readonly Option[] optionArray;

protected override T[] Values { get; }
protected override string Label { get; }
protected override float FontSize { get; set; } = 18;
private List<T> filterList;
protected override List<T> FilterList => filterList;
public override T[] DefaultFilter { get; set; } = Array.Empty<T>();
public override bool ResetWhenFull { get; set; } = false;

public BrowseFilter(LocalisableString label, BindableList<T> selected, IEnumerable<Option> options, InputManager input)
: base(input)
{
this.selected = selected;
filterList = selected.ToList();
Label = label.ToString();
optionArray = options.ToArray();
Values = optionArray.Select(o => o.Value).ToArray();
}

[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X;
Height = 20;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(4);
Padding = new MarginPadding { Horizontal = 0 };

Children = new Drawable[]
{
new TruncatingText
{
Text = title,
Width = 80,
WebFontSize = 12,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
}
};
ButtonFlow.Anchor = Anchor.CentreLeft;
ButtonFlow.Origin = Anchor.CentreLeft;
ButtonFlow.Margin = new MarginPadding { Left = Text.DrawWidth + 10 };

OnFilterChanged = updateBindableList;
UpdateAllButtons();
}

private void updateBindableList()
{
selected.Clear();
selected.AddRange(filterList);
}

protected override Drawable CreateButton(T value)
{
var option = optionArray.First(o => EqualityComparer<T>.Default.Equals(o.Value, value));
return new Button(option, selected, this);
}

options.ForEach(x => Add(new Button(x, selected)));
protected new void OnValueClick(T value)
{
base.OnValueClick(value);
OnFilterChanged.Invoke();
}

public class Option
Expand All @@ -53,25 +86,26 @@ public Option(T value, LocalisableString text, Colour4 color)
}
}

private partial class Button : CompositeDrawable
private partial class Button : CompositeDrawable, ISelectableButton<T>
{
[Resolved]
private UISamples samples { get; set; }
public Action<ISelectableButton<T>> OnRightClick { get; set; }

private readonly Option option;
private readonly BindableList<T> selected;
private readonly BrowseFilter<T> parent;

private Box background;
private HoverLayer hover;
private FlashLayer flash;
private FluXisSpriteText text;

private bool enabled;

public Button(Option option, BindableList<T> selected)
public Button(Option option, BindableList<T> selected, BrowseFilter<T> parent)
{
this.option = option;
this.selected = selected;
this.parent = parent;
}

[BackgroundDependencyLoader]
Expand All @@ -93,7 +127,7 @@ private void load()
Alpha = 0
},
hover = new HoverLayer(),
flash = new FlashLayer(),
flash = new FlashLayer() { Colour = option.Color.Lighten(.8f) },
text = new FluXisSpriteText
{
Text = option.Text,
Expand All @@ -106,18 +140,13 @@ private void load()
};
}

protected override void LoadComplete()
public void UpdateSelection()
{
base.LoadComplete();

selected.BindCollectionChanged((_, _) =>
{
enabled = selected.Contains(option.Value);
bool enabled = (parent.FilterList.Count == 0 && parent.DefaultFilter.Length == 0) || parent.FilterList.Contains(option.Value);

background.Alpha = enabled ? 1f : 0f;
text.Alpha = enabled ? 1f : .6f;
text.Colour = enabled ? Theme.TextDark : Theme.Text;
}, true);
background.FadeTo(enabled ? 1 : 0, 200);
text.FadeTo(enabled ? 1 : .6f, 200);
text.FadeColour(enabled ? (Theme.IsBright(background.Colour) ? Theme.TextDark : Theme.Text) : Theme.Text, 200);
}

protected override bool OnHover(HoverEvent e)
Expand All @@ -132,17 +161,23 @@ protected override void OnHoverLost(HoverLostEvent e)
hover.Hide();
}

protected override bool OnClick(ClickEvent e)
protected override bool OnMouseDown(MouseDownEvent e)
{
enabled = !enabled;
samples.Click();
flash.Show();

var v = option.Value;
if (!selected.Remove(v))
selected.Add(v);
if (e.Button == osuTK.Input.MouseButton.Right)
{
OnRightClick?.Invoke(this);
}
else
{
samples.Click();
flash.Show();

parent.OnValueClick(option.Value);
}

return true;
}

public void Flash() => flash.Show();
}
}
}
Loading