From: jpbruyere Date: Wed, 15 Mar 2017 04:59:51 +0000 (+0100) Subject: split graphic objects dir X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=45f74150dd79db5a162034ab0c43e6abdb83beea;p=jp%2Fcrow.git split graphic objects dir --- diff --git a/Crow.csproj b/Crow.csproj index 20b38cf8..0b6f22f4 100644 --- a/Crow.csproj +++ b/Crow.csproj @@ -44,60 +44,30 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -105,10 +75,6 @@ - - - - @@ -133,10 +99,6 @@ - - - - @@ -154,6 +116,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -186,6 +186,7 @@ + diff --git a/src/GraphicObjects/Base/Border.cs b/src/GraphicObjects/Base/Border.cs new file mode 100644 index 00000000..672d5ab8 --- /dev/null +++ b/src/GraphicObjects/Base/Border.cs @@ -0,0 +1,100 @@ +// +// Border.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.Xml.Serialization; +using System.ComponentModel; +using System.Diagnostics; + +namespace Crow +{ + public class Border : Container + { + #region CTOR + public Border () : base(){} + #endregion + + #region private fields + int _borderWidth; + #endregion + + #region public properties + [XmlAttributeAttribute()][DefaultValue(1)] + public virtual int BorderWidth { + get { return _borderWidth; } + set { + _borderWidth = value; + RegisterForGraphicUpdate (); + } + } + #endregion + + #region GraphicObject override + [XmlIgnore]public override Rectangle ClientRectangle { + get { + Rectangle cb = base.ClientRectangle; + cb.Inflate (- BorderWidth); + return cb; + } + } + + protected override int measureRawSize (LayoutingType lt) + { + int tmp = base.measureRawSize (lt); + return tmp < 0 ? tmp : tmp + 2 * BorderWidth; + } + protected override void onDraw (Cairo.Context gr) + { + Rectangle rBack = new Rectangle (Slot.Size); + + //rBack.Inflate (-Margin); +// if (BorderWidth > 0) +// rBack.Inflate (-BorderWidth / 2); + + Background.SetAsSource (gr, rBack); + CairoHelpers.CairoRectangle(gr, rBack, CornerRadius); + gr.Fill (); + + if (BorderWidth > 0) { + Foreground.SetAsSource (gr, rBack); + CairoHelpers.CairoRectangle(gr, rBack, CornerRadius, BorderWidth); + } + + gr.Save (); + if (ClipToClientRect) { + //clip to client zone + CairoHelpers.CairoRectangle (gr, ClientRectangle,Math.Max(0.0, CornerRadius-Margin)); + gr.Clip (); + } + + if (child != null) + child.Paint (ref gr); + gr.Restore (); + } + #endregion + } +} + diff --git a/src/GraphicObjects/Base/Button.cs b/src/GraphicObjects/Base/Button.cs new file mode 100644 index 00000000..75f43014 --- /dev/null +++ b/src/GraphicObjects/Base/Button.cs @@ -0,0 +1,124 @@ +// +// Button.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 System.Linq; +using System.Text; +//using OpenTK.Graphics.OpenGL; + +using System.Diagnostics; + +using System.Xml.Serialization; +using Cairo; +using System.ComponentModel; + +namespace Crow +{ + public class Button : TemplatedContainer + { + string image; + bool isPressed; + Container _contentContainer; + + #region CTOR + public Button() : base() + {} + #endregion + + public event EventHandler Pressed; + public event EventHandler Released; + + #region TemplatedContainer overrides + public override GraphicObject Content { + get { + return _contentContainer == null ? null : _contentContainer.Child; + } + set { + if (_contentContainer != null) + _contentContainer.SetChild(value); + } + } + protected override void loadTemplate(GraphicObject template = null) + { + base.loadTemplate (template); + + _contentContainer = this.child.FindByName ("Content") as Container; + } + #endregion + + #region GraphicObject Overrides + public override void onMouseDown (object sender, MouseButtonEventArgs e) + { + IsPressed = true; + + base.onMouseDown (sender, e); + + //TODO:remove + NotifyValueChanged ("State", "pressed"); + } + public override void onMouseUp (object sender, MouseButtonEventArgs e) + { + IsPressed = false; + + base.onMouseUp (sender, e); + + //TODO:remove + NotifyValueChanged ("State", "normal"); + } + #endregion + + [XmlAttributeAttribute][DefaultValue("#Crow.Images.button.svg")] + public string Image { + get { return image; } + set { + if (image == value) + return; + image = value; + NotifyValueChanged ("Image", image); + } + } + [XmlAttributeAttribute][DefaultValue(false)] + public bool IsPressed + { + get { return isPressed; } + set + { + if (isPressed == value) + return; + + isPressed = value; + + NotifyValueChanged ("IsPressed", isPressed); + + if (isPressed) + Pressed.Raise (this, null); + else + Released.Raise (this, null); + } + } + } +} diff --git a/src/GraphicObjects/Base/CheckBox.cs b/src/GraphicObjects/Base/CheckBox.cs new file mode 100644 index 00000000..0a40d269 --- /dev/null +++ b/src/GraphicObjects/Base/CheckBox.cs @@ -0,0 +1,71 @@ +// +// CheckBox.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.ComponentModel; +using System.Xml.Serialization; + +namespace Crow +{ + public class CheckBox : TemplatedControl + { + bool isChecked; + + #region CTOR + public CheckBox() : base() + {} + #endregion + + public event EventHandler Checked; + public event EventHandler Unchecked; + + [XmlAttributeAttribute()][DefaultValue(false)] + public bool IsChecked + { + get { return isChecked; } + set + { + if (isChecked == value) + return; + + isChecked = value; + + NotifyValueChanged ("IsChecked", value); + + if (isChecked) + Checked.Raise (this, null); + else + Unchecked.Raise (this, null); + } + } + + public override void onMouseClick (object sender, MouseButtonEventArgs e) + { + IsChecked = !IsChecked; + base.onMouseClick (sender, e); + } + } +} diff --git a/src/GraphicObjects/Base/ComboBox.cs b/src/GraphicObjects/Base/ComboBox.cs new file mode 100644 index 00000000..15b49dbe --- /dev/null +++ b/src/GraphicObjects/Base/ComboBox.cs @@ -0,0 +1,55 @@ +// +// ComboBox.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.Xml.Serialization; + +namespace Crow +{ + public class ComboBox : ListBox + { + #region CTOR + public ComboBox() : base(){ } + #endregion + + Size minimumPopupSize = "10,10"; + [XmlIgnore]public Size MinimumPopupSize{ + get { return minimumPopupSize; } + set { + minimumPopupSize = value; + NotifyValueChanged ("MinimumPopupSize", minimumPopupSize); + } + } + + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + + if (layoutType == LayoutingType.Width) + MinimumPopupSize = new Size (this.Slot.Width, minimumPopupSize.Height); + } + } +} diff --git a/src/GraphicObjects/Base/Container.cs b/src/GraphicObjects/Base/Container.cs new file mode 100644 index 00000000..aac35946 --- /dev/null +++ b/src/GraphicObjects/Base/Container.cs @@ -0,0 +1,109 @@ +// +// Container.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.Xml.Serialization; +using System.Reflection; +using System.ComponentModel; +using System.Linq; +using System.Threading; + +namespace Crow +{ + public class Container : PrivateContainer, IXmlSerializable + { + #region CTOR + public Container() + : base() + { + } + #endregion + + [XmlIgnore] + public GraphicObject Child { + get { return child; } + set { child = value; } + } + public virtual void SetChild(GraphicObject _child) + { + base.SetChild (_child); + } + + #region IXmlSerializable + + public override System.Xml.Schema.XmlSchema GetSchema() + { + return null; + } + public override void ReadXml(System.Xml.XmlReader reader) + { + //only read attributes in GraphicObject IXmlReader implementation + base.ReadXml(reader); + + + using (System.Xml.XmlReader subTree = reader.ReadSubtree()) + { + subTree.Read(); //skip current node + subTree.Read(); //read first child + + if (!subTree.IsStartElement()) + return; + + Type t = Type.GetType("Crow." + subTree.Name); + if (t == null) { + Assembly a = Assembly.GetEntryAssembly (); + foreach (Type expT in a.GetExportedTypes ()) { + if (expT.Name == subTree.Name) { + t = expT; + break; + } + } + } + GraphicObject go = (GraphicObject)Activator.CreateInstance(t); + + (go as IXmlSerializable).ReadXml(subTree); + + SetChild(go); + + subTree.Read();//closing tag + } + } + public override void WriteXml(System.Xml.XmlWriter writer) + { + base.WriteXml(writer); + + if (Child == null) + return; + + writer.WriteStartElement(Child.GetType().Name); + (Child as IXmlSerializable).WriteXml(writer); + writer.WriteEndElement(); + } + + #endregion + } +} + diff --git a/src/GraphicObjects/Base/Expandable.cs b/src/GraphicObjects/Base/Expandable.cs new file mode 100644 index 00000000..1cb36450 --- /dev/null +++ b/src/GraphicObjects/Base/Expandable.cs @@ -0,0 +1,141 @@ +// +// Expandable.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.ComponentModel; +using System.Xml.Serialization; + +namespace Crow +{ + public class Expandable : TemplatedContainer + { + #region CTOR + public Expandable() : base() + { + } + #endregion + + #region Private fields + bool _isExpanded; + string image; + Container _contentContainer; + #endregion + + #region Event Handlers + public event EventHandler Expand; + public event EventHandler Collapse; + #endregion + + public BooleanTestOnInstance GetIsExpandable; + + public void onClickForExpand (object sender, MouseButtonEventArgs e) + { + IsExpanded = !IsExpanded; + } + + public override GraphicObject Content { + get { + return _contentContainer == null ? null : _contentContainer.Child; + } + set { + _contentContainer.SetChild(value); + NotifyValueChanged ("HasContent", HasContent); + } + } + //TODO: move loadTemplate and ResolveBinding in TemplatedContainer + protected override void loadTemplate(GraphicObject template = null) + { + base.loadTemplate (template); + + _contentContainer = this.child.FindByName ("Content") as Container; + } + + #region Public properties + [XmlAttributeAttribute][DefaultValue("#Crow.Images.Icons.expandable.svg")] + public string Image { + get { return image; } + set { + if (image == value) + return; + image = value; + NotifyValueChanged ("Image", image); + } + } + [XmlAttributeAttribute][DefaultValue(false)] + public bool IsExpanded + { + get { return _isExpanded; } + set + { + if (value == _isExpanded) + return; + + _isExpanded = value; + + bool isExp = IsExpandable; + NotifyValueChanged ("IsExpandable", isExp); + if (!(HasContent & isExp)) + _isExpanded = false; + + NotifyValueChanged ("IsExpanded", _isExpanded); + + if (_isExpanded) + onExpand (this, null); + else + onCollapse (this, null); + } + } + [XmlIgnore]public bool HasContent { + get { return _contentContainer == null ? false : _contentContainer.Child != null; } + } + [XmlIgnore]public bool IsExpandable { + get { + try { + return GetIsExpandable == null ? true : GetIsExpandable (this); + } catch (Exception ex) { + System.Diagnostics.Debug.WriteLine ("Not Expandable error: " + ex.ToString ()); + return false; + } + } + } + #endregion + + public virtual void onExpand(object sender, EventArgs e) + { + if (_contentContainer != null) + _contentContainer.Visible = true; + + Expand.Raise (this, e); + } + public virtual void onCollapse(object sender, EventArgs e) + { + if (_contentContainer != null) + _contentContainer.Visible = false; + + Collapse.Raise (this, e); + } + } +} diff --git a/src/GraphicObjects/Base/GenericStack.cs b/src/GraphicObjects/Base/GenericStack.cs new file mode 100644 index 00000000..cecdaffd --- /dev/null +++ b/src/GraphicObjects/Base/GenericStack.cs @@ -0,0 +1,228 @@ +// +// GenericStack.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.ComponentModel; +using System.Diagnostics; +using System.Xml.Serialization; +using System; + +namespace Crow +{ + public class GenericStack : Group + { + #region CTOR + public GenericStack() + : base() + { + } + #endregion + + #region Private fields + int _spacing; + Orientation _orientation; + #endregion + + #region Public Properties + [XmlAttributeAttribute()][DefaultValue(2)] + public int Spacing + { + get { return _spacing; } + set { + if (_spacing == value) + return; + _spacing = value; + NotifyValueChanged ("Spacing", Spacing); + RegisterForLayouting (LayoutingType.Sizing|LayoutingType.ArrangeChildren); + } + } + [XmlAttributeAttribute()][DefaultValue(Orientation.Horizontal)] + public virtual Orientation Orientation + { + get { return _orientation; } + set { _orientation = value; } + } + #endregion + + #region GraphicObject Overrides + public override bool ArrangeChildren { get { return true; } } + public override void ChildrenLayoutingConstraints (ref LayoutingType layoutType) + { + //Prevent child repositionning in the direction of stacking + if (Orientation == Orientation.Horizontal) + layoutType &= (~LayoutingType.X); + else + layoutType &= (~LayoutingType.Y); + } + protected override int measureRawSize (LayoutingType lt) + { + int totSpace = 0; + for (int i = 0; i < Children.Count; i++) { + if (Children [i].Visible) + totSpace += Spacing; + } + if (totSpace > 0) + totSpace -= Spacing; + if (lt == LayoutingType.Width) { + if (Orientation == Orientation.Horizontal) + return contentSize.Width + totSpace + 2 * Margin; + }else if (Orientation == Orientation.Vertical) + return contentSize.Height + totSpace + 2 * Margin; + + return base.measureRawSize (lt); + } + public virtual void ComputeChildrenPositions() + { + int d = 0; + if (Orientation == Orientation.Horizontal) { + foreach (GraphicObject c in Children) { + if (!c.Visible) + continue; + c.Slot.X = d; + d += c.Slot.Width + Spacing; + } + } else { + foreach (GraphicObject c in Children) { + if (!c.Visible) + continue; + c.Slot.Y = d; + d += c.Slot.Height + Spacing; + } + } + IsDirty = true; + } + GraphicObject stretchedGO = null; + public override bool UpdateLayout (LayoutingType layoutType) + { + RegisteredLayoutings &= (~layoutType); + + if (layoutType == LayoutingType.ArrangeChildren) { + //allow 1 child to have size to 0 if stack has fixed or streched size policy, + //this child will occupy remaining space + //if stack size policy is Fit, no child may have stretch enabled + //in the direction of stacking. + ComputeChildrenPositions (); + + //if no layouting remains in queue for item, registre for redraw + if (RegisteredLayoutings == LayoutingType.None && IsDirty) + CurrentInterface.EnqueueForRepaint (this); + + return true; + } + + return base.UpdateLayout(layoutType); + } + + public override void OnChildLayoutChanges (object sender, LayoutingEventArgs arg) + { + GraphicObject go = sender as GraphicObject; + //Debug.WriteLine ("child layout change: " + go.LastSlots.ToString() + " => " + go.Slot.ToString()); + switch (arg.LayoutType) { + case LayoutingType.Width: + if (Orientation == Orientation.Horizontal) { + if (go.Width == Measure.Stretched) { + if (stretchedGO == null && Width != Measure.Fit) + stretchedGO = go; + else if (stretchedGO != go) { + go.Slot.Width = 0; + go.Width = Measure.Fit; + return; + } + } else + contentSize.Width += go.Slot.Width - go.LastSlots.Width; + + if (stretchedGO != null) { + int newW = Math.Max ( + this.ClientRectangle.Width - contentSize.Width - Spacing * (Children.Count - 1), + stretchedGO.MinimumSize.Width); + if (stretchedGO.MaximumSize.Width > 0) + newW = Math.Min (newW, stretchedGO.MaximumSize.Width); + if (newW != stretchedGO.Slot.Width) { + stretchedGO.Slot.Width = newW; + stretchedGO.IsDirty = true; +#if DEBUG_LAYOUTING + Debug.WriteLine ("\tAdjusting Width of " + stretchedGO.ToString()); +#endif + stretchedGO.LayoutChanged -= OnChildLayoutChanges; + stretchedGO.OnLayoutChanges (LayoutingType.Width); + stretchedGO.LayoutChanged += OnChildLayoutChanges; + stretchedGO.LastSlots.Width = stretchedGO.Slot.Width; + } + } + + if (Width == Measure.Fit) + this.RegisterForLayouting (LayoutingType.Width); + + this.RegisterForLayouting (LayoutingType.ArrangeChildren); + return; + } + break; + case LayoutingType.Height: + if (Orientation == Orientation.Vertical) { + if (go.Height == Measure.Stretched) { + if (stretchedGO == null && Height != Measure.Fit) + stretchedGO = go; + else if (stretchedGO != go){ + go.Slot.Height = 0; + go.Height = Measure.Fit; + return; + } + } else + contentSize.Height += go.Slot.Height - go.LastSlots.Height; + + if (stretchedGO != null) { + int newH = Math.Max ( + this.ClientRectangle.Height - contentSize.Height - Spacing * (Children.Count - 1), + stretchedGO.MinimumSize.Height); + if (stretchedGO.MaximumSize.Height > 0) + newH = Math.Min (newH, stretchedGO.MaximumSize.Height); + if (newH != stretchedGO.Slot.Height) { + stretchedGO.Slot.Height = newH; + stretchedGO.IsDirty = true; +#if DEBUG_LAYOUTING + Debug.WriteLine ("\tAdjusting Height of " + stretchedGO.ToString()); +#endif + stretchedGO.LayoutChanged -= OnChildLayoutChanges; + stretchedGO.OnLayoutChanges (LayoutingType.Height); + stretchedGO.LayoutChanged += OnChildLayoutChanges; + stretchedGO.LastSlots.Height = stretchedGO.Slot.Height; + } + } + + if (Height == Measure.Fit) + this.RegisterForLayouting (LayoutingType.Height); + + this.RegisterForLayouting (LayoutingType.ArrangeChildren); + return; + } + break; + } + base.OnChildLayoutChanges (sender, arg); + } + #endregion + + + } +} diff --git a/src/GraphicObjects/Base/GraphicObject.cs b/src/GraphicObjects/Base/GraphicObject.cs new file mode 100644 index 00000000..2afbe336 --- /dev/null +++ b/src/GraphicObjects/Base/GraphicObject.cs @@ -0,0 +1,1555 @@ +// +// GraphicObject.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 System.ComponentModel; +using System.Xml.Serialization; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using Cairo; +using System.Linq; +using System.Diagnostics; +using System.IO; + +namespace Crow +{ + public class GraphicObject : IXmlSerializable, ILayoutable, IValueChange, ICloneable + { + internal static ulong currentUid = 0; + internal ulong uid = 0; + + Interface currentInterface = null; + + [XmlIgnore]public Interface CurrentInterface { + get { + if (currentInterface == null) { + currentInterface = Interface.CurrentInterface; + Initialize (); + } + return currentInterface; + } + set { + currentInterface = value; + } + } + + Rectangles clipping = new Rectangles(); + public Rectangles Clipping { get { return clipping; }} + + #region IValueChange implementation + public event EventHandler ValueChanged; + public virtual void NotifyValueChanged(string MemberName, object _value) + { + //Debug.WriteLine ("Value changed: {0}->{1} = {2}", this, MemberName, _value); + ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value)); + } + #endregion + + #region CTOR + public GraphicObject () + { + #if DEBUG + uid = currentUid; + currentUid++; + #endif + } + #endregion + + /// + /// Initialize this Graphic object instance by setting style and default values and loading template if required + /// + public virtual void Initialize(){ + if (currentInterface == null) + currentInterface = Interface.CurrentInterface; + loadDefaultValues (); + } + #region private fields + LayoutingType registeredLayoutings = LayoutingType.All; + ILayoutable logicalParent; + ILayoutable parent; + string name; + Fill background = Color.Transparent; + Fill foreground = Color.White; + Font font = "droid, 10"; + Measure width, height; + int left, top; + double cornerRadius = 0; + int margin = 0; + bool focusable = false; + bool hasFocus = false; + bool isActive = false; + bool mouseRepeat; + protected bool isVisible = true; + bool isEnabled = true; + VerticalAlignment verticalAlignment = VerticalAlignment.Center; + HorizontalAlignment horizontalAlignment = HorizontalAlignment.Center; + Size maximumSize = "0,0"; + Size minimumSize = "0,0"; + bool cacheEnabled = false; + bool clipToClientRect = true; + protected object dataSource; + string style; + object tag; + #endregion + + #region public fields + /// + /// Current size and position computed during layouting pass + /// + public Rectangle Slot = new Rectangle (); + /// + /// keep last slot components for each layouting pass to track + /// changes and trigger update of other component accordingly + /// + public Rectangle LastSlots; + /// + /// keep last slot painted on screen to clear traces if moved or resized + /// TODO: we should ensure the whole parsed widget tree is the last painted + /// version to clear effective oldslot if parents have been moved or resized. + /// IDEA is to add a ScreenCoordinates function that use only lastPaintedSlots + /// + public Rectangle LastPaintedSlot; + /// Prevent requeuing multiple times the same widget + public bool IsQueueForRedraw = false; + /// drawing Cache, if null, a redraw is done, cached or not + public byte[] bmp; + public bool IsDirty = true; + /// + /// This size is computed on each child' layout changes. + /// In stacking widget, it is used to compute the remaining space for the stretched + /// widget inside the stack, which is never added to the contentSize, instead, its size + /// is deducted from (parent.ClientRectangle - contentSize) + /// + internal Size contentSize; + #endregion + + #region ILayoutable + [XmlIgnore]public LayoutingType RegisteredLayoutings { get { return registeredLayoutings; } set { registeredLayoutings = value; } } + //TODO: it would save the recurent cost of a cast in event bubbling if parent type was GraphicObject + // or we could add to the interface the mouse events + /// + /// Parent in the graphic tree, used for rendering and layouting + /// + [XmlIgnore]public virtual ILayoutable Parent { + get { return parent; } + set { + if (parent == value) + return; + DataSourceChangeEventArgs e = new DataSourceChangeEventArgs (parent, value); + lock (this) + parent = value; + + onParentChanged (this, e); + } + } + [XmlIgnore]public ILayoutable LogicalParent { + get { return logicalParent == null ? Parent : logicalParent; } + set { + if (logicalParent == value) + return; + if (logicalParent != null) + (logicalParent as GraphicObject).DataSourceChanged -= onLogicalParentDataSourceChanged; + DataSourceChangeEventArgs dsce = new DataSourceChangeEventArgs (LogicalParent, null); + logicalParent = value; + dsce.NewDataSource = LogicalParent; + if (logicalParent != null) + (logicalParent as GraphicObject).DataSourceChanged += onLogicalParentDataSourceChanged; + onLogicalParentChanged (this, dsce); + } + } + [XmlIgnore]public virtual Rectangle ClientRectangle { + get { + Rectangle cb = Slot.Size; + cb.Inflate ( - Margin); + return cb; + } + } + public virtual Rectangle ContextCoordinates(Rectangle r){ + GraphicObject go = Parent as GraphicObject; + if (go == null) + return r + Parent.ClientRectangle.Position; + return go.CacheEnabled ? + r + Parent.ClientRectangle.Position : + Parent.ContextCoordinates (r); + } + public virtual Rectangle ScreenCoordinates (Rectangle r){ + return + Parent.ScreenCoordinates(r) + Parent.getSlot().Position + Parent.ClientRectangle.Position; + } + public virtual Rectangle getSlot () { return Slot;} + #endregion + + #region EVENT HANDLERS + public event EventHandler MouseWheelChanged; + public event EventHandler MouseUp; + public event EventHandler MouseDown; + public event EventHandler MouseClick; + public event EventHandler MouseDoubleClick; + public event EventHandler MouseMove; + public event EventHandler MouseEnter; + public event EventHandler MouseLeave; + public event EventHandler KeyDown; + public event EventHandler KeyUp; + public event EventHandler KeyPress; + public event EventHandler Focused; + public event EventHandler Unfocused; + public event EventHandler Enabled; + public event EventHandler Disabled; + public event EventHandler LayoutChanged; + public event EventHandler DataSourceChanged; + public event EventHandler ParentChanged; + public event EventHandler LogicalParentChanged; + #endregion + + #region public properties + /// Random value placeholder + [XmlAttributeAttribute] + public object Tag { + get { return tag; } + set { + if (tag == value) + return; + tag = value; + NotifyValueChanged ("Tag", tag); + } + } + /// + /// If enabled, resulting bitmap of graphic object is cached in an byte array + /// speeding up rendering of complex object. Default is enabled. + /// + [XmlAttributeAttribute][DefaultValue(true)] + public virtual bool CacheEnabled { + get { return cacheEnabled; } + set { + if (cacheEnabled == value) + return; + cacheEnabled = value; + NotifyValueChanged ("CacheEnabled", cacheEnabled); + } + } + /// + /// If true, rendering of GraphicObject is clipped inside client rectangle + /// + [XmlAttributeAttribute][DefaultValue(true)] + public virtual bool ClipToClientRect { + get { return clipToClientRect; } + set { + if (clipToClientRect == value) + return; + clipToClientRect = value; + NotifyValueChanged ("ClipToClientRect", clipToClientRect); + this.RegisterForRedraw (); + } + } + /// + /// Name is used in binding to reference other GraphicObjects inside the graphic tree + /// + [XmlAttributeAttribute][DefaultValue(null)] + public virtual string Name { + get { + #if DEBUG + return string.IsNullOrEmpty(name) ? this.GetType().Name + uid.ToString () : name; + #else + return name; + #endif + } + set { + if (name == value) + return; + name = value; + NotifyValueChanged("Name", name); + } + } + [XmlAttributeAttribute ()][DefaultValue(VerticalAlignment.Center)] + public virtual VerticalAlignment VerticalAlignment { + get { return verticalAlignment; } + set { + if (verticalAlignment == value) + return; + + verticalAlignment = value; + NotifyValueChanged("VerticalAlignment", verticalAlignment); + RegisterForLayouting (LayoutingType.Y); + } + } + [XmlAttributeAttribute()][DefaultValue(HorizontalAlignment.Center)] + public virtual HorizontalAlignment HorizontalAlignment { + get { return horizontalAlignment; } + set { + if (horizontalAlignment == value) + return; + horizontalAlignment = value; + NotifyValueChanged("HorizontalAlignment", horizontalAlignment); + RegisterForLayouting (LayoutingType.X); + } + } + [XmlAttributeAttribute()][DefaultValue(0)] + public virtual int Left { + get { return left; } + set { + if (left == value) + return; + left = value; + NotifyValueChanged ("Left", left); + this.RegisterForLayouting (LayoutingType.X); + } + } + [XmlAttributeAttribute()][DefaultValue(0)] + public virtual int Top { + get { return top; } + set { + if (top == value) + return; + top = value; + NotifyValueChanged ("Top", top); + this.RegisterForLayouting (LayoutingType.Y); + } + } + /// + /// When set to True, the 's width and height will be set to Fit. + /// + [XmlAttributeAttribute()][DefaultValue(false)] + public virtual bool Fit { + get { return Width == Measure.Fit && Height == Measure.Fit ? true : false; } + set { + if (value == Fit) + return; + + Width = Height = Measure.Fit; + } + } + [XmlAttributeAttribute()][DefaultValue("Inherit")] + public virtual Measure Width { + get { + return width.Units == Unit.Inherit ? + Parent is GraphicObject ? (Parent as GraphicObject).WidthPolicy : + Measure.Stretched : width; + } + set { + if (width == value) + return; + if (value.IsFixed) { + if (value < MinimumSize.Width || (value > MaximumSize.Width && MaximumSize.Width > 0)) + return; + } + Measure lastWP = WidthPolicy; + width = value; + NotifyValueChanged ("Width", width); + if (WidthPolicy != lastWP) { + NotifyValueChanged ("WidthPolicy", WidthPolicy); + //contentSize in Stacks are only update on childLayoutChange, and the single stretched + //child of the stack is not counted in contentSize, so when changing size policy of a child + //we should adapt contentSize + //TODO:check case when child become stretched, and another stretched item already exists. + if (parent is GenericStack) {//TODO:check if I should test Group instead + if ((parent as GenericStack).Orientation == Orientation.Horizontal) { + if (lastWP == Measure.Fit) + (parent as GenericStack).contentSize.Width -= this.LastSlots.Width; + else + (parent as GenericStack).contentSize.Width += this.LastSlots.Width; + } + } + } + + this.RegisterForLayouting (LayoutingType.Width); + } + } + [XmlAttributeAttribute()][DefaultValue("Inherit")] + public virtual Measure Height { + get { + return height.Units == Unit.Inherit ? + Parent is GraphicObject ? (Parent as GraphicObject).HeightPolicy : + Measure.Stretched : height; + } + set { + if (height == value) + return; + if (value.IsFixed) { + if (value < MinimumSize.Height || (value > MaximumSize.Height && MaximumSize.Height > 0)) + return; + } + Measure lastHP = HeightPolicy; + height = value; + NotifyValueChanged ("Height", height); + if (HeightPolicy != lastHP) { + NotifyValueChanged ("HeightPolicy", HeightPolicy); + if (parent is GenericStack) { + if ((parent as GenericStack).Orientation == Orientation.Vertical) { + if (lastHP == Measure.Fit) + (parent as GenericStack).contentSize.Height -= this.LastSlots.Height; + else + (parent as GenericStack).contentSize.Height += this.LastSlots.Height; + } + } + } + + this.RegisterForLayouting (LayoutingType.Height); + } + } + /// + /// Used for binding on dimensions, this property will never hold fixed size, but instead only + /// Fit or Stretched + /// + [XmlIgnore]public virtual Measure WidthPolicy { get { + return Width.IsFit ? Measure.Fit : Measure.Stretched; } } + /// + /// Used for binding on dimensions, this property will never hold fixed size, but instead only + /// Fit or Stretched + /// + [XmlIgnore]public virtual Measure HeightPolicy { get { + return Height.IsFit ? Measure.Fit : Measure.Stretched; } } + [XmlAttributeAttribute()][DefaultValue(false)] + public virtual bool Focusable { + get { return focusable; } + set { + if (focusable == value) + return; + focusable = value; + NotifyValueChanged ("Focusable", focusable); + } + } + [XmlIgnore]public virtual bool HasFocus { + get { return hasFocus; } + set { + if (value == hasFocus) + return; + + hasFocus = value; + if (hasFocus) + onFocused (this, null); + else + onUnfocused (this, null); + NotifyValueChanged ("HasFocus", hasFocus); + } + } + [XmlIgnore]public virtual bool IsActive { + get { return isActive; } + set { + if (value == isActive) + return; + + isActive = value; + NotifyValueChanged ("IsActive", isActive); + } + } + [XmlAttributeAttribute()][DefaultValue(false)] + public virtual bool MouseRepeat { + get { return mouseRepeat; } + set { + if (mouseRepeat == value) + return; + mouseRepeat = value; + NotifyValueChanged ("MouseRepeat", mouseRepeat); + } + } + bool clearBackground = false; + [XmlAttributeAttribute()][DefaultValue("Transparent")] + public virtual Fill Background { + get { return background; } + set { + if (background == value) + return; + clearBackground = false; + if (value == null) + return; + background = value; + NotifyValueChanged ("Background", background); + RegisterForRedraw (); + if (background is SolidColor) { + if ((Background as SolidColor).Equals (Color.Clear)) + clearBackground = true; + } + } + } + [XmlAttributeAttribute()][DefaultValue("White")] + public virtual Fill Foreground { + get { return foreground; } + set { + if (foreground == value) + return; + foreground = value; + NotifyValueChanged ("Foreground", foreground); + RegisterForRedraw (); + } + } + [XmlAttributeAttribute()][DefaultValue("sans,10")] + public virtual Font Font { + get { return font; } + set { + if (value == font) + return; + font = value; + NotifyValueChanged ("Font", font); + RegisterForGraphicUpdate (); + } + } + [XmlAttributeAttribute()][DefaultValue(0.0)] + public virtual double CornerRadius { + get { return cornerRadius; } + set { + if (value == cornerRadius) + return; + cornerRadius = value; + NotifyValueChanged ("CornerRadius", cornerRadius); + RegisterForRedraw (); + } + } + [XmlAttributeAttribute()][DefaultValue(0)] + public virtual int Margin { + get { return margin; } + set { + if (value == margin) + return; + margin = value; + NotifyValueChanged ("Margin", margin); + RegisterForGraphicUpdate (); + } + } + [XmlAttributeAttribute][DefaultValue(true)] + public virtual bool Visible { + get { return isVisible; } + set { + if (value == isVisible) + return; + + isVisible = value; + + if (isVisible) + RegisterForLayouting (LayoutingType.Sizing); + else { + lock (CurrentInterface.UpdateMutex) { + Slot.Width = 0; + LayoutChanged.Raise (this, new LayoutingEventArgs (LayoutingType.Width)); + Slot.Height = 0; + LayoutChanged.Raise (this, new LayoutingEventArgs (LayoutingType.Height)); + if (this.parent != null) + CurrentInterface.EnqueueForRepaint (this); + LastSlots.Width = LastSlots.Height = 0; + } + } + + //trigger a mouse to handle possible hover changes + CurrentInterface.ProcessMouseMove (CurrentInterface.Mouse.X, CurrentInterface.Mouse.Y); + + NotifyValueChanged ("Visible", isVisible); + } + } + [XmlAttributeAttribute][DefaultValue(true)] + public virtual bool IsEnabled { + get { return isEnabled; } + set { + if (value == isEnabled) + return; + + isEnabled = value; + + if (isEnabled) + onEnable (this, null); + else + onDisable (this, null); + + NotifyValueChanged ("IsEnabled", isEnabled); + RegisterForRedraw (); + } + } + [XmlAttributeAttribute()][DefaultValue("1,1")] + public virtual Size MinimumSize { + get { return minimumSize; } + set { + if (value == minimumSize) + return; + + minimumSize = value; + + NotifyValueChanged ("MinimumSize", minimumSize); + RegisterForLayouting (LayoutingType.Sizing); + } + } + [XmlAttributeAttribute()][DefaultValue("0,0")] + public virtual Size MaximumSize { + get { return maximumSize; } + set { + if (value == maximumSize) + return; + + maximumSize = value; + + NotifyValueChanged ("MaximumSize", maximumSize); + RegisterForLayouting (LayoutingType.Sizing); + } + } + /// + /// Seek first logical tree upward if logicalParent is set, or seek graphic tree for + /// a not null dataSource that will be active for all descendants having dataSource=null + /// + [XmlAttributeAttribute][DefaultValue(null)] + public virtual object DataSource { + set { + if (DataSource == value) + return; + + DataSourceChangeEventArgs dse = new DataSourceChangeEventArgs (DataSource, null); + dataSource = value; + dse.NewDataSource = DataSource; + + OnDataSourceChanged (this, dse); + + NotifyValueChanged ("DataSource", DataSource); + } + get { + return dataSource == null ? + LogicalParent == null ? null : + LogicalParent is GraphicObject ? (LogicalParent as GraphicObject).DataSource : null : + dataSource; + } + } + protected virtual void onLogicalParentDataSourceChanged(object sender, DataSourceChangeEventArgs e){ + if (localDataSourceIsNull) + OnDataSourceChanged (this, e); + } + internal bool localDataSourceIsNull { get { return dataSource == null; } } + internal bool localLogicalParentIsNull { get { return logicalParent == null; } } + + public virtual void OnDataSourceChanged(object sender, DataSourceChangeEventArgs e){ + DataSourceChanged.Raise (this, e); + #if DEBUG_BINDING + Debug.WriteLine("New DataSource for => {0} \n\t{1}=>{2}", this.ToString(),e.OldDataSource,e.NewDataSource); + #endif + } + + [XmlAttributeAttribute] + public virtual string Style { + get { return style; } + set { + if (value == style) + return; + + style = value; + + NotifyValueChanged ("Style", style); + } + } + #endregion + + #region Default and Style Values loading + /// Loads the default values from XML attributes default + public void loadDefaultValues() + { + #if DEBUG_LOAD + Debug.WriteLine ("LoadDefValues for " + this.ToString ()); + #endif + + Type thisType = this.GetType (); + + if (!string.IsNullOrEmpty (Style)) { + if (Interface.DefaultValuesLoader.ContainsKey (Style)) { + Interface.DefaultValuesLoader [Style] (this); + return; + } + } else { + if (Interface.DefaultValuesLoader.ContainsKey (thisType.FullName)) { + Interface.DefaultValuesLoader [thisType.FullName] (this); + return; + } else if (!Interface.Styling.ContainsKey (thisType.FullName)) { + if (Interface.DefaultValuesLoader.ContainsKey (thisType.Name)) { + Interface.DefaultValuesLoader [thisType.Name] (this); + return; + } + } + } + + List