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 ;
2018-12-11 18:07:40 +08:00
using osu.Game.Graphics.UserInterface ;
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
{
2018-12-20 19:58:34 +08:00
public Action < Room > JoinRequested ;
2019-01-07 17:50:27 +08:00
private readonly IBindableList < Room > rooms = new BindableList < Room > ( ) ;
2018-12-11 18:07:40 +08:00
private readonly FillFlowContainer < DrawableRoom > roomFlow ;
2018-12-25 10:59:08 +08:00
public IReadOnlyList < DrawableRoom > Rooms = > roomFlow ;
2018-12-11 18:07:40 +08:00
2019-11-15 10:07:16 +08:00
[Resolved(CanBeNull = true)]
2019-11-15 07:23:56 +08:00
private Bindable < FilterCriteria > filter { get ; set ; }
2019-02-08 13:57:51 +08:00
[Resolved]
2020-02-27 18:23:21 +08:00
private Bindable < Room > selectedRoom { get ; set ; }
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 ;
2020-08-10 07:16:01 +08:00
InternalChild = new OsuContextMenuContainer
2018-12-11 18:07:40 +08:00
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
2020-08-10 07:16:01 +08:00
Child = roomFlow = new FillFlowContainer < DrawableRoom >
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
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
2020-02-06 13:28:09 +08:00
filter ? . BindValueChanged ( criteria = > Filter ( criteria . NewValue ) ) ;
2021-02-16 13:02:21 +08:00
selectedRoom . BindValueChanged ( selection = >
{
updateSelection ( ) ;
} , true ) ;
2019-11-15 07:23:56 +08:00
}
2021-02-16 13:02:21 +08:00
private void updateSelection ( ) = >
roomFlow . Children . ForEach ( r = > r . State = r . Room = = selectedRoom . Value ? SelectionState . Selected : SelectionState . NotSelected ) ;
2018-12-11 18:07:40 +08:00
public void Filter ( FilterCriteria criteria )
{
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 )
{
roomFlow . Add ( new DrawableRoom ( room )
{
Action = ( ) = >
{
if ( room = = selectedRoom . Value )
{
2020-07-09 16:33:02 +08:00
joinSelected ( ) ;
2020-07-09 16:15:16 +08:00
return ;
}
selectRoom ( room ) ;
}
} ) ;
}
2018-12-11 18:07:40 +08:00
2020-02-06 11:12:52 +08:00
Filter ( filter ? . Value ) ;
2021-02-16 13:02:21 +08:00
updateSelection ( ) ;
2018-12-11 18:07:40 +08:00
}
private void removeRooms ( IEnumerable < Room > rooms )
{
foreach ( var r in rooms )
{
var toRemove = roomFlow . Single ( d = > d . Room = = r ) ;
toRemove . Action = null ;
roomFlow . Remove ( toRemove ) ;
2018-12-20 14:17:08 +08:00
2018-12-20 19:58:34 +08:00
selectRoom ( 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 )
2019-02-21 17:56:34 +08:00
roomFlow . SetLayoutPosition ( room , room . Room . Position . Value ) ;
2018-12-28 00:45:19 +08:00
}
2021-02-16 13:02:21 +08:00
private void selectRoom ( Room room ) = > selectedRoom . Value = room ;
2020-07-09 16:15:16 +08:00
2020-07-09 16:33:02 +08:00
private void joinSelected ( )
{
if ( selectedRoom . Value = = null ) return ;
JoinRequested ? . Invoke ( selectedRoom . Value ) ;
}
2021-02-16 12:44:36 +08:00
protected override bool OnClick ( ClickEvent e )
{
selectRoom ( null ) ;
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
public bool OnPressed ( GlobalAction action )
{
switch ( action )
{
2020-07-09 16:33:02 +08:00
case GlobalAction . Select :
joinSelected ( ) ;
return true ;
2020-07-09 16:15:16 +08:00
case GlobalAction . SelectNext :
beginRepeatSelection ( ( ) = > selectNext ( 1 ) , action ) ;
return true ;
case GlobalAction . SelectPrevious :
beginRepeatSelection ( ( ) = > selectNext ( - 1 ) , action ) ;
return true ;
}
2018-12-11 18:07:40 +08:00
2020-07-09 16:15:16 +08:00
return false ;
}
public void OnReleased ( GlobalAction action )
{
switch ( action )
{
case GlobalAction . SelectNext :
case GlobalAction . SelectPrevious :
endRepeatSelection ( action ) ;
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 )
{
var visibleRooms = Rooms . AsEnumerable ( ) . Where ( r = > r . IsPresent ) ;
Room room ;
if ( selectedRoom . Value = = null )
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
2020-07-09 16:15:16 +08:00
room = visibleRooms . SkipWhile ( r = > r . Room ! = selectedRoom . Value ) . Skip ( 1 ) . FirstOrDefault ( ) ? . Room ;
}
// we already have a valid selection only change selection if we still have a room to switch to.
if ( room ! = null )
selectRoom ( 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
}
}