2019-01-24 16:43:03 +08:00
// 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.
2018-12-11 18:07:40 +08:00
using System ;
using System.Collections.Generic ;
2020-09-01 10:56:23 +08:00
using System.Collections.Specialized ;
2018-12-11 18:07:40 +08:00
using System.Linq ;
using osu.Framework.Allocation ;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables ;
2018-12-11 18:07:40 +08:00
using osu.Framework.Extensions.IEnumerableExtensions ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2020-07-09 16:15:16 +08:00
using osu.Framework.Input.Bindings ;
2021-02-16 12:44:36 +08:00
using osu.Framework.Input.Events ;
2020-07-09 16:15:16 +08:00
using osu.Framework.Threading ;
using osu.Game.Extensions ;
2020-12-25 23:50:00 +08:00
using osu.Game.Graphics.Cursor ;
2020-07-09 16:15:16 +08:00
using osu.Game.Input.Bindings ;
2020-12-25 12:38:11 +08:00
using osu.Game.Online.Rooms ;
2020-12-25 23:50:00 +08:00
using osuTK ;
2018-12-11 18:07:40 +08:00
2020-12-25 23:50:00 +08:00
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
2018-12-11 18:07:40 +08:00
{
2020-07-09 16:15:16 +08:00
public class RoomsContainer : CompositeDrawable , IKeyBindingHandler < GlobalAction >
2018-12-11 18:07:40 +08:00
{
2021-08-19 15:49:08 +08:00
public readonly Bindable < Room > SelectedRoom = new Bindable < Room > ( ) ;
public readonly Bindable < FilterCriteria > Filter = new Bindable < FilterCriteria > ( ) ;
2021-07-26 12:22:11 +08:00
public IReadOnlyList < DrawableRoom > Rooms = > roomFlow . FlowingChildren . Cast < DrawableRoom > ( ) . ToArray ( ) ;
2018-12-11 18:07:40 +08:00
2021-08-19 15:49:08 +08:00
private readonly IBindableList < Room > rooms = new BindableList < Room > ( ) ;
private readonly FillFlowContainer < DrawableLoungeRoom > roomFlow ;
2019-02-08 13:57:51 +08:00
2018-12-11 18:07:40 +08:00
[Resolved]
2018-12-25 10:45:50 +08:00
private IRoomManager roomManager { get ; set ; }
2018-12-11 18:07:40 +08:00
2020-08-16 05:27:49 +08:00
[Resolved(CanBeNull = true)]
2020-08-16 04:06:16 +08:00
private LoungeSubScreen loungeSubScreen { get ; set ; }
2020-08-10 07:16:01 +08:00
2021-02-16 12:44:36 +08:00
// handle deselection
public override bool ReceivePositionalInputAt ( Vector2 screenSpacePos ) = > true ;
2018-12-11 18:07:40 +08:00
public RoomsContainer ( )
{
RelativeSizeAxes = Axes . X ;
AutoSizeAxes = Axes . Y ;
2021-08-12 18:38:53 +08:00
// account for the fact we are in a scroll container and want a bit of spacing from the scroll bar.
Padding = new MarginPadding { Right = 5 } ;
2021-07-13 15:35:49 +08:00
InternalChild = new OsuContextMenuContainer
2018-12-11 18:07:40 +08:00
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
2021-08-18 19:53:17 +08:00
Child = roomFlow = new FillFlowContainer < DrawableLoungeRoom >
2020-08-10 07:16:01 +08:00
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
2021-07-13 15:35:49 +08:00
Direction = FillDirection . Vertical ,
2021-07-13 15:00:42 +08:00
Spacing = new Vector2 ( 10 ) ,
2020-08-10 07:16:01 +08:00
}
2018-12-11 18:07:40 +08:00
} ;
}
2020-02-06 11:12:52 +08:00
protected override void LoadComplete ( )
2018-12-11 18:07:40 +08:00
{
2020-09-01 10:56:23 +08:00
rooms . CollectionChanged + = roomsChanged ;
2018-12-28 00:45:19 +08:00
roomManager . RoomsUpdated + = updateSorting ;
2020-02-06 11:12:52 +08:00
rooms . BindTo ( roomManager . Rooms ) ;
2020-02-06 12:36:49 +08:00
2021-08-17 08:36:43 +08:00
Filter ? . BindValueChanged ( criteria = > applyFilterCriteria ( criteria . NewValue ) , true ) ;
2019-11-15 07:23:56 +08:00
}
2021-08-17 08:36:43 +08:00
private void applyFilterCriteria ( FilterCriteria criteria )
2018-12-11 18:07:40 +08:00
{
2018-12-19 15:56:51 +08:00
roomFlow . Children . ForEach ( r = >
{
if ( criteria = = null )
r . MatchingFilter = true ;
else
{
bool matchingFilter = true ;
2020-02-06 10:56:09 +08:00
2021-06-29 14:18:40 +08:00
matchingFilter & = r . Room . Playlist . Count = = 0 | | criteria . Ruleset = = null | | r . Room . Playlist . Any ( i = > i . Ruleset . Value . Equals ( criteria . Ruleset ) ) ;
2020-02-06 10:56:09 +08:00
2020-02-06 12:02:38 +08:00
if ( ! string . IsNullOrEmpty ( criteria . SearchString ) )
2020-11-02 01:54:44 +08:00
matchingFilter & = r . FilterTerms . Any ( term = > term . Contains ( criteria . SearchString , StringComparison . InvariantCultureIgnoreCase ) ) ;
2018-12-19 15:56:51 +08:00
r . MatchingFilter = matchingFilter ;
}
} ) ;
2018-12-11 18:07:40 +08:00
}
2020-09-01 10:56:23 +08:00
private void roomsChanged ( object sender , NotifyCollectionChangedEventArgs args )
{
switch ( args . Action )
{
case NotifyCollectionChangedAction . Add :
addRooms ( args . NewItems . Cast < Room > ( ) ) ;
break ;
case NotifyCollectionChangedAction . Remove :
removeRooms ( args . OldItems . Cast < Room > ( ) ) ;
break ;
}
}
2018-12-11 18:07:40 +08:00
private void addRooms ( IEnumerable < Room > rooms )
{
2020-07-09 16:15:16 +08:00
foreach ( var room in rooms )
2021-08-19 15:49:08 +08:00
roomFlow . Add ( new DrawableLoungeRoom ( room ) { SelectedRoom = { BindTarget = SelectedRoom } } ) ;
2018-12-11 18:07:40 +08:00
2021-08-17 08:36:43 +08:00
applyFilterCriteria ( Filter ? . Value ) ;
2018-12-11 18:07:40 +08:00
}
private void removeRooms ( IEnumerable < Room > rooms )
{
foreach ( var r in rooms )
{
2021-08-18 19:53:17 +08:00
roomFlow . RemoveAll ( d = > d . Room = = r ) ;
2018-12-20 14:17:08 +08:00
2021-08-12 13:56:58 +08:00
// selection may have a lease due to being in a sub screen.
2021-08-19 15:49:08 +08:00
if ( ! SelectedRoom . Disabled )
SelectedRoom . Value = null ;
2018-12-11 18:07:40 +08:00
}
}
2018-12-28 00:45:19 +08:00
private void updateSorting ( )
{
foreach ( var room in roomFlow )
2021-10-13 20:24:54 +08:00
roomFlow . SetLayoutPosition ( room , - ( room . Room . RoomID . Value ? ? 0 ) ) ;
2018-12-28 00:45:19 +08:00
}
2021-02-16 12:44:36 +08:00
protected override bool OnClick ( ClickEvent e )
{
2021-08-19 15:49:08 +08:00
if ( ! SelectedRoom . Disabled )
SelectedRoom . Value = null ;
2021-02-16 12:44:36 +08:00
return base . OnClick ( e ) ;
}
2020-07-09 16:33:02 +08:00
#region Key selection logic ( shared with BeatmapCarousel )
2020-07-09 16:15:16 +08:00
2021-09-16 17:26:12 +08:00
public bool OnPressed ( KeyBindingPressEvent < GlobalAction > e )
2020-07-09 16:15:16 +08:00
{
2021-09-16 17:26:12 +08:00
switch ( e . Action )
2020-07-09 16:15:16 +08:00
{
case GlobalAction . SelectNext :
2021-09-16 17:26:12 +08:00
beginRepeatSelection ( ( ) = > selectNext ( 1 ) , e . Action ) ;
2020-07-09 16:15:16 +08:00
return true ;
case GlobalAction . SelectPrevious :
2021-09-16 17:26:12 +08:00
beginRepeatSelection ( ( ) = > selectNext ( - 1 ) , e . Action ) ;
2020-07-09 16:15:16 +08:00
return true ;
}
2018-12-11 18:07:40 +08:00
2020-07-09 16:15:16 +08:00
return false ;
}
2021-09-16 17:26:12 +08:00
public void OnReleased ( KeyBindingReleaseEvent < GlobalAction > e )
2020-07-09 16:15:16 +08:00
{
2021-09-16 17:26:12 +08:00
switch ( e . Action )
2020-07-09 16:15:16 +08:00
{
case GlobalAction . SelectNext :
case GlobalAction . SelectPrevious :
2021-09-16 17:26:12 +08:00
endRepeatSelection ( e . Action ) ;
2020-07-09 16:15:16 +08:00
break ;
}
}
private ScheduledDelegate repeatDelegate ;
private object lastRepeatSource ;
/// <summary>
/// Begin repeating the specified selection action.
/// </summary>
/// <param name="action">The action to perform.</param>
/// <param name="source">The source of the action. Used in conjunction with <see cref="endRepeatSelection"/> to only cancel the correct action (most recently pressed key).</param>
private void beginRepeatSelection ( Action action , object source )
{
endRepeatSelection ( ) ;
lastRepeatSource = source ;
repeatDelegate = this . BeginKeyRepeat ( Scheduler , action ) ;
}
private void endRepeatSelection ( object source = null )
{
// only the most recent source should be able to cancel the current action.
if ( source ! = null & & ! EqualityComparer < object > . Default . Equals ( lastRepeatSource , source ) )
return ;
repeatDelegate ? . Cancel ( ) ;
repeatDelegate = null ;
lastRepeatSource = null ;
}
private void selectNext ( int direction )
{
2021-08-19 15:49:08 +08:00
if ( SelectedRoom . Disabled )
2021-08-13 13:29:28 +08:00
return ;
2020-07-09 16:15:16 +08:00
var visibleRooms = Rooms . AsEnumerable ( ) . Where ( r = > r . IsPresent ) ;
Room room ;
2021-08-19 15:49:08 +08:00
if ( SelectedRoom . Value = = null )
2020-07-09 16:15:16 +08:00
room = visibleRooms . FirstOrDefault ( ) ? . Room ;
2018-12-11 18:07:40 +08:00
else
2020-07-09 16:15:16 +08:00
{
if ( direction < 0 )
visibleRooms = visibleRooms . Reverse ( ) ;
2018-12-20 19:58:34 +08:00
2021-08-19 15:49:08 +08:00
room = visibleRooms . SkipWhile ( r = > r . Room ! = SelectedRoom . Value ) . Skip ( 1 ) . FirstOrDefault ( ) ? . Room ;
2020-07-09 16:15:16 +08:00
}
// we already have a valid selection only change selection if we still have a room to switch to.
if ( room ! = null )
2021-08-19 15:49:08 +08:00
SelectedRoom . Value = room ;
2018-12-11 18:07:40 +08:00
}
2018-12-28 00:45:19 +08:00
2020-07-09 16:15:16 +08:00
#endregion
2018-12-28 00:45:19 +08:00
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
if ( roomManager ! = null )
roomManager . RoomsUpdated - = updateSorting ;
}
2018-12-11 18:07:40 +08:00
}
}