Extensions/PanelExtensions.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BetterUI.Extensions;
/// <summary>
/// Extensions for <see cref="Panel"/>.
/// </summary>
public static class PanelExtensions
{
/// <summary>
/// Adds a CSS variable to the panel.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="name">The name of the CSS variable.</param>
/// <param name="value">The value of the CSS variable.</param>
/// <returns>The panel.</returns>
public static void AddCssVariable( this Panel panel, string name, string value )
{
if ( string.IsNullOrWhiteSpace( name ) )
throw new ArgumentException( "CSS variable name cannot be null or empty.", nameof(name) );
var styleSheet = new StyleSheet { Variables = new Dictionary<string, string> { { name, value } } };
panel.StyleSheet.Add( styleSheet );
}
/// <summary>
/// Adds a CSS variable to the panel if the condition is true.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="name">The name of the CSS variable.</param>
/// <param name="value">The value of the CSS variable.</param>
/// <param name="condition">The condition to check.</param>
/// <returns>The panel.</returns>
public static void BindCssVariable( this Panel panel, string name, string value, Func<bool> condition )
{
if ( string.IsNullOrWhiteSpace( name ) )
throw new ArgumentException( "CSS class name cannot be null or empty.", nameof(name) );
if ( !condition() ) return;
panel.AddCssVariable( name, value );
}
/// <summary>
/// Binds a CSS class to the panel when the task completes successfully.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="name">The name of the CSS class.</param>
/// <param name="task">The task.</param>
/// <returns>The panel.</returns>
public static Task AddClassOnTaskCompletion( this Panel panel, string name, Task task )
{
if ( string.IsNullOrWhiteSpace( name ) )
throw new ArgumentException( "CSS class name cannot be null or empty.", nameof(name) );
return panel.Task.WhenAll( task )
.ContinueWith( _ => panel.BindClass( name, () => task.IsCompletedSuccessfully ) );
}
/// <summary>
/// Binds a CSS class to the panel when the task completes successfully, and unbinds it when the task completes.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="name">The name of the CSS class.</param>
/// <param name="task">The task.</param>
/// <returns>The panel.</returns>
public static Task RemoveClassOnTaskCompletion( this Panel panel, string name, Task task )
{
if ( string.IsNullOrWhiteSpace( name ) )
throw new ArgumentException( "CSS class name cannot be null or empty.", nameof(name) );
return panel.Task.WhenAll( task )
.ContinueWith( _ => panel.BindClass( name, () => !task.IsCompletedSuccessfully ) );
}
/// <summary>
/// Switches the panel's CSS class between two classes based on the task's completion status.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="runningClass">The CSS class to apply while the task is running.</param>
/// <param name="task">The task.</param>
/// <param name="completedClass">The CSS class to apply when the task is completed. Defaults to <c>null</c>. If <c>null</c>, the class will be removed.</param>
/// <returns>The panel.</returns>
public static Task SwitchClassOnTaskCompletion( this Panel panel, string runningClass, Task task,
string? completedClass = null )
{
if ( string.IsNullOrWhiteSpace( runningClass ) )
throw new ArgumentException( "CSS class name cannot be null or empty.", nameof(runningClass) );
if ( string.IsNullOrWhiteSpace( completedClass ) )
throw new ArgumentException( "CSS class name cannot be null or empty.", nameof(completedClass) );
panel.AddClass( runningClass );
return panel.Task.WhenAll( task ).ContinueWith( _ =>
{
panel.RemoveClass( runningClass );
panel.BindClass( completedClass, () => task.IsCompletedSuccessfully );
} );
}
/// <summary>
/// Adds a CSS class to the panel after a specified delay.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="name">The name of the CSS class.</param>
/// <param name="delay">The delay in milliseconds.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public static Task AddClassAfterDelay( this Panel panel, string name, int delay )
{
if ( string.IsNullOrWhiteSpace( name ) )
throw new ArgumentException( "CSS class name cannot be null or empty.", nameof(name) );
return panel.Task.RunInThreadAsync( async () =>
{
await Task.Delay( delay );
panel.AddClass( name );
} );
}
/// <summary>
/// Removes a CSS class from the panel after a specified delay.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="name">The name of the CSS class.</param>
/// <param name="delay">The delay in milliseconds.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public static Task RemoveClassAfterDelay( this Panel panel, string name, int delay )
{
if ( string.IsNullOrWhiteSpace( name ) )
throw new ArgumentException( "CSS class name cannot be null or empty.", nameof(name) );
return panel.Task.RunInThreadAsync( async () =>
{
await Task.Delay( delay );
panel.RemoveClass( name );
} );
}
/// <summary>
/// Cycles the panel's CSS classes between the classes provided.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="interval">The interval between each class cycle.</param>
/// <param name="delay">The delay before the first cycle.</param>
/// <param name="classList">The list of CSS classes to cycle through.</param>
public static Task CycleClasses( this Panel panel, int interval, int delay = 0, params string[] classList )
{
return panel.Task.RunInThreadAsync( async () =>
{
while ( panel is { IsValid: true, DeletionToken.IsCancellationRequested: false } )
{
for ( var i = 0; i < classList.Length; i++ )
{
var previous = i > 0 ? classList[i - 1] : classList[^1];
var next = classList[i];
await Task.Delay( interval, panel.DeletionToken );
panel.RemoveClass( previous );
panel.AddClass( next );
await Task.Delay( delay );
}
}
} );
}
/// <summary>
/// Flashes a CSS class on the panel for a specified duration.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="className">The name of the CSS class to flash.</param>
/// <param name="duration">The duration of the flash in milliseconds.</param>
public static void FlashClass( this Panel panel, string className, int duration )
{
panel.Task.RunInThreadAsync( async () =>
{
panel.AddClass( className );
await Task.Delay( duration, panel.DeletionToken );
panel.RemoveClass( className );
} );
}
/// <summary>
/// Flashes a CSS class on the panel for a specified duration if a condition is true.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="className">The name of the CSS class to flash.</param>
/// <param name="duration">The duration of the flash in milliseconds.</param>
/// <param name="condition">The condition to check before flashing the class.</param>
public static void FlashClass( this Panel panel, string className, int duration, Func<bool> condition )
{
if ( !condition() ) return;
panel.FlashClass( className, duration );
}
/// <summary>
/// Gets a dictionary of user data associated with the panel.
/// </summary>
/// <returns>The user data dictionary.</returns>
private static Dictionary<string, object> GetBag( this Panel panel )
{
if ( panel.UserData is Dictionary<string, object> bag ) return bag;
bag = new Dictionary<string, object>();
panel.UserData = bag;
return bag;
}
/// <summary>
/// Gets a value from the panel's user data dictionary.
/// </summary>
/// <typeparam name="T">The type of the value to retrieve.</typeparam>
/// <param name="panel">The panel.</param>
/// <param name="name">The key to retrieve.</param>
/// <returns>The value associated with the key, or <c>null</c> if the key does not exist.</returns>
public static T? GetBag<T>( this Panel panel, string name ) where T : class
{
if ( panel.UserData is Dictionary<string, object> bag )
return (T?)bag[name];
bag = new Dictionary<string, object>();
panel.UserData = bag;
return null;
}
/// <summary>
/// Sets the value of a key in the panel's user data dictionary.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="name">The key to set.</param>
/// <param name="value">The value to set.</param>
public static void SetBag( this Panel panel, string name, object value )
{
panel.GetBag()[name] = value;
}
/// <summary>
/// Sets a state on the panel, which can be used to change styles
/// or behaviors. If the state already exists, it is removed.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="className">The name of the state.</param>
public static void SetState( this Panel panel, string className )
{
if ( string.IsNullOrWhiteSpace( className ) )
throw new ArgumentException( "State name cannot be null or empty.", nameof(className) );
if ( panel.GetBag<string>( "state" ) is var state )
panel.RemoveClass( state );
panel.GetBag()["state"] = className;
panel.AddClass( className );
}
/// <summary>
/// Sets a state on the panel, which can be used to change styles
/// or behaviors. If the state already exists, it is removed.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="className">The name of the state.</param>
/// <param name="condition">A condition that determines if the state should be set.</param>
public static void SetState( this Panel panel, string className, Func<bool> condition )
{
if ( !condition() ) return;
panel.SetState( className );
}
/// <summary>
/// Hides the panel by adding the "hidden" CSS class.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="delay">The delay in milliseconds.</param>
public static Task Hide( this Panel panel, int delay = 0 )
{
return panel.Task.RunInThreadAsync( async () =>
{
await Task.Delay( delay );
panel.AddClass( "hidden" );
} );
}
/// <summary>
/// Sets the display mode of the panel after a specified delay.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="displayMode">The display mode.</param>
/// <param name="delay">The delay in milliseconds.</param>
public static Task Display( this Panel panel, DisplayMode displayMode, int delay = 0 )
{
return GameTask.RunInThreadAsync( async () =>
{
await Task.Delay( delay );
panel.Style.Display = displayMode;
} );
}
/// <summary>
/// Shows the panel by removing the "hidden" CSS class.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="delay">The delay in milliseconds.</param>
public static Task Show( this Panel panel, int delay = 0 )
{
return panel.Task.RunInThreadAsync( async () =>
{
panel.RemoveClass( "hidden" );
await Task.Delay( delay );
} );
}
/// <summary>
/// Shows the panel if the condition is true, hides it otherwise.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="condition">The condition to check.</param>
public static void ShowIf( this Panel panel, Func<bool> condition )
{
if ( condition() ) panel.Show();
else panel.Hide();
}
/// <summary>
/// Hides the panel if the condition is true, shows it otherwise.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="condition">The condition to check.</param>
public static void HideIf( this Panel panel, Func<bool> condition )
{
if ( condition() ) panel.Hide();
else panel.Show();
}
/// <summary>
/// Checks if the panel is visible.
/// </summary>
/// <param name="panel">The panel.</param>
/// <returns>True if the panel is visible, false otherwise.</returns>
public static bool IsVisible( this Panel panel ) => !panel.IsHidden();
/// <summary>
/// Checks if the panel is hidden.
/// </summary>
/// <param name="panel">The panel.</param>
/// <returns>True if the panel is hidden, false otherwise.</returns>
public static bool IsHidden( this Panel panel ) => panel.HasClass( "hidden" );
/// <summary>
/// Binds the visibility of the panel to a condition.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="condition">The condition to check.</param>
public static void BindVisibility( this Panel panel, Func<bool> condition ) =>
panel.BindClass( "hidden", () => !condition() );
}