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

Merge pull request #11119 from smoogipoo/rework-multi-filtercontrol

This commit is contained in:
Dean Herbert 2020-12-08 13:15:42 +09:00 committed by GitHub
commit 88db7823b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 192 additions and 346 deletions

View File

@ -6,14 +6,17 @@ using osu.Game.Screens.Multi.Lounge.Components;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneLoungeFilterControl : OsuTestScene
public class TestSceneTimeshiftFilterControl : OsuTestScene
{
public TestSceneLoungeFilterControl()
public TestSceneTimeshiftFilterControl()
{
Child = new FilterControl
Child = new TimeshiftFilterControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Width = 0.7f,
Height = 80,
};
}
}

View File

@ -2,14 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.SearchableList
namespace osu.Game.Graphics.UserInterface
{
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
where T : struct, Enum

View File

@ -11,24 +11,24 @@ namespace osu.Game.Online.Multiplayer
{
public class GetRoomsRequest : APIRequest<List<Room>>
{
private readonly RoomStatusFilter statusFilter;
private readonly RoomCategoryFilter categoryFilter;
private readonly RoomStatusFilter status;
private readonly string category;
public GetRoomsRequest(RoomStatusFilter statusFilter, RoomCategoryFilter categoryFilter)
public GetRoomsRequest(RoomStatusFilter status, string category)
{
this.statusFilter = statusFilter;
this.categoryFilter = categoryFilter;
this.status = status;
this.category = category;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
if (statusFilter != RoomStatusFilter.Open)
req.AddParameter("mode", statusFilter.ToString().Underscore().ToLowerInvariant());
if (status != RoomStatusFilter.Open)
req.AddParameter("mode", status.ToString().Underscore().ToLowerInvariant());
if (categoryFilter != RoomCategoryFilter.Any)
req.AddParameter("category", categoryFilter.ToString().Underscore().ToLowerInvariant());
if (!string.IsNullOrEmpty(category))
req.AddParameter("category", category);
return req;
}

View File

@ -1,84 +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 osu.Framework.Bindables;
using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.SearchableList
{
public class DisplayStyleControl : CompositeDrawable
{
public readonly Bindable<PanelDisplayStyle> DisplayStyle = new Bindable<PanelDisplayStyle>();
public DisplayStyleControl()
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5f, 0f),
Direction = FillDirection.Horizontal,
Children = new[]
{
new DisplayStyleToggleButton(FontAwesome.Solid.ThLarge, PanelDisplayStyle.Grid, DisplayStyle),
new DisplayStyleToggleButton(FontAwesome.Solid.ListUl, PanelDisplayStyle.List, DisplayStyle),
},
};
DisplayStyle.Value = PanelDisplayStyle.Grid;
}
private class DisplayStyleToggleButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
private readonly PanelDisplayStyle style;
private readonly Bindable<PanelDisplayStyle> bindable;
public DisplayStyleToggleButton(IconUsage icon, PanelDisplayStyle style, Bindable<PanelDisplayStyle> bindable)
{
this.bindable = bindable;
this.style = style;
Size = new Vector2(25f);
Children = new Drawable[]
{
this.icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(18),
Alpha = 0.5f,
},
};
bindable.ValueChanged += Bindable_ValueChanged;
Bindable_ValueChanged(new ValueChangedEvent<PanelDisplayStyle>(bindable.Value, bindable.Value));
Action = () => bindable.Value = this.style;
}
private void Bindable_ValueChanged(ValueChangedEvent<PanelDisplayStyle> e)
{
icon.FadeTo(e.NewValue == style ? 1.0f : 0.5f, 100);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
bindable.ValueChanged -= Bindable_ValueChanged;
}
}
}
public enum PanelDisplayStyle
{
Grid,
List,
}
}

View File

@ -1,29 +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 osuTK.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.SearchableList
{
public class HeaderTabControl<T> : OsuTabControl<T>
{
protected override TabItem<T> CreateTabItem(T value) => new HeaderTabItem(value);
public HeaderTabControl()
{
Height = 26;
AccentColour = Color4.White;
}
private class HeaderTabItem : OsuTabItem
{
public HeaderTabItem(T value)
: base(value)
{
Text.Font = Text.Font.With(size: 16);
}
}
}
}

View File

@ -1,165 +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;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListFilterControl<TTab, TCategory> : Container
where TTab : struct, Enum
where TCategory : struct, Enum
{
private const float padding = 10;
private readonly Drawable filterContainer;
private readonly Drawable rightFilterContainer;
private readonly Box tabStrip;
public readonly SearchTextBox Search;
public readonly PageTabControl<TTab> Tabs;
public readonly SlimEnumDropdown<TCategory> Dropdown;
public readonly DisplayStyleControl DisplayStyleControl;
protected abstract Color4 BackgroundColour { get; }
protected abstract TTab DefaultTab { get; }
protected abstract TCategory DefaultCategory { get; }
protected virtual Drawable CreateSupplementaryControls() => null;
/// <summary>
/// The amount of padding added to content (does not affect background or tab control strip).
/// </summary>
protected virtual float ContentHorizontalPadding => WaveOverlayContainer.WIDTH_PADDING;
protected SearchableListFilterControl()
{
RelativeSizeAxes = Axes.X;
var controls = CreateSupplementaryControls();
Container controlsContainer;
Children = new[]
{
filterContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = BackgroundColour,
Alpha = 0.9f,
},
tabStrip = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Top = padding,
Horizontal = ContentHorizontalPadding
},
Children = new Drawable[]
{
Search = new FilterSearchTextBox
{
RelativeSizeAxes = Axes.X,
},
controlsContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = controls != null ? padding : 0 },
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Right = 225 },
Child = Tabs = new PageTabControl<TTab>
{
RelativeSizeAxes = Axes.X,
},
},
new Box // keep the tab strip part of autosize, but don't put it in the flow container
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = Color4.White.Opacity(0),
},
},
},
},
},
rightFilterContainer = new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
Dropdown = new SlimEnumDropdown<TCategory>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.None,
Width = 160f,
},
DisplayStyleControl = new DisplayStyleControl
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
}
}
};
if (controls != null) controlsContainer.Children = new[] { controls };
Tabs.Current.Value = DefaultTab;
Tabs.Current.TriggerChange();
Dropdown.Current.Value = DefaultCategory;
Dropdown.Current.TriggerChange();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabStrip.Colour = colours.Yellow;
}
protected override void Update()
{
base.Update();
Height = filterContainer.Height;
rightFilterContainer.Margin = new MarginPadding { Top = filterContainer.Height - 30, Right = ContentHorizontalPadding };
}
private class FilterSearchTextBox : SearchTextBox
{
[BackgroundDependencyLoader]
private void load()
{
BackgroundUnfocused = OsuColour.Gray(0.06f);
BackgroundFocused = OsuColour.Gray(0.12f);
}
}
}
}

View File

@ -1,24 +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 System.ComponentModel;
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.Threading;
using osu.Game.Overlays.SearchableList;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osuTK.Graphics;
namespace osu.Game.Screens.Multi.Lounge.Components
{
public class FilterControl : SearchableListFilterControl<RoomStatusFilter, RoomCategoryFilter>
public abstract class FilterControl : CompositeDrawable
{
protected override Color4 BackgroundColour => Color4.Black.Opacity(0.5f);
protected override RoomStatusFilter DefaultTab => RoomStatusFilter.Open;
protected override RoomCategoryFilter DefaultCategory => RoomCategoryFilter.Any;
protected override float ContentHorizontalPadding => base.ContentHorizontalPadding + OsuScreen.HORIZONTAL_OVERFLOW_PADDING;
protected const float VERTICAL_PADDING = 10;
protected const float HORIZONTAL_PADDING = 80;
[Resolved(CanBeNull = true)]
private Bindable<FilterCriteria> filter { get; set; }
@ -26,66 +25,109 @@ namespace osu.Game.Screens.Multi.Lounge.Components
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
public FilterControl()
private readonly Box tabStrip;
private readonly SearchTextBox search;
private readonly PageTabControl<RoomStatusFilter> tabs;
protected FilterControl()
{
DisplayStyleControl.Hide();
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.25f,
},
tabStrip = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Top = VERTICAL_PADDING,
Horizontal = HORIZONTAL_PADDING
},
Children = new Drawable[]
{
search = new FilterSearchTextBox
{
RelativeSizeAxes = Axes.X,
},
tabs = new PageTabControl<RoomStatusFilter>
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
},
}
}
};
tabs.Current.Value = RoomStatusFilter.Open;
tabs.Current.TriggerChange();
}
[BackgroundDependencyLoader]
private void load()
private void load(OsuColour colours)
{
filter ??= new Bindable<FilterCriteria>();
tabStrip.Colour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
ruleset.BindValueChanged(_ => updateFilter());
Search.Current.BindValueChanged(_ => scheduleUpdateFilter());
Dropdown.Current.BindValueChanged(_ => updateFilter());
Tabs.Current.BindValueChanged(_ => updateFilter(), true);
search.Current.BindValueChanged(_ => updateFilterDebounced());
ruleset.BindValueChanged(_ => UpdateFilter());
tabs.Current.BindValueChanged(_ => UpdateFilter(), true);
}
private ScheduledDelegate scheduledFilterUpdate;
private void scheduleUpdateFilter()
private void updateFilterDebounced()
{
scheduledFilterUpdate?.Cancel();
scheduledFilterUpdate = Scheduler.AddDelayed(updateFilter, 200);
scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200);
}
private void updateFilter()
protected void UpdateFilter()
{
scheduledFilterUpdate?.Cancel();
if (filter == null)
return;
var criteria = CreateCriteria();
criteria.SearchString = search.Current.Value;
criteria.Status = tabs.Current.Value;
criteria.Ruleset = ruleset.Value;
filter.Value = new FilterCriteria
filter.Value = criteria;
}
protected virtual FilterCriteria CreateCriteria() => new FilterCriteria();
public bool HoldFocus
{
get => search.HoldFocus;
set => search.HoldFocus = value;
}
public void TakeFocus() => search.TakeFocus();
private class FilterSearchTextBox : SearchTextBox
{
[BackgroundDependencyLoader]
private void load()
{
SearchString = Search.Current.Value ?? string.Empty,
StatusFilter = Tabs.Current.Value,
RoomCategoryFilter = Dropdown.Current.Value,
Ruleset = ruleset.Value
};
BackgroundUnfocused = OsuColour.Gray(0.06f);
BackgroundFocused = OsuColour.Gray(0.12f);
}
}
}
public enum RoomStatusFilter
{
Open,
[Description("Recently Ended")]
Ended,
Participated,
Owned,
}
public enum RoomCategoryFilter
{
Any,
Normal,
Spotlight
}
}

View File

@ -8,8 +8,8 @@ namespace osu.Game.Screens.Multi.Lounge.Components
public class FilterCriteria
{
public string SearchString;
public RoomStatusFilter StatusFilter;
public RoomCategoryFilter RoomCategoryFilter;
public RoomStatusFilter Status;
public string Category;
public RulesetInfo Ruleset;
}
}

View File

@ -0,0 +1,17 @@
// 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.ComponentModel;
namespace osu.Game.Screens.Multi.Lounge.Components
{
public enum RoomStatusFilter
{
Open,
[Description("Recently Ended")]
Ended,
Participated,
Owned,
}
}

View File

@ -0,0 +1,59 @@
// 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.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Multi.Lounge.Components
{
public class TimeshiftFilterControl : FilterControl
{
private readonly Dropdown<TimeshiftCategory> dropdown;
public TimeshiftFilterControl()
{
AddInternal(dropdown = new SlimEnumDropdown<TimeshiftCategory>
{
Anchor = Anchor.BottomRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.None,
Width = 160,
X = -HORIZONTAL_PADDING,
Y = -30
});
}
protected override void LoadComplete()
{
base.LoadComplete();
dropdown.Current.BindValueChanged(_ => UpdateFilter());
}
protected override FilterCriteria CreateCriteria()
{
var criteria = base.CreateCriteria();
switch (dropdown.Current.Value)
{
case TimeshiftCategory.Normal:
criteria.Category = "normal";
break;
case TimeshiftCategory.Spotlight:
criteria.Category = "spotlight";
break;
}
return criteria;
}
private enum TimeshiftCategory
{
Any,
Normal,
Spotlight
}
}
}

View File

@ -48,7 +48,6 @@ namespace osu.Game.Screens.Multi.Lounge
InternalChildren = new Drawable[]
{
Filter = new FilterControl { Depth = -1 },
content = new Container
{
RelativeSizeAxes = Axes.Both,
@ -79,6 +78,11 @@ namespace osu.Game.Screens.Multi.Lounge
},
},
},
Filter = new TimeshiftFilterControl
{
RelativeSizeAxes = Axes.X,
Height = 80,
},
};
// scroll selected room into view on selection.
@ -112,7 +116,7 @@ namespace osu.Game.Screens.Multi.Lounge
protected override void OnFocus(FocusEvent e)
{
Filter.Search.TakeFocus();
Filter.TakeFocus();
}
public override void OnEntering(IScreen last)
@ -136,19 +140,19 @@ namespace osu.Game.Screens.Multi.Lounge
private void onReturning()
{
Filter.Search.HoldFocus = true;
Filter.HoldFocus = true;
}
public override bool OnExiting(IScreen next)
{
Filter.Search.HoldFocus = false;
Filter.HoldFocus = false;
return base.OnExiting(next);
}
public override void OnSuspending(IScreen next)
{
base.OnSuspending(next);
Filter.Search.HoldFocus = false;
Filter.HoldFocus = false;
}
private void joinRequested(Room room)

View File

@ -317,7 +317,7 @@ namespace osu.Game.Screens.Multi
var tcs = new TaskCompletionSource<bool>();
pollReq?.Cancel();
pollReq = new GetRoomsRequest(currentFilter.Value.StatusFilter, currentFilter.Value.RoomCategoryFilter);
pollReq = new GetRoomsRequest(currentFilter.Value.Status, currentFilter.Value.Category);
pollReq.Success += result =>
{