1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-26 20:13:20 +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 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, 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. // See the LICENCE file in the repository root for full licence text.
using System; using System;
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.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osuTK; using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.SearchableList namespace osu.Game.Graphics.UserInterface
{ {
public class SlimEnumDropdown<T> : OsuEnumDropdown<T> public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
where T : struct, Enum where T : struct, Enum

View File

@ -11,24 +11,24 @@ namespace osu.Game.Online.Multiplayer
{ {
public class GetRoomsRequest : APIRequest<List<Room>> public class GetRoomsRequest : APIRequest<List<Room>>
{ {
private readonly RoomStatusFilter statusFilter; private readonly RoomStatusFilter status;
private readonly RoomCategoryFilter categoryFilter; private readonly string category;
public GetRoomsRequest(RoomStatusFilter statusFilter, RoomCategoryFilter categoryFilter) public GetRoomsRequest(RoomStatusFilter status, string category)
{ {
this.statusFilter = statusFilter; this.status = status;
this.categoryFilter = categoryFilter; this.category = category;
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
{ {
var req = base.CreateWebRequest(); var req = base.CreateWebRequest();
if (statusFilter != RoomStatusFilter.Open) if (status != RoomStatusFilter.Open)
req.AddParameter("mode", statusFilter.ToString().Underscore().ToLowerInvariant()); req.AddParameter("mode", status.ToString().Underscore().ToLowerInvariant());
if (categoryFilter != RoomCategoryFilter.Any) if (!string.IsNullOrEmpty(category))
req.AddParameter("category", categoryFilter.ToString().Underscore().ToLowerInvariant()); req.AddParameter("category", category);
return req; 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. // 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. // See the LICENCE file in the repository root for full licence text.
using System.ComponentModel;
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.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Overlays.SearchableList; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Screens.Multi.Lounge.Components 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 const float VERTICAL_PADDING = 10;
protected override RoomStatusFilter DefaultTab => RoomStatusFilter.Open; protected const float HORIZONTAL_PADDING = 80;
protected override RoomCategoryFilter DefaultCategory => RoomCategoryFilter.Any;
protected override float ContentHorizontalPadding => base.ContentHorizontalPadding + OsuScreen.HORIZONTAL_OVERFLOW_PADDING;
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private Bindable<FilterCriteria> filter { get; set; } private Bindable<FilterCriteria> filter { get; set; }
@ -26,66 +25,109 @@ namespace osu.Game.Screens.Multi.Lounge.Components
[Resolved] [Resolved]
private IBindable<RulesetInfo> ruleset { get; set; } 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] [BackgroundDependencyLoader]
private void load() private void load(OsuColour colours)
{ {
filter ??= new Bindable<FilterCriteria>(); filter ??= new Bindable<FilterCriteria>();
tabStrip.Colour = colours.Yellow;
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
ruleset.BindValueChanged(_ => updateFilter()); search.Current.BindValueChanged(_ => updateFilterDebounced());
Search.Current.BindValueChanged(_ => scheduleUpdateFilter()); ruleset.BindValueChanged(_ => UpdateFilter());
Dropdown.Current.BindValueChanged(_ => updateFilter()); tabs.Current.BindValueChanged(_ => UpdateFilter(), true);
Tabs.Current.BindValueChanged(_ => updateFilter(), true);
} }
private ScheduledDelegate scheduledFilterUpdate; private ScheduledDelegate scheduledFilterUpdate;
private void scheduleUpdateFilter() private void updateFilterDebounced()
{ {
scheduledFilterUpdate?.Cancel(); scheduledFilterUpdate?.Cancel();
scheduledFilterUpdate = Scheduler.AddDelayed(updateFilter, 200); scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200);
} }
private void updateFilter() protected void UpdateFilter()
{ {
scheduledFilterUpdate?.Cancel(); scheduledFilterUpdate?.Cancel();
if (filter == null) var criteria = CreateCriteria();
return; 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, BackgroundUnfocused = OsuColour.Gray(0.06f);
StatusFilter = Tabs.Current.Value, BackgroundFocused = OsuColour.Gray(0.12f);
RoomCategoryFilter = Dropdown.Current.Value, }
Ruleset = ruleset.Value
};
} }
} }
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 class FilterCriteria
{ {
public string SearchString; public string SearchString;
public RoomStatusFilter StatusFilter; public RoomStatusFilter Status;
public RoomCategoryFilter RoomCategoryFilter; public string Category;
public RulesetInfo Ruleset; 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[] InternalChildren = new Drawable[]
{ {
Filter = new FilterControl { Depth = -1 },
content = new Container content = new Container
{ {
RelativeSizeAxes = Axes.Both, 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. // scroll selected room into view on selection.
@ -112,7 +116,7 @@ namespace osu.Game.Screens.Multi.Lounge
protected override void OnFocus(FocusEvent e) protected override void OnFocus(FocusEvent e)
{ {
Filter.Search.TakeFocus(); Filter.TakeFocus();
} }
public override void OnEntering(IScreen last) public override void OnEntering(IScreen last)
@ -136,19 +140,19 @@ namespace osu.Game.Screens.Multi.Lounge
private void onReturning() private void onReturning()
{ {
Filter.Search.HoldFocus = true; Filter.HoldFocus = true;
} }
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
{ {
Filter.Search.HoldFocus = false; Filter.HoldFocus = false;
return base.OnExiting(next); return base.OnExiting(next);
} }
public override void OnSuspending(IScreen next) public override void OnSuspending(IScreen next)
{ {
base.OnSuspending(next); base.OnSuspending(next);
Filter.Search.HoldFocus = false; Filter.HoldFocus = false;
} }
private void joinRequested(Room room) private void joinRequested(Room room)

View File

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