// 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 } }