// ReSharper disable MemberCanBePrivate.Global
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace RGB.NET.Core
{
///
///
/// Represents a single LED of a RGB-device.
///
[DebuggerDisplay("{Id} {Color}")]
public class Led : AbstractBindable
{
#region Properties & Fields
///
/// Gets the this is associated with.
///
public IRGBDevice Device { get; }
///
/// Gets the of the .
///
public LedId Id { get; }
private Shape _shape = Shape.Rectangle;
///
/// Gets or sets the of the .
///
public Shape Shape
{
get => _shape;
set => SetProperty(ref _shape, value);
}
private string? _shapeData;
///
/// Gets or sets the data used for by the -.
///
public string? ShapeData
{
get => _shapeData;
set => SetProperty(ref _shapeData, value);
}
private Point _location;
///
/// Gets or sets the relative location of the .
///
public Point Location
{
get => _location;
set
{
if (SetProperty(ref _location, value))
{
UpdateActualData();
UpdateAbsoluteData();
}
}
}
private Size _size;
///
/// Gets or sets the size of the .
///
public Size Size
{
get => _size;
set
{
if (SetProperty(ref _size, value))
{
UpdateActualData();
UpdateAbsoluteData();
}
}
}
private Point _actualLocation;
///
/// Gets the actual location of the .
/// This includes device-scaling and rotation.
///
public Point ActualLocation
{
get => _actualLocation;
private set => SetProperty(ref _actualLocation, value);
}
private Size _actualSize;
///
/// Gets the actual size of the .
/// This includes device-scaling.
///
public Size ActualSize
{
get => _actualSize;
private set => SetProperty(ref _actualSize, value);
}
private Rectangle _ledRectangle;
///
/// Gets a rectangle representing the logical location of the relative to the .
///
public Rectangle LedRectangle
{
get => _ledRectangle;
private set => SetProperty(ref _ledRectangle, value);
}
private Rectangle _absoluteLedRectangle;
///
/// Gets a rectangle representing the logical location of the on the .
///
public Rectangle AbsoluteLedRectangle
{
get => _absoluteLedRectangle;
private set => SetProperty(ref _absoluteLedRectangle, value);
}
///
/// Indicates whether the is about to change it's color.
///
public bool IsDirty => RequestedColor.HasValue && (RequestedColor != Color);
private Color? _requestedColor;
///
/// Gets a copy of the the LED should be set to on the next update.
/// Null if there is no update-request for the next update.
///
public Color? RequestedColor
{
get => _requestedColor;
private set
{
SetProperty(ref _requestedColor, value);
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(IsDirty));
}
}
private Color _color = Color.Transparent;
///
/// Gets the current of the . Sets the for the next update.
///
public Color Color
{
get => _color;
set
{
if (RequestedColor.HasValue)
RequestedColor = RequestedColor.Value + value;
else
RequestedColor = value;
}
}
///
/// Gets the URI of an image of the or null if there is no image.
///
public Uri? Image { get; set; }
///
/// Gets the provider-specific data associated with this led.
///
public object? CustomData { get; }
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The the is associated with.
/// The of the .
/// The physical location of the relative to the .
/// The size of the .
/// The provider-specific data associated with this led.
internal Led(IRGBDevice device, LedId id, Point location, Size size, object? customData = null)
{
this.Device = device;
this.Id = id;
this.Location = location;
this.Size = size;
this.CustomData = customData;
device.PropertyChanged += DevicePropertyChanged;
}
#endregion
#region Methods
private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(IRGBDevice.Location):
UpdateAbsoluteData();
break;
case nameof(IRGBDevice.DeviceRectangle):
UpdateActualData();
UpdateAbsoluteData();
break;
}
}
private void UpdateActualData()
{
ActualSize = Size * Device.Scale;
Point actualLocation = (Location * Device.Scale);
Rectangle ledRectangle = new(Location * Device.Scale, Size * Device.Scale);
if (Device.Rotation.IsRotated)
{
Point deviceCenter = new Rectangle(Device.ActualSize).Center;
Point actualDeviceCenter = new Rectangle(Device.DeviceRectangle.Size).Center;
Point centerOffset = new(actualDeviceCenter.X - deviceCenter.X, actualDeviceCenter.Y - deviceCenter.Y);
actualLocation = actualLocation.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center) + centerOffset;
ledRectangle = new Rectangle(ledRectangle.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center)).Translate(centerOffset);
}
ActualLocation = actualLocation;
LedRectangle = ledRectangle;
}
private void UpdateAbsoluteData()
{
AbsoluteLedRectangle = LedRectangle.Translate(Device.Location);
}
///
/// Converts the and the of this to a human-readable string.
///
/// A string that contains the and the of this . For example "Enter [A: 255, R: 255, G: 0, B: 0]".
public override string ToString() => $"{Id} {Color}";
///
/// Updates the to the requested .
///
internal void Update()
{
if (!RequestedColor.HasValue) return;
_color = RequestedColor.Value;
RequestedColor = null;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(Color));
}
///
/// Resets the back to default.
///
internal void Reset()
{
_color = Color.Transparent;
RequestedColor = null;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(Color));
}
#endregion
#region Operators
///
/// Converts a to a .
///
/// The to convert.
public static implicit operator Color(Led led) => led.Color;
///
/// Converts a to a .
///
/// The to convert.
public static implicit operator Rectangle(Led led) => led.LedRectangle;
#endregion
}
}