From: Jean-Philippe Bruyère Date: Sat, 9 May 2020 13:07:52 +0000 (+0200) Subject: source tree organization X-Git-Tag: v0.9.5-beta~119 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=f81023f3b38b7fd4c0f0b29f8e0b56f958c29623;p=jp%2Fcrow.git source tree organization --- diff --git a/Crow/Crow.csproj b/Crow/Crow.csproj index 5f29d798..0094690c 100644 --- a/Crow/Crow.csproj +++ b/Crow/Crow.csproj @@ -4,13 +4,14 @@ netstandard2.0 $(CrowVersion) - 0.8.0 + $(CrowVersion) + $(CrowPackageVersion) + C# Rapid Open Widget Toolkit C.R.O.W. is a widget toolkit and rendering engine developed in C# with templates, styles, compositing, and bindings. MIT Jean-Philippe Bruyère https://github.com/jpbruyere/Crow - $(CrowPackageVersion) GUI Widget toolkit Interface C# .Net Mono False https://github.com/jpbruyere/Crow/wiki @@ -38,7 +39,7 @@ - + $(DefineConstants);STB_SHARP diff --git a/Crow/src/2d/Measure.cs b/Crow/src/2d/Measure.cs new file mode 100644 index 00000000..c77d66bd --- /dev/null +++ b/Crow/src/2d/Measure.cs @@ -0,0 +1,118 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace Crow +{ + /// + /// Measurement unit + /// + public enum Unit {Undefined, Pixel, Percent, Inherit } + /// + /// Measure class allow proportional sizes as well as stretched and fit on content. + /// + public struct Measure + { + /// + /// Integer value of the measure + /// + public int Value; + /// + /// Measurement unit + /// + public Unit Units; + + /// + /// Fit on content, this special measure is defined as a fixed integer set to -1 pixel + /// + public static Measure Fit = new Measure(-1,Unit.Percent); + /// + /// Stretched into parent client area. This special measure is defined as a proportional cote + /// set to 100 Percents + /// + public static Measure Stretched = new Measure(100, Unit.Percent); + public static Measure Inherit = new Measure (0, Unit.Inherit); + #region CTOR + public Measure (int _value, Unit _units = Unit.Pixel) + { + Value = _value; + Units = _units; + } + #endregion + + /// + /// True is size is fixed in pixels, this means not proportional, stretched nor fit. + /// + public bool IsFixed { get { return Units == Unit.Pixel; }} + public bool IsFit { get { return Value == -1 && Units == Unit.Percent; }} + public bool IsRelativeToParent { get { return Value >= 0 && Units == Unit.Percent; }} + #region Operators + public static implicit operator int(Measure m){ + return m.Value; + } + public static implicit operator Measure(int i){ + return new Measure(i); + } + public static implicit operator string(Measure m){ + return m.ToString(); + } + public static implicit operator Measure(string s){ + return Measure.Parse(s); + } + + public static bool operator ==(Measure m1, Measure m2){ + return m1.Value == m2.Value && m1.Units == m2.Units; + } + public static bool operator !=(Measure m1, Measure m2){ + return !(m1.Value == m2.Value && m1.Units == m2.Units); + } + #endregion + + #region Object overrides + public override int GetHashCode () + { + return Value.GetHashCode (); + } + public override bool Equals (object obj) + { + return (obj == null || obj.GetType() != typeof(Measure)) ? + false : + this == (Measure)obj; + } + public override string ToString () + { + return Units == Unit.Inherit ? "Inherit" : + Value == -1 ? "Fit" : + Units == Unit.Percent ? Value == 100 ? "Stretched" : + Value.ToString () + "%" : Value.ToString (); + } + #endregion + + public static Measure Parse(string s){ + if (string.IsNullOrEmpty (s)) + return default(Measure); + + string st = s.Trim (); + int tmp = 0; + + if (string.Equals ("Inherit", st, StringComparison.Ordinal)) + return Measure.Inherit; + else if (string.Equals ("Fit", st, StringComparison.Ordinal)) + return Measure.Fit; + else if (string.Equals ("Stretched", s, StringComparison.Ordinal)) + return Measure.Stretched; + else { + if (st.EndsWith ("%", StringComparison.Ordinal)) { + if (int.TryParse (s.Substring(0, st.Length - 1), out tmp)) + return new Measure (tmp, Unit.Percent); + }else if (int.TryParse (s, out tmp)) + return new Measure (tmp); + } + + throw new Exception ("Error parsing Measure."); + } + + } +} diff --git a/Crow/src/2d/Point.cs b/Crow/src/2d/Point.cs new file mode 100644 index 00000000..02f12e2f --- /dev/null +++ b/Crow/src/2d/Point.cs @@ -0,0 +1,86 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Crow +{ + public struct Point + { + public int X; + public int Y; + + public Point (int x, int y) + { + X = x; + Y = y; + } + + public int Length => (int)Math.Sqrt (Math.Pow (X, 2) + Math.Pow (Y, 2)); + public double LengthD => Math.Sqrt (Math.Pow (X, 2) + Math.Pow (Y, 2)); + public Point Normalized { + get { + int l = Length; + return new Point (X / l, Y / l); + } + } + public static implicit operator PointD (Point p) => new PointD (p.X, p.Y); + public static implicit operator Point (int i) => new Point (i, i); + + public static Point operator + (Point p1, Point p2) => new Point (p1.X + p2.X, p1.Y + p2.Y); + public static Point operator + (Point p, int i) => new Point (p.X + i, p.Y + i); + public static Point operator - (Point p1, Point p2) => new Point (p1.X - p2.X, p1.Y - p2.Y); + public static Point operator - (Point p, int i) => new Point (p.X - i, p.Y - i); + public static Point operator * (Point p1, Point p2) => new Point (p1.X * p2.X, p1.Y * p2.Y); + public static Point operator * (Point p, int d) => new Point (p.X * d, p.Y * d); + public static Point operator / (Point p1, Point p2) => new Point (p1.X / p2.X, p1.Y / p2.Y); + public static Point operator / (Point p, int d) => new Point (p.X / d, p.Y / d); + + public static bool operator == (Point s1, Point s2) => s1.X == s2.X && s1.Y == s2.Y; + public static bool operator == (Point s, int i) => s.X == i && s.Y == i; + public static bool operator != (Point s1, Point s2) => !(s1.X == s2.X && s1.Y == s2.Y); + public static bool operator != (Point s, int i) => !(s.X == i && s.Y == i); + public static bool operator > (Point p1, Point p2) => p1.X > p2.X && p1.Y > p2.Y; + public static bool operator > (Point s, int i) => s.X > i && s.Y > i; + public static bool operator < (Point p1, Point p2) => p1.X < p2.X && p1.Y < p2.Y; + public static bool operator < (Point s, int i) => s.X < i && s.Y < i; + public static bool operator >= (Point p1, Point p2) => p1.X >= p2.X && p1.Y >= p2.Y; + public static bool operator >= (Point s, int i) => s.X >= i && s.Y >= i; + public static bool operator <= (Point p1, Point p2) => p1.X <= p2.X && p1.Y <= p2.Y; + public static bool operator <= (Point s, int i) => s.X <= i && s.Y <= i; + + public override string ToString () => string.Format ("{0},{1}", X, Y); + public override bool Equals (object obj) => obj is Point ? this == (Point)obj : + obj is Point && (Point)this == (Point)obj; + public static Point Parse (string s) + { + if (string.IsNullOrEmpty (s)) + return default (Point); + string [] d = s.Trim ().Split (','); + if (d.Length == 2) + return new Point (int.Parse (d [0]), int.Parse (d [1])); + else if (d.Length == 1) { + int tmp = int.Parse (d [0]); + return new Point (tmp, tmp); + } + throw new Exception ("Crow.PointD Parsing Error: " + s); + } + + public override int GetHashCode () + { +#pragma warning disable RECS0025 // Champ autre qu’en lecture seule référencé dans « GetHashCode() » + unchecked { + var hashCode = 1861411795; + hashCode = hashCode * -1521134295 + X.GetHashCode (); + + hashCode = hashCode * -1521134295 + Y.GetHashCode (); + return hashCode; + } +#pragma warning restore RECS0025 // Champ autre qu’en lecture seule référencé dans « GetHashCode() » } + } + } +} diff --git a/Crow/src/2d/PointD.cs b/Crow/src/2d/PointD.cs new file mode 100644 index 00000000..d70e5b82 --- /dev/null +++ b/Crow/src/2d/PointD.cs @@ -0,0 +1,79 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace Crow { + public struct PointD { + public double X; + public double Y; + public PointD (double x, double y) + { + X = x; + Y = y; + } + + public double Length => Math.Sqrt (Math.Pow (X, 2) + Math.Pow (Y, 2)); + public PointD Normalized { + get { + double l = Length; + return new PointD (X / l, Y / l); + } + } + public static implicit operator Point (PointD p) => new Point ((int)Math.Round (p.X), (int)Math.Round (p.Y)); + public static implicit operator PointD (double i) => new PointD (i, i); + + public static PointD operator + (PointD p1, PointD p2) => new PointD (p1.X + p2.X, p1.Y + p2.Y); + public static PointD operator + (PointD p, double i) => new PointD (p.X + i, p.Y + i); + public static PointD operator - (PointD p1, PointD p2) => new PointD (p1.X - p2.X, p1.Y - p2.Y); + public static PointD operator - (PointD p, double i) => new PointD (p.X - i, p.Y - i); + public static PointD operator * (PointD p1, PointD p2) => new PointD (p1.X * p2.X, p1.Y * p2.Y); + public static PointD operator * (PointD p, double d) => new PointD (p.X * d, p.Y * d); + public static PointD operator / (PointD p1, PointD p2) => new PointD (p1.X / p2.X, p1.Y / p2.Y); + public static PointD operator / (PointD p, double d) => new PointD (p.X / d, p.Y / d); + + public static bool operator == (PointD s1, PointD s2) => s1.X == s2.X && s1.Y == s2.Y; + public static bool operator == (PointD s, double i) => s.X == i && s.Y == i; + public static bool operator != (PointD s1, PointD s2) => !(s1.X == s2.X && s1.Y == s2.Y); + public static bool operator != (PointD s, double i) => !(s.X == i && s.Y == i); + public static bool operator > (PointD p1, PointD p2) => p1.X > p2.X && p1.Y > p2.Y; + public static bool operator > (PointD s, double i) => s.X > i && s.Y > i; + public static bool operator < (PointD p1, PointD p2) => p1.X < p2.X && p1.Y < p2.Y; + public static bool operator < (PointD s, double i) => s.X < i && s.Y < i; + public static bool operator >= (PointD p1, PointD p2) => p1.X >= p2.X && p1.Y >= p2.Y; + public static bool operator >= (PointD s, double i) => s.X >= i && s.Y >= i; + public static bool operator <= (PointD p1, PointD p2) => p1.X <= p2.X && p1.Y <= p2.Y; + public static bool operator <= (PointD s, double i) => s.X <= i && s.Y <= i; + + public override string ToString () => string.Format ("{0},{1}", X, Y); + public override bool Equals (object obj) => obj is PointD ? this == (PointD)obj : + obj is Point && (Point)this == (Point)obj; + public static PointD Parse (string s) + { + if (string.IsNullOrEmpty (s)) + return default (PointD); + string [] d = s.Trim ().Split (','); + if (d.Length == 2) + return new PointD (double.Parse (d [0]), double.Parse (d [1])); + else if (d.Length == 1) { + double tmp = double.Parse (d [0]); + return new PointD (tmp, tmp); + } + throw new Exception ("Crow.PointD Parsing Error: " + s); + } + + public override int GetHashCode () + { +#pragma warning disable RECS0025 // Champ autre qu’en lecture seule référencé dans « GetHashCode() » + unchecked { + var hashCode = 1861411795; + hashCode = hashCode * -1521134295 + X.GetHashCode (); + + hashCode = hashCode * -1521134295 + Y.GetHashCode (); + return hashCode; + } +#pragma warning restore RECS0025 // Champ autre qu’en lecture seule référencé dans « GetHashCode() » } + } + } +} \ No newline at end of file diff --git a/Crow/src/2d/Rectangle.cs b/Crow/src/2d/Rectangle.cs new file mode 100644 index 00000000..a0e5ccf8 --- /dev/null +++ b/Crow/src/2d/Rectangle.cs @@ -0,0 +1,169 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Runtime.InteropServices; + +namespace Crow { + [StructLayout(LayoutKind.Sequential)] + public struct Rectangle + { + public static readonly Rectangle Zero = new Rectangle (0, 0, 0, 0); + + public int X, Y, Width, Height; + + #region ctor + public Rectangle(Point p, Size s): this (p.X, p.Y, s.Width, s.Height) { } + public Rectangle(Size s) : this (0, 0, s.Width, s.Height) { } + public Rectangle(int x, int y, int width, int height) { + X = x; + Y = y; + Width = width; + Height = height; + } + #endregion + + #region PROPERTIES + [XmlIgnore]public int Left{ + get => X; + set { X = value; } + } + [XmlIgnore]public int Top{ + get => Y; + set { Y = value; } + } + [XmlIgnore] public int Right => X + Width; + [XmlIgnore]public int Bottom => Y + Height; + [XmlIgnore]public Size Size{ + get => new Size (Width, Height); + set { + Width = value.Width; + Height = value.Height; + } + } + [XmlIgnore] + public SizeD SizeD => new SizeD (Width, Height); + [XmlIgnore]public Point Position{ + get => new Point (X, Y); + set { + X = value.X; + Y = value.Y; + } + } + [XmlIgnore]public Point TopLeft{ + get => new Point (X, Y); + set { + X = value.X; + Y = value.Y; + } + } + [XmlIgnore] public Point TopRight => new Point (Right, Y); + [XmlIgnore] public Point BottomLeft => new Point (X, Bottom); + [XmlIgnore] public Point BottomRight => new Point (Right, Bottom); + [XmlIgnore] public Point Center => new Point (Left + Width / 2, Top + Height / 2); + [XmlIgnore] public Point CenterD => new PointD (Left + Width / 2.0, Top + Height / 2.0); + + #endregion + + #region FUNCTIONS + public void Inflate(int xDelta, int yDelta) + { + this.X -= xDelta; + this.Width += 2 * xDelta; + this.Y -= yDelta; + this.Height += 2 * yDelta; + } + public void Inflate(int delta) + { + Inflate (delta, delta); + } + public Rectangle Inflated (int delta) => Inflated (delta, delta); + public Rectangle Inflated (int deltaX, int deltaY) { + Rectangle r = this; + r.Inflate (deltaX, deltaY); + return r; + } + public bool ContainsOrIsEqual (Point p) => (p.X >= X && p.X <= X + Width && p.Y >= Y && p.Y <= Y + Height); + public bool ContainsOrIsEqual (Rectangle r) => r.TopLeft >= this.TopLeft && r.BottomRight <= this.BottomRight; + public bool Intersect(Rectangle r) + { + int maxLeft = Math.Max(this.Left, r.Left); + int minRight = Math.Min(this.Right, r.Right); + int maxTop = Math.Max(this.Top, r.Top); + int minBottom = Math.Min(this.Bottom, r.Bottom); + + return (maxLeft < minRight) && (maxTop < minBottom); + } + public Rectangle Intersection(Rectangle r) + { + Rectangle result = new Rectangle(); + + if (r.Left >= Left) + result.Left = r.Left; + else + result.TopLeft = TopLeft; + + if (r.Right >= Right) + result.Width = Right - result.Left; + else + result.Width = r.Right - result.Left; + + if (r.Top >= Top) + result.Top = r.Top; + else + result.Top = Top; + + if (r.Bottom >= Bottom) + result.Height = Bottom - result.Top; + else + result.Height = r.Bottom - result.Top; + + return result; + } + #endregion + + #region operators + public static Rectangle operator +(Rectangle r1, Rectangle r2) + { + int x = Math.Min(r1.X, r2.X); + int y = Math.Min(r1.Y, r2.Y); + int x2 = Math.Max(r1.Right, r2.Right); + int y2 = Math.Max(r1.Bottom, r2.Bottom); + return new Rectangle(x, y, x2 - x, y2 - y); + } + public static Rectangle operator + (Rectangle r, Point p) => new Rectangle (r.X + p.X, r.Y + p.Y, r.Width, r.Height); + public static Rectangle operator - (Rectangle r, Point p) => new Rectangle (r.X - p.X, r.Y - p.Y, r.Width, r.Height); + public static bool operator == (Rectangle r1, Rectangle r2) => r1.TopLeft == r2.TopLeft && r1.Size == r2.Size; + public static bool operator != (Rectangle r1, Rectangle r2) => !(r1.TopLeft == r2.TopLeft && r1.Size == r2.Size); + + public static implicit operator Rectangle (RectangleD r) => new Rectangle ((int)Math.Round(r.X), (int)Math.Round (r.Y), + (int)Math.Round (r.Width), (int)Math.Round (r.Height)); + #endregion + + public override string ToString () => $"{X},{Y},{Width},{Height}"; + public static Rectangle Parse(string s) + { + string[] d = s.Split(new char[] { ',' }); + return new Rectangle( + int.Parse(d[0]), + int.Parse(d[1]), + int.Parse(d[2]), + int.Parse(d[3])); + } + public override int GetHashCode () + { + unchecked // Overflow is fine, just wrap + { + int hash = 17; + // Suitable nullity checks etc, of course :) + hash = hash * 23 + X.GetHashCode(); + hash = hash * 23 + Y.GetHashCode(); + hash = hash * 23 + Width.GetHashCode(); + hash = hash * 23 + Height.GetHashCode(); + return hash; + } + } + public override bool Equals (object obj) => (obj == null || obj.GetType () != typeof (Rectangle)) ? + false : this == (Rectangle)obj; + } +} diff --git a/Crow/src/2d/RectangleD.cs b/Crow/src/2d/RectangleD.cs new file mode 100644 index 00000000..817b3adc --- /dev/null +++ b/Crow/src/2d/RectangleD.cs @@ -0,0 +1,166 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Runtime.InteropServices; + +namespace Crow { + [StructLayout(LayoutKind.Sequential)] + public struct RectangleD + { + public static readonly RectangleD Zero = new RectangleD (0, 0, 0, 0); + + public double X, Y, Width, Height; + + #region ctor + public RectangleD(PointD p, Size s): this (p.X, p.Y, s.Width, s.Height) { } + public RectangleD(SizeD s) : this (0, 0, s.Width, s.Height) { } + public RectangleD(double x, double y, double width, double height) { + X = x; + Y = y; + Width = width; + Height = height; + } + #endregion + + #region PROPERTIES + [XmlIgnore]public double Left{ + get => X; + set { X = value; } + } + [XmlIgnore]public double Top{ + get => Y; + set { Y = value; } + } + [XmlIgnore] public double Right => X + Width; + [XmlIgnore]public double Bottom => Y + Height; + [XmlIgnore]public SizeD Size{ + get => new SizeD (Width, Height); + set { + Width = value.Width; + Height = value.Height; + } + } + [XmlIgnore]public PointD Position{ + get => new PointD (X, Y); + set { + X = value.X; + Y = value.Y; + } + } + [XmlIgnore]public PointD TopLeft{ + get => new PointD (X, Y); + set { + X = value.X; + Y = value.Y; + } + } + [XmlIgnore] public PointD TopRight => new PointD (Right, Y); + [XmlIgnore] public PointD BottomLeft => new PointD (X, Bottom); + [XmlIgnore] public PointD BottomRight => new PointD (Right, Bottom); + [XmlIgnore] public PointD Center => new PointD (Left + Width / 2, Top + Height / 2); + [XmlIgnore] public PointD CenterD => new PointD (Left + Width / 2.0, Top + Height / 2.0); + + #endregion + + #region FUNCTIONS + public void Inflate(double xDelta, double yDelta) + { + this.X -= xDelta; + this.Width += 2 * xDelta; + this.Y -= yDelta; + this.Height += 2 * yDelta; + } + public void Inflate(double delta) + { + Inflate (delta, delta); + } + public RectangleD Inflated (double delta) => Inflated (delta, delta); + public RectangleD Inflated (double deltaX, double deltaY) { + RectangleD r = this; + r.Inflate (deltaX, deltaY); + return r; + } + public bool ContainsOrIsEqual (PointD p) => (p.X >= X && p.X <= X + Width && p.Y >= Y && p.Y <= Y + Height); + public bool ContainsOrIsEqual (RectangleD r) => r.TopLeft >= this.TopLeft && r.BottomRight <= this.BottomRight; + public bool Intersect(RectangleD r) + { + double maxLeft = Math.Max(this.Left, r.Left); + double minRight = Math.Min(this.Right, r.Right); + double maxTop = Math.Max(this.Top, r.Top); + double minBottom = Math.Min(this.Bottom, r.Bottom); + + return (maxLeft < minRight) && (maxTop < minBottom); + } + public RectangleD Intersection(RectangleD r) + { + RectangleD result = new RectangleD(); + + if (r.Left >= Left) + result.Left = r.Left; + else + result.TopLeft = TopLeft; + + if (r.Right >= Right) + result.Width = Right - result.Left; + else + result.Width = r.Right - result.Left; + + if (r.Top >= Top) + result.Top = r.Top; + else + result.Top = Top; + + if (r.Bottom >= Bottom) + result.Height = Bottom - result.Top; + else + result.Height = r.Bottom - result.Top; + + return result; + } + #endregion + + #region operators + public static RectangleD operator +(RectangleD r1, RectangleD r2) + { + double x = Math.Min(r1.X, r2.X); + double y = Math.Min(r1.Y, r2.Y); + double x2 = Math.Max(r1.Right, r2.Right); + double y2 = Math.Max(r1.Bottom, r2.Bottom); + return new RectangleD(x, y, x2 - x, y2 - y); + } + public static RectangleD operator + (RectangleD r, PointD p) => new RectangleD (r.X + p.X, r.Y + p.Y, r.Width, r.Height); + public static RectangleD operator - (RectangleD r, PointD p) => new RectangleD (r.X - p.X, r.Y - p.Y, r.Width, r.Height); + public static bool operator == (RectangleD r1, RectangleD r2) => r1.TopLeft == r2.TopLeft && r1.Size == r2.Size; + public static bool operator != (RectangleD r1, RectangleD r2) => !(r1.TopLeft == r2.TopLeft && r1.Size == r2.Size); + + public static implicit operator RectangleD(Rectangle r) => new RectangleD(r.X,r.Y,r.Width,r.Height); + #endregion + + public override string ToString () => $"{X},{Y},{Width},{Height}"; + public static RectangleD Parse(string s) + { + string[] d = s.Split(new char[] { ',' }); + return new RectangleD( + double.Parse(d[0]), + double.Parse(d[1]), + double.Parse(d[2]), + double.Parse(d[3])); + } + public override int GetHashCode () + { + unchecked // Overflow is fine, just wrap + { + int hash = 17; + // Suitable nullity checks etc, of course :) + hash = hash * 23 + X.GetHashCode(); + hash = hash * 23 + Y.GetHashCode(); + hash = hash * 23 + Width.GetHashCode(); + hash = hash * 23 + Height.GetHashCode(); + return hash; + } + } + public override bool Equals (object obj) => (obj == null || obj.GetType () != typeof (RectangleD)) ? + false : this == (RectangleD)obj; + } +} diff --git a/Crow/src/2d/Size.cs b/Crow/src/2d/Size.cs new file mode 100644 index 00000000..77347f06 --- /dev/null +++ b/Crow/src/2d/Size.cs @@ -0,0 +1,72 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +namespace Crow +{ + public struct Size + { + public static Size Zero => new Size (0, 0); + + public int Width, Height; + + #region CTOR + public Size (int width, int height) + { + Width = width; + Height = height; + } + public Size (int size) + { + Width = size; + Height = size; + } + #endregion + + #region operators + public static implicit operator Rectangle(Size s)=> new Rectangle (s); + public static implicit operator Size(int i)=> new Size(i, i); + public static implicit operator string(Size s)=> s.ToString (); + public static implicit operator Size(string s)=> string.IsNullOrEmpty (s) ? Zero : Parse (s); + + public static bool operator == (Size s1, Size s2) => (s1.Width == s2.Width && s1.Height == s2.Height); + public static bool operator != (Size s1, Size s2) => (s1.Width != s2.Width || s1.Height != s2.Height); + public static bool operator > (Size s1, Size s2) => (s1.Width > s2.Width && s1.Height > s2.Height); + public static bool operator >= (Size s1, Size s2) => (s1.Width >= s2.Width && s1.Height >= s2.Height); + public static bool operator < (Size s1, Size s2) => (s1.Width < s2.Width) ? s1.Height <= s2.Height : + (s1.Width == s2.Width && s1.Height < s2.Height); + public static bool operator < (Size s, int i) => s.Width < i && s.Height < i; + public static bool operator <= (Size s, int i) => s.Width <= i && s.Height <= i; + public static bool operator > (Size s, int i) => s.Width > i && s.Height > i; + public static bool operator >= (Size s, int i) => s.Width >= i && s.Height >= i; + public static bool operator <= (Size s1, Size s2) => (s1.Width <= s2.Width && s1.Height <= s2.Height); + public static bool operator == (Size s, int i) => (s.Width == i && s.Height == i); + public static bool operator != (Size s, int i) => (s.Width != i || s.Height != i); + public static Size operator + (Size s1, Size s2) => new Size (s1.Width + s2.Width, s1.Height + s2.Height); + public static Size operator + (Size s, int i) => new Size (s.Width + i, s.Height + i); + public static Size operator * (Size s, int i) => new Size (s.Width * i, s.Height * i); + public static Size operator / (Size s, int i) => new Size (s.Width / i, s.Height / i); + #endregion + + public override int GetHashCode () + { + unchecked // Overflow is fine, just wrap + { + int hash = 17; + // Suitable nullity checks etc, of course :) + hash = hash * 23 + Width.GetHashCode(); + hash = hash * 23 + Height.GetHashCode(); + return hash; + } + } + public override bool Equals (object obj) => (obj == null || obj.GetType () != typeof (Size)) ? false : this == (Size)obj; + public override string ToString () => $"{Width},{Height}"; + public static Size Parse(string s) + { + string[] d = s.Split(new char[] { ',' }); + return d.Length == 1 ? new Size(int.Parse(d[0])) : new Size( + int.Parse(d[0]), + int.Parse(d[1])); + } + } +} diff --git a/Crow/src/2d/SizeD.cs b/Crow/src/2d/SizeD.cs new file mode 100644 index 00000000..2dbdb02b --- /dev/null +++ b/Crow/src/2d/SizeD.cs @@ -0,0 +1,71 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +namespace Crow +{ + public struct SizeD { + public static SizeD Zero => new SizeD (0, 0); + + public double Width, Height; + + #region CTOR + public SizeD (double width, double height) + { + Width = width; + Height = height; + } + public SizeD (double size) + { + Width = size; + Height = size; + } + #endregion + + #region operators + public static implicit operator RectangleD (SizeD s) => new RectangleD (s); + public static implicit operator SizeD (double i) => new SizeD (i, i); + public static implicit operator string (SizeD s) => s.ToString (); + public static implicit operator SizeD (string s) => string.IsNullOrEmpty (s) ? Zero : Parse (s); + + public static bool operator == (SizeD s1, SizeD s2) => (s1.Width == s2.Width && s1.Height == s2.Height); + public static bool operator != (SizeD s1, SizeD s2) => (s1.Width == s2.Width && s1.Height == s2.Height); + public static bool operator > (SizeD s1, SizeD s2) => (s1.Width > s2.Width && s1.Height > s2.Height); + public static bool operator >= (SizeD s1, SizeD s2) => (s1.Width >= s2.Width && s1.Height >= s2.Height); + public static bool operator < (SizeD s1, SizeD s2) => (s1.Width < s2.Width) ? s1.Height <= s2.Height : + (s1.Width == s2.Width && s1.Height < s2.Height); + public static bool operator < (SizeD s, double i) => s.Width < i && s.Height < i; + public static bool operator <= (SizeD s, double i) => s.Width <= i && s.Height <= i; + public static bool operator > (SizeD s, double i) => s.Width > i && s.Height > i; + public static bool operator >= (SizeD s, double i) => s.Width >= i && s.Height >= i; + public static bool operator <= (SizeD s1, SizeD s2) => (s1.Width <= s2.Width && s1.Height <= s2.Height); + public static bool operator == (SizeD s, double i) => (s.Width == i && s.Height == i); + public static bool operator != (SizeD s, double i) => (s.Width == i && s.Height == i); + public static SizeD operator + (SizeD s1, SizeD s2) => new SizeD (s1.Width + s2.Width, s1.Height + s2.Height); + public static SizeD operator + (SizeD s, double i) => new SizeD (s.Width + i, s.Height + i); + public static SizeD operator * (SizeD s, double i) => new SizeD (s.Width * i, s.Height * i); + public static SizeD operator / (SizeD s, double i) => new SizeD (s.Width / i, s.Height / i); + #endregion + + public override int GetHashCode () + { + unchecked // Overflow is fine, just wrap + { + int hash = 17; + // Suitable nullity checks etc, of course :) + hash = hash * 23 + Width.GetHashCode (); + hash = hash * 23 + Height.GetHashCode (); + return hash; + } + } + public override bool Equals (object obj) => (obj == null || obj.GetType () != typeof (SizeD)) ? false : this == (SizeD)obj; + public override string ToString () => $"{Width},{Height}"; + public static SizeD Parse (string s) + { + string [] d = s.Split (new char [] { ',' }); + return d.Length == 1 ? new SizeD (double.Parse (d [0])) : new SizeD ( + double.Parse (d [0]), + double.Parse (d [1])); + } + } +} diff --git a/Crow/src/DragDropEventArgs.cs b/Crow/src/DragDropEventArgs.cs deleted file mode 100644 index b79c3669..00000000 --- a/Crow/src/DragDropEventArgs.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// LayoutingEventArgs.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; - -namespace Crow -{ - public class DragDropEventArgs: EventArgs - { - /// - /// Source of the drag and drop operation - /// - public Widget DragSource; - /// - /// Target of the drag and drop operation - /// - public Widget DropTarget; - - //public DragDropEventArgs (GraphicObject source, GraphicObject target = null) : base() - public DragDropEventArgs (Widget source = null, Widget target = null) : base() - { - DragSource = source; - DropTarget = target; - } - - public override string ToString () - { - return string.Format ("{0} => {1}", DragSource,DropTarget); - } - } -} - diff --git a/Crow/src/EventArgs/DragDropEventArgs.cs b/Crow/src/EventArgs/DragDropEventArgs.cs new file mode 100644 index 00000000..b79c3669 --- /dev/null +++ b/Crow/src/EventArgs/DragDropEventArgs.cs @@ -0,0 +1,55 @@ +// +// LayoutingEventArgs.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Crow +{ + public class DragDropEventArgs: EventArgs + { + /// + /// Source of the drag and drop operation + /// + public Widget DragSource; + /// + /// Target of the drag and drop operation + /// + public Widget DropTarget; + + //public DragDropEventArgs (GraphicObject source, GraphicObject target = null) : base() + public DragDropEventArgs (Widget source = null, Widget target = null) : base() + { + DragSource = source; + DropTarget = target; + } + + public override string ToString () + { + return string.Format ("{0} => {1}", DragSource,DropTarget); + } + } +} + diff --git a/Crow/src/EventArgs/LayoutingEventArgs.cs b/Crow/src/EventArgs/LayoutingEventArgs.cs new file mode 100644 index 00000000..9254aec4 --- /dev/null +++ b/Crow/src/EventArgs/LayoutingEventArgs.cs @@ -0,0 +1,41 @@ +// +// LayoutingEventArgs.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Crow +{ + public class LayoutingEventArgs: EventArgs + { + public LayoutingType LayoutType; + + public LayoutingEventArgs (LayoutingType _layoutType) : base() + { + LayoutType = _layoutType; + } + } +} + diff --git a/Crow/src/EventArgs/ListChangedEventArg.cs b/Crow/src/EventArgs/ListChangedEventArg.cs new file mode 100644 index 00000000..3fc509e4 --- /dev/null +++ b/Crow/src/EventArgs/ListChangedEventArg.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +namespace Crow +{ + public class ListChangedEventArg : EventArgs + { + public int Index; + public object Element; + public ListChangedEventArg (int index, object element) + { + Index = index; + Element = element; + } + } +} diff --git a/Crow/src/EventArgs/MouseCursorChangedEventArgs.cs b/Crow/src/EventArgs/MouseCursorChangedEventArgs.cs new file mode 100644 index 00000000..5e58b861 --- /dev/null +++ b/Crow/src/EventArgs/MouseCursorChangedEventArgs.cs @@ -0,0 +1,39 @@ +// +// MouseCursorChangedEventArgs.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Crow +{ + public class MouseCursorChangedEventArgs : EventArgs + { + public MouseCursor NewCursor; + public MouseCursorChangedEventArgs (MouseCursor newCursor) : base() + { + NewCursor = newCursor; + } + } +} diff --git a/Crow/src/EventArgs/ScrollingEventArgs.cs b/Crow/src/EventArgs/ScrollingEventArgs.cs new file mode 100644 index 00000000..3ff42065 --- /dev/null +++ b/Crow/src/EventArgs/ScrollingEventArgs.cs @@ -0,0 +1,41 @@ +// +// ScrollingEventArgs.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Crow +{ + public class ScrollingEventArgs: EventArgs + { + public Orientation Direction; + + public ScrollingEventArgs (Orientation _direction) : base() + { + Direction = _direction; + } + } +} + diff --git a/Crow/src/EventArgs/SelectionChangeEventArgs.cs b/Crow/src/EventArgs/SelectionChangeEventArgs.cs new file mode 100644 index 00000000..988a6975 --- /dev/null +++ b/Crow/src/EventArgs/SelectionChangeEventArgs.cs @@ -0,0 +1,42 @@ +// +// SelectionChangeEventArgs.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Crow +{ + public class SelectionChangeEventArgs: EventArgs + { + public object NewValue; + + + public SelectionChangeEventArgs (object _newValue) : base() + { + NewValue = _newValue; + } + } +} + diff --git a/Crow/src/EventArgs/TextChangeEventArgs.cs b/Crow/src/EventArgs/TextChangeEventArgs.cs new file mode 100644 index 00000000..33ca405c --- /dev/null +++ b/Crow/src/EventArgs/TextChangeEventArgs.cs @@ -0,0 +1,41 @@ +// +// TextChangeEventArgs.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Crow +{ + public class TextChangeEventArgs: EventArgs + { + public String Text; + + public TextChangeEventArgs (string _newValue) : base() + { + Text = _newValue; + } + } +} + diff --git a/Crow/src/EventArgs/ValueChangeEventArgs.cs b/Crow/src/EventArgs/ValueChangeEventArgs.cs new file mode 100644 index 00000000..854e2cd3 --- /dev/null +++ b/Crow/src/EventArgs/ValueChangeEventArgs.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace Crow +{ + /// + /// Arguments for the ValueChange event used for Binding + /// + public class ValueChangeEventArgs: EventArgs + { + /// The name of the member whose value has changed + public string MemberName; + /// New value for that member + public object NewValue; + + /// + /// Initializes a new instance of the class. + /// + /// Member name. + /// New value. + public ValueChangeEventArgs (string _memberName, object _newValue) : base() + { + MemberName = _memberName; + NewValue = _newValue; + } + } +} + diff --git a/Crow/src/Fill/Gradient.cs b/Crow/src/Fill/Gradient.cs new file mode 100644 index 00000000..0e814843 --- /dev/null +++ b/Crow/src/Fill/Gradient.cs @@ -0,0 +1,140 @@ +// +// Gradient.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using Crow.Cairo; + +namespace Crow +{ + public class Gradient : Fill + { + public enum Type + { + Vertical, + Horizontal, + Oblic, + Radial + } + public class ColorStop + { + public double Offset; + public Color Color; + + public ColorStop(double offset, Color color){ + Offset = offset; + Color = color; + } + public static object Parse(string s) + { + if (string.IsNullOrEmpty (s)) + return null; + + string[] parts = s.Trim ().Split (':'); + + if (parts.Length > 2) + throw new Exception ("too many parameters in color stop: " + s); + + if (parts.Length == 2) + return new ColorStop (double.Parse (parts [0]), (Color)parts [1]); + + return new ColorStop (-1, (Color)parts [0]); + } + } + public Gradient.Type GradientType = Type.Vertical; +// public double x0; +// public double y0; +// public double x1; +// public double y1; +// public double Radius1; +// public double Radius2; + public List Stops = new List(); + public Gradient(Gradient.Type _type) + { + GradientType = _type; + } + + #region implemented abstract members of Fill + + public override void SetAsSource (Context ctx, Rectangle bounds = default(Rectangle)) + { + Cairo.Gradient grad = null; + switch (GradientType) { + case Type.Vertical: + grad = new Cairo.LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); + break; + case Type.Horizontal: + grad = new Cairo.LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); + break; + case Type.Oblic: + grad = new Cairo.LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); + break; + case Type.Radial: + throw new NotImplementedException (); + } + + foreach (ColorStop cs in Stops) { + if (cs == null) + continue; + grad.AddColorStop (cs.Offset, cs.Color); + } + + ctx.SetSource (grad); + grad.Dispose (); + } + #endregion + + public static new object Parse(string s) + { + if (string.IsNullOrEmpty (s)) + return Color.White; + + Crow.Gradient tmp; + + string[] stops = s.Trim ().Split ('|'); + + switch (stops[0].Trim()) { + case "vgradient": + tmp = new Gradient (Type.Vertical); + break; + case "hgradient": + tmp = new Gradient (Type.Horizontal); + break; + case "ogradient": + tmp = new Gradient (Type.Oblic); + break; + default: + throw new Exception ("Unknown gradient type: " + stops [0]); + } + + for (int i = 1; i < stops.Length; i++) + tmp.Stops.Add((ColorStop)ColorStop.Parse(stops[i])); + + return tmp; + } + } +} + diff --git a/Crow/src/Gradient.cs b/Crow/src/Gradient.cs deleted file mode 100644 index 0e814843..00000000 --- a/Crow/src/Gradient.cs +++ /dev/null @@ -1,140 +0,0 @@ -// -// Gradient.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using Crow.Cairo; - -namespace Crow -{ - public class Gradient : Fill - { - public enum Type - { - Vertical, - Horizontal, - Oblic, - Radial - } - public class ColorStop - { - public double Offset; - public Color Color; - - public ColorStop(double offset, Color color){ - Offset = offset; - Color = color; - } - public static object Parse(string s) - { - if (string.IsNullOrEmpty (s)) - return null; - - string[] parts = s.Trim ().Split (':'); - - if (parts.Length > 2) - throw new Exception ("too many parameters in color stop: " + s); - - if (parts.Length == 2) - return new ColorStop (double.Parse (parts [0]), (Color)parts [1]); - - return new ColorStop (-1, (Color)parts [0]); - } - } - public Gradient.Type GradientType = Type.Vertical; -// public double x0; -// public double y0; -// public double x1; -// public double y1; -// public double Radius1; -// public double Radius2; - public List Stops = new List(); - public Gradient(Gradient.Type _type) - { - GradientType = _type; - } - - #region implemented abstract members of Fill - - public override void SetAsSource (Context ctx, Rectangle bounds = default(Rectangle)) - { - Cairo.Gradient grad = null; - switch (GradientType) { - case Type.Vertical: - grad = new Cairo.LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); - break; - case Type.Horizontal: - grad = new Cairo.LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); - break; - case Type.Oblic: - grad = new Cairo.LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); - break; - case Type.Radial: - throw new NotImplementedException (); - } - - foreach (ColorStop cs in Stops) { - if (cs == null) - continue; - grad.AddColorStop (cs.Offset, cs.Color); - } - - ctx.SetSource (grad); - grad.Dispose (); - } - #endregion - - public static new object Parse(string s) - { - if (string.IsNullOrEmpty (s)) - return Color.White; - - Crow.Gradient tmp; - - string[] stops = s.Trim ().Split ('|'); - - switch (stops[0].Trim()) { - case "vgradient": - tmp = new Gradient (Type.Vertical); - break; - case "hgradient": - tmp = new Gradient (Type.Horizontal); - break; - case "ogradient": - tmp = new Gradient (Type.Oblic); - break; - default: - throw new Exception ("Unknown gradient type: " + stops [0]); - } - - for (int i = 1; i < stops.Length; i++) - tmp.Stops.Add((ColorStop)ColorStop.Parse(stops[i])); - - return tmp; - } - } -} - diff --git a/Crow/src/IListChanged.cs b/Crow/src/IListChanged.cs deleted file mode 100644 index fd6e60a7..00000000 --- a/Crow/src/IListChanged.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// IListChanged.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -using System; - -namespace Crow -{ - public class ListChangedEventArg : EventArgs { - public int Index; - public object Element; - public ListChangedEventArg (int index, object element) { - Index = index; - Element = element; - } - } - public interface IObservableList { - event EventHandler ListAdd; - event EventHandler ListRemove; - event EventHandler ListEdit; - - void Insert (); - void Remove (); - void RaiseEdit (); - } -} - diff --git a/Crow/src/IML/IMLAttributes.cs b/Crow/src/IML/IMLAttributes.cs new file mode 100644 index 00000000..929fd4f8 --- /dev/null +++ b/Crow/src/IML/IMLAttributes.cs @@ -0,0 +1,44 @@ +// +// IMLAttributes.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace Crow +{ + public class DesignIgnore : Attribute + { + } + + public class DesignCategory : Attribute + { + public string Name { get; set; } + + public DesignCategory (string name) + { + Name = name; + } + } +} + diff --git a/Crow/src/IML/Instantiator.cs b/Crow/src/IML/Instantiator.cs new file mode 100644 index 00000000..760b5b1a --- /dev/null +++ b/Crow/src/IML/Instantiator.cs @@ -0,0 +1,1465 @@ +// Copyright (c) 2013-2019 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Xml; + +namespace Crow.IML { + public class InstantiatorException : Exception { + public string Path; + public InstantiatorException (string path, Exception innerException) + : base ("ITor error:" + path, innerException){ + Path = path; + } + } + public delegate object InstanciatorInvoker(Interface iface); + + /// + /// Reflexion being very slow, the settings of the starting values for widgets are set by a dynamic method. + /// This method is created on the first instacing and is recalled for further widget instancing. + /// + /// It includes: + /// - XML values setting + /// - Default values (appearing as attribute in C#) loading + /// - Styling + /// + /// Instantiators are shared amongs interfaces. Their are stored with their path as key, and inlined template + /// and itemtemplate are stored with a generated uuid + /// + public class Instantiator + { + #region Dynamic Method ID generation + static long curId = 0; + internal static long NewId { + get { return curId++; } + } + #endregion + + internal static Dictionary knownGOTypes = new Dictionary (); + + public Type RootType; + InstanciatorInvoker loader; + protected Interface iface; + + internal string sourcePath; + + #if DESIGN_MODE + public static int NextInstantiatorID = 0; + public int currentInstantiatorID = 0; + int currentDesignID = 0; + internal string NextDesignID { get { return string.Format ("{0}_{1}",currentInstantiatorID, currentDesignID++); }} + #endif + + #region CTOR + /// + /// Initializes a new instance of the Instantiator class. + /// + public Instantiator (Interface _iface, string path) : this (_iface, _iface.GetStreamFromPath(path), path) { + + } + /// + /// Initializes a new instance of the Instantiator class. + /// + public Instantiator (Interface _iface, Stream stream, string srcPath = null) + { + #if DESIGN_MODE + currentInstantiatorID = NextInstantiatorID++; + #endif + iface = _iface; + sourcePath = srcPath; + #if DEBUG_LOAD + Stopwatch loadingTime = Stopwatch.StartNew (); + #endif + try { + using (XmlReader itr = XmlReader.Create (stream)) { + parseIML (itr); + } + } catch (Exception ex) { + throw new InstantiatorException(sourcePath, ex); + } finally { + stream?.Dispose (); +#if DEBUG_LOAD + loadingTime.Stop (); + using (StreamWriter sw = new StreamWriter ("loading.log", true)) { + sw.WriteLine ($"ITOR;{sourcePath,-50};{loadingTime.ElapsedTicks,8};{loadingTime.ElapsedMilliseconds,8}"); + } +#endif + } + } + /// + /// Initializes a new instance of the Instantiator class with an already openned xml reader + /// positionned on the start tag inside the itemTemplate + /// + public Instantiator (Interface _iface, XmlReader itr){ + #if DESIGN_MODE + currentInstantiatorID = NextInstantiatorID++; + #endif + iface = _iface; + parseIML (itr); + } + //TODO:check if still used + public Instantiator (Interface _iface, Type _root, InstanciatorInvoker _loader) + { + #if DESIGN_MODE + currentInstantiatorID = NextInstantiatorID++; + #endif + iface = _iface; + RootType = _root; + loader = _loader; + } + /// + /// Create a new instantiator from IML fragment provided directely as a string + /// + /// A new instantiator + /// IML string + public static Instantiator CreateFromImlFragment (Interface _iface, string fragment) + { + using (Stream s = new MemoryStream (Encoding.UTF8.GetBytes (fragment))) { + return new Instantiator (_iface, s); + } + } + #endregion + + /// + /// Creates a new instance of the GraphicObject compiled in the instantiator + /// + /// The new graphic object instance + public Widget CreateInstance(){ +#if DEBUG_LOAD + Stopwatch loadingTime = Stopwatch.StartNew (); + GraphicObject o = loader (iface) as GraphicObject; + loadingTime.Stop (); + using (StreamWriter sw = new StreamWriter ("loading.log", true)) { + sw.WriteLine ($"NEW ;{sourcePath,-50};{loadingTime.ElapsedTicks,8};{loadingTime.ElapsedMilliseconds,8}"); + } + return o; +#else + return loader (iface) as Widget; +#endif + } + /// + /// Creates a new instance of T compiled in the instantiator + /// and bind it the an interface + /// + /// The new T instance + public T CreateInstance(){ +#if DEBUG_LOAD + Stopwatch loadingTime = Stopwatch.StartNew (); + T i = (T)loader (iface); + loadingTime.Stop (); + using (StreamWriter sw = new StreamWriter ("loading.log", true)) { + sw.WriteLine ($"NEW ;{sourcePath,-50};{loadingTime.ElapsedTicks,8};{loadingTime.ElapsedMilliseconds,8}"); + } + return i; +#else + return (T)loader (iface); +#endif + } + List dsValueChangedDynMeths = new List(); + List cachedDelegates = new List(); + /// + /// store indices of template delegate to be handled by root parentChanged event + /// + List templateCachedDelegateIndices = new List(); + /// + /// Store template bindings in the instantiator + /// + Delegate templateBinding; + +#if DESIGN_MODE + public List DsValueChangedDynMeths =>dsValueChangedDynMeths; + public List CachedDelegates => cachedDelegates; + /// + /// store indices of template delegate to be handled by root parentChanged event + /// + public List TemplateCachedDelegateIndices => templateCachedDelegateIndices; + /// + /// Store template bindings in the instantiator + /// + public Delegate TemplateBinding => templateBinding; + +#endif + #region IML parsing + /// + /// Parses IML and build a dynamic method that will be used to instantiate one or multiple occurences of the IML file or fragment + /// + void parseIML (XmlReader reader) { + IMLContext ctx = new IMLContext (findRootType (reader)); + + ctx.PushNode (ctx.RootType); + emitLoader (reader, ctx); + ctx.PopNode (); + + foreach (int idx in templateCachedDelegateIndices) + ctx.emitCachedDelegateHandlerAddition(idx, CompilerServices.eiLogicalParentChanged); + + ctx.ResolveNamedTargets (); + + emitBindingDelegates (ctx); + + ctx.il.Emit (OpCodes.Ldloc_0);//load root obj to return + ctx.il.Emit(OpCodes.Ret); + + reader.Read ();//close tag + RootType = ctx.RootType; + loader = (InstanciatorInvoker)ctx.dm.CreateDelegate (typeof (InstanciatorInvoker), this); + } + /// + /// read first node to set GraphicObject class for loading + /// and let reader position on that node + /// + Type findRootType (XmlReader reader) + { + string root = "Object"; + while (reader.NodeType != XmlNodeType.Element) + reader.Read (); + root = reader.Name; + Type t = tryGetGOType (root); + if (t == null) + throw new Exception ("IML parsing error: undefined root type (" + root + ")"); + return t; + } + /// + /// main parsing entry point + /// + void emitLoader (XmlReader reader, IMLContext ctx) + { + int curLine = ctx.curLine; + + #if DESIGN_MODE + IXmlLineInfo li = (IXmlLineInfo)reader; + ctx.curLine += li.LineNumber - 1; + #endif + + string tmpXml = reader.ReadOuterXml (); + + if (ctx.nodesStack.Peek().HasTemplate) + emitTemplateLoad (ctx, tmpXml); + + emitGOLoad (ctx, tmpXml); + + ctx.curLine = curLine; + } + /// + /// Parses the item template tag. + /// + /// the string triplet dataType, itemTmpID read as attribute of this tag + /// current xml text reader + /// file containing the templates if its a dedicated one + string[] parseItemTemplateTag (IMLContext ctx, XmlReader reader, string itemTemplatePath = "") { + string dataType = "default", datas = "", path = "", dataTest = "TypeOf"; + while (reader.MoveToNextAttribute ()) { + if (reader.Name == "DataType") + dataType = reader.Value; + else if (reader.Name == "Data") + datas = reader.Value; + else if (reader.Name == "Path") + path = reader.Value; + else if (reader.Name == "DataTest") + dataTest = reader.Value; + } + reader.MoveToElement (); + + string itemTmpID = itemTemplatePath; + + if (string.IsNullOrEmpty (path)) { + itemTmpID += Guid.NewGuid ().ToString (); + iface.ItemTemplates [itemTmpID] = + new ItemTemplate (iface, new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataTest, dataType, datas); + + } else { + if (!reader.IsEmptyElement) + throw new Exception ("ItemTemplate with Path attribute set may not include sub nodes"); + itemTmpID += path+dataType+datas; + if (!iface.ItemTemplates.ContainsKey (itemTmpID)) + iface.ItemTemplates [itemTmpID] = + new ItemTemplate (iface, path, ctx.CurrentNodeType, dataTest, dataType, datas); + } + return new string [] { dataType, itemTmpID, datas, dataTest }; + } + /// + /// process template and item template definition prior to + /// other attributes or childs processing + /// + /// Loading Context + /// xml fragment + void emitTemplateLoad (IMLContext ctx, string tmpXml) { + //if its a template, first read template elements + using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { + List itemTemplateIds = new List (); + bool inlineTemplate = false; + + reader.Read (); + string templatePath = reader.GetAttribute ("Template"); + string itemTemplatePath = reader.GetAttribute ("ItemTemplate"); + + int depth = reader.Depth + 1; + while (reader.Read ()) { + if (!reader.IsStartElement () || reader.Depth > depth) + continue; + if (reader.Name == "Template") { + inlineTemplate = true; + #if DESIGN_MODE + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Ldc_I4_1); + ctx.il.Emit (OpCodes.Stfld, typeof(TemplatedControl).GetField("design_inlineTemplate")); + #endif + reader.Read (); + readChildren (reader, ctx, -1); + } else if (reader.Name == "ItemTemplate") + itemTemplateIds.Add (parseItemTemplateTag (ctx, reader)); + } + + if (!inlineTemplate) {//load from path or default template + + if (!string.IsNullOrEmpty (templatePath)) { + ctx.il.Emit (OpCodes.Ldloc_0);//Load current templatedControl ref + // ctx.il.Emit (OpCodes.Ldnull);//default template loading + //} else { + ctx.il.Emit (OpCodes.Ldarg_1);//load currentInterface + ctx.il.Emit (OpCodes.Ldstr, templatePath); //Load template path string + //get declaring type for search fallback assembly + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Call, CompilerServices.miGetType); + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miIFaceCreateTemplateInst); + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadTmp);//load template + } + } + if (itemTemplateIds.Count == 0) { + //try to load ItemTemplate(s) from ItemTemplate attribute of TemplatedGroup + if (!string.IsNullOrEmpty (itemTemplatePath)) { + //check if it is already loaded in cache as a single itemTemplate instantiator + if (iface.ItemTemplates.ContainsKey (itemTemplatePath)) { + itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "" }); + } else { + using (Stream stream = iface.GetTemplateStreamFromPath (itemTemplatePath, ctx.CurrentNodeType)) { + //itemtemplate files may have multiple root nodes + XmlReaderSettings itrSettings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment }; + using (XmlReader itr = XmlReader.Create (stream, itrSettings)) { + while (itr.Read ()) { + if (!itr.IsStartElement ()) + continue; + if (itr.NodeType == XmlNodeType.Element) { + if (itr.Name != "ItemTemplate") { + //the file contains a single template to use as default + iface.ItemTemplates [itemTemplatePath] = + new ItemTemplate (iface, itr); + itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "", "TypeOf" }); + break;//we should be at the end of the file + } + itemTemplateIds.Add (parseItemTemplateTag (ctx, itr, itemTemplatePath)); + } + } + } + } + } + } + } + if (!ctx.nodesStack.Peek ().IsTemplatedGroup) + return; + //add the default item template if no default is defined + if (!itemTemplateIds.Any(ids=>ids[0] == "default")) + itemTemplateIds.Add (new string [] { "default", "#Crow.DefaultItem.template", "", "TypeOf"}); + //get item templates + foreach (string [] iTempId in itemTemplateIds) { + ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref + ctx.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates);//load ItemTemplates dic field + + //prepare argument to add itemTemplate to templated group dic of ItemTemplates + ctx.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key + //load itemTemplate + ctx.il.Emit (OpCodes.Ldarg_1);//load currentInterface + ctx.il.Emit (OpCodes.Ldstr, iTempId [1]);//load path + //second arg is Type, to find assembly where to search if not in entry + ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref + ctx.il.Emit (OpCodes.Call, CompilerServices.miGetType); + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITemp); + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miAddITemp); + + if (!string.IsNullOrEmpty (iTempId [2])) { + //expand delegate creation + ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref + ctx.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates); + ctx.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITempFromDic); + ctx.il.Emit (OpCodes.Ldloc_0);//load root of treeView + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miCreateExpDel); + } + } + } + } + + #if DESIGN_MODE + void emitSetDesignAttribute (IMLContext ctx, string name, string value){ + //store member value in iml + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Ldfld, typeof(Widget).GetField("design_iml_values")); + ctx.il.Emit (OpCodes.Ldstr, name); + if (string.IsNullOrEmpty (value)) + ctx.il.Emit (OpCodes.Ldnull); + else + ctx.il.Emit (OpCodes.Ldstr, value); + ctx.il.Emit (OpCodes.Call, CompilerServices.miDicStrStrAdd); + } + #endif + + /// + /// process styling, attributes and children loading. + /// + /// parsing context + /// xml fragment + void emitGOLoad (IMLContext ctx, string tmpXml) { + using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { + reader.Read (); + +#if DESIGN_MODE + IXmlLineInfo li = (IXmlLineInfo)reader; + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Ldstr, this.NextDesignID); + ctx.il.Emit (OpCodes.Stfld, typeof(Widget).GetField("design_id")); + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Ldc_I4, ctx.curLine + li.LineNumber); + ctx.il.Emit (OpCodes.Stfld, typeof(Widget).GetField("design_line")); + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Ldc_I4, li.LinePosition); + ctx.il.Emit (OpCodes.Stfld, typeof(Widget).GetField("design_column")); + if (!string.IsNullOrEmpty (sourcePath)) { + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Ldstr, sourcePath); + ctx.il.Emit (OpCodes.Stfld, typeof(Widget).GetField("design_imlPath")); + } +#endif + #region Styling and default values loading + //first check for Style attribute then trigger default value loading + if (reader.HasAttributes) { + string style = reader.GetAttribute ("Style"); + if (!string.IsNullOrEmpty (style)) { + CompilerServices.EmitSetValue (ctx.il, CompilerServices.piStyle, style); +#if DESIGN_MODE + emitSetDesignAttribute (ctx, "Style", style); +#endif + } + //check for dataSourceType, if set, datasource bindings will use direct setter/getter + //instead of reflexion + string dataSourceType = reader.GetAttribute ("DataSourceType"); + if (string.IsNullOrEmpty (dataSourceType)) { + //if not set but dataSource is not null, reset dsType to null + string ds = reader.GetAttribute ("DataSource"); + if (!string.IsNullOrEmpty (ds)) + ctx.SetDataSourceTypeForCurrentNode (null); + } else + ctx.SetDataSourceTypeForCurrentNode(CompilerServices.getTypeFromName (dataSourceType)); + } + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Call, CompilerServices.miLoadDefaultVals); +#endregion + + + #region Attributes reading + if (reader.HasAttributes) { + + while (reader.MoveToNextAttribute ()) { + if (reader.Name == "Style" || reader.Name == "DataSourceType" || reader.Name == "Template") + continue; + + #if DESIGN_MODE + emitSetDesignAttribute (ctx, reader.Name, reader.Value); + #endif + + MemberInfo mi = ctx.CurrentNodeType.GetMember (reader.Name).FirstOrDefault (); + if (mi == null) + throw new Exception ("Member '" + reader.Name + "' not found in " + ctx.CurrentNodeType.Name); + + if (mi.MemberType == MemberTypes.Event) { + foreach (string exp in reader.Value.Split (';')) { + string trimed = exp.Trim(); + if (trimed.StartsWith ("{", StringComparison.Ordinal)) + compileAndStoreDynHandler (ctx, mi as EventInfo, trimed.Substring (1, trimed.Length - 2)); + else + emitHandlerBinding (ctx, mi as EventInfo, trimed); + } + + continue; + } + PropertyInfo pi = mi as PropertyInfo; + if (pi == null) + throw new Exception ("Member '" + reader.Name + "' is not a property in " + ctx.CurrentNodeType.Name); + + if (pi.Name == "Name") + ctx.StoreCurrentName (reader.Value); + + if (reader.Value.StartsWith ("{", StringComparison.Ordinal)) + readPropertyBinding (ctx, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); + else + CompilerServices.EmitSetValue (ctx.il, pi, reader.Value); + + } + reader.MoveToElement (); + } + #endregion + + readChildren (reader, ctx); + + ctx.nodesStack.ResetCurrentNodeIndex (); + } + } + /// + /// Parse child node an generate corresponding msil + /// + void readChildren (XmlReader reader, IMLContext ctx, int startingIdx = 0) + { + bool endTagReached = false; + int nodeIdx = startingIdx; + while (reader.Read ()) { + switch (reader.NodeType) { + case XmlNodeType.EndElement: + endTagReached = true; + break; + case XmlNodeType.Element: + //skip Templates + if (reader.Name == "Template" || + reader.Name == "ItemTemplate") { + reader.Skip (); + continue; + } + + //push 2x current instance on stack for parenting and reseting loc0 to parent + //loc_0 will be used for child + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Ldloc_0); + + Type t = tryGetGOType (reader.Name); + if (t == null) + throw new Exception (reader.Name + " type not found"); + ConstructorInfo ci = t.GetConstructor ( + BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, + null, Type.EmptyTypes, null); + if (ci == null) + throw new Exception ("No default parameterless constructor found in " + t.Name); + ctx.il.Emit (OpCodes.Newobj, ci); + ctx.il.Emit (OpCodes.Stloc_0);//child is now loc_0 + CompilerServices.emitSetCurInterface (ctx.il); + + ctx.nodesStack.Push (new Node (t, nodeIdx)); + emitLoader (reader, ctx); + ctx.nodesStack.Pop (); + + ctx.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting + ctx.il.Emit (OpCodes.Callvirt, ctx.nodesStack.Peek().GetAddMethod(nodeIdx)); + ctx.il.Emit (OpCodes.Stloc_0); //reset local to current go + + nodeIdx++; + + break; + } + if (endTagReached) + break; + } + } + #endregion + /// + /// Reads binding expression found as attribute value in iml + /// + /// IML Context + /// IML Attribute name + /// Binding Expression with accollades trimed + void readPropertyBinding (IMLContext ctx, string sourceMember, string expression) + { + NodeAddress sourceNA = ctx.CurrentNodeAddress; + BindingDefinition bindingDef = sourceNA.GetBindingDef (sourceMember, expression); + +#if DEBUG_BINDING + Debug.WriteLine("Property Binding: " + bindingDef.ToString()); +#endif + + if (bindingDef.IsDataSourceBinding) {//bind on data source + if (ctx.CurrentNodeHasDataSourceType) + emitDataSourceBindings (ctx, bindingDef, ctx.CurrentDataSourceType); + else + emitDataSourceBindings (ctx, bindingDef); + } else + ctx.StorePropertyBinding (bindingDef); + } + + #region Emit Helper + void dataSourceChangedEmitHelper(object dscSource, object dataSource, int dynMethIdx){ + if (dataSource is IValueChange) + (dataSource as IValueChange).ValueChanged += + (EventHandler)dsValueChangedDynMeths [dynMethIdx].CreateDelegate (typeof(EventHandler), dscSource); + } + /// Emits remove old data source event handler. + void emitRemoveOldDataSourceHandler(ILGenerator il, string eventName, string delegateName, bool DSSide = true){ + System.Reflection.Emit.Label cancel = il.DefineLabel (); + + il.Emit (OpCodes.Ldarg_2);//load old parent + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + il.Emit (OpCodes.Brfalse, cancel);//old parent is null + + //remove handler + if (DSSide){//event is defined in the dataSource instance + il.Emit (OpCodes.Ldarg_2);//1st arg load old datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + }else//the event is in the source + il.Emit (OpCodes.Ldarg_1);//1st arg load old datasource + il.Emit (OpCodes.Ldstr, eventName);//2nd arg event name + il.Emit (OpCodes.Ldstr, delegateName);//3d arg: delegate name + il.Emit (OpCodes.Call, CompilerServices.miRemEvtHdlByName); + il.MarkLabel(cancel); + } + #endregion + + #region Event Bindings + /// + /// Compile events expression in IML attributes, and store the result in the instanciator + /// Those handlers will be bound when instatiing + /// + void compileAndStoreDynHandler (IMLContext ctx, EventInfo sourceEvent, string expression) + { + //store event handler dynamic method in instanciator + int dmIdx = cachedDelegates.Count; + cachedDelegates.Add (CompilerServices.compileDynEventHandler (sourceEvent, expression, ctx.CurrentNodeAddress)); + ctx.emitCachedDelegateHandlerAddition(dmIdx, sourceEvent); + } + /// Emits handler method bindings + void emitHandlerBinding (IMLContext ctx, EventInfo sourceEvent, string expression){ + NodeAddress currentNode = ctx.CurrentNodeAddress; + BindingDefinition bindingDef = currentNode.GetBindingDef (sourceEvent.Name, expression); + + #if DEBUG_BINDING + Debug.WriteLine("Event Binding: " + bindingDef.ToString()); + #endif + + if (bindingDef.IsTemplateBinding | bindingDef.IsDataSourceBinding) { + //we need to bind datasource method to source event + DynamicMethod dm = new DynamicMethod ("dyn_dsORtmpChangedForHandler" + NewId, + typeof(void), + CompilerServices.argsBoundDSChange, true); + + ILGenerator il = dm.GetILGenerator (256); + System.Reflection.Emit.Label cancel = il.DefineLabel (); + + il.DeclareLocal (typeof(MethodInfo));//used to cancel binding if method doesn't exist + + il.Emit (OpCodes.Nop); + + emitRemoveOldDataSourceHandler (il, sourceEvent.Name, bindingDef.TargetMember, false); + + + //fetch method in datasource and test if it exist + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Brfalse, cancel);//cancel if new datasource is null + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load handler method name + il.Emit (OpCodes.Call, CompilerServices.miGetMethInfoWithRefx); + il.Emit (OpCodes.Stloc_0);//save MethodInfo + il.Emit (OpCodes.Ldloc_0);//push mi for test if null + il.Emit (OpCodes.Brfalse, cancel);//cancel if null + + il.Emit (OpCodes.Ldarg_1);//load datasource change source where the event is as 1st arg of handler.add + if (bindingDef.IsTemplateBinding)//fetch source instance with address + CompilerServices.emitGetInstance (il, bindingDef.SourceNA); + + //load handlerType of sourceEvent to create delegate (1st arg) + il.Emit (OpCodes.Ldtoken, sourceEvent.EventHandlerType); + il.Emit (OpCodes.Call, CompilerServices.miGetTypeFromHandle); + il.Emit (OpCodes.Ldarg_2);//load new datasource where the method is defined + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Ldloc_0);//load methodInfo (3rd arg) + + il.Emit (OpCodes.Call, CompilerServices.miCreateBoundDel); + il.Emit (OpCodes.Callvirt, sourceEvent.AddMethod);//call add event + + System.Reflection.Emit.Label finish = il.DefineLabel (); + il.Emit (OpCodes.Br, finish); + il.MarkLabel (cancel); + #if DEBUG_BINDING + il.EmitWriteLine (string.Format ("Handler method '{0}' for '{1}' NOT FOUND in new dataSource", bindingDef.TargetMember, sourceEvent.Name)); + #endif + il.MarkLabel (finish); + #if DEBUG_BINDING + il.EmitWriteLine (string.Format ("Handler method '{0}' for '{1}' FOUND in new dataSource", bindingDef.TargetMember, sourceEvent.Name)); + #endif + + il.Emit (OpCodes.Ret); + + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = cachedDelegates.Count; + cachedDelegates.Add (dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); + + if (bindingDef.IsDataSourceBinding) + ctx.emitCachedDelegateHandlerAddition (delDSIndex, CompilerServices.eiDSChange); + else //template handler binding, will be added to root parentChanged + templateCachedDelegateIndices.Add (delDSIndex); + } else {//normal in tree handler binding, store until tree is complete (end of parse) + ctx.UnresolvedTargets.Add (new EventBinding ( + bindingDef.SourceNA, sourceEvent, + bindingDef.TargetNA, bindingDef.TargetMember, bindingDef.TargetName)); + } + } + #endregion + + #region Property Bindings + /// + /// Create and store in the instanciator the ValueChanged delegates + /// those delegates uses grtree functions to set destination value so they don't + /// need to be bound to destination instance as in the ancient system. + /// + void emitBindingDelegates(IMLContext ctx){ + foreach (KeyValuePair>> bindings in ctx.Bindings ) { + if (bindings.Key.Count == 0)//template binding + emitTemplateBindings (ctx, bindings.Value); + else + emitPropertyBindings (ctx, bindings.Key, bindings.Value); + } + } + void emitPropertyBindings(IMLContext ctx, NodeAddress origine, Dictionary> bindings){ + Type origineNodeType = origine.NodeType; + + //value changed dyn method + DynamicMethod dm = new DynamicMethod ("dyn_valueChanged" + NewId, + typeof (void), CompilerServices.argsValueChange, true); + ILGenerator il = dm.GetILGenerator (256); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (typeof(object)); + + il.Emit (OpCodes.Nop); + + int i = 0; + foreach (KeyValuePair> bindingCase in bindings ) { + + System.Reflection.Emit.Label nextTest = il.DefineLabel (); + + #region member name test + //load source member name + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Call, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case + #endregion + + #region destination member affectations + PropertyInfo piOrig = origineNodeType.GetProperty (bindingCase.Key); + Type origineType = null; + if (piOrig != null) + origineType = piOrig.PropertyType; + foreach (MemberAddress ma in bindingCase.Value) { + //first we have to load destination instance onto the stack, it is access + //with graphic tree functions deducted from nodes topology + il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event + + NodeAddress destination = ma.Address; + + if (destination.Count == 0){//template reverse binding + //fetch destination instance (which is the template root) + for (int j = 0; j < origine.Count ; j++) + il.Emit(OpCodes.Callvirt, CompilerServices.miGetLogicalParent); + }else + CompilerServices.emitGetInstance (il, origine, destination); + + if (origineType != null && destination.Count > 0){//else, prop less binding or reverse template bind, no init requiered + //for initialisation dynmeth, push destination instance loc_0 is root node in ctx + ctx.il.Emit(OpCodes.Ldloc_0); + CompilerServices.emitGetInstance (ctx.il, destination); + + //init dynmeth: load actual value from origine + ctx.il.Emit (OpCodes.Ldloc_0); + CompilerServices.emitGetInstance (ctx.il, origine); + ctx.il.Emit (OpCodes.Callvirt, origineNodeType.GetProperty (bindingCase.Key).GetGetMethod()); + } + //load new value + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + if (origineType == null)//property less binding, no init + CompilerServices.emitConvert (il, ma.Property.PropertyType); + else if (destination.Count > 0) { + if (origineType.IsValueType) + ctx.il.Emit(OpCodes.Box, origineType); + + CompilerServices.emitConvert (ctx.il, origineType, ma.Property.PropertyType); + CompilerServices.emitConvert (il, origineType, ma.Property.PropertyType); + + ctx.il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod());//set init value + } else {// reverse templateBinding + il.Emit (OpCodes.Ldstr, ma.memberName);//arg 3 of setValueWithReflexion + il.Emit (OpCodes.Call, CompilerServices.miSetValWithRefx); + continue; + } + il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod());//set value on value changes + } + #endregion + il.Emit (OpCodes.Br, endMethod); + il.MarkLabel (nextTest); + + i++; + } + + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + + //store and emit Add in ctx + int dmIdx = cachedDelegates.Count; + cachedDelegates.Add (dm.CreateDelegate (typeof(EventHandler))); + ctx.emitCachedDelegateHandlerAddition (dmIdx, CompilerServices.eiValueChange, origine); + + #if DEBUG_BINDING + Debug.WriteLine("\tCrow property binding: " + dm.Name); + #endif + + } + void emitTemplateBindings(IMLContext ctx, Dictionary> bindings){ + //value changed dyn method + DynamicMethod dm = new DynamicMethod ("dyn_tmpValueChanged" + NewId, + typeof (void), CompilerServices.argsValueChange, true); + ILGenerator il = dm.GetILGenerator (256); + + //create parentchanged dyn meth in parallel to have only one loop over bindings + DynamicMethod dmPC = new DynamicMethod ("dyn_InitAndLogicalParentChanged" + NewId, + typeof (void), + CompilerServices.argsBoundDSChange, true); + ILGenerator ilPC = dmPC.GetILGenerator (256); + + il.Emit (OpCodes.Nop); + ilPC.Emit (OpCodes.Nop); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (typeof(object)); + ilPC.DeclareLocal (typeof(object));//used for checking propery less bindings + ilPC.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings + + System.Reflection.Emit.Label cancel = ilPC.DefineLabel (); + + #region Unregister previous parent event handler + //unregister previous parent handler if not null + ilPC.Emit (OpCodes.Ldarg_2);//load old parent + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + ilPC.Emit (OpCodes.Brfalse, cancel);//old parent is null + + ilPC.Emit (OpCodes.Ldarg_2);//load old parent + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + //Load cached delegate + ilPC.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + ilPC.Emit(OpCodes.Ldfld, CompilerServices.fiTemplateBinding); + + //add template bindings dynValueChanged delegate to new parent event + ilPC.Emit(OpCodes.Callvirt, CompilerServices.eiValueChange.RemoveMethod);//call remove event + #endregion + + ilPC.MarkLabel(cancel); + + #region check if new parent is null + cancel = ilPC.DefineLabel (); + ilPC.Emit (OpCodes.Ldarg_2);//load datasource change arg + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + ilPC.Emit (OpCodes.Brfalse, cancel);//new ds is null + #endregion + + int i = 0; + foreach (KeyValuePair> bindingCase in bindings ) { + + System.Reflection.Emit.Label nextTest = il.DefineLabel (); + + #region member name test + //load source member name + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Call, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case + #endregion + + #region destination member affectations + + foreach (MemberAddress ma in bindingCase.Value) { + if (ma.Address.Count == 0){ + Debug.WriteLine("\t\tBUG: reverse template binding in normal template binding"); + continue;//template binding + } + //first we try to get memberInfo of new parent, if it doesn't exist, it's a propery less binding + ilPC.Emit (OpCodes.Ldarg_2);//load new parent onto the stack for handler addition + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + ilPC.Emit (OpCodes.Stloc_0);//save new parent + //get parent type + ilPC.Emit (OpCodes.Ldloc_0);//push parent instance + ilPC.Emit (OpCodes.Ldstr, bindingCase.Key);//load member name + ilPC.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + ilPC.Emit (OpCodes.Stloc_1);//save memberInfo + ilPC.Emit (OpCodes.Ldloc_1);//push mi for test if null + System.Reflection.Emit.Label propLessReturn = ilPC.DefineLabel (); + ilPC.Emit (OpCodes.Brfalse, propLessReturn); + + + //first we have to load destination instance onto the stack, it is access + //with graphic tree functions deducted from nodes topology + il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event + CompilerServices.emitGetChild (il, typeof(TemplatedControl), -1); + CompilerServices.emitGetInstance (il, ma.Address); + + ilPC.Emit (OpCodes.Ldarg_2);//load destination instance to set actual value of member + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + CompilerServices.emitGetChild (ilPC, typeof(TemplatedControl), -1); + CompilerServices.emitGetInstance (ilPC, ma.Address); + + //load new value + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + //for the parent changed dyn meth we need to fetch actual value for initialisation thrue reflexion + ilPC.Emit (OpCodes.Ldloc_0);//push root instance of instanciator as parentChanged source + ilPC.Emit (OpCodes.Ldloc_1);//push mi for value fetching + ilPC.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); + + CompilerServices.emitConvert (il, ma.Property.PropertyType); + CompilerServices.emitConvert (ilPC, ma.Property.PropertyType); + + il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); + ilPC.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); + + ilPC.MarkLabel(propLessReturn); + } + #endregion + il.Emit (OpCodes.Br, endMethod); + il.MarkLabel (nextTest); + + i++; + } + //il.Emit (OpCodes.Pop); + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + + //store template bindings in instanciator + templateBinding = dm.CreateDelegate (typeof(EventHandler)); + + #region emit LogicalParentChanged method + + //load new parent onto the stack for handler addition + ilPC.Emit (OpCodes.Ldarg_2); + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + + //Load cached delegate + ilPC.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + ilPC.Emit(OpCodes.Ldfld, CompilerServices.fiTemplateBinding); + + //add template bindings dynValueChanged delegate to new parent event + ilPC.Emit(OpCodes.Callvirt, CompilerServices.eiValueChange.AddMethod);//call add event + + ilPC.MarkLabel (cancel); + ilPC.Emit (OpCodes.Ret); + + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = cachedDelegates.Count; + cachedDelegates.Add(dmPC.CreateDelegate (CompilerServices.ehTypeDSChange, this)); + #endregion + + ctx.emitCachedDelegateHandlerAddition(delDSIndex, CompilerServices.eiLogicalParentChanged); + } + /// + /// data source binding with known data type + /// + void emitDataSourceBindings (IMLContext ctx, BindingDefinition bindingDef, Type dsType) + { +#if DEBUG_BINDING_FUNC_CALLS + Console.WriteLine ($"emitDataSourceBindings with data type knows: {bindingDef}"); +#endif + DynamicMethod dm = null; + ILGenerator il = null; + int dmVC = 0; + PropertyInfo piSource = ctx.CurrentNodeType.GetProperty (bindingDef.SourceMember); + //if no dataSource member name is provided, valuechange is not handle and datasource change + //will be used as origine value + string delName = "dyn_DSvalueChangedKnownType" + NewId; + if (!string.IsNullOrEmpty (bindingDef.TargetMember)) { + #region create valuechanged method + dm = new DynamicMethod (delName, + typeof (void), + CompilerServices.argsBoundValueChange, true); + + il = dm.GetILGenerator (256); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (typeof (object)); + + il.Emit (OpCodes.Nop); + + //load value changed member name onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + //test if it's the expected one + il.Emit (OpCodes.Ldstr, bindingDef.TargetMember); + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Call, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, endMethod); + //set destination member with valueChanged new value + //load destination ref + il.Emit (OpCodes.Ldarg_0); + //load new value onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + //by default, source value type is deducted from target member type to allow + //memberless binding, if targetMember exists, it will be used to determine target + //value type for conversion + CompilerServices.emitConvert (il, piSource.PropertyType); + + if (!piSource.CanWrite) + throw new Exception ("Source member of bindind is read only:" + piSource.ToString ()); + + il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); + + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + + //vc dyn meth is stored in a cached list, it will be bound to datasource only + //when datasource of source graphic object changed + dmVC = dsValueChangedDynMeths.Count; + dsValueChangedDynMeths.Add (dm); + #endregion + } + + #region emit dataSourceChanged event handler + //now we create the datasource changed method that will init the destination member with + //the actual value of the origin member of the datasource and then will bind the value changed + //dyn methode. + //dm is bound to the instanciator instance to have access to cached dyn meth and delegates + dm = new DynamicMethod ("dyn_dschanged" + NewId, + typeof (void), + CompilerServices.argsBoundDSChange, true); + + il = dm.GetILGenerator (256); + + il.DeclareLocal (typeof (object));//used for checking propery less bindings + il.DeclareLocal (typeof (MemberInfo));//used for checking propery less bindings + il.DeclareLocal (typeof (object));//new datasource store, save one field access + System.Reflection.Emit.Label cancel = il.DefineLabel (); + System.Reflection.Emit.Label newDSIsNull = il.DefineLabel (); + System.Reflection.Emit.Label cancelInit = il.DefineLabel (); + + il.Emit (OpCodes.Nop); + + il.Emit (OpCodes.Ldarg_2);//load datasource change arg + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Stloc_2);//new ds is now loc 2 + + emitRemoveOldDataSourceHandler (il, "ValueChanged", delName, true); + + if (!string.IsNullOrEmpty (bindingDef.TargetMember)) { + if (bindingDef.TwoWay) //remove handler + emitRemoveOldDataSourceHandler (il, "ValueChanged", delName + "_reverse", false); + //test if new ds is null + il.Emit (OpCodes.Ldloc_2); + il.Emit (OpCodes.Brfalse, newDSIsNull);//new ds is null + //test if new ds is of expected type + il.Emit (OpCodes.Ldloc_2); + il.Emit (OpCodes.Isinst, dsType); + //il.Emit (OpCodes.Call, CompilerServices.miGetMDToken); + //il.Emit (OpCodes.Ldc_I4, dsType.MetadataToken); + il.Emit (OpCodes.Brfalse, newDSIsNull); + } + + #region fetch initial Value + if (!string.IsNullOrEmpty (bindingDef.TargetMember)) { + Type mbType; + MemberInfo mi = CompilerServices.GetMemberInfo (dsType, bindingDef.TargetMember, out mbType); + if (mi != null) { + il.Emit (OpCodes.Ldarg_1);//load source of dataSourceChanged which is the dest instance + il.Emit (OpCodes.Ldloc_2);//load new ds + CompilerServices.emitGetMemberValue (il, dsType, mi); + if (mbType != piSource.PropertyType) + CompilerServices.emitConvert (il, mbType, piSource.PropertyType); + il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); + } + } + #endregion + + if (!string.IsNullOrEmpty (bindingDef.TargetMember)) { + il.MarkLabel (cancelInit); + //check if new dataSource implement IValueChange + il.Emit (OpCodes.Ldloc_2);//load new datasource + il.Emit (OpCodes.Isinst, typeof (IValueChange)); + il.Emit (OpCodes.Brfalse, cancel); + + il.Emit (OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + il.Emit (OpCodes.Ldarg_1);//load datasource change source + il.Emit (OpCodes.Ldloc_2);//load new datasource + il.Emit (OpCodes.Ldc_I4, dmVC);//load index of dynmathod + il.Emit (OpCodes.Call, CompilerServices.miDSChangeEmitHelper); + + il.MarkLabel (cancel); + + if (bindingDef.TwoWay) { + il.Emit (OpCodes.Ldstr, delName + "_reverse");//load delName used for removing on ds changed + il.Emit (OpCodes.Ldarg_1);//arg1: dataSourceChange source, the origine of the binding + il.Emit (OpCodes.Ldstr, bindingDef.SourceMember);//arg2: orig member + il.Emit (OpCodes.Ldloc_2);//arg3: new datasource + il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//arg4: dest member + il.Emit (OpCodes.Call, CompilerServices.miDSReverseBinding); + } + + } + il.MarkLabel (newDSIsNull); + il.Emit (OpCodes.Ret); + + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = cachedDelegates.Count; + + //Int32 fiLength = (Int32)il.GetType ().GetField ("code_len", BindingFlags.Instance | BindingFlags.NonPublic).GetValue (il); + //byte [] bytes = (byte[])il.GetType ().GetField ("code", BindingFlags.Instance | BindingFlags.NonPublic).GetValue (il); + + cachedDelegates.Add (dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); + #endregion + + ctx.emitCachedDelegateHandlerAddition (delDSIndex, CompilerServices.eiDSChange); + +#if DEBUG_BINDING + Debug.WriteLine("\tDataSource ValueChanged: " + delName); + Debug.WriteLine("\tDataSource Changed: " + dm.Name); +#endif + } + + /// + /// create the valuechanged handler, the datasourcechanged handler and emit event handling + /// + void emitDataSourceBindings (IMLContext ctx, BindingDefinition bindingDef) + { + Delegate del = emitDataSourceBindings (ctx.CurrentNodeType.GetProperty (bindingDef.SourceMember), bindingDef); + + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = cachedDelegates.Count; + cachedDelegates.Add (del); + + ctx.emitCachedDelegateHandlerAddition (delDSIndex, CompilerServices.eiDSChange); + } + + /// + /// create the valuechanged handler and the datasourcechanged handler and return the + /// DataSourceChange delegate + /// + public Delegate emitDataSourceBindings (PropertyInfo piSource, BindingDefinition bindingDef){ + +#if DEBUG_BINDING_FUNC_CALLS + Console.WriteLine ($"emitDataSourceBindings: {bindingDef}"); +#endif + DynamicMethod dm = null; + ILGenerator il = null; + int dmVC = 0; + + //if no dataSource member name is provided, valuechange is not handle and datasource change + //will be used as origine value + string delName = $"dyn_DSvalueChanged_{bindingDef.SourceMember}_{bindingDef.TargetMember}_{NewId}"; + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + #region create valuechanged method + dm = new DynamicMethod (delName, + typeof (void), + CompilerServices.argsBoundValueChange, true); + + il = dm.GetILGenerator (64); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (typeof(object)); + + il.Emit (OpCodes.Nop); + + //load value changed member name onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + //test if it's the expected one + il.Emit (OpCodes.Ldstr, bindingDef.HasUnresolvedTargetName ? $"{bindingDef.TargetName}.{bindingDef.TargetMember}" : bindingDef.TargetMember); + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Call, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, endMethod); + //set destination member with valueChanged new value + //load destination ref + il.Emit (OpCodes.Ldarg_0); + //load new value onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + //by default, source value type is deducted from target member type to allow + //memberless binding, if targetMember exists, it will be used to determine target + //value type for conversion + CompilerServices.emitConvert (il, piSource.PropertyType); + + if (!piSource.CanWrite) + throw new Exception ("Source member of bindind is read only:" + piSource.ToString()); + + il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); + + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + + //vc dyn meth is stored in a cached list, it will be bound to datasource only + //when datasource of source graphic object changed + dmVC = dsValueChangedDynMeths.Count; + dsValueChangedDynMeths.Add (dm); +#endregion + } + + #region emit dataSourceChanged event handler + //now we create the datasource changed method that will init the destination member with + //the actual value of the origin member of the datasource and then will bind the value changed + //dyn methode. + //dm is bound to the instanciator instance to have access to cached dyn meth and delegates + dm = new DynamicMethod ("dyn_dschanged" + NewId, + typeof (void), + CompilerServices.argsBoundDSChange, true); + + il = dm.GetILGenerator (64); + + il.DeclareLocal (typeof(object));//used for checking propery less bindings + il.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings + il.DeclareLocal (typeof (object));//new datasource store, save one field access + il.DeclareLocal (typeof (MemberInfo));//used for binding with datasource.object.member (2 levels) + System.Reflection.Emit.Label cancel = il.DefineLabel (); + System.Reflection.Emit.Label newDSIsNull = il.DefineLabel (); + System.Reflection.Emit.Label cancelInit = il.DefineLabel (); + + il.Emit (OpCodes.Nop); + + il.Emit (OpCodes.Ldarg_2);//load datasource change arg + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Stloc_2);//new ds is now loc 2 + + emitRemoveOldDataSourceHandler (il, "ValueChanged", delName, true); + + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + if (bindingDef.TwoWay)//remove handler + emitRemoveOldDataSourceHandler(il, "ValueChanged", delName + "_reverse", false); + + il.Emit (OpCodes.Ldloc_2); + il.Emit (OpCodes.Brfalse, newDSIsNull);//new ds is null + } + + #region fetch initial Value + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + il.Emit (OpCodes.Ldloc_2); + if (bindingDef.HasUnresolvedTargetName) { + il.Emit (OpCodes.Ldstr, bindingDef.TargetName);//load parent object + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Stloc_3); + il.Emit (OpCodes.Ldloc_3); + il.Emit (OpCodes.Brfalse, cancelInit);//may be propertyLessBinding + il.Emit (OpCodes.Ldloc_2);//load datasource + il.Emit (OpCodes.Ldloc_3);//load first memberInfo + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx);//get first member level + } + il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load member name + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Stloc_1);//save memberInfo + il.Emit (OpCodes.Ldloc_1);//push mi for test if null + il.Emit (OpCodes.Brfalse, cancelInit);//propertyLessBinding + } + + il.Emit (OpCodes.Ldarg_1);//load source of dataSourceChanged which is the dest instance + il.Emit (OpCodes.Ldloc_2);//load new datasource + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + if (bindingDef.HasUnresolvedTargetName) { + il.Emit (OpCodes.Ldloc_3); + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx);//get first member level + } + il.Emit (OpCodes.Ldloc_1);//push mi for value fetching + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); + } + CompilerServices.emitConvert (il, piSource.PropertyType); + il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); + #endregion + + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + il.MarkLabel(cancelInit); + //check if new dataSource implement IValueChange + il.Emit (OpCodes.Ldloc_2);//load new datasource + il.Emit (OpCodes.Isinst, typeof(IValueChange)); + il.Emit (OpCodes.Brfalse, cancel); + + il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + il.Emit (OpCodes.Ldarg_1);//load datasource change source + il.Emit (OpCodes.Ldloc_2);//load new datasource + il.Emit(OpCodes.Ldc_I4, dmVC);//load index of dynMethod + il.Emit (OpCodes.Call, CompilerServices.miDSChangeEmitHelper); + + il.MarkLabel (cancel); + + if (bindingDef.TwoWay){ + il.Emit (OpCodes.Ldstr, delName + "_reverse");//load delName used for removing on ds changed + il.Emit (OpCodes.Ldarg_1);//arg1: dataSourceChange source, the origine of the binding + il.Emit (OpCodes.Ldstr, bindingDef.SourceMember);//arg2: orig member + il.Emit (OpCodes.Ldloc_2);//arg3: new datasource + il.Emit (OpCodes.Ldstr, bindingDef.HasUnresolvedTargetName ? + $"{bindingDef.TargetName}.{bindingDef.TargetMember}" : bindingDef.TargetMember);//arg4: dest member + il.Emit (OpCodes.Call, CompilerServices.miDSReverseBinding); + } + + } + il.MarkLabel (newDSIsNull); + il.Emit (OpCodes.Ret); + +#if DEBUG_BINDING + Debug.WriteLine("\tDataSource ValueChanged: " + delName); + Debug.WriteLine("\tDataSource Changed: " + dm.Name); +#endif + + return dm.CreateDelegate (CompilerServices.ehTypeDSChange, this); +#endregion + } + + static void emitSetValue (ILGenerator il, MemberInfo mi) + { + if (mi.MemberType == MemberTypes.Field) + il.Emit (OpCodes.Stfld, mi as FieldInfo); + else if (mi.MemberType == MemberTypes.Property) { + MethodInfo mt = (mi as PropertyInfo).GetSetMethod (); + il.Emit (mt.IsVirtual?OpCodes.Callvirt:OpCodes.Call, mt); + } else + throw new NotImplementedException (); + } + /// + /// Two way binding for datasource, graphicObj=>dataSource link, datasource value has priority + /// and will be set as init for source property (in emitDataSourceBindings func) + /// + /// delegate name + /// Graphic object instance, source of binding + /// Origin member name + /// datasource instance, target of the binding + /// Destination member name + static void dataSourceReverseBinding(string delName, IValueChange orig, string origMember, object dest, string destMember){ + Type tOrig = orig.GetType (); + Type tDest = dest.GetType (); + PropertyInfo piOrig = tOrig.GetProperty (origMember); + List miDests = new List (); + Type curType = tDest; + foreach (string m in destMember.Split('.')) { + MemberInfo miDest = curType.GetMember (m).FirstOrDefault (); + if (miDest == null) { + Debug.WriteLine ($"Member '{destMember}' not found in new DataSource '{dest}' of '{orig}'"); + return; + } + miDests.Add (miDest); + curType = CompilerServices.GetMemberInfoType (miDest); + } + +#if DEBUG_BINDING + Debug.WriteLine ("DS Reverse binding: Member '{0}' found in new DS '{1}' of '{2}'", destMember, dest, orig); +#endif + +#region ValueChanged emit + DynamicMethod dm = new DynamicMethod (delName, + typeof (void), CompilerServices.argsBoundValueChange, true); + ILGenerator il = dm.GetILGenerator (64); + + + Stack locals = new Stack (); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.Emit (OpCodes.Nop); + + //load value changed member name onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + //test if it's the expected one + il.Emit (OpCodes.Ldstr, origMember); + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Call, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, endMethod); + //set destination member with valueChanged new value + //load destination ref + il.Emit (OpCodes.Ldarg_0); + for (int i = 0; i < miDests.Count - 1; i++) { + if (miDests [i].MemberType == MemberTypes.Field) + il.Emit (OpCodes.Ldflda, miDests [i] as FieldInfo); + else if (miDests [i].MemberType == MemberTypes.Property) { + PropertyInfo pi = miDests [i] as PropertyInfo; + MethodInfo mi_g = pi.GetGetMethod (); + if (pi.PropertyType.IsValueType) { + il.Emit (OpCodes.Dup);//dup parent for calling property set afterward + il.Emit (mi_g.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi_g); + LocalBuilder lb = il.DeclareLocal (pi.PropertyType); + il.Emit (OpCodes.Stloc, lb); + il.Emit (OpCodes.Ldloca, lb); + locals.Push (lb); + } else { + il.Emit (mi_g.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi_g); + } + } else + throw new NotImplementedException (); + } + + //load new value onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + CompilerServices.emitConvert (il, piOrig.PropertyType, curType); + + emitSetValue (il, miDests.Last ()); + + for (int i = miDests.Count -2; i >= 0; i--) { + if (miDests [i].MemberType != MemberTypes.Property) + continue; + PropertyInfo pi = miDests [i] as PropertyInfo; + if (!pi.PropertyType.IsValueType) + continue; + MethodInfo mi_s = pi.GetSetMethod (); + il.Emit (OpCodes.Ldloc, locals.Pop()); + il.Emit (mi_s.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi_s); + } + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + #endregion + EventHandler tmp = (EventHandler)dm.CreateDelegate (typeof (EventHandler), dest); + orig.ValueChanged += tmp; + } + #endregion + + /// + /// search for graphic object type in crow assembly, if not found, + /// search for type independently of namespace in all the loaded assemblies + /// + /// + /// + /// the corresponding type object + /// graphic object type name without its namespace + Type tryGetGOType (string typeName){ + if (knownGOTypes.ContainsKey (typeName)) + return knownGOTypes [typeName]; + Type t = Type.GetType ("Crow." + typeName); + if (t != null) { + knownGOTypes.Add (typeName, t); + return t; + } + foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) { + if (a.IsDynamic) + continue; + foreach (Type expT in a.GetExportedTypes ()) { + if (expT.Name != typeName) + continue; + knownGOTypes.Add (typeName, expT); + return expT; + } + } + return null; + } + } +} + diff --git a/Crow/src/IMLAttributes.cs b/Crow/src/IMLAttributes.cs deleted file mode 100644 index 929fd4f8..00000000 --- a/Crow/src/IMLAttributes.cs +++ /dev/null @@ -1,44 +0,0 @@ -// -// IMLAttributes.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -using System; - -namespace Crow -{ - public class DesignIgnore : Attribute - { - } - - public class DesignCategory : Attribute - { - public string Name { get; set; } - - public DesignCategory (string name) - { - Name = name; - } - } -} - diff --git a/Crow/src/IObservableList.cs b/Crow/src/IObservableList.cs new file mode 100644 index 00000000..311a21a3 --- /dev/null +++ b/Crow/src/IObservableList.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace Crow +{ + public interface IObservableList { + event EventHandler ListAdd; + event EventHandler ListRemove; + event EventHandler ListEdit; + + void Insert (); + void Remove (); + void RaiseEdit (); + } +} + diff --git a/Crow/src/IValueChange.cs b/Crow/src/IValueChange.cs new file mode 100644 index 00000000..011f7e80 --- /dev/null +++ b/Crow/src/IValueChange.cs @@ -0,0 +1,47 @@ +// Copyright (c) 20132020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Collections.Generic; + +namespace Crow +{ + /// + /// implement `IValueChange` interface in object you want to bind to the interface. + /// For each property updated in code, raise a value change in the container class + /// to inform Crow binding system that the value has changed. + /// + public interface IValueChange + { + event EventHandler ValueChanged; + } + /// + /// Container for net primitive value type implementing IValueChange + /// + public class ValueContainer : IValueChange, IEquatable//, IConvertible + { + public event EventHandler ValueChanged; + T val; + public T Value { + get => val; + set { + if (EqualityComparer.Default.Equals (value, val)) + return; + val = value; + ValueChanged?.Invoke (this, new Crow.ValueChangeEventArgs ("Value", val)); + } + } + + public static implicit operator ValueContainer(T v) => new ValueContainer (v); + public static implicit operator T (ValueContainer v) => v.Value; + + public ValueContainer (T _val) { val = _val; } + + public bool Equals (T other) => val.Equals (other); + public override bool Equals (object obj) => obj is ValueContainer v && Equals (v); + public override int GetHashCode () => val.GetHashCode (); + public override string ToString () => val.ToString (); + } +} + diff --git a/Crow/src/Instantiator.cs b/Crow/src/Instantiator.cs deleted file mode 100644 index 760b5b1a..00000000 --- a/Crow/src/Instantiator.cs +++ /dev/null @@ -1,1465 +0,0 @@ -// Copyright (c) 2013-2019 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using System.Xml; - -namespace Crow.IML { - public class InstantiatorException : Exception { - public string Path; - public InstantiatorException (string path, Exception innerException) - : base ("ITor error:" + path, innerException){ - Path = path; - } - } - public delegate object InstanciatorInvoker(Interface iface); - - /// - /// Reflexion being very slow, the settings of the starting values for widgets are set by a dynamic method. - /// This method is created on the first instacing and is recalled for further widget instancing. - /// - /// It includes: - /// - XML values setting - /// - Default values (appearing as attribute in C#) loading - /// - Styling - /// - /// Instantiators are shared amongs interfaces. Their are stored with their path as key, and inlined template - /// and itemtemplate are stored with a generated uuid - /// - public class Instantiator - { - #region Dynamic Method ID generation - static long curId = 0; - internal static long NewId { - get { return curId++; } - } - #endregion - - internal static Dictionary knownGOTypes = new Dictionary (); - - public Type RootType; - InstanciatorInvoker loader; - protected Interface iface; - - internal string sourcePath; - - #if DESIGN_MODE - public static int NextInstantiatorID = 0; - public int currentInstantiatorID = 0; - int currentDesignID = 0; - internal string NextDesignID { get { return string.Format ("{0}_{1}",currentInstantiatorID, currentDesignID++); }} - #endif - - #region CTOR - /// - /// Initializes a new instance of the Instantiator class. - /// - public Instantiator (Interface _iface, string path) : this (_iface, _iface.GetStreamFromPath(path), path) { - - } - /// - /// Initializes a new instance of the Instantiator class. - /// - public Instantiator (Interface _iface, Stream stream, string srcPath = null) - { - #if DESIGN_MODE - currentInstantiatorID = NextInstantiatorID++; - #endif - iface = _iface; - sourcePath = srcPath; - #if DEBUG_LOAD - Stopwatch loadingTime = Stopwatch.StartNew (); - #endif - try { - using (XmlReader itr = XmlReader.Create (stream)) { - parseIML (itr); - } - } catch (Exception ex) { - throw new InstantiatorException(sourcePath, ex); - } finally { - stream?.Dispose (); -#if DEBUG_LOAD - loadingTime.Stop (); - using (StreamWriter sw = new StreamWriter ("loading.log", true)) { - sw.WriteLine ($"ITOR;{sourcePath,-50};{loadingTime.ElapsedTicks,8};{loadingTime.ElapsedMilliseconds,8}"); - } -#endif - } - } - /// - /// Initializes a new instance of the Instantiator class with an already openned xml reader - /// positionned on the start tag inside the itemTemplate - /// - public Instantiator (Interface _iface, XmlReader itr){ - #if DESIGN_MODE - currentInstantiatorID = NextInstantiatorID++; - #endif - iface = _iface; - parseIML (itr); - } - //TODO:check if still used - public Instantiator (Interface _iface, Type _root, InstanciatorInvoker _loader) - { - #if DESIGN_MODE - currentInstantiatorID = NextInstantiatorID++; - #endif - iface = _iface; - RootType = _root; - loader = _loader; - } - /// - /// Create a new instantiator from IML fragment provided directely as a string - /// - /// A new instantiator - /// IML string - public static Instantiator CreateFromImlFragment (Interface _iface, string fragment) - { - using (Stream s = new MemoryStream (Encoding.UTF8.GetBytes (fragment))) { - return new Instantiator (_iface, s); - } - } - #endregion - - /// - /// Creates a new instance of the GraphicObject compiled in the instantiator - /// - /// The new graphic object instance - public Widget CreateInstance(){ -#if DEBUG_LOAD - Stopwatch loadingTime = Stopwatch.StartNew (); - GraphicObject o = loader (iface) as GraphicObject; - loadingTime.Stop (); - using (StreamWriter sw = new StreamWriter ("loading.log", true)) { - sw.WriteLine ($"NEW ;{sourcePath,-50};{loadingTime.ElapsedTicks,8};{loadingTime.ElapsedMilliseconds,8}"); - } - return o; -#else - return loader (iface) as Widget; -#endif - } - /// - /// Creates a new instance of T compiled in the instantiator - /// and bind it the an interface - /// - /// The new T instance - public T CreateInstance(){ -#if DEBUG_LOAD - Stopwatch loadingTime = Stopwatch.StartNew (); - T i = (T)loader (iface); - loadingTime.Stop (); - using (StreamWriter sw = new StreamWriter ("loading.log", true)) { - sw.WriteLine ($"NEW ;{sourcePath,-50};{loadingTime.ElapsedTicks,8};{loadingTime.ElapsedMilliseconds,8}"); - } - return i; -#else - return (T)loader (iface); -#endif - } - List dsValueChangedDynMeths = new List(); - List cachedDelegates = new List(); - /// - /// store indices of template delegate to be handled by root parentChanged event - /// - List templateCachedDelegateIndices = new List(); - /// - /// Store template bindings in the instantiator - /// - Delegate templateBinding; - -#if DESIGN_MODE - public List DsValueChangedDynMeths =>dsValueChangedDynMeths; - public List CachedDelegates => cachedDelegates; - /// - /// store indices of template delegate to be handled by root parentChanged event - /// - public List TemplateCachedDelegateIndices => templateCachedDelegateIndices; - /// - /// Store template bindings in the instantiator - /// - public Delegate TemplateBinding => templateBinding; - -#endif - #region IML parsing - /// - /// Parses IML and build a dynamic method that will be used to instantiate one or multiple occurences of the IML file or fragment - /// - void parseIML (XmlReader reader) { - IMLContext ctx = new IMLContext (findRootType (reader)); - - ctx.PushNode (ctx.RootType); - emitLoader (reader, ctx); - ctx.PopNode (); - - foreach (int idx in templateCachedDelegateIndices) - ctx.emitCachedDelegateHandlerAddition(idx, CompilerServices.eiLogicalParentChanged); - - ctx.ResolveNamedTargets (); - - emitBindingDelegates (ctx); - - ctx.il.Emit (OpCodes.Ldloc_0);//load root obj to return - ctx.il.Emit(OpCodes.Ret); - - reader.Read ();//close tag - RootType = ctx.RootType; - loader = (InstanciatorInvoker)ctx.dm.CreateDelegate (typeof (InstanciatorInvoker), this); - } - /// - /// read first node to set GraphicObject class for loading - /// and let reader position on that node - /// - Type findRootType (XmlReader reader) - { - string root = "Object"; - while (reader.NodeType != XmlNodeType.Element) - reader.Read (); - root = reader.Name; - Type t = tryGetGOType (root); - if (t == null) - throw new Exception ("IML parsing error: undefined root type (" + root + ")"); - return t; - } - /// - /// main parsing entry point - /// - void emitLoader (XmlReader reader, IMLContext ctx) - { - int curLine = ctx.curLine; - - #if DESIGN_MODE - IXmlLineInfo li = (IXmlLineInfo)reader; - ctx.curLine += li.LineNumber - 1; - #endif - - string tmpXml = reader.ReadOuterXml (); - - if (ctx.nodesStack.Peek().HasTemplate) - emitTemplateLoad (ctx, tmpXml); - - emitGOLoad (ctx, tmpXml); - - ctx.curLine = curLine; - } - /// - /// Parses the item template tag. - /// - /// the string triplet dataType, itemTmpID read as attribute of this tag - /// current xml text reader - /// file containing the templates if its a dedicated one - string[] parseItemTemplateTag (IMLContext ctx, XmlReader reader, string itemTemplatePath = "") { - string dataType = "default", datas = "", path = "", dataTest = "TypeOf"; - while (reader.MoveToNextAttribute ()) { - if (reader.Name == "DataType") - dataType = reader.Value; - else if (reader.Name == "Data") - datas = reader.Value; - else if (reader.Name == "Path") - path = reader.Value; - else if (reader.Name == "DataTest") - dataTest = reader.Value; - } - reader.MoveToElement (); - - string itemTmpID = itemTemplatePath; - - if (string.IsNullOrEmpty (path)) { - itemTmpID += Guid.NewGuid ().ToString (); - iface.ItemTemplates [itemTmpID] = - new ItemTemplate (iface, new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataTest, dataType, datas); - - } else { - if (!reader.IsEmptyElement) - throw new Exception ("ItemTemplate with Path attribute set may not include sub nodes"); - itemTmpID += path+dataType+datas; - if (!iface.ItemTemplates.ContainsKey (itemTmpID)) - iface.ItemTemplates [itemTmpID] = - new ItemTemplate (iface, path, ctx.CurrentNodeType, dataTest, dataType, datas); - } - return new string [] { dataType, itemTmpID, datas, dataTest }; - } - /// - /// process template and item template definition prior to - /// other attributes or childs processing - /// - /// Loading Context - /// xml fragment - void emitTemplateLoad (IMLContext ctx, string tmpXml) { - //if its a template, first read template elements - using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { - List itemTemplateIds = new List (); - bool inlineTemplate = false; - - reader.Read (); - string templatePath = reader.GetAttribute ("Template"); - string itemTemplatePath = reader.GetAttribute ("ItemTemplate"); - - int depth = reader.Depth + 1; - while (reader.Read ()) { - if (!reader.IsStartElement () || reader.Depth > depth) - continue; - if (reader.Name == "Template") { - inlineTemplate = true; - #if DESIGN_MODE - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Ldc_I4_1); - ctx.il.Emit (OpCodes.Stfld, typeof(TemplatedControl).GetField("design_inlineTemplate")); - #endif - reader.Read (); - readChildren (reader, ctx, -1); - } else if (reader.Name == "ItemTemplate") - itemTemplateIds.Add (parseItemTemplateTag (ctx, reader)); - } - - if (!inlineTemplate) {//load from path or default template - - if (!string.IsNullOrEmpty (templatePath)) { - ctx.il.Emit (OpCodes.Ldloc_0);//Load current templatedControl ref - // ctx.il.Emit (OpCodes.Ldnull);//default template loading - //} else { - ctx.il.Emit (OpCodes.Ldarg_1);//load currentInterface - ctx.il.Emit (OpCodes.Ldstr, templatePath); //Load template path string - //get declaring type for search fallback assembly - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Call, CompilerServices.miGetType); - ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miIFaceCreateTemplateInst); - ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadTmp);//load template - } - } - if (itemTemplateIds.Count == 0) { - //try to load ItemTemplate(s) from ItemTemplate attribute of TemplatedGroup - if (!string.IsNullOrEmpty (itemTemplatePath)) { - //check if it is already loaded in cache as a single itemTemplate instantiator - if (iface.ItemTemplates.ContainsKey (itemTemplatePath)) { - itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "" }); - } else { - using (Stream stream = iface.GetTemplateStreamFromPath (itemTemplatePath, ctx.CurrentNodeType)) { - //itemtemplate files may have multiple root nodes - XmlReaderSettings itrSettings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment }; - using (XmlReader itr = XmlReader.Create (stream, itrSettings)) { - while (itr.Read ()) { - if (!itr.IsStartElement ()) - continue; - if (itr.NodeType == XmlNodeType.Element) { - if (itr.Name != "ItemTemplate") { - //the file contains a single template to use as default - iface.ItemTemplates [itemTemplatePath] = - new ItemTemplate (iface, itr); - itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "", "TypeOf" }); - break;//we should be at the end of the file - } - itemTemplateIds.Add (parseItemTemplateTag (ctx, itr, itemTemplatePath)); - } - } - } - } - } - } - } - if (!ctx.nodesStack.Peek ().IsTemplatedGroup) - return; - //add the default item template if no default is defined - if (!itemTemplateIds.Any(ids=>ids[0] == "default")) - itemTemplateIds.Add (new string [] { "default", "#Crow.DefaultItem.template", "", "TypeOf"}); - //get item templates - foreach (string [] iTempId in itemTemplateIds) { - ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref - ctx.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates);//load ItemTemplates dic field - - //prepare argument to add itemTemplate to templated group dic of ItemTemplates - ctx.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key - //load itemTemplate - ctx.il.Emit (OpCodes.Ldarg_1);//load currentInterface - ctx.il.Emit (OpCodes.Ldstr, iTempId [1]);//load path - //second arg is Type, to find assembly where to search if not in entry - ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref - ctx.il.Emit (OpCodes.Call, CompilerServices.miGetType); - ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITemp); - ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miAddITemp); - - if (!string.IsNullOrEmpty (iTempId [2])) { - //expand delegate creation - ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref - ctx.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates); - ctx.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key - ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITempFromDic); - ctx.il.Emit (OpCodes.Ldloc_0);//load root of treeView - ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miCreateExpDel); - } - } - } - } - - #if DESIGN_MODE - void emitSetDesignAttribute (IMLContext ctx, string name, string value){ - //store member value in iml - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Ldfld, typeof(Widget).GetField("design_iml_values")); - ctx.il.Emit (OpCodes.Ldstr, name); - if (string.IsNullOrEmpty (value)) - ctx.il.Emit (OpCodes.Ldnull); - else - ctx.il.Emit (OpCodes.Ldstr, value); - ctx.il.Emit (OpCodes.Call, CompilerServices.miDicStrStrAdd); - } - #endif - - /// - /// process styling, attributes and children loading. - /// - /// parsing context - /// xml fragment - void emitGOLoad (IMLContext ctx, string tmpXml) { - using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { - reader.Read (); - -#if DESIGN_MODE - IXmlLineInfo li = (IXmlLineInfo)reader; - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Ldstr, this.NextDesignID); - ctx.il.Emit (OpCodes.Stfld, typeof(Widget).GetField("design_id")); - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Ldc_I4, ctx.curLine + li.LineNumber); - ctx.il.Emit (OpCodes.Stfld, typeof(Widget).GetField("design_line")); - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Ldc_I4, li.LinePosition); - ctx.il.Emit (OpCodes.Stfld, typeof(Widget).GetField("design_column")); - if (!string.IsNullOrEmpty (sourcePath)) { - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Ldstr, sourcePath); - ctx.il.Emit (OpCodes.Stfld, typeof(Widget).GetField("design_imlPath")); - } -#endif - #region Styling and default values loading - //first check for Style attribute then trigger default value loading - if (reader.HasAttributes) { - string style = reader.GetAttribute ("Style"); - if (!string.IsNullOrEmpty (style)) { - CompilerServices.EmitSetValue (ctx.il, CompilerServices.piStyle, style); -#if DESIGN_MODE - emitSetDesignAttribute (ctx, "Style", style); -#endif - } - //check for dataSourceType, if set, datasource bindings will use direct setter/getter - //instead of reflexion - string dataSourceType = reader.GetAttribute ("DataSourceType"); - if (string.IsNullOrEmpty (dataSourceType)) { - //if not set but dataSource is not null, reset dsType to null - string ds = reader.GetAttribute ("DataSource"); - if (!string.IsNullOrEmpty (ds)) - ctx.SetDataSourceTypeForCurrentNode (null); - } else - ctx.SetDataSourceTypeForCurrentNode(CompilerServices.getTypeFromName (dataSourceType)); - } - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Call, CompilerServices.miLoadDefaultVals); -#endregion - - - #region Attributes reading - if (reader.HasAttributes) { - - while (reader.MoveToNextAttribute ()) { - if (reader.Name == "Style" || reader.Name == "DataSourceType" || reader.Name == "Template") - continue; - - #if DESIGN_MODE - emitSetDesignAttribute (ctx, reader.Name, reader.Value); - #endif - - MemberInfo mi = ctx.CurrentNodeType.GetMember (reader.Name).FirstOrDefault (); - if (mi == null) - throw new Exception ("Member '" + reader.Name + "' not found in " + ctx.CurrentNodeType.Name); - - if (mi.MemberType == MemberTypes.Event) { - foreach (string exp in reader.Value.Split (';')) { - string trimed = exp.Trim(); - if (trimed.StartsWith ("{", StringComparison.Ordinal)) - compileAndStoreDynHandler (ctx, mi as EventInfo, trimed.Substring (1, trimed.Length - 2)); - else - emitHandlerBinding (ctx, mi as EventInfo, trimed); - } - - continue; - } - PropertyInfo pi = mi as PropertyInfo; - if (pi == null) - throw new Exception ("Member '" + reader.Name + "' is not a property in " + ctx.CurrentNodeType.Name); - - if (pi.Name == "Name") - ctx.StoreCurrentName (reader.Value); - - if (reader.Value.StartsWith ("{", StringComparison.Ordinal)) - readPropertyBinding (ctx, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); - else - CompilerServices.EmitSetValue (ctx.il, pi, reader.Value); - - } - reader.MoveToElement (); - } - #endregion - - readChildren (reader, ctx); - - ctx.nodesStack.ResetCurrentNodeIndex (); - } - } - /// - /// Parse child node an generate corresponding msil - /// - void readChildren (XmlReader reader, IMLContext ctx, int startingIdx = 0) - { - bool endTagReached = false; - int nodeIdx = startingIdx; - while (reader.Read ()) { - switch (reader.NodeType) { - case XmlNodeType.EndElement: - endTagReached = true; - break; - case XmlNodeType.Element: - //skip Templates - if (reader.Name == "Template" || - reader.Name == "ItemTemplate") { - reader.Skip (); - continue; - } - - //push 2x current instance on stack for parenting and reseting loc0 to parent - //loc_0 will be used for child - ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Ldloc_0); - - Type t = tryGetGOType (reader.Name); - if (t == null) - throw new Exception (reader.Name + " type not found"); - ConstructorInfo ci = t.GetConstructor ( - BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, - null, Type.EmptyTypes, null); - if (ci == null) - throw new Exception ("No default parameterless constructor found in " + t.Name); - ctx.il.Emit (OpCodes.Newobj, ci); - ctx.il.Emit (OpCodes.Stloc_0);//child is now loc_0 - CompilerServices.emitSetCurInterface (ctx.il); - - ctx.nodesStack.Push (new Node (t, nodeIdx)); - emitLoader (reader, ctx); - ctx.nodesStack.Pop (); - - ctx.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting - ctx.il.Emit (OpCodes.Callvirt, ctx.nodesStack.Peek().GetAddMethod(nodeIdx)); - ctx.il.Emit (OpCodes.Stloc_0); //reset local to current go - - nodeIdx++; - - break; - } - if (endTagReached) - break; - } - } - #endregion - /// - /// Reads binding expression found as attribute value in iml - /// - /// IML Context - /// IML Attribute name - /// Binding Expression with accollades trimed - void readPropertyBinding (IMLContext ctx, string sourceMember, string expression) - { - NodeAddress sourceNA = ctx.CurrentNodeAddress; - BindingDefinition bindingDef = sourceNA.GetBindingDef (sourceMember, expression); - -#if DEBUG_BINDING - Debug.WriteLine("Property Binding: " + bindingDef.ToString()); -#endif - - if (bindingDef.IsDataSourceBinding) {//bind on data source - if (ctx.CurrentNodeHasDataSourceType) - emitDataSourceBindings (ctx, bindingDef, ctx.CurrentDataSourceType); - else - emitDataSourceBindings (ctx, bindingDef); - } else - ctx.StorePropertyBinding (bindingDef); - } - - #region Emit Helper - void dataSourceChangedEmitHelper(object dscSource, object dataSource, int dynMethIdx){ - if (dataSource is IValueChange) - (dataSource as IValueChange).ValueChanged += - (EventHandler)dsValueChangedDynMeths [dynMethIdx].CreateDelegate (typeof(EventHandler), dscSource); - } - /// Emits remove old data source event handler. - void emitRemoveOldDataSourceHandler(ILGenerator il, string eventName, string delegateName, bool DSSide = true){ - System.Reflection.Emit.Label cancel = il.DefineLabel (); - - il.Emit (OpCodes.Ldarg_2);//load old parent - il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); - il.Emit (OpCodes.Brfalse, cancel);//old parent is null - - //remove handler - if (DSSide){//event is defined in the dataSource instance - il.Emit (OpCodes.Ldarg_2);//1st arg load old datasource - il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); - }else//the event is in the source - il.Emit (OpCodes.Ldarg_1);//1st arg load old datasource - il.Emit (OpCodes.Ldstr, eventName);//2nd arg event name - il.Emit (OpCodes.Ldstr, delegateName);//3d arg: delegate name - il.Emit (OpCodes.Call, CompilerServices.miRemEvtHdlByName); - il.MarkLabel(cancel); - } - #endregion - - #region Event Bindings - /// - /// Compile events expression in IML attributes, and store the result in the instanciator - /// Those handlers will be bound when instatiing - /// - void compileAndStoreDynHandler (IMLContext ctx, EventInfo sourceEvent, string expression) - { - //store event handler dynamic method in instanciator - int dmIdx = cachedDelegates.Count; - cachedDelegates.Add (CompilerServices.compileDynEventHandler (sourceEvent, expression, ctx.CurrentNodeAddress)); - ctx.emitCachedDelegateHandlerAddition(dmIdx, sourceEvent); - } - /// Emits handler method bindings - void emitHandlerBinding (IMLContext ctx, EventInfo sourceEvent, string expression){ - NodeAddress currentNode = ctx.CurrentNodeAddress; - BindingDefinition bindingDef = currentNode.GetBindingDef (sourceEvent.Name, expression); - - #if DEBUG_BINDING - Debug.WriteLine("Event Binding: " + bindingDef.ToString()); - #endif - - if (bindingDef.IsTemplateBinding | bindingDef.IsDataSourceBinding) { - //we need to bind datasource method to source event - DynamicMethod dm = new DynamicMethod ("dyn_dsORtmpChangedForHandler" + NewId, - typeof(void), - CompilerServices.argsBoundDSChange, true); - - ILGenerator il = dm.GetILGenerator (256); - System.Reflection.Emit.Label cancel = il.DefineLabel (); - - il.DeclareLocal (typeof(MethodInfo));//used to cancel binding if method doesn't exist - - il.Emit (OpCodes.Nop); - - emitRemoveOldDataSourceHandler (il, sourceEvent.Name, bindingDef.TargetMember, false); - - - //fetch method in datasource and test if it exist - il.Emit (OpCodes.Ldarg_2);//load new datasource - il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - il.Emit (OpCodes.Brfalse, cancel);//cancel if new datasource is null - il.Emit (OpCodes.Ldarg_2);//load new datasource - il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load handler method name - il.Emit (OpCodes.Call, CompilerServices.miGetMethInfoWithRefx); - il.Emit (OpCodes.Stloc_0);//save MethodInfo - il.Emit (OpCodes.Ldloc_0);//push mi for test if null - il.Emit (OpCodes.Brfalse, cancel);//cancel if null - - il.Emit (OpCodes.Ldarg_1);//load datasource change source where the event is as 1st arg of handler.add - if (bindingDef.IsTemplateBinding)//fetch source instance with address - CompilerServices.emitGetInstance (il, bindingDef.SourceNA); - - //load handlerType of sourceEvent to create delegate (1st arg) - il.Emit (OpCodes.Ldtoken, sourceEvent.EventHandlerType); - il.Emit (OpCodes.Call, CompilerServices.miGetTypeFromHandle); - il.Emit (OpCodes.Ldarg_2);//load new datasource where the method is defined - il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - il.Emit (OpCodes.Ldloc_0);//load methodInfo (3rd arg) - - il.Emit (OpCodes.Call, CompilerServices.miCreateBoundDel); - il.Emit (OpCodes.Callvirt, sourceEvent.AddMethod);//call add event - - System.Reflection.Emit.Label finish = il.DefineLabel (); - il.Emit (OpCodes.Br, finish); - il.MarkLabel (cancel); - #if DEBUG_BINDING - il.EmitWriteLine (string.Format ("Handler method '{0}' for '{1}' NOT FOUND in new dataSource", bindingDef.TargetMember, sourceEvent.Name)); - #endif - il.MarkLabel (finish); - #if DEBUG_BINDING - il.EmitWriteLine (string.Format ("Handler method '{0}' for '{1}' FOUND in new dataSource", bindingDef.TargetMember, sourceEvent.Name)); - #endif - - il.Emit (OpCodes.Ret); - - //store dschange delegate in instatiator instance for access while instancing graphic object - int delDSIndex = cachedDelegates.Count; - cachedDelegates.Add (dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); - - if (bindingDef.IsDataSourceBinding) - ctx.emitCachedDelegateHandlerAddition (delDSIndex, CompilerServices.eiDSChange); - else //template handler binding, will be added to root parentChanged - templateCachedDelegateIndices.Add (delDSIndex); - } else {//normal in tree handler binding, store until tree is complete (end of parse) - ctx.UnresolvedTargets.Add (new EventBinding ( - bindingDef.SourceNA, sourceEvent, - bindingDef.TargetNA, bindingDef.TargetMember, bindingDef.TargetName)); - } - } - #endregion - - #region Property Bindings - /// - /// Create and store in the instanciator the ValueChanged delegates - /// those delegates uses grtree functions to set destination value so they don't - /// need to be bound to destination instance as in the ancient system. - /// - void emitBindingDelegates(IMLContext ctx){ - foreach (KeyValuePair>> bindings in ctx.Bindings ) { - if (bindings.Key.Count == 0)//template binding - emitTemplateBindings (ctx, bindings.Value); - else - emitPropertyBindings (ctx, bindings.Key, bindings.Value); - } - } - void emitPropertyBindings(IMLContext ctx, NodeAddress origine, Dictionary> bindings){ - Type origineNodeType = origine.NodeType; - - //value changed dyn method - DynamicMethod dm = new DynamicMethod ("dyn_valueChanged" + NewId, - typeof (void), CompilerServices.argsValueChange, true); - ILGenerator il = dm.GetILGenerator (256); - - System.Reflection.Emit.Label endMethod = il.DefineLabel (); - - il.DeclareLocal (typeof(object)); - - il.Emit (OpCodes.Nop); - - int i = 0; - foreach (KeyValuePair> bindingCase in bindings ) { - - System.Reflection.Emit.Label nextTest = il.DefineLabel (); - - #region member name test - //load source member name - il.Emit (OpCodes.Ldarg_1); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); - - il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Call, CompilerServices.stringEquals); - il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case - #endregion - - #region destination member affectations - PropertyInfo piOrig = origineNodeType.GetProperty (bindingCase.Key); - Type origineType = null; - if (piOrig != null) - origineType = piOrig.PropertyType; - foreach (MemberAddress ma in bindingCase.Value) { - //first we have to load destination instance onto the stack, it is access - //with graphic tree functions deducted from nodes topology - il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event - - NodeAddress destination = ma.Address; - - if (destination.Count == 0){//template reverse binding - //fetch destination instance (which is the template root) - for (int j = 0; j < origine.Count ; j++) - il.Emit(OpCodes.Callvirt, CompilerServices.miGetLogicalParent); - }else - CompilerServices.emitGetInstance (il, origine, destination); - - if (origineType != null && destination.Count > 0){//else, prop less binding or reverse template bind, no init requiered - //for initialisation dynmeth, push destination instance loc_0 is root node in ctx - ctx.il.Emit(OpCodes.Ldloc_0); - CompilerServices.emitGetInstance (ctx.il, destination); - - //init dynmeth: load actual value from origine - ctx.il.Emit (OpCodes.Ldloc_0); - CompilerServices.emitGetInstance (ctx.il, origine); - ctx.il.Emit (OpCodes.Callvirt, origineNodeType.GetProperty (bindingCase.Key).GetGetMethod()); - } - //load new value - il.Emit (OpCodes.Ldarg_1); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); - - if (origineType == null)//property less binding, no init - CompilerServices.emitConvert (il, ma.Property.PropertyType); - else if (destination.Count > 0) { - if (origineType.IsValueType) - ctx.il.Emit(OpCodes.Box, origineType); - - CompilerServices.emitConvert (ctx.il, origineType, ma.Property.PropertyType); - CompilerServices.emitConvert (il, origineType, ma.Property.PropertyType); - - ctx.il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod());//set init value - } else {// reverse templateBinding - il.Emit (OpCodes.Ldstr, ma.memberName);//arg 3 of setValueWithReflexion - il.Emit (OpCodes.Call, CompilerServices.miSetValWithRefx); - continue; - } - il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod());//set value on value changes - } - #endregion - il.Emit (OpCodes.Br, endMethod); - il.MarkLabel (nextTest); - - i++; - } - - il.MarkLabel (endMethod); - il.Emit (OpCodes.Ret); - - //store and emit Add in ctx - int dmIdx = cachedDelegates.Count; - cachedDelegates.Add (dm.CreateDelegate (typeof(EventHandler))); - ctx.emitCachedDelegateHandlerAddition (dmIdx, CompilerServices.eiValueChange, origine); - - #if DEBUG_BINDING - Debug.WriteLine("\tCrow property binding: " + dm.Name); - #endif - - } - void emitTemplateBindings(IMLContext ctx, Dictionary> bindings){ - //value changed dyn method - DynamicMethod dm = new DynamicMethod ("dyn_tmpValueChanged" + NewId, - typeof (void), CompilerServices.argsValueChange, true); - ILGenerator il = dm.GetILGenerator (256); - - //create parentchanged dyn meth in parallel to have only one loop over bindings - DynamicMethod dmPC = new DynamicMethod ("dyn_InitAndLogicalParentChanged" + NewId, - typeof (void), - CompilerServices.argsBoundDSChange, true); - ILGenerator ilPC = dmPC.GetILGenerator (256); - - il.Emit (OpCodes.Nop); - ilPC.Emit (OpCodes.Nop); - - System.Reflection.Emit.Label endMethod = il.DefineLabel (); - - il.DeclareLocal (typeof(object)); - ilPC.DeclareLocal (typeof(object));//used for checking propery less bindings - ilPC.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings - - System.Reflection.Emit.Label cancel = ilPC.DefineLabel (); - - #region Unregister previous parent event handler - //unregister previous parent handler if not null - ilPC.Emit (OpCodes.Ldarg_2);//load old parent - ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); - ilPC.Emit (OpCodes.Brfalse, cancel);//old parent is null - - ilPC.Emit (OpCodes.Ldarg_2);//load old parent - ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); - //Load cached delegate - ilPC.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack - ilPC.Emit(OpCodes.Ldfld, CompilerServices.fiTemplateBinding); - - //add template bindings dynValueChanged delegate to new parent event - ilPC.Emit(OpCodes.Callvirt, CompilerServices.eiValueChange.RemoveMethod);//call remove event - #endregion - - ilPC.MarkLabel(cancel); - - #region check if new parent is null - cancel = ilPC.DefineLabel (); - ilPC.Emit (OpCodes.Ldarg_2);//load datasource change arg - ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - ilPC.Emit (OpCodes.Brfalse, cancel);//new ds is null - #endregion - - int i = 0; - foreach (KeyValuePair> bindingCase in bindings ) { - - System.Reflection.Emit.Label nextTest = il.DefineLabel (); - - #region member name test - //load source member name - il.Emit (OpCodes.Ldarg_1); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); - - il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Call, CompilerServices.stringEquals); - il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case - #endregion - - #region destination member affectations - - foreach (MemberAddress ma in bindingCase.Value) { - if (ma.Address.Count == 0){ - Debug.WriteLine("\t\tBUG: reverse template binding in normal template binding"); - continue;//template binding - } - //first we try to get memberInfo of new parent, if it doesn't exist, it's a propery less binding - ilPC.Emit (OpCodes.Ldarg_2);//load new parent onto the stack for handler addition - ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - ilPC.Emit (OpCodes.Stloc_0);//save new parent - //get parent type - ilPC.Emit (OpCodes.Ldloc_0);//push parent instance - ilPC.Emit (OpCodes.Ldstr, bindingCase.Key);//load member name - ilPC.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); - ilPC.Emit (OpCodes.Stloc_1);//save memberInfo - ilPC.Emit (OpCodes.Ldloc_1);//push mi for test if null - System.Reflection.Emit.Label propLessReturn = ilPC.DefineLabel (); - ilPC.Emit (OpCodes.Brfalse, propLessReturn); - - - //first we have to load destination instance onto the stack, it is access - //with graphic tree functions deducted from nodes topology - il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event - CompilerServices.emitGetChild (il, typeof(TemplatedControl), -1); - CompilerServices.emitGetInstance (il, ma.Address); - - ilPC.Emit (OpCodes.Ldarg_2);//load destination instance to set actual value of member - ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - CompilerServices.emitGetChild (ilPC, typeof(TemplatedControl), -1); - CompilerServices.emitGetInstance (ilPC, ma.Address); - - //load new value - il.Emit (OpCodes.Ldarg_1); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); - - //for the parent changed dyn meth we need to fetch actual value for initialisation thrue reflexion - ilPC.Emit (OpCodes.Ldloc_0);//push root instance of instanciator as parentChanged source - ilPC.Emit (OpCodes.Ldloc_1);//push mi for value fetching - ilPC.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); - - CompilerServices.emitConvert (il, ma.Property.PropertyType); - CompilerServices.emitConvert (ilPC, ma.Property.PropertyType); - - il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); - ilPC.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); - - ilPC.MarkLabel(propLessReturn); - } - #endregion - il.Emit (OpCodes.Br, endMethod); - il.MarkLabel (nextTest); - - i++; - } - //il.Emit (OpCodes.Pop); - il.MarkLabel (endMethod); - il.Emit (OpCodes.Ret); - - //store template bindings in instanciator - templateBinding = dm.CreateDelegate (typeof(EventHandler)); - - #region emit LogicalParentChanged method - - //load new parent onto the stack for handler addition - ilPC.Emit (OpCodes.Ldarg_2); - ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - - //Load cached delegate - ilPC.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack - ilPC.Emit(OpCodes.Ldfld, CompilerServices.fiTemplateBinding); - - //add template bindings dynValueChanged delegate to new parent event - ilPC.Emit(OpCodes.Callvirt, CompilerServices.eiValueChange.AddMethod);//call add event - - ilPC.MarkLabel (cancel); - ilPC.Emit (OpCodes.Ret); - - //store dschange delegate in instatiator instance for access while instancing graphic object - int delDSIndex = cachedDelegates.Count; - cachedDelegates.Add(dmPC.CreateDelegate (CompilerServices.ehTypeDSChange, this)); - #endregion - - ctx.emitCachedDelegateHandlerAddition(delDSIndex, CompilerServices.eiLogicalParentChanged); - } - /// - /// data source binding with known data type - /// - void emitDataSourceBindings (IMLContext ctx, BindingDefinition bindingDef, Type dsType) - { -#if DEBUG_BINDING_FUNC_CALLS - Console.WriteLine ($"emitDataSourceBindings with data type knows: {bindingDef}"); -#endif - DynamicMethod dm = null; - ILGenerator il = null; - int dmVC = 0; - PropertyInfo piSource = ctx.CurrentNodeType.GetProperty (bindingDef.SourceMember); - //if no dataSource member name is provided, valuechange is not handle and datasource change - //will be used as origine value - string delName = "dyn_DSvalueChangedKnownType" + NewId; - if (!string.IsNullOrEmpty (bindingDef.TargetMember)) { - #region create valuechanged method - dm = new DynamicMethod (delName, - typeof (void), - CompilerServices.argsBoundValueChange, true); - - il = dm.GetILGenerator (256); - - System.Reflection.Emit.Label endMethod = il.DefineLabel (); - - il.DeclareLocal (typeof (object)); - - il.Emit (OpCodes.Nop); - - //load value changed member name onto the stack - il.Emit (OpCodes.Ldarg_2); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); - - //test if it's the expected one - il.Emit (OpCodes.Ldstr, bindingDef.TargetMember); - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Call, CompilerServices.stringEquals); - il.Emit (OpCodes.Brfalse, endMethod); - //set destination member with valueChanged new value - //load destination ref - il.Emit (OpCodes.Ldarg_0); - //load new value onto the stack - il.Emit (OpCodes.Ldarg_2); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); - - //by default, source value type is deducted from target member type to allow - //memberless binding, if targetMember exists, it will be used to determine target - //value type for conversion - CompilerServices.emitConvert (il, piSource.PropertyType); - - if (!piSource.CanWrite) - throw new Exception ("Source member of bindind is read only:" + piSource.ToString ()); - - il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); - - il.MarkLabel (endMethod); - il.Emit (OpCodes.Ret); - - //vc dyn meth is stored in a cached list, it will be bound to datasource only - //when datasource of source graphic object changed - dmVC = dsValueChangedDynMeths.Count; - dsValueChangedDynMeths.Add (dm); - #endregion - } - - #region emit dataSourceChanged event handler - //now we create the datasource changed method that will init the destination member with - //the actual value of the origin member of the datasource and then will bind the value changed - //dyn methode. - //dm is bound to the instanciator instance to have access to cached dyn meth and delegates - dm = new DynamicMethod ("dyn_dschanged" + NewId, - typeof (void), - CompilerServices.argsBoundDSChange, true); - - il = dm.GetILGenerator (256); - - il.DeclareLocal (typeof (object));//used for checking propery less bindings - il.DeclareLocal (typeof (MemberInfo));//used for checking propery less bindings - il.DeclareLocal (typeof (object));//new datasource store, save one field access - System.Reflection.Emit.Label cancel = il.DefineLabel (); - System.Reflection.Emit.Label newDSIsNull = il.DefineLabel (); - System.Reflection.Emit.Label cancelInit = il.DefineLabel (); - - il.Emit (OpCodes.Nop); - - il.Emit (OpCodes.Ldarg_2);//load datasource change arg - il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - il.Emit (OpCodes.Stloc_2);//new ds is now loc 2 - - emitRemoveOldDataSourceHandler (il, "ValueChanged", delName, true); - - if (!string.IsNullOrEmpty (bindingDef.TargetMember)) { - if (bindingDef.TwoWay) //remove handler - emitRemoveOldDataSourceHandler (il, "ValueChanged", delName + "_reverse", false); - //test if new ds is null - il.Emit (OpCodes.Ldloc_2); - il.Emit (OpCodes.Brfalse, newDSIsNull);//new ds is null - //test if new ds is of expected type - il.Emit (OpCodes.Ldloc_2); - il.Emit (OpCodes.Isinst, dsType); - //il.Emit (OpCodes.Call, CompilerServices.miGetMDToken); - //il.Emit (OpCodes.Ldc_I4, dsType.MetadataToken); - il.Emit (OpCodes.Brfalse, newDSIsNull); - } - - #region fetch initial Value - if (!string.IsNullOrEmpty (bindingDef.TargetMember)) { - Type mbType; - MemberInfo mi = CompilerServices.GetMemberInfo (dsType, bindingDef.TargetMember, out mbType); - if (mi != null) { - il.Emit (OpCodes.Ldarg_1);//load source of dataSourceChanged which is the dest instance - il.Emit (OpCodes.Ldloc_2);//load new ds - CompilerServices.emitGetMemberValue (il, dsType, mi); - if (mbType != piSource.PropertyType) - CompilerServices.emitConvert (il, mbType, piSource.PropertyType); - il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); - } - } - #endregion - - if (!string.IsNullOrEmpty (bindingDef.TargetMember)) { - il.MarkLabel (cancelInit); - //check if new dataSource implement IValueChange - il.Emit (OpCodes.Ldloc_2);//load new datasource - il.Emit (OpCodes.Isinst, typeof (IValueChange)); - il.Emit (OpCodes.Brfalse, cancel); - - il.Emit (OpCodes.Ldarg_0);//load ref to this instanciator onto the stack - il.Emit (OpCodes.Ldarg_1);//load datasource change source - il.Emit (OpCodes.Ldloc_2);//load new datasource - il.Emit (OpCodes.Ldc_I4, dmVC);//load index of dynmathod - il.Emit (OpCodes.Call, CompilerServices.miDSChangeEmitHelper); - - il.MarkLabel (cancel); - - if (bindingDef.TwoWay) { - il.Emit (OpCodes.Ldstr, delName + "_reverse");//load delName used for removing on ds changed - il.Emit (OpCodes.Ldarg_1);//arg1: dataSourceChange source, the origine of the binding - il.Emit (OpCodes.Ldstr, bindingDef.SourceMember);//arg2: orig member - il.Emit (OpCodes.Ldloc_2);//arg3: new datasource - il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//arg4: dest member - il.Emit (OpCodes.Call, CompilerServices.miDSReverseBinding); - } - - } - il.MarkLabel (newDSIsNull); - il.Emit (OpCodes.Ret); - - //store dschange delegate in instatiator instance for access while instancing graphic object - int delDSIndex = cachedDelegates.Count; - - //Int32 fiLength = (Int32)il.GetType ().GetField ("code_len", BindingFlags.Instance | BindingFlags.NonPublic).GetValue (il); - //byte [] bytes = (byte[])il.GetType ().GetField ("code", BindingFlags.Instance | BindingFlags.NonPublic).GetValue (il); - - cachedDelegates.Add (dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); - #endregion - - ctx.emitCachedDelegateHandlerAddition (delDSIndex, CompilerServices.eiDSChange); - -#if DEBUG_BINDING - Debug.WriteLine("\tDataSource ValueChanged: " + delName); - Debug.WriteLine("\tDataSource Changed: " + dm.Name); -#endif - } - - /// - /// create the valuechanged handler, the datasourcechanged handler and emit event handling - /// - void emitDataSourceBindings (IMLContext ctx, BindingDefinition bindingDef) - { - Delegate del = emitDataSourceBindings (ctx.CurrentNodeType.GetProperty (bindingDef.SourceMember), bindingDef); - - //store dschange delegate in instatiator instance for access while instancing graphic object - int delDSIndex = cachedDelegates.Count; - cachedDelegates.Add (del); - - ctx.emitCachedDelegateHandlerAddition (delDSIndex, CompilerServices.eiDSChange); - } - - /// - /// create the valuechanged handler and the datasourcechanged handler and return the - /// DataSourceChange delegate - /// - public Delegate emitDataSourceBindings (PropertyInfo piSource, BindingDefinition bindingDef){ - -#if DEBUG_BINDING_FUNC_CALLS - Console.WriteLine ($"emitDataSourceBindings: {bindingDef}"); -#endif - DynamicMethod dm = null; - ILGenerator il = null; - int dmVC = 0; - - //if no dataSource member name is provided, valuechange is not handle and datasource change - //will be used as origine value - string delName = $"dyn_DSvalueChanged_{bindingDef.SourceMember}_{bindingDef.TargetMember}_{NewId}"; - if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ - #region create valuechanged method - dm = new DynamicMethod (delName, - typeof (void), - CompilerServices.argsBoundValueChange, true); - - il = dm.GetILGenerator (64); - - System.Reflection.Emit.Label endMethod = il.DefineLabel (); - - il.DeclareLocal (typeof(object)); - - il.Emit (OpCodes.Nop); - - //load value changed member name onto the stack - il.Emit (OpCodes.Ldarg_2); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); - - //test if it's the expected one - il.Emit (OpCodes.Ldstr, bindingDef.HasUnresolvedTargetName ? $"{bindingDef.TargetName}.{bindingDef.TargetMember}" : bindingDef.TargetMember); - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Call, CompilerServices.stringEquals); - il.Emit (OpCodes.Brfalse, endMethod); - //set destination member with valueChanged new value - //load destination ref - il.Emit (OpCodes.Ldarg_0); - //load new value onto the stack - il.Emit (OpCodes.Ldarg_2); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); - - //by default, source value type is deducted from target member type to allow - //memberless binding, if targetMember exists, it will be used to determine target - //value type for conversion - CompilerServices.emitConvert (il, piSource.PropertyType); - - if (!piSource.CanWrite) - throw new Exception ("Source member of bindind is read only:" + piSource.ToString()); - - il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); - - il.MarkLabel (endMethod); - il.Emit (OpCodes.Ret); - - //vc dyn meth is stored in a cached list, it will be bound to datasource only - //when datasource of source graphic object changed - dmVC = dsValueChangedDynMeths.Count; - dsValueChangedDynMeths.Add (dm); -#endregion - } - - #region emit dataSourceChanged event handler - //now we create the datasource changed method that will init the destination member with - //the actual value of the origin member of the datasource and then will bind the value changed - //dyn methode. - //dm is bound to the instanciator instance to have access to cached dyn meth and delegates - dm = new DynamicMethod ("dyn_dschanged" + NewId, - typeof (void), - CompilerServices.argsBoundDSChange, true); - - il = dm.GetILGenerator (64); - - il.DeclareLocal (typeof(object));//used for checking propery less bindings - il.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings - il.DeclareLocal (typeof (object));//new datasource store, save one field access - il.DeclareLocal (typeof (MemberInfo));//used for binding with datasource.object.member (2 levels) - System.Reflection.Emit.Label cancel = il.DefineLabel (); - System.Reflection.Emit.Label newDSIsNull = il.DefineLabel (); - System.Reflection.Emit.Label cancelInit = il.DefineLabel (); - - il.Emit (OpCodes.Nop); - - il.Emit (OpCodes.Ldarg_2);//load datasource change arg - il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); - il.Emit (OpCodes.Stloc_2);//new ds is now loc 2 - - emitRemoveOldDataSourceHandler (il, "ValueChanged", delName, true); - - if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ - if (bindingDef.TwoWay)//remove handler - emitRemoveOldDataSourceHandler(il, "ValueChanged", delName + "_reverse", false); - - il.Emit (OpCodes.Ldloc_2); - il.Emit (OpCodes.Brfalse, newDSIsNull);//new ds is null - } - - #region fetch initial Value - if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ - il.Emit (OpCodes.Ldloc_2); - if (bindingDef.HasUnresolvedTargetName) { - il.Emit (OpCodes.Ldstr, bindingDef.TargetName);//load parent object - il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); - il.Emit (OpCodes.Stloc_3); - il.Emit (OpCodes.Ldloc_3); - il.Emit (OpCodes.Brfalse, cancelInit);//may be propertyLessBinding - il.Emit (OpCodes.Ldloc_2);//load datasource - il.Emit (OpCodes.Ldloc_3);//load first memberInfo - il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx);//get first member level - } - il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load member name - il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); - il.Emit (OpCodes.Stloc_1);//save memberInfo - il.Emit (OpCodes.Ldloc_1);//push mi for test if null - il.Emit (OpCodes.Brfalse, cancelInit);//propertyLessBinding - } - - il.Emit (OpCodes.Ldarg_1);//load source of dataSourceChanged which is the dest instance - il.Emit (OpCodes.Ldloc_2);//load new datasource - if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ - if (bindingDef.HasUnresolvedTargetName) { - il.Emit (OpCodes.Ldloc_3); - il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx);//get first member level - } - il.Emit (OpCodes.Ldloc_1);//push mi for value fetching - il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); - } - CompilerServices.emitConvert (il, piSource.PropertyType); - il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); - #endregion - - if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ - il.MarkLabel(cancelInit); - //check if new dataSource implement IValueChange - il.Emit (OpCodes.Ldloc_2);//load new datasource - il.Emit (OpCodes.Isinst, typeof(IValueChange)); - il.Emit (OpCodes.Brfalse, cancel); - - il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack - il.Emit (OpCodes.Ldarg_1);//load datasource change source - il.Emit (OpCodes.Ldloc_2);//load new datasource - il.Emit(OpCodes.Ldc_I4, dmVC);//load index of dynMethod - il.Emit (OpCodes.Call, CompilerServices.miDSChangeEmitHelper); - - il.MarkLabel (cancel); - - if (bindingDef.TwoWay){ - il.Emit (OpCodes.Ldstr, delName + "_reverse");//load delName used for removing on ds changed - il.Emit (OpCodes.Ldarg_1);//arg1: dataSourceChange source, the origine of the binding - il.Emit (OpCodes.Ldstr, bindingDef.SourceMember);//arg2: orig member - il.Emit (OpCodes.Ldloc_2);//arg3: new datasource - il.Emit (OpCodes.Ldstr, bindingDef.HasUnresolvedTargetName ? - $"{bindingDef.TargetName}.{bindingDef.TargetMember}" : bindingDef.TargetMember);//arg4: dest member - il.Emit (OpCodes.Call, CompilerServices.miDSReverseBinding); - } - - } - il.MarkLabel (newDSIsNull); - il.Emit (OpCodes.Ret); - -#if DEBUG_BINDING - Debug.WriteLine("\tDataSource ValueChanged: " + delName); - Debug.WriteLine("\tDataSource Changed: " + dm.Name); -#endif - - return dm.CreateDelegate (CompilerServices.ehTypeDSChange, this); -#endregion - } - - static void emitSetValue (ILGenerator il, MemberInfo mi) - { - if (mi.MemberType == MemberTypes.Field) - il.Emit (OpCodes.Stfld, mi as FieldInfo); - else if (mi.MemberType == MemberTypes.Property) { - MethodInfo mt = (mi as PropertyInfo).GetSetMethod (); - il.Emit (mt.IsVirtual?OpCodes.Callvirt:OpCodes.Call, mt); - } else - throw new NotImplementedException (); - } - /// - /// Two way binding for datasource, graphicObj=>dataSource link, datasource value has priority - /// and will be set as init for source property (in emitDataSourceBindings func) - /// - /// delegate name - /// Graphic object instance, source of binding - /// Origin member name - /// datasource instance, target of the binding - /// Destination member name - static void dataSourceReverseBinding(string delName, IValueChange orig, string origMember, object dest, string destMember){ - Type tOrig = orig.GetType (); - Type tDest = dest.GetType (); - PropertyInfo piOrig = tOrig.GetProperty (origMember); - List miDests = new List (); - Type curType = tDest; - foreach (string m in destMember.Split('.')) { - MemberInfo miDest = curType.GetMember (m).FirstOrDefault (); - if (miDest == null) { - Debug.WriteLine ($"Member '{destMember}' not found in new DataSource '{dest}' of '{orig}'"); - return; - } - miDests.Add (miDest); - curType = CompilerServices.GetMemberInfoType (miDest); - } - -#if DEBUG_BINDING - Debug.WriteLine ("DS Reverse binding: Member '{0}' found in new DS '{1}' of '{2}'", destMember, dest, orig); -#endif - -#region ValueChanged emit - DynamicMethod dm = new DynamicMethod (delName, - typeof (void), CompilerServices.argsBoundValueChange, true); - ILGenerator il = dm.GetILGenerator (64); - - - Stack locals = new Stack (); - - System.Reflection.Emit.Label endMethod = il.DefineLabel (); - - il.Emit (OpCodes.Nop); - - //load value changed member name onto the stack - il.Emit (OpCodes.Ldarg_2); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); - - //test if it's the expected one - il.Emit (OpCodes.Ldstr, origMember); - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Call, CompilerServices.stringEquals); - il.Emit (OpCodes.Brfalse, endMethod); - //set destination member with valueChanged new value - //load destination ref - il.Emit (OpCodes.Ldarg_0); - for (int i = 0; i < miDests.Count - 1; i++) { - if (miDests [i].MemberType == MemberTypes.Field) - il.Emit (OpCodes.Ldflda, miDests [i] as FieldInfo); - else if (miDests [i].MemberType == MemberTypes.Property) { - PropertyInfo pi = miDests [i] as PropertyInfo; - MethodInfo mi_g = pi.GetGetMethod (); - if (pi.PropertyType.IsValueType) { - il.Emit (OpCodes.Dup);//dup parent for calling property set afterward - il.Emit (mi_g.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi_g); - LocalBuilder lb = il.DeclareLocal (pi.PropertyType); - il.Emit (OpCodes.Stloc, lb); - il.Emit (OpCodes.Ldloca, lb); - locals.Push (lb); - } else { - il.Emit (mi_g.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi_g); - } - } else - throw new NotImplementedException (); - } - - //load new value onto the stack - il.Emit (OpCodes.Ldarg_2); - il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); - - CompilerServices.emitConvert (il, piOrig.PropertyType, curType); - - emitSetValue (il, miDests.Last ()); - - for (int i = miDests.Count -2; i >= 0; i--) { - if (miDests [i].MemberType != MemberTypes.Property) - continue; - PropertyInfo pi = miDests [i] as PropertyInfo; - if (!pi.PropertyType.IsValueType) - continue; - MethodInfo mi_s = pi.GetSetMethod (); - il.Emit (OpCodes.Ldloc, locals.Pop()); - il.Emit (mi_s.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi_s); - } - il.MarkLabel (endMethod); - il.Emit (OpCodes.Ret); - #endregion - EventHandler tmp = (EventHandler)dm.CreateDelegate (typeof (EventHandler), dest); - orig.ValueChanged += tmp; - } - #endregion - - /// - /// search for graphic object type in crow assembly, if not found, - /// search for type independently of namespace in all the loaded assemblies - /// - /// - /// - /// the corresponding type object - /// graphic object type name without its namespace - Type tryGetGOType (string typeName){ - if (knownGOTypes.ContainsKey (typeName)) - return knownGOTypes [typeName]; - Type t = Type.GetType ("Crow." + typeName); - if (t != null) { - knownGOTypes.Add (typeName, t); - return t; - } - foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) { - if (a.IsDynamic) - continue; - foreach (Type expT in a.GetExportedTypes ()) { - if (expT.Name != typeName) - continue; - knownGOTypes.Add (typeName, expT); - return expT; - } - } - return null; - } - } -} - diff --git a/Crow/src/LayoutingEventArgs.cs b/Crow/src/LayoutingEventArgs.cs deleted file mode 100644 index 9254aec4..00000000 --- a/Crow/src/LayoutingEventArgs.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// LayoutingEventArgs.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; - -namespace Crow -{ - public class LayoutingEventArgs: EventArgs - { - public LayoutingType LayoutType; - - public LayoutingEventArgs (LayoutingType _layoutType) : base() - { - LayoutType = _layoutType; - } - } -} - diff --git a/Crow/src/Measure.cs b/Crow/src/Measure.cs deleted file mode 100644 index c77d66bd..00000000 --- a/Crow/src/Measure.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; - -namespace Crow -{ - /// - /// Measurement unit - /// - public enum Unit {Undefined, Pixel, Percent, Inherit } - /// - /// Measure class allow proportional sizes as well as stretched and fit on content. - /// - public struct Measure - { - /// - /// Integer value of the measure - /// - public int Value; - /// - /// Measurement unit - /// - public Unit Units; - - /// - /// Fit on content, this special measure is defined as a fixed integer set to -1 pixel - /// - public static Measure Fit = new Measure(-1,Unit.Percent); - /// - /// Stretched into parent client area. This special measure is defined as a proportional cote - /// set to 100 Percents - /// - public static Measure Stretched = new Measure(100, Unit.Percent); - public static Measure Inherit = new Measure (0, Unit.Inherit); - #region CTOR - public Measure (int _value, Unit _units = Unit.Pixel) - { - Value = _value; - Units = _units; - } - #endregion - - /// - /// True is size is fixed in pixels, this means not proportional, stretched nor fit. - /// - public bool IsFixed { get { return Units == Unit.Pixel; }} - public bool IsFit { get { return Value == -1 && Units == Unit.Percent; }} - public bool IsRelativeToParent { get { return Value >= 0 && Units == Unit.Percent; }} - #region Operators - public static implicit operator int(Measure m){ - return m.Value; - } - public static implicit operator Measure(int i){ - return new Measure(i); - } - public static implicit operator string(Measure m){ - return m.ToString(); - } - public static implicit operator Measure(string s){ - return Measure.Parse(s); - } - - public static bool operator ==(Measure m1, Measure m2){ - return m1.Value == m2.Value && m1.Units == m2.Units; - } - public static bool operator !=(Measure m1, Measure m2){ - return !(m1.Value == m2.Value && m1.Units == m2.Units); - } - #endregion - - #region Object overrides - public override int GetHashCode () - { - return Value.GetHashCode (); - } - public override bool Equals (object obj) - { - return (obj == null || obj.GetType() != typeof(Measure)) ? - false : - this == (Measure)obj; - } - public override string ToString () - { - return Units == Unit.Inherit ? "Inherit" : - Value == -1 ? "Fit" : - Units == Unit.Percent ? Value == 100 ? "Stretched" : - Value.ToString () + "%" : Value.ToString (); - } - #endregion - - public static Measure Parse(string s){ - if (string.IsNullOrEmpty (s)) - return default(Measure); - - string st = s.Trim (); - int tmp = 0; - - if (string.Equals ("Inherit", st, StringComparison.Ordinal)) - return Measure.Inherit; - else if (string.Equals ("Fit", st, StringComparison.Ordinal)) - return Measure.Fit; - else if (string.Equals ("Stretched", s, StringComparison.Ordinal)) - return Measure.Stretched; - else { - if (st.EndsWith ("%", StringComparison.Ordinal)) { - if (int.TryParse (s.Substring(0, st.Length - 1), out tmp)) - return new Measure (tmp, Unit.Percent); - }else if (int.TryParse (s, out tmp)) - return new Measure (tmp); - } - - throw new Exception ("Error parsing Measure."); - } - - } -} diff --git a/Crow/src/MouseCursorChangedEventArgs.cs b/Crow/src/MouseCursorChangedEventArgs.cs deleted file mode 100644 index 5e58b861..00000000 --- a/Crow/src/MouseCursorChangedEventArgs.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -// MouseCursorChangedEventArgs.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; - -namespace Crow -{ - public class MouseCursorChangedEventArgs : EventArgs - { - public MouseCursor NewCursor; - public MouseCursorChangedEventArgs (MouseCursor newCursor) : base() - { - NewCursor = newCursor; - } - } -} diff --git a/Crow/src/Point.cs b/Crow/src/Point.cs deleted file mode 100644 index 02f12e2f..00000000 --- a/Crow/src/Point.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Crow -{ - public struct Point - { - public int X; - public int Y; - - public Point (int x, int y) - { - X = x; - Y = y; - } - - public int Length => (int)Math.Sqrt (Math.Pow (X, 2) + Math.Pow (Y, 2)); - public double LengthD => Math.Sqrt (Math.Pow (X, 2) + Math.Pow (Y, 2)); - public Point Normalized { - get { - int l = Length; - return new Point (X / l, Y / l); - } - } - public static implicit operator PointD (Point p) => new PointD (p.X, p.Y); - public static implicit operator Point (int i) => new Point (i, i); - - public static Point operator + (Point p1, Point p2) => new Point (p1.X + p2.X, p1.Y + p2.Y); - public static Point operator + (Point p, int i) => new Point (p.X + i, p.Y + i); - public static Point operator - (Point p1, Point p2) => new Point (p1.X - p2.X, p1.Y - p2.Y); - public static Point operator - (Point p, int i) => new Point (p.X - i, p.Y - i); - public static Point operator * (Point p1, Point p2) => new Point (p1.X * p2.X, p1.Y * p2.Y); - public static Point operator * (Point p, int d) => new Point (p.X * d, p.Y * d); - public static Point operator / (Point p1, Point p2) => new Point (p1.X / p2.X, p1.Y / p2.Y); - public static Point operator / (Point p, int d) => new Point (p.X / d, p.Y / d); - - public static bool operator == (Point s1, Point s2) => s1.X == s2.X && s1.Y == s2.Y; - public static bool operator == (Point s, int i) => s.X == i && s.Y == i; - public static bool operator != (Point s1, Point s2) => !(s1.X == s2.X && s1.Y == s2.Y); - public static bool operator != (Point s, int i) => !(s.X == i && s.Y == i); - public static bool operator > (Point p1, Point p2) => p1.X > p2.X && p1.Y > p2.Y; - public static bool operator > (Point s, int i) => s.X > i && s.Y > i; - public static bool operator < (Point p1, Point p2) => p1.X < p2.X && p1.Y < p2.Y; - public static bool operator < (Point s, int i) => s.X < i && s.Y < i; - public static bool operator >= (Point p1, Point p2) => p1.X >= p2.X && p1.Y >= p2.Y; - public static bool operator >= (Point s, int i) => s.X >= i && s.Y >= i; - public static bool operator <= (Point p1, Point p2) => p1.X <= p2.X && p1.Y <= p2.Y; - public static bool operator <= (Point s, int i) => s.X <= i && s.Y <= i; - - public override string ToString () => string.Format ("{0},{1}", X, Y); - public override bool Equals (object obj) => obj is Point ? this == (Point)obj : - obj is Point && (Point)this == (Point)obj; - public static Point Parse (string s) - { - if (string.IsNullOrEmpty (s)) - return default (Point); - string [] d = s.Trim ().Split (','); - if (d.Length == 2) - return new Point (int.Parse (d [0]), int.Parse (d [1])); - else if (d.Length == 1) { - int tmp = int.Parse (d [0]); - return new Point (tmp, tmp); - } - throw new Exception ("Crow.PointD Parsing Error: " + s); - } - - public override int GetHashCode () - { -#pragma warning disable RECS0025 // Champ autre qu’en lecture seule référencé dans « GetHashCode() » - unchecked { - var hashCode = 1861411795; - hashCode = hashCode * -1521134295 + X.GetHashCode (); - - hashCode = hashCode * -1521134295 + Y.GetHashCode (); - return hashCode; - } -#pragma warning restore RECS0025 // Champ autre qu’en lecture seule référencé dans « GetHashCode() » } - } - } -} diff --git a/Crow/src/PointD.cs b/Crow/src/PointD.cs deleted file mode 100644 index d70e5b82..00000000 --- a/Crow/src/PointD.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; - -namespace Crow { - public struct PointD { - public double X; - public double Y; - public PointD (double x, double y) - { - X = x; - Y = y; - } - - public double Length => Math.Sqrt (Math.Pow (X, 2) + Math.Pow (Y, 2)); - public PointD Normalized { - get { - double l = Length; - return new PointD (X / l, Y / l); - } - } - public static implicit operator Point (PointD p) => new Point ((int)Math.Round (p.X), (int)Math.Round (p.Y)); - public static implicit operator PointD (double i) => new PointD (i, i); - - public static PointD operator + (PointD p1, PointD p2) => new PointD (p1.X + p2.X, p1.Y + p2.Y); - public static PointD operator + (PointD p, double i) => new PointD (p.X + i, p.Y + i); - public static PointD operator - (PointD p1, PointD p2) => new PointD (p1.X - p2.X, p1.Y - p2.Y); - public static PointD operator - (PointD p, double i) => new PointD (p.X - i, p.Y - i); - public static PointD operator * (PointD p1, PointD p2) => new PointD (p1.X * p2.X, p1.Y * p2.Y); - public static PointD operator * (PointD p, double d) => new PointD (p.X * d, p.Y * d); - public static PointD operator / (PointD p1, PointD p2) => new PointD (p1.X / p2.X, p1.Y / p2.Y); - public static PointD operator / (PointD p, double d) => new PointD (p.X / d, p.Y / d); - - public static bool operator == (PointD s1, PointD s2) => s1.X == s2.X && s1.Y == s2.Y; - public static bool operator == (PointD s, double i) => s.X == i && s.Y == i; - public static bool operator != (PointD s1, PointD s2) => !(s1.X == s2.X && s1.Y == s2.Y); - public static bool operator != (PointD s, double i) => !(s.X == i && s.Y == i); - public static bool operator > (PointD p1, PointD p2) => p1.X > p2.X && p1.Y > p2.Y; - public static bool operator > (PointD s, double i) => s.X > i && s.Y > i; - public static bool operator < (PointD p1, PointD p2) => p1.X < p2.X && p1.Y < p2.Y; - public static bool operator < (PointD s, double i) => s.X < i && s.Y < i; - public static bool operator >= (PointD p1, PointD p2) => p1.X >= p2.X && p1.Y >= p2.Y; - public static bool operator >= (PointD s, double i) => s.X >= i && s.Y >= i; - public static bool operator <= (PointD p1, PointD p2) => p1.X <= p2.X && p1.Y <= p2.Y; - public static bool operator <= (PointD s, double i) => s.X <= i && s.Y <= i; - - public override string ToString () => string.Format ("{0},{1}", X, Y); - public override bool Equals (object obj) => obj is PointD ? this == (PointD)obj : - obj is Point && (Point)this == (Point)obj; - public static PointD Parse (string s) - { - if (string.IsNullOrEmpty (s)) - return default (PointD); - string [] d = s.Trim ().Split (','); - if (d.Length == 2) - return new PointD (double.Parse (d [0]), double.Parse (d [1])); - else if (d.Length == 1) { - double tmp = double.Parse (d [0]); - return new PointD (tmp, tmp); - } - throw new Exception ("Crow.PointD Parsing Error: " + s); - } - - public override int GetHashCode () - { -#pragma warning disable RECS0025 // Champ autre qu’en lecture seule référencé dans « GetHashCode() » - unchecked { - var hashCode = 1861411795; - hashCode = hashCode * -1521134295 + X.GetHashCode (); - - hashCode = hashCode * -1521134295 + Y.GetHashCode (); - return hashCode; - } -#pragma warning restore RECS0025 // Champ autre qu’en lecture seule référencé dans « GetHashCode() » } - } - } -} \ No newline at end of file diff --git a/Crow/src/Rectangle.cs b/Crow/src/Rectangle.cs deleted file mode 100644 index a0e5ccf8..00000000 --- a/Crow/src/Rectangle.cs +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -using System; -using System.Runtime.InteropServices; - -namespace Crow { - [StructLayout(LayoutKind.Sequential)] - public struct Rectangle - { - public static readonly Rectangle Zero = new Rectangle (0, 0, 0, 0); - - public int X, Y, Width, Height; - - #region ctor - public Rectangle(Point p, Size s): this (p.X, p.Y, s.Width, s.Height) { } - public Rectangle(Size s) : this (0, 0, s.Width, s.Height) { } - public Rectangle(int x, int y, int width, int height) { - X = x; - Y = y; - Width = width; - Height = height; - } - #endregion - - #region PROPERTIES - [XmlIgnore]public int Left{ - get => X; - set { X = value; } - } - [XmlIgnore]public int Top{ - get => Y; - set { Y = value; } - } - [XmlIgnore] public int Right => X + Width; - [XmlIgnore]public int Bottom => Y + Height; - [XmlIgnore]public Size Size{ - get => new Size (Width, Height); - set { - Width = value.Width; - Height = value.Height; - } - } - [XmlIgnore] - public SizeD SizeD => new SizeD (Width, Height); - [XmlIgnore]public Point Position{ - get => new Point (X, Y); - set { - X = value.X; - Y = value.Y; - } - } - [XmlIgnore]public Point TopLeft{ - get => new Point (X, Y); - set { - X = value.X; - Y = value.Y; - } - } - [XmlIgnore] public Point TopRight => new Point (Right, Y); - [XmlIgnore] public Point BottomLeft => new Point (X, Bottom); - [XmlIgnore] public Point BottomRight => new Point (Right, Bottom); - [XmlIgnore] public Point Center => new Point (Left + Width / 2, Top + Height / 2); - [XmlIgnore] public Point CenterD => new PointD (Left + Width / 2.0, Top + Height / 2.0); - - #endregion - - #region FUNCTIONS - public void Inflate(int xDelta, int yDelta) - { - this.X -= xDelta; - this.Width += 2 * xDelta; - this.Y -= yDelta; - this.Height += 2 * yDelta; - } - public void Inflate(int delta) - { - Inflate (delta, delta); - } - public Rectangle Inflated (int delta) => Inflated (delta, delta); - public Rectangle Inflated (int deltaX, int deltaY) { - Rectangle r = this; - r.Inflate (deltaX, deltaY); - return r; - } - public bool ContainsOrIsEqual (Point p) => (p.X >= X && p.X <= X + Width && p.Y >= Y && p.Y <= Y + Height); - public bool ContainsOrIsEqual (Rectangle r) => r.TopLeft >= this.TopLeft && r.BottomRight <= this.BottomRight; - public bool Intersect(Rectangle r) - { - int maxLeft = Math.Max(this.Left, r.Left); - int minRight = Math.Min(this.Right, r.Right); - int maxTop = Math.Max(this.Top, r.Top); - int minBottom = Math.Min(this.Bottom, r.Bottom); - - return (maxLeft < minRight) && (maxTop < minBottom); - } - public Rectangle Intersection(Rectangle r) - { - Rectangle result = new Rectangle(); - - if (r.Left >= Left) - result.Left = r.Left; - else - result.TopLeft = TopLeft; - - if (r.Right >= Right) - result.Width = Right - result.Left; - else - result.Width = r.Right - result.Left; - - if (r.Top >= Top) - result.Top = r.Top; - else - result.Top = Top; - - if (r.Bottom >= Bottom) - result.Height = Bottom - result.Top; - else - result.Height = r.Bottom - result.Top; - - return result; - } - #endregion - - #region operators - public static Rectangle operator +(Rectangle r1, Rectangle r2) - { - int x = Math.Min(r1.X, r2.X); - int y = Math.Min(r1.Y, r2.Y); - int x2 = Math.Max(r1.Right, r2.Right); - int y2 = Math.Max(r1.Bottom, r2.Bottom); - return new Rectangle(x, y, x2 - x, y2 - y); - } - public static Rectangle operator + (Rectangle r, Point p) => new Rectangle (r.X + p.X, r.Y + p.Y, r.Width, r.Height); - public static Rectangle operator - (Rectangle r, Point p) => new Rectangle (r.X - p.X, r.Y - p.Y, r.Width, r.Height); - public static bool operator == (Rectangle r1, Rectangle r2) => r1.TopLeft == r2.TopLeft && r1.Size == r2.Size; - public static bool operator != (Rectangle r1, Rectangle r2) => !(r1.TopLeft == r2.TopLeft && r1.Size == r2.Size); - - public static implicit operator Rectangle (RectangleD r) => new Rectangle ((int)Math.Round(r.X), (int)Math.Round (r.Y), - (int)Math.Round (r.Width), (int)Math.Round (r.Height)); - #endregion - - public override string ToString () => $"{X},{Y},{Width},{Height}"; - public static Rectangle Parse(string s) - { - string[] d = s.Split(new char[] { ',' }); - return new Rectangle( - int.Parse(d[0]), - int.Parse(d[1]), - int.Parse(d[2]), - int.Parse(d[3])); - } - public override int GetHashCode () - { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - // Suitable nullity checks etc, of course :) - hash = hash * 23 + X.GetHashCode(); - hash = hash * 23 + Y.GetHashCode(); - hash = hash * 23 + Width.GetHashCode(); - hash = hash * 23 + Height.GetHashCode(); - return hash; - } - } - public override bool Equals (object obj) => (obj == null || obj.GetType () != typeof (Rectangle)) ? - false : this == (Rectangle)obj; - } -} diff --git a/Crow/src/RectangleD.cs b/Crow/src/RectangleD.cs deleted file mode 100644 index 817b3adc..00000000 --- a/Crow/src/RectangleD.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -using System; -using System.Runtime.InteropServices; - -namespace Crow { - [StructLayout(LayoutKind.Sequential)] - public struct RectangleD - { - public static readonly RectangleD Zero = new RectangleD (0, 0, 0, 0); - - public double X, Y, Width, Height; - - #region ctor - public RectangleD(PointD p, Size s): this (p.X, p.Y, s.Width, s.Height) { } - public RectangleD(SizeD s) : this (0, 0, s.Width, s.Height) { } - public RectangleD(double x, double y, double width, double height) { - X = x; - Y = y; - Width = width; - Height = height; - } - #endregion - - #region PROPERTIES - [XmlIgnore]public double Left{ - get => X; - set { X = value; } - } - [XmlIgnore]public double Top{ - get => Y; - set { Y = value; } - } - [XmlIgnore] public double Right => X + Width; - [XmlIgnore]public double Bottom => Y + Height; - [XmlIgnore]public SizeD Size{ - get => new SizeD (Width, Height); - set { - Width = value.Width; - Height = value.Height; - } - } - [XmlIgnore]public PointD Position{ - get => new PointD (X, Y); - set { - X = value.X; - Y = value.Y; - } - } - [XmlIgnore]public PointD TopLeft{ - get => new PointD (X, Y); - set { - X = value.X; - Y = value.Y; - } - } - [XmlIgnore] public PointD TopRight => new PointD (Right, Y); - [XmlIgnore] public PointD BottomLeft => new PointD (X, Bottom); - [XmlIgnore] public PointD BottomRight => new PointD (Right, Bottom); - [XmlIgnore] public PointD Center => new PointD (Left + Width / 2, Top + Height / 2); - [XmlIgnore] public PointD CenterD => new PointD (Left + Width / 2.0, Top + Height / 2.0); - - #endregion - - #region FUNCTIONS - public void Inflate(double xDelta, double yDelta) - { - this.X -= xDelta; - this.Width += 2 * xDelta; - this.Y -= yDelta; - this.Height += 2 * yDelta; - } - public void Inflate(double delta) - { - Inflate (delta, delta); - } - public RectangleD Inflated (double delta) => Inflated (delta, delta); - public RectangleD Inflated (double deltaX, double deltaY) { - RectangleD r = this; - r.Inflate (deltaX, deltaY); - return r; - } - public bool ContainsOrIsEqual (PointD p) => (p.X >= X && p.X <= X + Width && p.Y >= Y && p.Y <= Y + Height); - public bool ContainsOrIsEqual (RectangleD r) => r.TopLeft >= this.TopLeft && r.BottomRight <= this.BottomRight; - public bool Intersect(RectangleD r) - { - double maxLeft = Math.Max(this.Left, r.Left); - double minRight = Math.Min(this.Right, r.Right); - double maxTop = Math.Max(this.Top, r.Top); - double minBottom = Math.Min(this.Bottom, r.Bottom); - - return (maxLeft < minRight) && (maxTop < minBottom); - } - public RectangleD Intersection(RectangleD r) - { - RectangleD result = new RectangleD(); - - if (r.Left >= Left) - result.Left = r.Left; - else - result.TopLeft = TopLeft; - - if (r.Right >= Right) - result.Width = Right - result.Left; - else - result.Width = r.Right - result.Left; - - if (r.Top >= Top) - result.Top = r.Top; - else - result.Top = Top; - - if (r.Bottom >= Bottom) - result.Height = Bottom - result.Top; - else - result.Height = r.Bottom - result.Top; - - return result; - } - #endregion - - #region operators - public static RectangleD operator +(RectangleD r1, RectangleD r2) - { - double x = Math.Min(r1.X, r2.X); - double y = Math.Min(r1.Y, r2.Y); - double x2 = Math.Max(r1.Right, r2.Right); - double y2 = Math.Max(r1.Bottom, r2.Bottom); - return new RectangleD(x, y, x2 - x, y2 - y); - } - public static RectangleD operator + (RectangleD r, PointD p) => new RectangleD (r.X + p.X, r.Y + p.Y, r.Width, r.Height); - public static RectangleD operator - (RectangleD r, PointD p) => new RectangleD (r.X - p.X, r.Y - p.Y, r.Width, r.Height); - public static bool operator == (RectangleD r1, RectangleD r2) => r1.TopLeft == r2.TopLeft && r1.Size == r2.Size; - public static bool operator != (RectangleD r1, RectangleD r2) => !(r1.TopLeft == r2.TopLeft && r1.Size == r2.Size); - - public static implicit operator RectangleD(Rectangle r) => new RectangleD(r.X,r.Y,r.Width,r.Height); - #endregion - - public override string ToString () => $"{X},{Y},{Width},{Height}"; - public static RectangleD Parse(string s) - { - string[] d = s.Split(new char[] { ',' }); - return new RectangleD( - double.Parse(d[0]), - double.Parse(d[1]), - double.Parse(d[2]), - double.Parse(d[3])); - } - public override int GetHashCode () - { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - // Suitable nullity checks etc, of course :) - hash = hash * 23 + X.GetHashCode(); - hash = hash * 23 + Y.GetHashCode(); - hash = hash * 23 + Width.GetHashCode(); - hash = hash * 23 + Height.GetHashCode(); - return hash; - } - } - public override bool Equals (object obj) => (obj == null || obj.GetType () != typeof (RectangleD)) ? - false : this == (RectangleD)obj; - } -} diff --git a/Crow/src/ReflexionExtensions.cs b/Crow/src/ReflexionExtensions.cs index 8c0b4f27..25e2334b 100644 --- a/Crow/src/ReflexionExtensions.cs +++ b/Crow/src/ReflexionExtensions.cs @@ -1,28 +1,6 @@ -// -// ReflexionExtensions.cs +// Copyright (c) 2013-2020 Jean-Philippe Bruyère // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Reflection; diff --git a/Crow/src/ScrollingEventArgs.cs b/Crow/src/ScrollingEventArgs.cs deleted file mode 100644 index 3ff42065..00000000 --- a/Crow/src/ScrollingEventArgs.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// ScrollingEventArgs.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; - -namespace Crow -{ - public class ScrollingEventArgs: EventArgs - { - public Orientation Direction; - - public ScrollingEventArgs (Orientation _direction) : base() - { - Direction = _direction; - } - } -} - diff --git a/Crow/src/SelectionChangeEventArgs.cs b/Crow/src/SelectionChangeEventArgs.cs deleted file mode 100644 index 988a6975..00000000 --- a/Crow/src/SelectionChangeEventArgs.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// SelectionChangeEventArgs.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; - -namespace Crow -{ - public class SelectionChangeEventArgs: EventArgs - { - public object NewValue; - - - public SelectionChangeEventArgs (object _newValue) : base() - { - NewValue = _newValue; - } - } -} - diff --git a/Crow/src/Size.cs b/Crow/src/Size.cs deleted file mode 100644 index 77347f06..00000000 --- a/Crow/src/Size.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -namespace Crow -{ - public struct Size - { - public static Size Zero => new Size (0, 0); - - public int Width, Height; - - #region CTOR - public Size (int width, int height) - { - Width = width; - Height = height; - } - public Size (int size) - { - Width = size; - Height = size; - } - #endregion - - #region operators - public static implicit operator Rectangle(Size s)=> new Rectangle (s); - public static implicit operator Size(int i)=> new Size(i, i); - public static implicit operator string(Size s)=> s.ToString (); - public static implicit operator Size(string s)=> string.IsNullOrEmpty (s) ? Zero : Parse (s); - - public static bool operator == (Size s1, Size s2) => (s1.Width == s2.Width && s1.Height == s2.Height); - public static bool operator != (Size s1, Size s2) => (s1.Width != s2.Width || s1.Height != s2.Height); - public static bool operator > (Size s1, Size s2) => (s1.Width > s2.Width && s1.Height > s2.Height); - public static bool operator >= (Size s1, Size s2) => (s1.Width >= s2.Width && s1.Height >= s2.Height); - public static bool operator < (Size s1, Size s2) => (s1.Width < s2.Width) ? s1.Height <= s2.Height : - (s1.Width == s2.Width && s1.Height < s2.Height); - public static bool operator < (Size s, int i) => s.Width < i && s.Height < i; - public static bool operator <= (Size s, int i) => s.Width <= i && s.Height <= i; - public static bool operator > (Size s, int i) => s.Width > i && s.Height > i; - public static bool operator >= (Size s, int i) => s.Width >= i && s.Height >= i; - public static bool operator <= (Size s1, Size s2) => (s1.Width <= s2.Width && s1.Height <= s2.Height); - public static bool operator == (Size s, int i) => (s.Width == i && s.Height == i); - public static bool operator != (Size s, int i) => (s.Width != i || s.Height != i); - public static Size operator + (Size s1, Size s2) => new Size (s1.Width + s2.Width, s1.Height + s2.Height); - public static Size operator + (Size s, int i) => new Size (s.Width + i, s.Height + i); - public static Size operator * (Size s, int i) => new Size (s.Width * i, s.Height * i); - public static Size operator / (Size s, int i) => new Size (s.Width / i, s.Height / i); - #endregion - - public override int GetHashCode () - { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - // Suitable nullity checks etc, of course :) - hash = hash * 23 + Width.GetHashCode(); - hash = hash * 23 + Height.GetHashCode(); - return hash; - } - } - public override bool Equals (object obj) => (obj == null || obj.GetType () != typeof (Size)) ? false : this == (Size)obj; - public override string ToString () => $"{Width},{Height}"; - public static Size Parse(string s) - { - string[] d = s.Split(new char[] { ',' }); - return d.Length == 1 ? new Size(int.Parse(d[0])) : new Size( - int.Parse(d[0]), - int.Parse(d[1])); - } - } -} diff --git a/Crow/src/SizeD.cs b/Crow/src/SizeD.cs deleted file mode 100644 index 2dbdb02b..00000000 --- a/Crow/src/SizeD.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2013-2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -namespace Crow -{ - public struct SizeD { - public static SizeD Zero => new SizeD (0, 0); - - public double Width, Height; - - #region CTOR - public SizeD (double width, double height) - { - Width = width; - Height = height; - } - public SizeD (double size) - { - Width = size; - Height = size; - } - #endregion - - #region operators - public static implicit operator RectangleD (SizeD s) => new RectangleD (s); - public static implicit operator SizeD (double i) => new SizeD (i, i); - public static implicit operator string (SizeD s) => s.ToString (); - public static implicit operator SizeD (string s) => string.IsNullOrEmpty (s) ? Zero : Parse (s); - - public static bool operator == (SizeD s1, SizeD s2) => (s1.Width == s2.Width && s1.Height == s2.Height); - public static bool operator != (SizeD s1, SizeD s2) => (s1.Width == s2.Width && s1.Height == s2.Height); - public static bool operator > (SizeD s1, SizeD s2) => (s1.Width > s2.Width && s1.Height > s2.Height); - public static bool operator >= (SizeD s1, SizeD s2) => (s1.Width >= s2.Width && s1.Height >= s2.Height); - public static bool operator < (SizeD s1, SizeD s2) => (s1.Width < s2.Width) ? s1.Height <= s2.Height : - (s1.Width == s2.Width && s1.Height < s2.Height); - public static bool operator < (SizeD s, double i) => s.Width < i && s.Height < i; - public static bool operator <= (SizeD s, double i) => s.Width <= i && s.Height <= i; - public static bool operator > (SizeD s, double i) => s.Width > i && s.Height > i; - public static bool operator >= (SizeD s, double i) => s.Width >= i && s.Height >= i; - public static bool operator <= (SizeD s1, SizeD s2) => (s1.Width <= s2.Width && s1.Height <= s2.Height); - public static bool operator == (SizeD s, double i) => (s.Width == i && s.Height == i); - public static bool operator != (SizeD s, double i) => (s.Width == i && s.Height == i); - public static SizeD operator + (SizeD s1, SizeD s2) => new SizeD (s1.Width + s2.Width, s1.Height + s2.Height); - public static SizeD operator + (SizeD s, double i) => new SizeD (s.Width + i, s.Height + i); - public static SizeD operator * (SizeD s, double i) => new SizeD (s.Width * i, s.Height * i); - public static SizeD operator / (SizeD s, double i) => new SizeD (s.Width / i, s.Height / i); - #endregion - - public override int GetHashCode () - { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - // Suitable nullity checks etc, of course :) - hash = hash * 23 + Width.GetHashCode (); - hash = hash * 23 + Height.GetHashCode (); - return hash; - } - } - public override bool Equals (object obj) => (obj == null || obj.GetType () != typeof (SizeD)) ? false : this == (SizeD)obj; - public override string ToString () => $"{Width},{Height}"; - public static SizeD Parse (string s) - { - string [] d = s.Split (new char [] { ',' }); - return d.Length == 1 ? new SizeD (double.Parse (d [0])) : new SizeD ( - double.Parse (d [0]), - double.Parse (d [1])); - } - } -} diff --git a/Crow/src/Style.cs b/Crow/src/Style.cs deleted file mode 100644 index ded06b60..00000000 --- a/Crow/src/Style.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using System.Collections.Generic; - -namespace Crow -{ - public struct FileLocation { - public string FilePath; - public int Line; - public int Column; - public int Length; - - public FileLocation(string filePath, int line, int column, int length = 0){ - FilePath = filePath; - Line = line; - Column = column; - Length = length; - } - public override string ToString () - { - return string.Format ("{0} ({1},{2})", FilePath, Line, Column); - } - } - public class Style : Dictionary - { - #if DESIGN_MODE - public Dictionary Locations = new Dictionary(); - #endif - //public Dictionary SubStyles;//TODO:implement substyles for all tags inside a style - public Style () : base() - { - } - - } -} - diff --git a/Crow/src/StyleReader.cs b/Crow/src/StyleReader.cs deleted file mode 100644 index 77b8967d..00000000 --- a/Crow/src/StyleReader.cs +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text.RegularExpressions; -using Crow.Coding; - -namespace Crow -{ - /// - /// Parser for style files. - /// - //TODO: style key shared by different class may use only first encouneter class setter, which can cause bug. - public class StyleReader : StreamReader - { - enum States { init, classNames, members, value, endOfStatement } - - States curState = States.init; - - int column = 1; - int line = 1; - - #region Character ValidityCheck - static Regex rxValidChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); - static Regex rxNameStartChar = new Regex(@"_|\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}"); - static Regex rxNameChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); - static Regex rxDecimal = new Regex(@"[0-9]+"); - static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+"); - - public bool nextCharIsValidCharStartName - { - get { return rxNameStartChar.IsMatch(new string(new char[]{PeekChar()})); } - } - public bool nextCharIsValidCharName - { - get { return rxNameChar.IsMatch(new string(new char[]{PeekChar()})); } - } - #endregion - - char ReadChar () { - column++; - return (Char)Read(); - } - char PeekChar () { - return (Char)Peek(); - } - void SkipWhiteSpaceAndLineBreak (){ - while (!EndOfStream){ - if (!PeekChar ().IsWhiteSpaceOrNewLine ()) - break; - if (ReadChar () == '\n') { - line++; - column = 0; - } - } - } - - public StyleReader (Dictionary styling, Stream stream, string resId) - : base(stream) - { - string styleKey = resId.Substring (0, resId.Length - 6); - string token = ""; - List targetsClasses = new List (); - string currentProperty = ""; - - while (!EndOfStream) { - SkipWhiteSpaceAndLineBreak (); - if (EndOfStream) - break; - - switch (Peek()) { - case '/': - ReadChar (); - if (PeekChar () != '/') - throw new ParserException (line, column, "Unexpected char '/'", resId); - ReadLine (); - break; - case ',': - ReadChar (); - if (!(curState == States.init || curState == States.classNames) || string.IsNullOrEmpty (token)) - throw new ParserException (line, column, "Unexpected char ','", resId); - targetsClasses.Add (token); - token = ""; - curState = States.classNames; - break; - case '{': - ReadChar (); - if (!(curState == States.init || curState == States.classNames) || string.IsNullOrEmpty (token)) - throw new ParserException (line, column, "Unexpected char '{'", resId); - targetsClasses.Add (token); - token = ""; - curState = States.members; - break; - case '}': - ReadChar (); - if (curState != States.members) - throw new ParserException (line, column, "Unexpected char '}'", resId); - curState = States.classNames; - targetsClasses.Clear (); - break; - case '=': - ReadChar (); - if (!(curState == States.init || curState == States.members)) - throw new ParserException (line, column, "Unexpected char '='", resId); - currentProperty = token; - token = ""; - curState = States.value; - break; - case '"': - if (curState != States.value) - throw new ParserException (line, column, "Unexpected char '\"'", resId); - ReadChar (); - - while (!EndOfStream) { - char c = PeekChar(); - if (c == '\"') { - ReadChar (); - break; - } - token += ReadChar(); - if (c == '\\' && !EndOfStream) - token += ReadChar(); - } - curState = States.endOfStatement; - break; - case ';': - if (curState != States.endOfStatement) - throw new ParserException (line, column, "Unexpected end of statement", resId); - ReadChar (); - foreach (string tc in targetsClasses) { - if (!styling.ContainsKey (tc)) - styling [tc] = new Style (); - else if (styling [tc].ContainsKey (currentProperty)) - continue; - styling [tc] [currentProperty] = token; - #if DESIGN_MODE - styling [tc].Locations[currentProperty] = new FileLocation(resId, line, column - token.Length - 1, token.Length); - #endif - //System.Diagnostics.Debug.WriteLine ("Style: {3} : {0}.{1} = {2}", tc, currentProperty, token, resId); - } - token = ""; - curState = States.members; - break; - default: - if (curState == States.value) - throw new ParserException (line, column, "expecting value enclosed in '\"'", resId); - if (curState == States.endOfStatement) - throw new ParserException (line, column, "expecting end of statement", resId); - - if (nextCharIsValidCharStartName) { - token += ReadChar(); - while (nextCharIsValidCharName) - token += ReadChar(); - } - break; - } - } - } - } -} diff --git a/Crow/src/TextChangeEventArgs.cs b/Crow/src/TextChangeEventArgs.cs deleted file mode 100644 index 33ca405c..00000000 --- a/Crow/src/TextChangeEventArgs.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// TextChangeEventArgs.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; - -namespace Crow -{ - public class TextChangeEventArgs: EventArgs - { - public String Text; - - public TextChangeEventArgs (string _newValue) : base() - { - Text = _newValue; - } - } -} - diff --git a/Crow/src/ValueChangeEventArgs.cs b/Crow/src/ValueChangeEventArgs.cs deleted file mode 100644 index 854e2cd3..00000000 --- a/Crow/src/ValueChangeEventArgs.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; - -namespace Crow -{ - /// - /// Arguments for the ValueChange event used for Binding - /// - public class ValueChangeEventArgs: EventArgs - { - /// The name of the member whose value has changed - public string MemberName; - /// New value for that member - public object NewValue; - - /// - /// Initializes a new instance of the class. - /// - /// Member name. - /// New value. - public ValueChangeEventArgs (string _memberName, object _newValue) : base() - { - MemberName = _memberName; - NewValue = _newValue; - } - } -} - diff --git a/Crow/src/Widgets/IValueChange.cs b/Crow/src/Widgets/IValueChange.cs deleted file mode 100644 index 7e18e5e5..00000000 --- a/Crow/src/Widgets/IValueChange.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -// IValueChange.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; - -namespace Crow -{ - public interface IValueChange - { - event EventHandler ValueChanged; - } -} - diff --git a/Crow/src/styling/Style.cs b/Crow/src/styling/Style.cs new file mode 100644 index 00000000..ded06b60 --- /dev/null +++ b/Crow/src/styling/Style.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Collections.Generic; + +namespace Crow +{ + public struct FileLocation { + public string FilePath; + public int Line; + public int Column; + public int Length; + + public FileLocation(string filePath, int line, int column, int length = 0){ + FilePath = filePath; + Line = line; + Column = column; + Length = length; + } + public override string ToString () + { + return string.Format ("{0} ({1},{2})", FilePath, Line, Column); + } + } + public class Style : Dictionary + { + #if DESIGN_MODE + public Dictionary Locations = new Dictionary(); + #endif + //public Dictionary SubStyles;//TODO:implement substyles for all tags inside a style + public Style () : base() + { + } + + } +} + diff --git a/Crow/src/styling/StyleReader.cs b/Crow/src/styling/StyleReader.cs new file mode 100644 index 00000000..77b8967d --- /dev/null +++ b/Crow/src/styling/StyleReader.cs @@ -0,0 +1,164 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using Crow.Coding; + +namespace Crow +{ + /// + /// Parser for style files. + /// + //TODO: style key shared by different class may use only first encouneter class setter, which can cause bug. + public class StyleReader : StreamReader + { + enum States { init, classNames, members, value, endOfStatement } + + States curState = States.init; + + int column = 1; + int line = 1; + + #region Character ValidityCheck + static Regex rxValidChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); + static Regex rxNameStartChar = new Regex(@"_|\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}"); + static Regex rxNameChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); + static Regex rxDecimal = new Regex(@"[0-9]+"); + static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+"); + + public bool nextCharIsValidCharStartName + { + get { return rxNameStartChar.IsMatch(new string(new char[]{PeekChar()})); } + } + public bool nextCharIsValidCharName + { + get { return rxNameChar.IsMatch(new string(new char[]{PeekChar()})); } + } + #endregion + + char ReadChar () { + column++; + return (Char)Read(); + } + char PeekChar () { + return (Char)Peek(); + } + void SkipWhiteSpaceAndLineBreak (){ + while (!EndOfStream){ + if (!PeekChar ().IsWhiteSpaceOrNewLine ()) + break; + if (ReadChar () == '\n') { + line++; + column = 0; + } + } + } + + public StyleReader (Dictionary styling, Stream stream, string resId) + : base(stream) + { + string styleKey = resId.Substring (0, resId.Length - 6); + string token = ""; + List targetsClasses = new List (); + string currentProperty = ""; + + while (!EndOfStream) { + SkipWhiteSpaceAndLineBreak (); + if (EndOfStream) + break; + + switch (Peek()) { + case '/': + ReadChar (); + if (PeekChar () != '/') + throw new ParserException (line, column, "Unexpected char '/'", resId); + ReadLine (); + break; + case ',': + ReadChar (); + if (!(curState == States.init || curState == States.classNames) || string.IsNullOrEmpty (token)) + throw new ParserException (line, column, "Unexpected char ','", resId); + targetsClasses.Add (token); + token = ""; + curState = States.classNames; + break; + case '{': + ReadChar (); + if (!(curState == States.init || curState == States.classNames) || string.IsNullOrEmpty (token)) + throw new ParserException (line, column, "Unexpected char '{'", resId); + targetsClasses.Add (token); + token = ""; + curState = States.members; + break; + case '}': + ReadChar (); + if (curState != States.members) + throw new ParserException (line, column, "Unexpected char '}'", resId); + curState = States.classNames; + targetsClasses.Clear (); + break; + case '=': + ReadChar (); + if (!(curState == States.init || curState == States.members)) + throw new ParserException (line, column, "Unexpected char '='", resId); + currentProperty = token; + token = ""; + curState = States.value; + break; + case '"': + if (curState != States.value) + throw new ParserException (line, column, "Unexpected char '\"'", resId); + ReadChar (); + + while (!EndOfStream) { + char c = PeekChar(); + if (c == '\"') { + ReadChar (); + break; + } + token += ReadChar(); + if (c == '\\' && !EndOfStream) + token += ReadChar(); + } + curState = States.endOfStatement; + break; + case ';': + if (curState != States.endOfStatement) + throw new ParserException (line, column, "Unexpected end of statement", resId); + ReadChar (); + foreach (string tc in targetsClasses) { + if (!styling.ContainsKey (tc)) + styling [tc] = new Style (); + else if (styling [tc].ContainsKey (currentProperty)) + continue; + styling [tc] [currentProperty] = token; + #if DESIGN_MODE + styling [tc].Locations[currentProperty] = new FileLocation(resId, line, column - token.Length - 1, token.Length); + #endif + //System.Diagnostics.Debug.WriteLine ("Style: {3} : {0}.{1} = {2}", tc, currentProperty, token, resId); + } + token = ""; + curState = States.members; + break; + default: + if (curState == States.value) + throw new ParserException (line, column, "expecting value enclosed in '\"'", resId); + if (curState == States.endOfStatement) + throw new ParserException (line, column, "expecting end of statement", resId); + + if (nextCharIsValidCharStartName) { + token += ReadChar(); + while (nextCharIsValidCharName) + token += ReadChar(); + } + break; + } + } + } + } +}