Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<DebugType>portable</DebugType>
<Product>$(AssemblyName) ($(TargetFramework))</Product>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
namespace SkiaSharp.Extended.UI.Controls;

/// <summary>
/// A collection of <see cref="Color"/> values used for confetti particles.
/// </summary>
[TypeConverter(typeof(Converters.SKConfettiColorCollectionTypeConverter))]
public class SKConfettiColorCollection : List<Color>
{
/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiColorCollection"/> class.
/// </summary>
public SKConfettiColorCollection()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiColorCollection"/> class with items from the specified collection.
/// </summary>
/// <param name="collection">The collection of colors to add.</param>
public SKConfettiColorCollection(IEnumerable<Color> collection)
: base(collection)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiColorCollection"/> class with the specified initial capacity.
/// </summary>
/// <param name="capacity">The initial capacity.</param>
public SKConfettiColorCollection(int capacity)
: base(capacity)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
namespace SkiaSharp.Extended.UI.Controls.Converters;

/// <summary>
/// Converts comma-separated color strings to <see cref="SKConfettiColorCollection"/> instances.
/// </summary>
public class SKConfettiColorCollectionTypeConverter : StringTypeConverter
{
/// <inheritdoc/>
protected override object? ConvertFromStringCore(string? value)
{
if (value == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,32 @@

namespace SkiaSharp.Extended.UI.Controls
{
/// <summary>
/// Controls the rate and duration of particle emission for a confetti system.
/// </summary>
public class SKConfettiEmitter : BindableObject
{
/// <summary>
/// Identifies the <see cref="ParticleRate"/> bindable property.
/// </summary>
public static readonly BindableProperty ParticleRateProperty = BindableProperty.Create(
nameof(ParticleRate),
typeof(int),
typeof(SKConfettiEmitter),
100);

/// <summary>
/// Identifies the <see cref="MaxParticles"/> bindable property.
/// </summary>
public static readonly BindableProperty MaxParticlesProperty = BindableProperty.Create(
nameof(MaxParticles),
typeof(int),
typeof(SKConfettiEmitter),
-1);

/// <summary>
/// Identifies the <see cref="Duration"/> bindable property.
/// </summary>
public static readonly BindableProperty DurationProperty = BindableProperty.Create(
nameof(Duration),
typeof(double),
Expand All @@ -29,16 +41,28 @@ public class SKConfettiEmitter : BindableObject
false,
defaultBindingMode: BindingMode.OneWayToSource);

/// <summary>
/// Identifies the <see cref="IsComplete"/> bindable property.
/// </summary>
public static readonly BindableProperty IsCompleteProperty = IsCompletePropertyKey.BindableProperty;

private int totalParticles = 0;
private double totalDuration = 0;

/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiEmitter"/> class.
/// </summary>
public SKConfettiEmitter()
{
DebugUtils.LogPropertyChanged(this);
}

/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiEmitter"/> class with the specified settings.
/// </summary>
/// <param name="particleRate">The number of particles emitted per second.</param>
/// <param name="maxParticles">The maximum number of particles, or -1 for unlimited.</param>
/// <param name="duration">The emission duration in seconds, 0 for burst, or -1 for infinite.</param>
public SKConfettiEmitter(int particleRate, int maxParticles, double duration)
: this()
{
Expand All @@ -47,32 +71,51 @@ public SKConfettiEmitter(int particleRate, int maxParticles, double duration)
Duration = duration;
}

/// <summary>
/// Gets or sets the number of particles emitted per second.
/// </summary>
public int ParticleRate
{
get => (int)GetValue(ParticleRateProperty);
set => SetValue(ParticleRateProperty, value);
}

/// <summary>
/// Gets or sets the maximum total number of particles. Use -1 for unlimited.
/// </summary>
public int MaxParticles
{
get => (int)GetValue(MaxParticlesProperty);
set => SetValue(MaxParticlesProperty, value);
}

/// <summary>
/// Gets or sets the emission duration in seconds. Use 0 for burst mode or -1 for infinite.
/// </summary>
public double Duration
{
get => (double)GetValue(DurationProperty);
set => SetValue(DurationProperty, value);
}

/// <summary>
/// Gets a value indicating whether the emitter has completed emitting particles.
/// </summary>
public bool IsComplete
{
get => (bool)GetValue(IsCompleteProperty);
private set => SetValue(IsCompletePropertyKey, value);
}

/// <summary>
/// Occurs when new particles have been created.
/// </summary>
public event Action<int>? ParticlesCreated;

/// <summary>
/// Updates the emitter state for the given elapsed time.
/// </summary>
/// <param name="deltaTime">The time elapsed since the last update.</param>
public void Update(TimeSpan deltaTime)
{
if (IsComplete)
Expand Down Expand Up @@ -103,15 +146,37 @@ public void Update(TimeSpan deltaTime)
(Duration > 0 && totalDuration >= Duration); // reached the max duration
}

/// <summary>
/// Creates a burst emitter that emits all particles at once.
/// </summary>
/// <param name="particles">The number of particles to emit.</param>
/// <returns>A new burst-mode emitter.</returns>
public static SKConfettiEmitter Burst(int particles) =>
new SKConfettiEmitter(particles, -1, 0);

/// <summary>
/// Creates a stream emitter that emits particles over a specified duration.
/// </summary>
/// <param name="particleRate">The number of particles emitted per second.</param>
/// <param name="duration">The emission duration in seconds.</param>
/// <returns>A new stream-mode emitter.</returns>
public static SKConfettiEmitter Stream(int particleRate, double duration) =>
new SKConfettiEmitter(particleRate, -1, duration);

/// <summary>
/// Creates an infinite emitter with no particle limit.
/// </summary>
/// <param name="particleRate">The number of particles emitted per second.</param>
/// <returns>A new infinite emitter.</returns>
public static SKConfettiEmitter Infinite(int particleRate) =>
new SKConfettiEmitter(particleRate, -1, -1);

/// <summary>
/// Creates an infinite emitter with a maximum particle limit.
/// </summary>
/// <param name="particleRate">The number of particles emitted per second.</param>
/// <param name="maxParticles">The maximum total number of particles.</param>
/// <returns>A new infinite emitter with a particle cap.</returns>
public static SKConfettiEmitter Infinite(int particleRate, int maxParticles) =>
new SKConfettiEmitter(particleRate, maxParticles, -1);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,55 @@
namespace SkiaSharp.Extended.UI.Controls;

/// <summary>
/// Defines the bounds from which a confetti emitter spawns particles.
/// </summary>
[TypeConverter(typeof(Converters.SKConfettiEmitterBoundsTypeConverter))]
public readonly struct SKConfettiEmitterBounds
{
/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiEmitterBounds"/> struct for the specified side.
/// </summary>
/// <param name="side">The side of the view to emit from.</param>
public SKConfettiEmitterBounds(SKConfettiEmitterSide side)
: this(Rect.Zero, side)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiEmitterBounds"/> struct for a specific point.
/// </summary>
/// <param name="x">The x-coordinate of the emission point.</param>
/// <param name="y">The y-coordinate of the emission point.</param>
public SKConfettiEmitterBounds(double x, double y)
: this(new Rect(x, y, 0, 0), SKConfettiEmitterSide.Bounds)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiEmitterBounds"/> struct for a specific point.
/// </summary>
/// <param name="point">The emission point.</param>
public SKConfettiEmitterBounds(Point point)
: this(new Rect(point, Size.Zero), SKConfettiEmitterSide.Bounds)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiEmitterBounds"/> struct for a specific rectangular area.
/// </summary>
/// <param name="x">The x-coordinate of the bounds.</param>
/// <param name="y">The y-coordinate of the bounds.</param>
/// <param name="width">The width of the bounds.</param>
/// <param name="height">The height of the bounds.</param>
public SKConfettiEmitterBounds(double x, double y, double width, double height)
: this(new Rect(x, y, width, height), SKConfettiEmitterSide.Bounds)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SKConfettiEmitterBounds"/> struct for a specific rectangle.
/// </summary>
/// <param name="rect">The emission bounds rectangle.</param>
public SKConfettiEmitterBounds(Rect rect)
: this(rect, SKConfettiEmitterSide.Bounds)
{
Expand All @@ -36,47 +63,104 @@ private SKConfettiEmitterBounds(Rect rect, SKConfettiEmitterSide side)

//

/// <summary>
/// Gets the rectangle defining the emission area.
/// </summary>
public Rect Rect { get; }

/// <summary>
/// Gets the side from which particles are emitted.
/// </summary>
public SKConfettiEmitterSide Side { get; }

//

/// <summary>
/// Converts an <see cref="SKConfettiEmitterSide"/> to an <see cref="SKConfettiEmitterBounds"/>.
/// </summary>
/// <param name="side">The emitter side.</param>
public static implicit operator SKConfettiEmitterBounds(SKConfettiEmitterSide side) =>
new SKConfettiEmitterBounds(side);

/// <summary>
/// Converts a <see cref="Microsoft.Maui.Graphics.Point"/> to an <see cref="SKConfettiEmitterBounds"/>.
/// </summary>
/// <param name="point">The point.</param>
public static implicit operator SKConfettiEmitterBounds(Point point) =>
new SKConfettiEmitterBounds(point);

/// <summary>
/// Converts a <see cref="Microsoft.Maui.Graphics.Rect"/> to an <see cref="SKConfettiEmitterBounds"/>.
/// </summary>
/// <param name="rect">The rectangle.</param>
public static implicit operator SKConfettiEmitterBounds(Rect rect) =>
new SKConfettiEmitterBounds(rect);

//

/// <summary>
/// Gets emitter bounds that emit from the top side of the view.
/// </summary>
public static SKConfettiEmitterBounds Top =>
new SKConfettiEmitterBounds(SKConfettiEmitterSide.Top);

/// <summary>
/// Gets emitter bounds that emit from the left side of the view.
/// </summary>
public static SKConfettiEmitterBounds Left =>
new SKConfettiEmitterBounds(SKConfettiEmitterSide.Left);

/// <summary>
/// Gets emitter bounds that emit from the right side of the view.
/// </summary>
public static SKConfettiEmitterBounds Right =>
new SKConfettiEmitterBounds(SKConfettiEmitterSide.Right);

/// <summary>
/// Gets emitter bounds that emit from the bottom side of the view.
/// </summary>
public static SKConfettiEmitterBounds Bottom =>
new SKConfettiEmitterBounds(SKConfettiEmitterSide.Bottom);

/// <summary>
/// Gets emitter bounds that emit from the center of the view.
/// </summary>
public static SKConfettiEmitterBounds Center =>
new SKConfettiEmitterBounds(SKConfettiEmitterSide.Center);

/// <summary>
/// Creates emitter bounds for a specific rectangular area.
/// </summary>
/// <param name="x">The x-coordinate of the bounds.</param>
/// <param name="y">The y-coordinate of the bounds.</param>
/// <param name="width">The width of the bounds.</param>
/// <param name="height">The height of the bounds.</param>
/// <returns>A new <see cref="SKConfettiEmitterBounds"/> for the specified area.</returns>
public static SKConfettiEmitterBounds Bounds(double x, double y, double width, double height) =>
new SKConfettiEmitterBounds(x, y, width, height);

/// <summary>
/// Creates emitter bounds for a specific rectangle.
/// </summary>
/// <param name="rect">The emission bounds rectangle.</param>
/// <returns>A new <see cref="SKConfettiEmitterBounds"/> for the specified rectangle.</returns>
public static SKConfettiEmitterBounds Bounds(Rect rect) =>
new SKConfettiEmitterBounds(rect);

/// <summary>
/// Creates emitter bounds for a specific point.
/// </summary>
/// <param name="x">The x-coordinate of the emission point.</param>
/// <param name="y">The y-coordinate of the emission point.</param>
/// <returns>A new <see cref="SKConfettiEmitterBounds"/> for the specified point.</returns>
public static SKConfettiEmitterBounds Point(double x, double y) =>
new SKConfettiEmitterBounds(x, y);

/// <summary>
/// Creates emitter bounds for a specific point.
/// </summary>
/// <param name="point">The emission point.</param>
/// <returns>A new <see cref="SKConfettiEmitterBounds"/> for the specified point.</returns>
public static SKConfettiEmitterBounds Point(Point point) =>
new SKConfettiEmitterBounds(point);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
namespace SkiaSharp.Extended.UI.Controls.Converters;

/// <summary>
/// Converts string values to <see cref="SKConfettiEmitterBounds"/> instances.
/// </summary>
public class SKConfettiEmitterBoundsTypeConverter : StringTypeConverter
{
/// <inheritdoc/>
protected override object? ConvertFromStringCore(string? value)
{
if (value == null)
Expand Down
Loading