using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Globalization; using System.Runtime.InteropServices; #pragma warning disable IDE1006 // Naming Styles namespace Vanara.PInvoke { /// Defines the coordinates of the upper-left and lower-right corners of a rectangle. /// /// By convention, the right and bottom edges of the rectangle are normally considered exclusive. In other words, the pixel whose /// coordinates are ( right, bottom ) lies immediately outside of the rectangle. For example, when RECT is passed to the FillRect /// function, the rectangle is filled up to, but not including, the right column and bottom row of pixels. This structure is identical to /// the RECT structure. /// [StructLayout(LayoutKind.Sequential), TypeConverter(typeof(RECTConverter))] public struct RECT : IEquatable, IEquatable, IEquatable { /// The x-coordinate of the upper-left corner of the rectangle. public int left; /// The y-coordinate of the upper-left corner of the rectangle. public int top; /// The x-coordinate of the lower-right corner of the rectangle. public int right; /// The y-coordinate of the lower-right corner of the rectangle. public int bottom; /// Initializes a new instance of the struct. /// The left. /// The top. /// The right. /// The bottom. public RECT(int left, int top, int right, int bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } /// Initializes a new instance of the struct. /// The rectangle. public RECT(Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { } /// The x-coordinate of the upper-left corner of the rectangle. public int Left { get => left; set => left = value; } /// The x-coordinate of the lower-right corner of the rectangle. public int Right { get => right; set => right = value; } /// The y-coordinate of the upper-left corner of the rectangle. public int Top { get => top; set => top = value; } /// The y-coordinate of the lower-right corner of the rectangle. public int Bottom { get => bottom; set => bottom = value; } /// Gets or sets the x-coordinate of the upper-left corner of this structure. /// The x-coordinate of the upper-left corner of this structure. The default is 0. public int X { get => left; set { right -= left - value; left = value; } } /// Gets or sets the y-coordinate of the upper-left corner of this structure. /// The y-coordinate of the upper-left corner of this structure. The default is 0. public int Y { get => top; set { bottom -= top - value; top = value; } } /// Gets or sets the height of this structure. /// The height of this structure. The default is 0. public int Height { get => bottom - top; set => bottom = value + top; } /// Gets or sets the width of this structure. /// The width of this structure. The default is 0. public int Width { get => right - left; set => right = value + left; } /// Gets or sets the coordinates of the upper-left corner of this structure. /// A Point that represents the upper-left corner of this structure. public POINT Location { get => new(left, top); set { X = value.X; Y = value.Y; } } /// Gets or sets the size of this structure. /// A Size that represents the width and height of this structure. public Size Size { get => new(Width, Height); set { Width = value.Width; Height = value.Height; } } /// Tests whether all numeric properties of this have values of zero. /// true if this instance is empty; otherwise, false. public bool IsEmpty => left == 0 && top == 0 && right == 0 && bottom == 0; /// Performs an implicit conversion from to . /// The structure. /// The result of the conversion. public static implicit operator Rectangle(RECT r) => new(r.left, r.top, r.Width, r.Height); /// Performs an implicit conversion from to . /// The Rectangle structure. /// The result of the conversion. public static implicit operator RECT(Rectangle r) => new(r); /// Tests whether two structures have equal values. /// The first structure. /// The second structure. /// The result of the operator. public static bool operator ==(RECT r1, RECT r2) => r1.Equals(r2); /// Tests whether two structures have different values. /// The first structure. /// The second structure. /// The result of the operator. public static bool operator !=(RECT r1, RECT r2) => !r1.Equals(r2); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public bool Equals(RECT r) => r.left == left && r.top == top && r.right == right && r.bottom == bottom; /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public bool Equals(PRECT r) => r is not null && Equals(r.rect); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public bool Equals(Rectangle r) => r.Left == left && r.Top == top && r.Right == right && r.Bottom == bottom; /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object obj) => obj switch { null => false, RECT r => Equals(r), PRECT r => Equals(r), Rectangle r => Equals(r), _ => false, }; /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => ((Rectangle)this).GetHashCode(); /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() => $"{{left={left},top={top},right={right},bottom={bottom}}}"; /// Represents an empty instance where all values are set to 0. public static readonly RECT Empty = new(); } /// Defines the coordinates of the upper-left and lower-right corners of a rectangle. /// /// By convention, the right and bottom edges of the rectangle are normally considered exclusive. In other words, the pixel whose /// coordinates are ( right, bottom ) lies immediately outside of the rectangle. For example, when RECT is passed to the FillRect /// function, the rectangle is filled up to, but not including, the right column and bottom row of pixels. This structure is identical to /// the RECT structure. /// [StructLayout(LayoutKind.Sequential), TypeConverter(typeof(PRECTConverter))] public class PRECT : IEquatable, IEquatable, IEquatable { internal RECT rect; /// Initializes a new instance of the class with all values set to 0. public PRECT() { } /// Initializes a new instance of the class. /// The left. /// The top. /// The right. /// The bottom. public PRECT(int left, int top, int right, int bottom) => rect = new RECT(left, top, right, bottom); /// Initializes a new instance of the class. /// The structure. public PRECT(Rectangle r) => rect = new RECT(r); /// Initializes a new instance of the class. /// The r. [ExcludeFromCodeCoverage] private PRECT(RECT r) => rect = r; /// The x-coordinate of the upper-left corner of the rectangle. public int left { get => rect.left; set => rect.left = value; } /// The y-coordinate of the upper-left corner of the rectangle. public int top { get => rect.top; set => rect.top = value; } /// he x-coordinate of the lower-right corner of the rectangle. public int right { get => rect.right; set => rect.right = value; } /// he y-coordinate of the lower-right corner of the rectangle. public int bottom { get => rect.bottom; set => rect.bottom = value; } /// Gets or sets the x-coordinate of the upper-left corner of this structure. /// The x-coordinate of the upper-left corner of this structure. The default is 0. public int X { get => rect.X; set => rect.X = value; } /// Gets or sets the y-coordinate of the upper-left corner of this structure. /// The y-coordinate of the upper-left corner of this structure. The default is 0. public int Y { get => rect.Y; set => rect.Y = value; } /// Gets or sets the height of this structure. /// The height of this structure. The default is 0. public int Height { get => rect.Height; set => rect.Height = value; } /// Gets or sets the width of this structure. /// The width of this structure. The default is 0. public int Width { get => rect.Width; set => rect.Width = value; } /// Gets or sets the coordinates of the upper-left corner of this structure. /// A Point that represents the upper-left corner of this structure. public POINT Location { get => rect.Location; set => rect.Location = value; } /// Gets or sets the size of this structure. /// A Size that represents the width and height of this structure. public Size Size { get => rect.Size; set => rect.Size = value; } /// Tests whether all numeric properties of this have values of zero. /// true if this instance is empty; otherwise, false. public bool IsEmpty => rect.IsEmpty; /// Performs an implicit conversion from to . /// The to convert. /// The result of the conversion. public static implicit operator Rectangle(PRECT r) => r.rect; /// Performs an implicit conversion from to . /// The to convert. /// The result of the conversion. public static implicit operator PRECT(Rectangle? r) => r.HasValue ? new PRECT(r.Value) : null; /// Performs an implicit conversion from to . /// The to convert. /// The result of the conversion. public static implicit operator PRECT(Rectangle r) => new(r); /// Performs an implicit conversion from to . /// The to convert. /// The result of the conversion. public static implicit operator PRECT(RECT r) => new(r); /// Implements the operator ==. /// The first structure. /// The second structure. /// The result of the operator. public static bool operator ==(PRECT r1, PRECT r2) { if (ReferenceEquals(r1, r2)) return true; if (r1 is null || r2 is null) return false; return r1.Equals(r2); } /// Implements the operator !=. /// The first structure. /// The second structure. /// The result of the operator. public static bool operator !=(PRECT r1, PRECT r2) => !(r1 == r2); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public bool Equals(PRECT r) => rect == r?.rect; /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public bool Equals(RECT r) => rect.Equals(r); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public bool Equals(Rectangle r) => rect.Equals(r); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object obj) => rect.Equals(obj); /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => rect.GetHashCode(); /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() => rect.ToString(); } internal class PRECTConverter : RECTConverter { public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { var b = base.ConvertFrom(context, culture, value); return b is RECT r ? new PRECT(r) : b; } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value is PRECT prect && destinationType == typeof(InstanceDescriptor)) { var ctor = typeof(PRECT).GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(int) }); return new InstanceDescriptor(ctor, new object[] { prect.left, prect.top, prect.right, prect.bottom }); } return base.ConvertTo(context, culture, value, destinationType); } public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { if (propertyValues is null) throw new ArgumentNullException(nameof(propertyValues)); var left = propertyValues["left"] ?? 0; var top = propertyValues["top"] ?? 0; var right = propertyValues["right"] ?? 0; var bottom = propertyValues["bottom"] ?? 0; return left is int l && top is int t && right is int r && bottom is int b ? new PRECT(l, t, r, b) : throw new ArgumentException(@"Invalid property value."); } public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { var props = TypeDescriptor.GetProperties(typeof(PRECT), attributes); return props.Sort(new[] { "left", "top", "right", "bottom" }); } } internal class RECTConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string strValue) { var text = strValue.Trim(); if (text.Length == 0) return null; culture ??= CultureInfo.CurrentCulture; var tokens = text.Split(culture.TextInfo.ListSeparator[0]); if (tokens.Length == 4) { var intConverter = TypeDescriptor.GetConverter(typeof(int)); var values = Array.ConvertAll(tokens, i => (int)intConverter.ConvertFromString(context, culture, i)); return new RECT(values[0], values[1], values[2], values[3]); } } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType is null) throw new ArgumentNullException(nameof(destinationType)); if (value is not RECT rect) return base.ConvertTo(context, culture, value, destinationType); culture ??= CultureInfo.CurrentCulture; if (destinationType == typeof(string)) return IntConvertToString(context, culture, rect); if (destinationType == typeof(InstanceDescriptor)) { var ctor = typeof(RECT).GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(int) }); return new InstanceDescriptor(ctor, new[] { rect.left, rect.top, rect.right, rect.bottom }); } return base.ConvertTo(context, culture, value, destinationType); } public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { if (propertyValues is null) throw new ArgumentNullException(nameof(propertyValues)); var left = propertyValues["left"]; var top = propertyValues["top"]; var right = propertyValues["right"]; var bottom = propertyValues["bottom"]; return left is int l && top is int t && right is int r && bottom is int b ? new RECT(l, t, r, b) : throw new ArgumentException(@"Invalid property value."); } public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) => true; public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { var props = TypeDescriptor.GetProperties(typeof(RECT), attributes); return props.Sort(new[] { "left", "top", "right", "bottom" }); } public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; protected static string IntConvertToString(ITypeDescriptorContext context, CultureInfo culture, RECT rect) { var intConverter = TypeDescriptor.GetConverter(typeof(int)); var args = new string[4]; var nArg = 0; args[nArg++] = intConverter.ConvertToString(context, culture, rect.left); args[nArg++] = intConverter.ConvertToString(context, culture, rect.top); args[nArg++] = intConverter.ConvertToString(context, culture, rect.right); args[nArg++] = intConverter.ConvertToString(context, culture, rect.bottom); return string.Join(culture.TextInfo.ListSeparator + " ", args); } } }