aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/GraphicObjects/Base
diff options
context:
space:
mode:
Diffstat (limited to 'src/GraphicObjects/Base')
-rw-r--r--src/GraphicObjects/Base/Border.cs100
-rw-r--r--src/GraphicObjects/Base/Button.cs124
-rw-r--r--src/GraphicObjects/Base/CheckBox.cs71
-rw-r--r--src/GraphicObjects/Base/ComboBox.cs55
-rw-r--r--src/GraphicObjects/Base/Container.cs109
-rw-r--r--src/GraphicObjects/Base/Expandable.cs141
-rw-r--r--src/GraphicObjects/Base/GenericStack.cs228
-rw-r--r--src/GraphicObjects/Base/GraphicObject.cs1555
-rw-r--r--src/GraphicObjects/Base/Group.cs408
-rw-r--r--src/GraphicObjects/Base/GroupBox.cs58
-rw-r--r--src/GraphicObjects/Base/HorizontalStack.cs48
-rw-r--r--src/GraphicObjects/Base/Image.cs181
-rw-r--r--src/GraphicObjects/Base/Label.cs825
-rw-r--r--src/GraphicObjects/Base/ListBox.cs47
-rw-r--r--src/GraphicObjects/Base/Menu.cs76
-rw-r--r--src/GraphicObjects/Base/MenuItem.cs174
-rw-r--r--src/GraphicObjects/Base/MessageBox.cs150
-rw-r--r--src/GraphicObjects/Base/NumericControl.cs125
-rw-r--r--src/GraphicObjects/Base/Popper.cs245
-rw-r--r--src/GraphicObjects/Base/PrivateContainer.cs207
-rw-r--r--src/GraphicObjects/Base/ProgressBar.cs67
-rw-r--r--src/GraphicObjects/Base/RadioButton.cs82
-rw-r--r--src/GraphicObjects/Base/ScrollBar.cs68
-rw-r--r--src/GraphicObjects/Base/Scroller.cs239
-rw-r--r--src/GraphicObjects/Base/Slider.cs219
-rw-r--r--src/GraphicObjects/Base/Spinner.cs55
-rw-r--r--src/GraphicObjects/Base/Splitter.cs218
-rw-r--r--src/GraphicObjects/Base/TabItem.cs210
-rw-r--r--src/GraphicObjects/Base/TabView.cs210
-rw-r--r--src/GraphicObjects/Base/TemplatedContainer.cs128
-rw-r--r--src/GraphicObjects/Base/TemplatedControl.cs210
-rw-r--r--src/GraphicObjects/Base/TemplatedGroup.cs489
-rw-r--r--src/GraphicObjects/Base/TextBox.cs228
-rw-r--r--src/GraphicObjects/Base/TextRun.cs315
-rw-r--r--src/GraphicObjects/Base/TreeView.cs120
-rw-r--r--src/GraphicObjects/Base/VerticalStack.cs49
-rw-r--r--src/GraphicObjects/Base/Window.cs399
-rw-r--r--src/GraphicObjects/Base/Wrapper.cs233
38 files changed, 8466 insertions, 0 deletions
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 <jp.bruyere@hotmail.com>
+//
+// 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 <jp.bruyere@hotmail.com>
+//
+// 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 <jp.bruyere@hotmail.com>
+//
+// 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 <jp.bruyere@hotmail.com>
+//
+// 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 <jp.bruyere@hotmail.com>
+//
+// 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 <jp.bruyere@hotmail.com>
+//
+// 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 <jp.bruyere@hotmail.com>
+//
+// 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 <jp.bruyere@hotmail.com>
+//
+// 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<ValueChangeEventArgs> 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
+
+ /// <summary>
+ /// Initialize this Graphic object instance by setting style and default values and loading template if required
+ /// </summary>
+ 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
+ /// <summary>
+ /// Current size and position computed during layouting pass
+ /// </summary>
+ public Rectangle Slot = new Rectangle ();
+ /// <summary>
+ /// keep last slot components for each layouting pass to track
+ /// changes and trigger update of other component accordingly
+ /// </summary>
+ public Rectangle LastSlots;
+ /// <summary>
+ /// 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
+ /// </summary>
+ public Rectangle LastPaintedSlot;
+ /// <summary>Prevent requeuing multiple times the same widget</summary>
+ public bool IsQueueForRedraw = false;
+ /// <summary>drawing Cache, if null, a redraw is done, cached or not</summary>
+ public byte[] bmp;
+ public bool IsDirty = true;
+ /// <summary>
+ /// 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)
+ /// </summary>
+ 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
+ /// <summary>
+ /// Parent in the graphic tree, used for rendering and layouting
+ /// </summary>
+ [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<MouseWheelEventArgs> MouseWheelChanged;
+ public event EventHandler<MouseButtonEventArgs> MouseUp;
+ public event EventHandler<MouseButtonEventArgs> MouseDown;
+ public event EventHandler<MouseButtonEventArgs> MouseClick;
+ public event EventHandler<MouseButtonEventArgs> MouseDoubleClick;
+ public event EventHandler<MouseMoveEventArgs> MouseMove;
+ public event EventHandler<MouseMoveEventArgs> MouseEnter;
+ public event EventHandler<MouseMoveEventArgs> MouseLeave;
+ public event EventHandler<KeyboardKeyEventArgs> KeyDown;
+ public event EventHandler<KeyboardKeyEventArgs> KeyUp;
+ public event EventHandler<KeyPressEventArgs> KeyPress;
+ public event EventHandler Focused;
+ public event EventHandler Unfocused;
+ public event EventHandler Enabled;
+ public event EventHandler Disabled;
+ public event EventHandler<LayoutingEventArgs> LayoutChanged;
+ public event EventHandler<DataSourceChangeEventArgs> DataSourceChanged;
+ public event EventHandler<DataSourceChangeEventArgs> ParentChanged;
+ public event EventHandler<DataSourceChangeEventArgs> LogicalParentChanged;
+ #endregion
+
+ #region public properties
+ /// <summary>Random value placeholder</summary>
+ [XmlAttributeAttribute]
+ public object Tag {
+ get { return tag; }
+ set {
+ if (tag == value)
+ return;
+ tag = value;
+ NotifyValueChanged ("Tag", tag);
+ }
+ }
+ /// <summary>
+ /// If enabled, resulting bitmap of graphic object is cached in an byte array
+ /// speeding up rendering of complex object. Default is enabled.
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public virtual bool CacheEnabled {
+ get { return cacheEnabled; }
+ set {
+ if (cacheEnabled == value)
+ return;
+ cacheEnabled = value;
+ NotifyValueChanged ("CacheEnabled", cacheEnabled);
+ }
+ }
+ /// <summary>
+ /// If true, rendering of GraphicObject is clipped inside client rectangle
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public virtual bool ClipToClientRect {
+ get { return clipToClientRect; }
+ set {
+ if (clipToClientRect == value)
+ return;
+ clipToClientRect = value;
+ NotifyValueChanged ("ClipToClientRect", clipToClientRect);
+ this.RegisterForRedraw ();
+ }
+ }
+ /// <summary>
+ /// Name is used in binding to reference other GraphicObjects inside the graphic tree
+ /// </summary>
+ [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);
+ }
+ }
+ /// <summary>
+ /// When set to True, the <see cref="T:Crow.GraphicObject"/>'s width and height will be set to Fit.
+ /// </summary>
+ [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);
+ }
+ }
+ /// <summary>
+ /// Used for binding on dimensions, this property will never hold fixed size, but instead only
+ /// Fit or Stretched
+ /// </summary>
+ [XmlIgnore]public virtual Measure WidthPolicy { get {
+ return Width.IsFit ? Measure.Fit : Measure.Stretched; } }
+ /// <summary>
+ /// Used for binding on dimensions, this property will never hold fixed size, but instead only
+ /// Fit or Stretched
+ /// </summary>
+ [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);
+ }
+ }
+ /// <summary>
+ /// 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
+ /// </summary>
+ [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
+ /// <summary> Loads the default values from XML attributes default </summary>
+ 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<Style> styling = new List<Style>();
+
+ //Search for a style matching :
+ //1: Full class name, with full namespace
+ //2: class name
+ //3: style may have been registered with their ressource ID minus .style extention
+ // those files being placed in a Styles folder
+ string styleKey = Style;
+ if (!string.IsNullOrEmpty (Style)) {
+ if (Interface.Styling.ContainsKey (Style)) {
+ styling.Add (Interface.Styling [Style]);
+ }
+ }
+ if (Interface.Styling.ContainsKey (thisType.FullName)) {
+ styling.Add (Interface.Styling [thisType.FullName]);
+ if (string.IsNullOrEmpty (styleKey))
+ styleKey = thisType.FullName;
+ }
+ if (Interface.Styling.ContainsKey (thisType.Name)) {
+ styling.Add (Interface.Styling [thisType.Name]);
+ if (string.IsNullOrEmpty (styleKey))
+ styleKey = thisType.Name;
+ }
+
+ if (string.IsNullOrEmpty (styleKey))
+ styleKey = thisType.FullName;
+
+
+ //Reflexion being very slow compared to dyn method or delegates,
+ //I compile the initial values coded in the CustomAttribs of the class,
+ //all other instance of this type would not longer use reflexion to init properly
+ //but will fetch the dynamic initialisation method compiled for this precise type
+ //TODO:measure speed gain.
+ #region Delfault values Loading dynamic compilation
+ DynamicMethod dm = null;
+ ILGenerator il = null;
+
+ dm = new DynamicMethod("dyn_loadDefValues",
+ MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot,
+ CallingConventions.Standard,
+ typeof(void),new Type[] {CompilerServices.TObject},thisType,true);
+
+ il = dm.GetILGenerator(256);
+ il.DeclareLocal(CompilerServices.TObject);
+ il.Emit(OpCodes.Nop);
+ //set local GraphicObject to root object passed as 1st argument
+ il.Emit (OpCodes.Ldarg_0);
+ il.Emit (OpCodes.Stloc_0);
+
+ foreach (EventInfo ei in thisType.GetEvents(BindingFlags.Public | BindingFlags.Instance)) {
+ string expression;
+ if (!getDefaultEvent(ei, styling, out expression))
+ continue;
+ //TODO:dynEventHandler could be cached somewhere, maybe a style instanciator class holding the styling delegate and bound to it.
+ foreach (string exp in CompilerServices.splitOnSemiColumnOutsideAccolades(expression)) {
+ string trimed = exp.Trim();
+ if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase)){
+ il.Emit (OpCodes.Ldloc_0);//load this as 1st arg of event Add
+
+ //push eventInfo as 1st arg of compile
+ il.Emit (OpCodes.Ldloc_0);
+ il.Emit (OpCodes.Call, CompilerServices.miGetType);
+ il.Emit (OpCodes.Ldstr, ei.Name);//push event name
+ il.Emit (OpCodes.Call, CompilerServices.miGetEvent);
+ //push expression as 2nd arg of compile
+ il.Emit (OpCodes.Ldstr, trimed.Substring (1, trimed.Length - 2));
+ //push null as 3rd arg, currentNode, not known when instanciing
+ il.Emit (OpCodes.Ldnull);
+ il.Emit (OpCodes.Callvirt, CompilerServices.miCompileDynEventHandler);
+ il.Emit (OpCodes.Castclass, ei.EventHandlerType);
+ il.Emit (OpCodes.Callvirt, ei.AddMethod);
+ }else
+ Debug.WriteLine("error in styling, event not handled : " + trimed);
+ }
+ }
+
+ foreach (PropertyInfo pi in thisType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
+ if (pi.GetSetMethod () == null)
+ continue;
+ object defaultValue;
+ if (!getDefaultValue (pi, styling, out defaultValue))
+ continue;
+
+ CompilerServices.EmitSetValue (il, pi, defaultValue);
+ }
+ il.Emit(OpCodes.Ret);
+ #endregion
+
+ try {
+ Interface.DefaultValuesLoader[styleKey] = (Interface.LoaderInvoker)dm.CreateDelegate(typeof(Interface.LoaderInvoker));
+ Interface.DefaultValuesLoader[styleKey] (this);
+ } catch (Exception ex) {
+ throw new Exception ("Error applying style <" + styleKey + ">:", ex);
+ }
+ }
+ bool getDefaultEvent(EventInfo ei, List<Style> styling,
+ out string expression){
+ expression = "";
+ if (styling.Count > 0){
+ for (int i = 0; i < styling.Count; i++) {
+ if (styling[i].ContainsKey (ei.Name)){
+ expression = (string)styling[i] [ei.Name];
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ bool getDefaultValue(PropertyInfo pi, List<Style> styling,
+ out object defaultValue){
+ defaultValue = null;
+ string name = "";
+
+ XmlIgnoreAttribute xia = (XmlIgnoreAttribute)pi.GetCustomAttribute (typeof(XmlIgnoreAttribute));
+ if (xia != null)
+ return false;
+ XmlAttributeAttribute xaa = (XmlAttributeAttribute)pi.GetCustomAttribute (typeof(XmlAttributeAttribute));
+ if (xaa != null) {
+ if (string.IsNullOrEmpty (xaa.AttributeName))
+ name = pi.Name;
+ else
+ name = xaa.AttributeName;
+ }
+
+ int styleIndex = -1;
+ if (styling.Count > 0){
+ for (int i = 0; i < styling.Count; i++) {
+ if (styling[i].ContainsKey (name)){
+ styleIndex = i;
+ break;
+ }
+ }
+ }
+ if (styleIndex >= 0){
+ if (pi.PropertyType.IsEnum)//maybe should be in parser..
+ defaultValue = Enum.Parse(pi.PropertyType, (string)styling[styleIndex] [name], true);
+ else
+ defaultValue = styling[styleIndex] [name];
+ }else {
+ DefaultValueAttribute dv = (DefaultValueAttribute)pi.GetCustomAttribute (typeof (DefaultValueAttribute));
+ if (dv == null)
+ return false;
+ defaultValue = dv.Value;
+ }
+ return true;
+ }
+ #endregion
+
+ public virtual GraphicObject FindByName(string nameToFind){
+ return string.Equals(nameToFind, name, StringComparison.Ordinal) ? this : null;
+ }
+ public virtual bool Contains(GraphicObject goToFind){
+ return false;
+ }
+
+ #region Queuing
+ /// <summary>
+ /// Register old and new slot for clipping
+ /// </summary>
+ public virtual void ClippingRegistration(){
+ IsQueueForRedraw = false;
+ if (Parent == null)
+ return;
+ Parent.RegisterClip (LastPaintedSlot);
+ Parent.RegisterClip (Slot);
+ }
+ /// <summary>
+ /// Add clip rectangle to this.clipping and propagate up to root
+ /// </summary>
+ /// <param name="clip">Clip rectangle</param>
+ public virtual void RegisterClip(Rectangle clip){
+ if (CacheEnabled && !IsDirty)
+ Clipping.AddRectangle (clip + ClientRectangle.Position);
+ if (Parent == null)
+ return;
+ GraphicObject p = Parent as GraphicObject;
+ if (p?.IsDirty == true && p?.CacheEnabled == true)
+ return;
+ Parent.RegisterClip (clip + Slot.Position + ClientRectangle.Position);
+ }
+ /// <summary> Full update, taking care of sizing policy </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void RegisterForGraphicUpdate ()
+ {
+ IsDirty = true;
+ if (Width.IsFit || Height.IsFit)
+ RegisterForLayouting (LayoutingType.Sizing);
+ else if (RegisteredLayoutings == LayoutingType.None)
+ CurrentInterface.EnqueueForRepaint (this);
+ }
+ /// <summary> query an update of the content, a redraw </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void RegisterForRedraw ()
+ {
+ IsDirty = true;
+ if (RegisteredLayoutings == LayoutingType.None)
+ CurrentInterface.EnqueueForRepaint (this);
+ }
+ #endregion
+
+ #region Layouting
+
+ /// <summary> return size of content + margins </summary>
+ protected virtual int measureRawSize (LayoutingType lt) {
+ return lt == LayoutingType.Width ?
+ contentSize.Width + 2 * Margin: contentSize.Height + 2 * Margin;
+ }
+ /// <summary> By default in groups, LayoutingType.ArrangeChildren is reset </summary>
+ public virtual void ChildrenLayoutingConstraints(ref LayoutingType layoutType){
+ }
+ public virtual bool ArrangeChildren { get { return false; } }
+ public virtual void RegisterForLayouting(LayoutingType layoutType){
+ if (Parent == null)
+ return;
+ lock (CurrentInterface.LayoutMutex) {
+ //prevent queueing same LayoutingType for this
+ layoutType &= (~RegisteredLayoutings);
+
+ if (layoutType == LayoutingType.None)
+ return;
+ //dont set position for stretched item
+ if (Width == Measure.Stretched)
+ layoutType &= (~LayoutingType.X);
+ if (Height == Measure.Stretched)
+ layoutType &= (~LayoutingType.Y);
+
+ if (!ArrangeChildren)
+ layoutType &= (~LayoutingType.ArrangeChildren);
+
+ //apply constraints depending on parent type
+ if (Parent is GraphicObject)
+ (Parent as GraphicObject).ChildrenLayoutingConstraints (ref layoutType);
+
+// //prevent queueing same LayoutingType for this
+// layoutType &= (~RegisteredLayoutings);
+
+ if (layoutType == LayoutingType.None)
+ return;
+
+ //enqueue LQI LayoutingTypes separately
+ if (layoutType.HasFlag (LayoutingType.Width))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.Width, this));
+ if (layoutType.HasFlag (LayoutingType.Height))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.Height, this));
+ if (layoutType.HasFlag (LayoutingType.X))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.X, this));
+ if (layoutType.HasFlag (LayoutingType.Y))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.Y, this));
+ if (layoutType.HasFlag (LayoutingType.ArrangeChildren))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.ArrangeChildren, this));
+ }
+ }
+
+ /// <summary> trigger dependant sizing component update </summary>
+ public virtual void OnLayoutChanges(LayoutingType layoutType)
+ {
+ #if DEBUG_LAYOUTING
+ CurrentInterface.currentLQI.Slot = LastSlots;
+ CurrentInterface.currentLQI.NewSlot = Slot;
+ Debug.WriteLine ("\t\t{0} => {1}",LastSlots,Slot);
+ #endif
+
+ switch (layoutType) {
+ case LayoutingType.Width:
+ RegisterForLayouting (LayoutingType.X);
+ break;
+ case LayoutingType.Height:
+ RegisterForLayouting (LayoutingType.Y);
+ break;
+ }
+ LayoutChanged.Raise (this, new LayoutingEventArgs (layoutType));
+ }
+ internal protected void raiseLayoutChanged(LayoutingEventArgs e){
+ LayoutChanged.Raise (this, e);
+ }
+ /// <summary> Update layout component only one at a time, this is where the computation of alignement
+ /// and size take place.
+ /// The redrawing will only be triggered if final slot size has changed </summary>
+ /// <returns><c>true</c>, if layouting was possible, <c>false</c> if conditions were not
+ /// met and LQI has to be re-queued</returns>
+ public virtual bool UpdateLayout (LayoutingType layoutType)
+ {
+ //unset bit, it would be reset if LQI is re-queued
+ registeredLayoutings &= (~layoutType);
+
+ switch (layoutType) {
+ case LayoutingType.X:
+ if (Left == 0) {
+
+ if (Parent.RegisteredLayoutings.HasFlag (LayoutingType.Width) ||
+ RegisteredLayoutings.HasFlag (LayoutingType.Width))
+ return false;
+
+ switch (HorizontalAlignment) {
+ case HorizontalAlignment.Left:
+ Slot.X = 0;
+ break;
+ case HorizontalAlignment.Right:
+ Slot.X = Parent.ClientRectangle.Width - Slot.Width;
+ break;
+ case HorizontalAlignment.Center:
+ Slot.X = Parent.ClientRectangle.Width / 2 - Slot.Width / 2;
+ break;
+ }
+ } else
+ Slot.X = Left;
+
+ if (LastSlots.X == Slot.X)
+ break;
+
+ IsDirty = true;
+
+ OnLayoutChanges (layoutType);
+
+ LastSlots.X = Slot.X;
+ break;
+ case LayoutingType.Y:
+ if (Top == 0) {
+
+ if (Parent.RegisteredLayoutings.HasFlag (LayoutingType.Height) ||
+ RegisteredLayoutings.HasFlag (LayoutingType.Height))
+ return false;
+
+ switch (VerticalAlignment) {
+ case VerticalAlignment.Top://this could be processed even if parent Height is not known
+ Slot.Y = 0;
+ break;
+ case VerticalAlignment.Bottom:
+ Slot.Y = Parent.ClientRectangle.Height - Slot.Height;
+ break;
+ case VerticalAlignment.Center:
+ Slot.Y = Parent.ClientRectangle.Height / 2 - Slot.Height / 2;
+ break;
+ }
+ } else
+ Slot.Y = Top;
+
+ if (LastSlots.Y == Slot.Y)
+ break;
+
+ IsDirty = true;
+
+ OnLayoutChanges (layoutType);
+
+ LastSlots.Y = Slot.Y;
+ break;
+ case LayoutingType.Width:
+ if (Visible) {
+ if (Width.IsFixed)
+ Slot.Width = Width;
+ else if (Width == Measure.Fit) {
+ Slot.Width = measureRawSize (LayoutingType.Width);
+ } else if (Parent.RegisteredLayoutings.HasFlag (LayoutingType.Width))
+ return false;
+ else if (Width == Measure.Stretched)
+ Slot.Width = Parent.ClientRectangle.Width;
+ else
+ Slot.Width = (int)Math.Round ((double)(Parent.ClientRectangle.Width * Width) / 100.0);
+
+ if (Slot.Width < 0)
+ return false;
+
+ //size constrain
+ if (Slot.Width < MinimumSize.Width) {
+ Slot.Width = MinimumSize.Width;
+ //NotifyValueChanged ("WidthPolicy", Measure.Stretched);
+ } else if (Slot.Width > MaximumSize.Width && MaximumSize.Width > 0) {
+ Slot.Width = MaximumSize.Width;
+ //NotifyValueChanged ("WidthPolicy", Measure.Stretched);
+ }
+ } else
+ Slot.Width = 0;
+
+ if (LastSlots.Width == Slot.Width)
+ break;
+
+ IsDirty = true;
+
+ OnLayoutChanges (layoutType);
+
+ LastSlots.Width = Slot.Width;
+ break;
+ case LayoutingType.Height:
+ if (Visible) {
+ if (Height.IsFixed)
+ Slot.Height = Height;
+ else if (Height == Measure.Fit) {
+ Slot.Height = measureRawSize (LayoutingType.Height);
+ } else if (Parent.RegisteredLayoutings.HasFlag (LayoutingType.Height))
+ return false;
+ else if (Height == Measure.Stretched)
+ Slot.Height = Parent.ClientRectangle.Height;
+ else
+ Slot.Height = (int)Math.Round ((double)(Parent.ClientRectangle.Height * Height) / 100.0);
+
+ if (Slot.Height < 0)
+ return false;
+
+ //size constrain
+ if (Slot.Height < MinimumSize.Height) {
+ Slot.Height = MinimumSize.Height;
+ //NotifyValueChanged ("HeightPolicy", Measure.Stretched);
+ } else if (Slot.Height > MaximumSize.Height && MaximumSize.Height > 0) {
+ Slot.Height = MaximumSize.Height;
+ //NotifyValueChanged ("HeightPolicy", Measure.Stretched);
+ }
+ } else
+ Slot.Height = 0;
+
+ if (LastSlots.Height == Slot.Height)
+ break;
+
+ IsDirty = true;
+
+ OnLayoutChanges (layoutType);
+
+ LastSlots.Height = Slot.Height;
+ break;
+ }
+
+ //if no layouting remains in queue for item, registre for redraw
+ if (this.registeredLayoutings == LayoutingType.None && IsDirty)
+ CurrentInterface.EnqueueForRepaint (this);
+
+ return true;
+ }
+ #endregion
+
+ #region Rendering
+ /// <summary> This is the common overridable drawing routine to create new widget </summary>
+ protected virtual void onDraw(Context gr)
+ {
+ Rectangle rBack = new Rectangle (Slot.Size);
+
+ Background.SetAsSource (gr, rBack);
+ CairoHelpers.CairoRectangle(gr,rBack,cornerRadius);
+ gr.Fill ();
+ }
+
+ /// <summary>
+ /// Internal drawing context creation on a cached surface limited to slot size
+ /// this trigger the effective drawing routine </summary>
+ protected virtual void RecreateCache ()
+ {
+ int stride = 4 * Slot.Width;
+
+ int bmpSize = Math.Abs (stride) * Slot.Height;
+ bmp = new byte[bmpSize];
+ IsDirty = false;
+ using (ImageSurface draw =
+ new ImageSurface(bmp, Format.Argb32, Slot.Width, Slot.Height, stride)) {
+ using (Context gr = new Context (draw)) {
+ gr.Antialias = Interface.Antialias;
+ onDraw (gr);
+ }
+ draw.Flush ();
+ }
+ }
+ protected virtual void UpdateCache(Context ctx){
+ Rectangle rb = Slot + Parent.ClientRectangle.Position;
+ using (ImageSurface cache = new ImageSurface (bmp, Format.Argb32, Slot.Width, Slot.Height, 4 * Slot.Width)) {
+ if (clearBackground) {
+ ctx.Save ();
+ ctx.Operator = Operator.Clear;
+ ctx.Rectangle (rb);
+ ctx.Fill ();
+ ctx.Restore ();
+ }
+ ctx.SetSourceSurface (cache, rb.X, rb.Y);
+ ctx.Paint ();
+ }
+ //Clipping.clearAndClip (ctx);
+ Clipping.Reset();
+ }
+ /// <summary> Chained painting routine on the parent context of the actual cached version
+ /// of the widget </summary>
+ public virtual void Paint (ref Context ctx)
+ {
+ //TODO:this test should not be necessary
+ if (Slot.Height < 0 || Slot.Width < 0 || parent == null)
+ return;
+ lock (this) {
+ if (cacheEnabled) {
+ if (Slot.Width > Interface.MaxCacheSize || Slot.Height > Interface.MaxCacheSize)
+ cacheEnabled = false;
+ }
+
+ if (cacheEnabled) {
+ if (IsDirty)
+ RecreateCache ();
+
+ UpdateCache (ctx);
+ if (!isEnabled)
+ paintDisabled (ctx, Slot + Parent.ClientRectangle.Position);
+ } else {
+ Rectangle rb = Slot + Parent.ClientRectangle.Position;
+ ctx.Save ();
+
+ ctx.Translate (rb.X, rb.Y);
+
+ onDraw (ctx);
+ if (!isEnabled)
+ paintDisabled (ctx, Slot);
+
+ ctx.Restore ();
+ }
+ LastPaintedSlot = Slot;
+ }
+ }
+ void paintDisabled(Context gr, Rectangle rb){
+ gr.Operator = Operator.Xor;
+ gr.SetSourceRGBA (0.6, 0.6, 0.6, 0.3);
+ gr.Rectangle (rb);
+ gr.Fill ();
+ gr.Operator = Operator.Over;
+ }
+ #endregion
+
+ #region Keyboard handling
+ public virtual void onKeyDown(object sender, KeyboardKeyEventArgs e){
+ KeyDown.Raise (sender, e);
+ }
+ public virtual void onKeyUp(object sender, KeyboardKeyEventArgs e){
+ KeyUp.Raise (sender, e);
+ }
+ public virtual void onKeyPress(object sender, KeyPressEventArgs e){
+ KeyPress.Raise (sender, e);
+ }
+ #endregion
+
+ #region Mouse handling
+ public virtual bool MouseIsIn(Point m)
+ {
+ try {
+ if (!(Visible & isEnabled))
+ return false;
+ if (ScreenCoordinates (Slot).ContainsOrIsEqual (m)) {
+ Scroller scr = Parent as Scroller;
+ if (scr == null) {
+ if (Parent is GraphicObject)
+ return (Parent as GraphicObject).MouseIsIn (m);
+ else return true;
+ }
+ return scr.MouseIsIn (scr.savedMousePos);
+ }
+ } catch (Exception ex) {
+ return false;
+ }
+ return false;
+ }
+ public virtual void checkHoverWidget(MouseMoveEventArgs e)
+ {
+ if (CurrentInterface.HoverWidget != this) {
+ CurrentInterface.HoverWidget = this;
+ onMouseEnter (this, e);
+ }
+
+ this.onMouseMove (this, e);//without this, window border doesn't work, should be removed
+ }
+ public virtual void onMouseMove(object sender, MouseMoveEventArgs e)
+ {
+ //bubble event to the top
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseMove(sender,e);
+
+ MouseMove.Raise (this, e);
+ }
+ public virtual void onMouseDown(object sender, MouseButtonEventArgs e){
+ if (CurrentInterface.eligibleForDoubleClick == this && CurrentInterface.clickTimer.ElapsedMilliseconds < Interface.DoubleClick)
+ onMouseDoubleClick (this, e);
+ else
+ currentInterface.clickTimer.Restart();
+ CurrentInterface.eligibleForDoubleClick = null;
+
+ if (CurrentInterface.ActiveWidget == null)
+ CurrentInterface.ActiveWidget = this;
+ if (this.Focusable && !Interface.FocusOnHover) {
+ BubblingMouseButtonEventArg be = e as BubblingMouseButtonEventArg;
+ if (be.Focused == null) {
+ be.Focused = this;
+ CurrentInterface.FocusedWidget = this;
+ }
+ }
+ //bubble event to the top
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseDown(sender,e);
+
+ MouseDown.Raise (this, e);
+ }
+ public virtual void onMouseUp(object sender, MouseButtonEventArgs e){
+ //bubble event to the top
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseUp(sender,e);
+
+ MouseUp.Raise (this, e);
+
+ if (MouseIsIn (e.Position) && IsActive) {
+ if (CurrentInterface.clickTimer.ElapsedMilliseconds < Interface.DoubleClick)
+ CurrentInterface.eligibleForDoubleClick = this;
+ onMouseClick (this, e);
+ }
+ }
+ public virtual void onMouseClick(object sender, MouseButtonEventArgs e){
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseClick(sender,e);
+ MouseClick.Raise (this, e);
+ }
+ public virtual void onMouseDoubleClick(object sender, MouseButtonEventArgs e){
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseDoubleClick(sender,e);
+ MouseDoubleClick.Raise (this, e);
+ }
+ public virtual void onMouseWheel(object sender, MouseWheelEventArgs e){
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseWheel(sender,e);
+
+ MouseWheelChanged.Raise (this, e);
+ }
+ public virtual void onMouseEnter(object sender, MouseMoveEventArgs e)
+ {
+ #if DEBUG_FOCUS
+ Debug.WriteLine("MouseEnter => " + this.ToString());
+ #endif
+ MouseEnter.Raise (this, e);
+ }
+ public virtual void onMouseLeave(object sender, MouseMoveEventArgs e)
+ {
+ #if DEBUG_FOCUS
+ Debug.WriteLine("MouseLeave => " + this.ToString());
+ #endif
+ MouseLeave.Raise (this, e);
+ }
+ #endregion
+
+ protected virtual void onFocused(object sender, EventArgs e){
+ #if DEBUG_FOCUS
+ Debug.WriteLine("Focused => " + this.ToString());
+ #endif
+ Focused.Raise (this, e);
+ }
+ protected virtual void onUnfocused(object sender, EventArgs e){
+ #if DEBUG_FOCUS
+ Debug.WriteLine("UnFocused => " + this.ToString());
+ #endif
+ Unfocused.Raise (this, e);
+ }
+ public virtual void onEnable(object sender, EventArgs e){
+ Enabled.Raise (this, e);
+ }
+ public virtual void onDisable(object sender, EventArgs e){
+ Disabled.Raise (this, e);
+ }
+ protected virtual void onParentChanged(object sender, DataSourceChangeEventArgs e) {
+ ParentChanged.Raise (this, e);
+ if (logicalParent == null)
+ LogicalParentChanged.Raise (this, e);
+ }
+ protected virtual void onLogicalParentChanged(object sender, DataSourceChangeEventArgs e) {
+ LogicalParentChanged.Raise (this, e);
+ }
+ #region IXmlSerializable
+ public virtual System.Xml.Schema.XmlSchema GetSchema ()
+ {
+ return null;
+ }
+// void affectMember(string name, string value){
+// Type thisType = this.GetType ();
+//
+// if (string.IsNullOrEmpty (value))
+// return;
+//
+// MemberInfo mi = thisType.GetMember (name).FirstOrDefault();
+// if (mi == null) {
+// Debug.WriteLine ("XML: Unknown attribute in " + thisType.ToString() + " : " + name);
+// return;
+// }
+// if (mi.MemberType == MemberTypes.Event) {
+// this.Bindings.Add (new Binding (new MemberReference(this, mi), value));
+// return;
+// }
+// if (mi.MemberType == MemberTypes.Property) {
+// PropertyInfo pi = mi as PropertyInfo;
+//
+// if (pi.GetSetMethod () == null) {
+// Debug.WriteLine ("XML: Read only property in " + thisType.ToString() + " : " + name);
+// return;
+// }
+//
+// XmlAttributeAttribute xaa = (XmlAttributeAttribute)pi.GetCustomAttribute (typeof(XmlAttributeAttribute));
+// if (xaa != null) {
+// if (!string.IsNullOrEmpty (xaa.AttributeName))
+// name = xaa.AttributeName;
+// }
+// if (value.StartsWith("{",StringComparison.Ordinal)) {
+// //binding
+// if (!value.EndsWith("}", StringComparison.Ordinal))
+// throw new Exception (string.Format("XML:Malformed binding: {0}", value));
+//
+// this.Bindings.Add (new Binding (new MemberReference(this, pi), value.Substring (1, value.Length - 2)));
+// return;
+// }
+// if (pi.GetCustomAttribute (typeof(XmlIgnoreAttribute)) != null)
+// return;
+// if (xaa == null)//not define as xmlAttribute
+// return;
+//
+// if (pi.PropertyType == typeof(string)) {
+// pi.SetValue (this, value, null);
+// return;
+// }
+//
+// if (pi.PropertyType.IsEnum) {
+// pi.SetValue (this, Enum.Parse (pi.PropertyType, value), null);
+// } else {
+// MethodInfo me = pi.PropertyType.GetMethod ("Parse", new Type[] { typeof(string) });
+// pi.SetValue (this, me.Invoke (null, new string[] { value }), null);
+// }
+// }
+// }
+ public virtual void ReadXml (System.Xml.XmlReader reader)
+ {
+ if (reader.HasAttributes) {
+
+ style = reader.GetAttribute ("Style");
+
+ loadDefaultValues ();
+
+ while (reader.MoveToNextAttribute ()) {
+ if (reader.Name == "Style")
+ continue;
+
+ //affectMember (reader.Name, reader.Value);
+ }
+ reader.MoveToElement ();
+ }else
+ loadDefaultValues ();
+ }
+ public virtual void WriteXml (System.Xml.XmlWriter writer)
+ {
+ foreach (PropertyInfo pi in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
+ if (pi.GetSetMethod () == null)
+ continue;
+
+ bool isAttribute = false;
+ bool hasDefaultValue = false;
+ bool ignore = false;
+ string name = "";
+ object value = null;
+ Type valueType = null;
+
+
+ MemberInfo mi = pi.GetGetMethod ();
+
+ if (mi == null)
+ continue;
+
+ value = pi.GetValue (this, null);
+ valueType = pi.PropertyType;
+ name = pi.Name;
+
+
+
+ object[] att = pi.GetCustomAttributes (false);
+
+ foreach (object o in att) {
+ XmlAttributeAttribute xaa = o as XmlAttributeAttribute;
+ if (xaa != null) {
+ isAttribute = true;
+ if (string.IsNullOrEmpty (xaa.AttributeName))
+ name = pi.Name;
+ else
+ name = xaa.AttributeName;
+ continue;
+ }
+
+ XmlIgnoreAttribute xia = o as XmlIgnoreAttribute;
+ if (xia != null) {
+ ignore = true;
+ continue;
+ }
+
+ DefaultValueAttribute dv = o as DefaultValueAttribute;
+ if (dv != null) {
+ if (dv.Value.Equals (value))
+ hasDefaultValue = true;
+ if (dv.Value.ToString () == value.ToString ())
+ hasDefaultValue = true;
+
+ continue;
+ }
+
+
+ }
+
+ if (hasDefaultValue || ignore || value==null)
+ continue;
+
+ if (isAttribute)
+ writer.WriteAttributeString (name, value.ToString ());
+ else {
+ if (valueType.GetInterface ("IXmlSerializable") == null)
+ continue;
+
+ (pi.GetValue (this, null) as IXmlSerializable).WriteXml (writer);
+ }
+ }
+ foreach (EventInfo ei in this.GetType().GetEvents()) {
+ FieldInfo fi = this.GetType().GetField(ei.Name,
+ BindingFlags.NonPublic |
+ BindingFlags.Instance |
+ BindingFlags.GetField);
+
+ Delegate dg = (System.Delegate)fi.GetValue (this);
+
+ if (dg == null)
+ continue;
+
+ foreach (Delegate d in dg.GetInvocationList()) {
+ if (!d.Method.Name.StartsWith ("<"))//Skipping empty handler, not clear it's trikky
+ writer.WriteAttributeString (ei.Name, d.Method.Name);
+ }
+ }
+ }
+ #endregion
+
+ #region ICloneable implementation
+ public object Clone ()
+ {
+ Type type = this.GetType ();
+ GraphicObject result = (GraphicObject)Activator.CreateInstance (type);
+ result.CurrentInterface = CurrentInterface;
+
+ foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
+ if (pi.GetSetMethod () == null)
+ continue;
+
+ if (pi.GetCustomAttribute<XmlIgnoreAttribute> () != null)
+ continue;
+ if (pi.Name == "DataSource")
+ continue;
+
+ pi.SetValue(result, pi.GetValue(this));
+ }
+ return result;
+ }
+ #endregion
+ /// <summary>
+ /// full GraphicTree clone with binding definition
+ /// </summary>
+
+ public override string ToString ()
+ {
+ string tmp ="";
+
+ if (Parent != null)
+ tmp = Parent.ToString () + tmp;
+ #if DEBUG_LAYOUTING
+ return Name == "unamed" ? tmp + "." + this.GetType ().Name + uid.ToString(): tmp + "." + Name;
+ #else
+ return Name == "unamed" ? tmp + "." + this.GetType ().Name : tmp + "." + Name;
+ #endif
+ }
+ }
+}
diff --git a/src/GraphicObjects/Base/Group.cs b/src/GraphicObjects/Base/Group.cs
new file mode 100644
index 00000000..d00449cb
--- /dev/null
+++ b/src/GraphicObjects/Base/Group.cs
@@ -0,0 +1,408 @@
+//
+// Group.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Cairo;
+using OpenTK.Input;
+using System.Diagnostics;
+using System.Reflection;
+
+
+namespace Crow
+{
+ public class Group : GraphicObject, IXmlSerializable
+ {
+ #region CTOR
+ public Group()
+ : base(){}
+ #endregion
+
+ #region EVENT HANDLERS
+ public event EventHandler<EventArgs> ChildrenCleared;
+ #endregion
+
+ internal GraphicObject largestChild = null;
+ internal GraphicObject tallestChild = null;
+
+ bool _multiSelect = false;
+ List<GraphicObject> children = new List<GraphicObject>();
+
+ public virtual List<GraphicObject> Children {
+ get { return children; }
+ }
+ [XmlAttributeAttribute()][DefaultValue(false)]
+ public bool MultiSelect
+ {
+ get { return _multiSelect; }
+ set { _multiSelect = value; }
+ }
+ public virtual void AddChild(GraphicObject g){
+ lock (children) {
+ g.Parent = this;
+ Children.Add (g);
+ }
+ g.RegisteredLayoutings = LayoutingType.None;
+ g.RegisterForLayouting (LayoutingType.Sizing | LayoutingType.ArrangeChildren);
+ g.LayoutChanged += OnChildLayoutChanges;
+ }
+ public virtual void RemoveChild(GraphicObject child)
+ {
+ child.LayoutChanged -= OnChildLayoutChanges;
+ //child.Parent = null;
+ lock (children)
+ Children.Remove(child);
+
+ if (child == largestChild && Width == Measure.Fit)
+ searchLargestChild ();
+ if (child == tallestChild && Height == Measure.Fit)
+ searchTallestChild ();
+
+ this.RegisterForLayouting (LayoutingType.Sizing | LayoutingType.ArrangeChildren);
+ }
+ public virtual void ClearChildren()
+ {
+ lock (children) {
+ while (Children.Count > 0) {
+ GraphicObject g = Children [Children.Count - 1];
+ g.LayoutChanged -= OnChildLayoutChanges;
+ g.Parent = null;
+ Children.RemoveAt (Children.Count - 1);
+ }
+ }
+
+ resetChildrenMaxSize ();
+
+ this.RegisterForLayouting (LayoutingType.Sizing);
+ ChildrenCleared.Raise (this, new EventArgs ());
+ }
+
+ public void putWidgetOnTop(GraphicObject w)
+ {
+ if (Children.Contains(w))
+ {
+ lock (children) {
+ Children.Remove (w);
+ Children.Add (w);
+ }
+ }
+ }
+ public void putWidgetOnBottom(GraphicObject w)
+ {
+ if (Children.Contains(w))
+ {
+ lock (children) {
+ Children.Remove (w);
+ Children.Insert (0, w);
+ }
+ }
+ }
+
+ #region GraphicObject overrides
+ public override void OnDataSourceChanged (object sender, DataSourceChangeEventArgs e)
+ {
+ base.OnDataSourceChanged (this, e);
+ lock (children) {
+ foreach (GraphicObject g in children)
+ if (g.localDataSourceIsNull & g.localLogicalParentIsNull)
+ g.OnDataSourceChanged (sender, e);
+ }
+ }
+ public override GraphicObject FindByName (string nameToFind)
+ {
+ if (Name == nameToFind)
+ return this;
+ GraphicObject tmp = null;
+ lock (children) {
+ foreach (GraphicObject w in Children) {
+ tmp = w.FindByName (nameToFind);
+ if (tmp != null)
+ break;
+ }
+ }
+ return tmp;
+ }
+ public override bool Contains (GraphicObject goToFind)
+ {
+ foreach (GraphicObject w in Children) {
+ if (w == goToFind)
+ return true;
+ if (w.Contains (goToFind))
+ return true;
+ }
+ return false;
+ }
+ protected override int measureRawSize (LayoutingType lt)
+ {
+ if (Children.Count > 0) {
+ if (lt == LayoutingType.Width) {
+ if (largestChild == null)
+ searchLargestChild ();
+ if (largestChild == null) {
+ //if still null, not possible to determine a width
+ //because all children are stretched, force first one to fit
+ Children [0].Width = Measure.Fit;
+ return -1;//cancel actual sizing to let child computation take place
+ }
+ } else {
+ if (tallestChild == null)
+ searchTallestChild ();
+ if (tallestChild == null) {
+ Children [0].Height = Measure.Fit;
+ return -1;
+ }
+ }
+ }
+ return base.measureRawSize (lt);
+ }
+
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+
+ //position smaller objects in group when group size is fit
+ switch (layoutType) {
+ case LayoutingType.Width:
+ foreach (GraphicObject c in Children){
+ if (c.Width.Units == Unit.Percent)
+ c.RegisterForLayouting (LayoutingType.Width);
+ else
+ c.RegisterForLayouting (LayoutingType.X);
+ }
+ break;
+ case LayoutingType.Height:
+ foreach (GraphicObject c in Children) {
+ if (c.Height.Units == Unit.Percent)
+ c.RegisterForLayouting (LayoutingType.Height);
+ else
+ c.RegisterForLayouting (LayoutingType.Y);
+ }
+ break;
+ }
+ }
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ gr.Save ();
+
+ if (ClipToClientRect) {
+ //clip to client zone
+ CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius);
+ gr.Clip ();
+ }
+
+ lock (children) {
+ foreach (GraphicObject g in Children) {
+ g.Paint (ref gr);
+ }
+ }
+ gr.Restore ();
+ }
+ protected override void UpdateCache (Context ctx)
+ {
+ Rectangle rb = Slot + Parent.ClientRectangle.Position;
+
+ using (ImageSurface cache = new ImageSurface (bmp, Format.Argb32, Slot.Width, Slot.Height, 4 * Slot.Width)) {
+ Context gr = new Context (cache);
+
+ if (Clipping.count > 0) {
+ Clipping.clearAndClip (gr);
+ base.onDraw (gr);
+
+ //clip to client zone
+ CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius);
+ gr.Clip ();
+
+ lock (Children) {
+ foreach (GraphicObject c in Children) {
+ if (!c.Visible)
+ continue;
+ if (Clipping.intersect (c.Slot + ClientRectangle.Position))
+ c.Paint (ref gr);
+ }
+ }
+
+ #if DEBUG_CLIP_RECTANGLE
+ Clipping.stroke (gr, Color.Amaranth.AdjustAlpha (0.8));
+ #endif
+ }
+ gr.Dispose ();
+
+ ctx.SetSourceSurface (cache, rb.X, rb.Y);
+ ctx.Paint ();
+ }
+ Clipping.Reset();
+ }
+ #endregion
+
+ public virtual void OnChildLayoutChanges (object sender, LayoutingEventArgs arg)
+ {
+ GraphicObject g = sender as GraphicObject;
+
+ switch (arg.LayoutType) {
+ case LayoutingType.Width:
+ if (Width != Measure.Fit)
+ return;
+ if (g.Slot.Width > contentSize.Width) {
+ largestChild = g;
+ contentSize.Width = g.Slot.Width;
+ } else if (g == largestChild)
+ searchLargestChild ();
+
+ this.RegisterForLayouting (LayoutingType.Width);
+ break;
+ case LayoutingType.Height:
+ if (Height != Measure.Fit)
+ return;
+ if (g.Slot.Height > contentSize.Height) {
+ tallestChild = g;
+ contentSize.Height = g.Slot.Height;
+ } else if (g == tallestChild)
+ searchTallestChild ();
+
+ this.RegisterForLayouting (LayoutingType.Height);
+ break;
+ }
+ }
+ //TODO: x,y position should be taken in account for computation of width and height
+ void resetChildrenMaxSize(){
+ largestChild = null;
+ tallestChild = null;
+ contentSize = 0;
+ }
+ void searchLargestChild(){
+ #if DEBUG_LAYOUTING
+ Debug.WriteLine("\tSearch largest child");
+ #endif
+ largestChild = null;
+ contentSize.Width = 0;
+ for (int i = 0; i < Children.Count; i++) {
+ if (!Children [i].Visible)
+ continue;
+ if (children [i].RegisteredLayoutings.HasFlag (LayoutingType.Width))
+ continue;
+ if (Children [i].Slot.Width > contentSize.Width) {
+ contentSize.Width = Children [i].Slot.Width;
+ largestChild = Children [i];
+ }
+ }
+ }
+ void searchTallestChild(){
+ #if DEBUG_LAYOUTING
+ Debug.WriteLine("\tSearch tallest child");
+ #endif
+ tallestChild = null;
+ contentSize.Height = 0;
+ for (int i = 0; i < Children.Count; i++) {
+ if (!Children [i].Visible)
+ continue;
+ if (children [i].RegisteredLayoutings.HasFlag (LayoutingType.Height))
+ continue;
+ if (Children [i].Slot.Height > contentSize.Height) {
+ contentSize.Height = Children [i].Slot.Height;
+ tallestChild = Children [i];
+ }
+ }
+ }
+
+
+ #region Mouse handling
+ public override void checkHoverWidget (MouseMoveEventArgs e)
+ {
+ if (CurrentInterface.HoverWidget != this) {
+ CurrentInterface.HoverWidget = this;
+ onMouseEnter (this, e);
+ }
+ for (int i = Children.Count - 1; i >= 0; i--) {
+ if (Children[i].MouseIsIn(e.Position))
+ {
+ Children[i].checkHoverWidget (e);
+ return;
+ }
+ }
+ base.checkHoverWidget (e);
+ }
+ #endregion
+
+
+ #region IXmlSerializable
+
+ public override System.Xml.Schema.XmlSchema GetSchema()
+ {
+ return null;
+ }
+ public override void ReadXml(System.Xml.XmlReader reader)
+ {
+ base.ReadXml(reader);
+
+ using (System.Xml.XmlReader subTree = reader.ReadSubtree())
+ {
+ subTree.Read();
+
+ while (!subTree.EOF)
+ {
+ subTree.Read();
+
+ if (!subTree.IsStartElement())
+ break;
+
+ 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;
+ }
+ }
+ }
+ if (t == null)
+ throw new Exception (subTree.Name + " type not found");
+ GraphicObject go = (GraphicObject)Activator.CreateInstance(t);
+ (go as IXmlSerializable).ReadXml(subTree);
+ AddChild(go);
+ }
+ }
+ }
+ public override void WriteXml(System.Xml.XmlWriter writer)
+ {
+ base.WriteXml(writer);
+
+ foreach (GraphicObject go in Children)
+ {
+ writer.WriteStartElement(go.GetType().Name);
+ (go as IXmlSerializable).WriteXml(writer);
+ writer.WriteEndElement();
+ }
+ }
+
+ #endregion
+
+ }
+}
diff --git a/src/GraphicObjects/Base/GroupBox.cs b/src/GraphicObjects/Base/GroupBox.cs
new file mode 100644
index 00000000..9cabbb51
--- /dev/null
+++ b/src/GraphicObjects/Base/GroupBox.cs
@@ -0,0 +1,58 @@
+//
+// GroupBox.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+
+namespace Crow
+{
+ public class GroupBox : TemplatedContainer
+ {
+ Container _contentContainer;
+
+ #region CTOR
+ public GroupBox() : base(){}
+ #endregion
+
+ #region Template overrides
+ public override GraphicObject Content {
+ get {
+ return _contentContainer == null ? null : _contentContainer.Child;
+ }
+ set {
+ _contentContainer.SetChild(value);
+ }
+ }
+ protected override void loadTemplate(GraphicObject template = null)
+ {
+ base.loadTemplate (template);
+
+ _contentContainer = this.child.FindByName ("Content") as Container;
+ }
+ #endregion
+ }
+}
diff --git a/src/GraphicObjects/Base/HorizontalStack.cs b/src/GraphicObjects/Base/HorizontalStack.cs
new file mode 100644
index 00000000..620dab22
--- /dev/null
+++ b/src/GraphicObjects/Base/HorizontalStack.cs
@@ -0,0 +1,48 @@
+//
+// HorizontalStack.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 System.Xml.Serialization;
+
+namespace Crow
+{
+ public class HorizontalStack : GenericStack
+ {
+ public HorizontalStack()
+ : base()
+ {
+ }
+
+ [XmlIgnore]
+ public override Orientation Orientation
+ {
+ get { return Orientation.Horizontal; }
+ }
+ }
+}
diff --git a/src/GraphicObjects/Base/Image.cs b/src/GraphicObjects/Base/Image.cs
new file mode 100644
index 00000000..6a4c590d
--- /dev/null
+++ b/src/GraphicObjects/Base/Image.cs
@@ -0,0 +1,181 @@
+//
+// Image.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Cairo;
+using System.Xml.Serialization;
+using System.ComponentModel;
+using System.Diagnostics;
+
+
+namespace Crow
+{
+ public class Image : GraphicObject
+ {
+ Picture _pic;
+ string _svgSub;
+ bool scaled, keepProps;
+ double opacity;
+
+ #region Public properties
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public virtual bool Scaled {
+ get { return scaled; }
+ set {
+ if (scaled == value)
+ return;
+ scaled = value;
+ NotifyValueChanged ("Scaled", scaled);
+ if (_pic == null)
+ return;
+ _pic.Scaled = scaled;
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public virtual bool KeepProportions {
+ get { return keepProps; }
+ set {
+ if (keepProps == value)
+ return;
+ keepProps = value;
+ NotifyValueChanged ("KeepProportions", keepProps);
+ if (_pic == null)
+ return;
+ _pic.KeepProportions = keepProps;
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute]
+ public string Path {
+ get { return _pic == null ? "" : _pic.Path; }
+ set {
+ if (value == Path)
+ return;
+ try {
+ if (string.IsNullOrEmpty(value))
+ Picture = null;
+ else {
+ lock(CurrentInterface.LayoutMutex){
+ LoadImage (value);
+ }
+ }
+ } catch (Exception ex) {
+ Debug.WriteLine (ex.Message);
+ _pic = null;
+ }
+ NotifyValueChanged ("Path", Path);
+ }
+ }
+ [XmlAttributeAttribute]
+ public string SvgSub {
+ get { return _svgSub; }
+ set {
+ if (_svgSub == value)
+ return;
+ _svgSub = value;
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute]
+ public Picture Picture {
+ get { return _pic; }
+ set {
+ if (_pic == value)
+ return;
+ _pic = value;
+ NotifyValueChanged ("Picture", _pic);
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(1.0)]
+ public virtual double Opacity {
+ get { return opacity; }
+ set {
+ if (opacity == value)
+ return;
+ opacity = value;
+ NotifyValueChanged ("Faded", opacity);
+ RegisterForRedraw ();
+ }
+ }
+ #endregion
+
+ #region CTOR
+ public Image () : base()
+ {
+ }
+ #endregion
+
+ #region Image Loading
+ public void LoadImage (string path)
+ {
+ Picture pic;
+ if (path.EndsWith (".svg", true, System.Globalization.CultureInfo.InvariantCulture))
+ pic = new SvgPicture ();
+ else
+ pic = new BmpPicture ();
+
+ pic.LoadImage (path);
+ pic.Scaled = scaled;
+ pic.KeepProportions = keepProps;
+
+ Picture = pic;
+ }
+ #endregion
+
+ #region GraphicObject overrides
+ protected override int measureRawSize (LayoutingType lt)
+ {
+ if (_pic == null)
+ return 2 * Margin;
+ //_pic = "#Crow.Images.Icons.IconAlerte.svg";
+ //TODO:take scalling in account
+ if (lt == LayoutingType.Width)
+ return _pic.Dimensions.Width + 2 * Margin;
+ else
+ return _pic.Dimensions.Height + 2 * Margin;
+ }
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ if (_pic == null)
+ return;
+
+ _pic.Paint (gr, ClientRectangle, _svgSub);
+
+ if (Opacity<1.0) {
+ gr.SetSourceRGBA (0.0, 0.0, 0.0, 1.0-Opacity);
+ gr.Operator = Operator.DestOut;
+ gr.Rectangle (ClientRectangle);
+ gr.Fill ();
+ gr.Operator = Operator.Over;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/src/GraphicObjects/Base/Label.cs b/src/GraphicObjects/Base/Label.cs
new file mode 100644
index 00000000..64ee9688
--- /dev/null
+++ b/src/GraphicObjects/Base/Label.cs
@@ -0,0 +1,825 @@
+//
+// Label.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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.Diagnostics;
+using Cairo;
+using System.Text.RegularExpressions;
+using System.Xml.Serialization;
+using System.ComponentModel;
+
+namespace Crow
+{
+ public class Label : GraphicObject
+ {
+ #region CTOR
+ public Label()
+ {
+
+ }
+ public Label(string _text)
+ : base()
+ {
+ Text = _text;
+ }
+ #endregion
+
+ public event EventHandler<TextChangeEventArgs> TextChanged;
+
+ public virtual void OnTextChanged(Object sender, TextChangeEventArgs e)
+ {
+ NotifyValueChanged ("Text", Text);
+ TextChanged.Raise (this, e);
+ }
+ //TODO:change protected to private
+
+ #region private and protected fields
+ string _text = "label";
+ Alignment _textAlignment;
+ bool horizontalStretch;
+ bool verticalStretch;
+ bool _selectable;
+ bool _multiline;
+ Color selBackground;
+ Color selForeground;
+ Point mouseLocalPos = -1;//mouse coord in widget space, filled only when clicked
+ int _currentCol; //0 based cursor position in string
+ int _currentLine;
+ Point _selBegin = -1; //selection start (row,column)
+ Point _selRelease = -1; //selection end (row,column)
+ double textCursorPos; //cursor position in cairo units in widget client coord.
+ double SelStartCursorPos = -1;
+ double SelEndCursorPos = -1;
+ bool SelectionInProgress = false;
+
+ protected Rectangle rText;
+ protected float widthRatio = 1f;
+ protected float heightRatio = 1f;
+ protected FontExtents fe;
+ protected TextExtents te;
+ #endregion
+
+ [XmlAttributeAttribute][DefaultValue("BlueGray")]
+ public virtual Color SelectionBackground {
+ get { return selBackground; }
+ set {
+ if (value == selBackground)
+ return;
+ selBackground = value;
+ NotifyValueChanged ("SelectionBackground", selBackground);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("White")]
+ public virtual Color SelectionForeground {
+ get { return selForeground; }
+ set {
+ if (value == selForeground)
+ return;
+ selForeground = value;
+ NotifyValueChanged ("SelectionForeground", selForeground);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(Alignment.Left)]
+ public Alignment TextAlignment
+ {
+ get { return _textAlignment; }
+ set {
+ if (value == _textAlignment)
+ return;
+ _textAlignment = value;
+ RegisterForRedraw ();
+ NotifyValueChanged ("TextAlignment", _textAlignment);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public virtual bool HorizontalStretch {
+ get { return horizontalStretch; }
+ set {
+ if (horizontalStretch == value)
+ return;
+ horizontalStretch = value;
+ RegisterForRedraw ();
+ NotifyValueChanged ("HorizontalStretch", horizontalStretch);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public virtual bool VerticalStretch {
+ get { return verticalStretch; }
+ set {
+ if (verticalStretch == value)
+ return;
+ verticalStretch = value;
+ RegisterForRedraw ();
+ NotifyValueChanged ("VerticalStretch", verticalStretch);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("label")]
+ public string Text
+ {
+ get {
+ return lines == null ?
+ _text : lines.Aggregate((i, j) => i + Interface.LineBreak + j);
+ }
+ set
+ {
+ if (string.Equals (value, _text, StringComparison.Ordinal))
+ return;
+
+ _text = value;
+
+ if (string.IsNullOrEmpty(_text))
+ _text = "";
+
+ lines = getLines;
+
+ OnTextChanged (this, new TextChangeEventArgs (Text));
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public bool Selectable
+ {
+ get { return _selectable; }
+ set
+ {
+ if (value == _selectable)
+ return;
+ _selectable = value;
+ NotifyValueChanged ("Selectable", _selectable);
+ SelBegin = -1;
+ SelRelease = -1;
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public bool Multiline
+ {
+ get { return _multiline; }
+ set
+ {
+ if (value == _multiline)
+ return;
+ _multiline = value;
+ NotifyValueChanged ("Multiline", _multiline);
+ RegisterForGraphicUpdate();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(0)]
+ public int CurrentColumn{
+ get { return _currentCol; }
+ set {
+ if (value == _currentCol)
+ return;
+ if (value < 0)
+ _currentCol = 0;
+ else if (value > lines [_currentLine].Length)
+ _currentCol = lines [_currentLine].Length;
+ else
+ _currentCol = value;
+ NotifyValueChanged ("CurrentColumn", _currentCol);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(0)]
+ public int CurrentLine{
+ get { return _currentLine; }
+ set {
+ if (value == _currentLine)
+ return;
+ if (value >= lines.Count)
+ _currentLine = lines.Count-1;
+ else if (value < 0)
+ _currentLine = 0;
+ else
+ _currentLine = value;
+ //force recheck of currentCol for bounding
+ int cc = _currentCol;
+ _currentCol = 0;
+ CurrentColumn = cc;
+ NotifyValueChanged ("CurrentLine", _currentLine);
+ }
+ }
+ [XmlIgnore]public Point CurrentPosition {
+ get { return new Point(_currentCol, CurrentLine); }
+ }
+ //TODO:using HasFocus for drawing selection cause SelBegin and Release binding not to work
+ /// <summary>
+ /// Selection begin position in char units
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue("-1")]
+ public Point SelBegin {
+ get {
+ return _selBegin;
+ }
+ set {
+ if (value == _selBegin)
+ return;
+ _selBegin = value;
+ NotifyValueChanged ("SelBegin", _selBegin);
+ NotifyValueChanged ("SelectedText", SelectedText);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("-1")]
+ public Point SelRelease {
+ get {
+ return _selRelease;
+ }
+ set {
+ if (value == _selRelease)
+ return;
+ _selRelease = value;
+ NotifyValueChanged ("SelRelease", _selRelease);
+ NotifyValueChanged ("SelectedText", SelectedText);
+ }
+ }
+ /// <summary>
+ /// return char at CurrentLine, CurrentColumn
+ /// </summary>
+ [XmlIgnore]protected Char CurrentChar
+ {
+ get {
+ return lines [CurrentLine] [CurrentColumn];
+ }
+ }
+ /// <summary>
+ /// ordered selection start and end positions in char units
+ /// </summary>
+ [XmlIgnore]protected Point selectionStart
+ {
+ get {
+ return SelRelease < 0 || SelBegin.Y < SelRelease.Y ? SelBegin :
+ SelBegin.Y > SelRelease.Y ? SelRelease :
+ SelBegin.X < SelRelease.X ? SelBegin : SelRelease;
+ }
+ }
+ [XmlIgnore]public Point selectionEnd
+ {
+ get {
+ return SelRelease < 0 || SelBegin.Y > SelRelease.Y ? SelBegin :
+ SelBegin.Y < SelRelease.Y ? SelRelease :
+ SelBegin.X > SelRelease.X ? SelBegin : SelRelease;
+ }
+ }
+ [XmlIgnore]public string SelectedText
+ {
+ get {
+
+ if (SelRelease < 0 || SelBegin < 0)
+ return "";
+ if (selectionStart.Y == selectionEnd.Y)
+ return lines [selectionStart.Y].Substring (selectionStart.X, selectionEnd.X - selectionStart.X);
+ string tmp = "";
+ tmp = lines [selectionStart.Y].Substring (selectionStart.X);
+ for (int l = selectionStart.Y + 1; l < selectionEnd.Y; l++) {
+ tmp += Interface.LineBreak + lines [l];
+ }
+ tmp += Interface.LineBreak + lines [selectionEnd.Y].Substring (0, selectionEnd.X);
+ return tmp;
+ }
+ }
+ [XmlIgnore]public bool selectionIsEmpty
+ { get { return SelRelease < 0; } }
+
+ List<string> lines;
+ List<string> getLines {
+ get {
+ return _multiline ?
+ Regex.Split (_text, "\r\n|\r|\n|\\\\n").ToList() :
+ new List<string>(new string[] { _text });
+ }
+ }
+ /// <summary>
+ /// Moves cursor one char to the left.
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ public bool MoveLeft(){
+ int tmp = _currentCol - 1;
+ if (tmp < 0) {
+ if (_currentLine == 0)
+ return false;
+ CurrentLine--;
+ CurrentColumn = int.MaxValue;
+ } else
+ CurrentColumn = tmp;
+ return true;
+ }
+ /// <summary>
+ /// Moves cursor one char to the right.
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ public bool MoveRight(){
+ int tmp = _currentCol + 1;
+ if (tmp > lines [_currentLine].Length){
+ if (CurrentLine == lines.Count - 1)
+ return false;
+ CurrentLine++;
+ CurrentColumn = 0;
+ } else
+ CurrentColumn = tmp;
+ return true;
+ }
+ public void GotoWordStart(){
+ CurrentColumn--;
+ //skip white spaces
+ while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0)
+ CurrentColumn--;
+ while (char.IsLetterOrDigit (lines [CurrentLine] [CurrentColumn]) && CurrentColumn > 0)
+ CurrentColumn--;
+ if (!char.IsLetterOrDigit (this.CurrentChar))
+ CurrentColumn++;
+ }
+ public void GotoWordEnd(){
+ //skip white spaces
+ if (CurrentColumn >= lines [CurrentLine].Length - 1)
+ return;
+ while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < lines [CurrentLine].Length-1)
+ CurrentColumn++;
+ while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < lines [CurrentLine].Length-1)
+ CurrentColumn++;
+ if (char.IsLetterOrDigit (this.CurrentChar))
+ CurrentColumn++;
+ }
+ public void DeleteChar()
+ {
+ if (selectionIsEmpty) {
+ if (CurrentColumn == 0) {
+ if (CurrentLine == 0 && lines.Count == 1)
+ return;
+ CurrentLine--;
+ CurrentColumn = lines [CurrentLine].Length;
+ lines [CurrentLine] += lines [CurrentLine + 1];
+ lines.RemoveAt (CurrentLine + 1);
+
+ OnTextChanged (this, new TextChangeEventArgs (Text));
+ return;
+ }
+ CurrentColumn--;
+ lines [CurrentLine] = lines [CurrentLine].Remove (CurrentColumn, 1);
+ } else {
+ int linesToRemove = selectionEnd.Y - selectionStart.Y + 1;
+ int l = selectionStart.Y;
+
+ if (linesToRemove > 0) {
+ lines [l] = lines [l].Remove (selectionStart.X, lines [l].Length - selectionStart.X) +
+ lines [selectionEnd.Y].Substring (selectionEnd.X, lines [selectionEnd.Y].Length - selectionEnd.X);
+ l++;
+ for (int c = 0; c < linesToRemove-1; c++)
+ lines.RemoveAt (l);
+ CurrentLine = selectionStart.Y;
+ CurrentColumn = selectionStart.X;
+ } else
+ lines [l] = lines [l].Remove (selectionStart.X, selectionEnd.X - selectionStart.X);
+ CurrentColumn = selectionStart.X;
+ SelBegin = -1;
+ SelRelease = -1;
+ }
+ OnTextChanged (this, new TextChangeEventArgs (Text));
+ }
+ /// <summary>
+ /// Insert new string at caret position, should be sure no line break is inside.
+ /// </summary>
+ /// <param name="str">String.</param>
+ protected void Insert(string str)
+ {
+ if (!selectionIsEmpty)
+ this.DeleteChar ();
+ if (_multiline) {
+ string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray();
+ lines [CurrentLine] = lines [CurrentLine].Insert (CurrentColumn, strLines[0]);
+ CurrentColumn += strLines[0].Length;
+ for (int i = 1; i < strLines.Length; i++) {
+ InsertLineBreak ();
+ lines [CurrentLine] = lines [CurrentLine].Insert (CurrentColumn, strLines[i]);
+ CurrentColumn += strLines[i].Length;
+ }
+ } else {
+ lines [CurrentLine] = lines [CurrentLine].Insert (CurrentColumn, str);
+ CurrentColumn += str.Length;
+ }
+ OnTextChanged (this, new TextChangeEventArgs (Text));
+ }
+ /// <summary>
+ /// Insert a line break.
+ /// </summary>
+ protected void InsertLineBreak()
+ {
+ lines.Insert(CurrentLine + 1, lines[CurrentLine].Substring(CurrentColumn));
+ lines [CurrentLine] = lines [CurrentLine].Substring (0, CurrentColumn);
+ CurrentLine++;
+ CurrentColumn = 0;
+ OnTextChanged (this, new TextChangeEventArgs (Text));
+ }
+
+ #region GraphicObject overrides
+ protected override int measureRawSize(LayoutingType lt)
+ {
+ if (lines == null)
+ lines = getLines;
+
+ using (ImageSurface img = new ImageSurface (Format.Argb32, 10, 10)) {
+ using (Context gr = new Context (img)) {
+ //Cairo.FontFace cf = gr.GetContextFontFace ();
+
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+
+
+ fe = gr.FontExtents;
+ te = new TextExtents();
+
+ if (lt == LayoutingType.Height){
+ int lc = lines.Count;
+ //ensure minimal height = text line height
+ if (lc == 0)
+ lc = 1;
+
+ return (int)Math.Ceiling(fe.Height * lc) + Margin * 2;
+ }
+ try {
+ foreach (string s in lines) {
+ string l = s.Replace("\t", new String (' ', Interface.TabSize));
+
+ TextExtents tmp = gr.TextExtents (l);
+
+ if (tmp.XAdvance > te.XAdvance)
+ te = tmp;
+ }
+ } catch (Exception ex) {
+ return -1;
+ }
+ return (int)Math.Ceiling (te.XAdvance) + Margin * 2;
+ }
+ }
+ }
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+ gr.FontOptions = Interface.FontRenderingOptions;
+ gr.Antialias = Interface.Antialias;
+
+ rText = new Rectangle(new Size(
+ measureRawSize(LayoutingType.Width), measureRawSize(LayoutingType.Height)));
+ rText.Width -= 2 * Margin;
+ rText.Height -= 2 * Margin;
+
+ widthRatio = 1f;
+ heightRatio = 1f;
+
+ Rectangle cb = ClientRectangle;
+
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+
+ if (horizontalStretch) {
+ widthRatio = (float)cb.Width / (float)rText.Width;
+ if (!verticalStretch)
+ heightRatio = widthRatio;
+ }
+
+ if (verticalStretch) {
+ heightRatio = (float)cb.Height / (float)rText.Height;
+ if (!horizontalStretch)
+ widthRatio = heightRatio;
+ }
+
+ rText.Width = (int)(widthRatio * (float)rText.Width);
+ rText.Height = (int)(heightRatio * (float)rText.Height);
+
+ switch (TextAlignment)
+ {
+ case Alignment.TopLeft: //ok
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+ break;
+ case Alignment.Top: //ok
+ rText.Y = cb.Y;
+ rText.X = cb.X + cb.Width / 2 - rText.Width / 2;
+ break;
+ case Alignment.TopRight: //ok
+ rText.Y = cb.Y;
+ rText.X = cb.Right - rText.Width;
+ break;
+ case Alignment.Left://ok
+ rText.X = cb.X;
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ break;
+ case Alignment.Right://ok
+ rText.X = cb.X + cb.Width - rText.Width;
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ break;
+ case Alignment.Bottom://ok
+ rText.X = cb.Width / 2 - rText.Width / 2;
+ rText.Y = cb.Height - rText.Height;
+ break;
+ case Alignment.BottomLeft://ok
+ rText.X = cb.X;
+ rText.Y = cb.Bottom - rText.Height;
+ break;
+ case Alignment.BottomRight://ok
+ rText.Y = cb.Bottom - rText.Height;
+ rText.X = cb.Right - rText.Width;
+ break;
+ case Alignment.Center://ok
+ rText.X = cb.X + cb.Width / 2 - rText.Width / 2;
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ break;
+ }
+
+ gr.FontMatrix = new Matrix(widthRatio * (float)Font.Size, 0, 0, heightRatio * (float)Font.Size, 0, 0);
+ fe = gr.FontExtents;
+
+ #region draw text cursor
+ if (HasFocus && Selectable)
+ {
+ if (mouseLocalPos >= 0)
+ {
+ computeTextCursor(gr);
+
+ if (SelectionInProgress)
+ {
+ if (SelBegin < 0){
+ SelBegin = new Point(CurrentColumn, CurrentLine);
+ SelStartCursorPos = textCursorPos;
+ SelRelease = -1;
+ }else{
+ SelRelease = new Point(CurrentColumn, CurrentLine);
+ if (SelRelease == SelBegin)
+ SelRelease = -1;
+ else
+ SelEndCursorPos = textCursorPos;
+ }
+ }else
+ computeTextCursorPosition(gr);
+ }else
+ computeTextCursorPosition(gr);
+
+ Foreground.SetAsSource (gr);
+ gr.LineWidth = 1.0;
+ gr.MoveTo (0.5 + textCursorPos + rText.X, rText.Y + CurrentLine * fe.Height);
+ gr.LineTo (0.5 + textCursorPos + rText.X, rText.Y + (CurrentLine + 1) * fe.Height);
+ gr.Stroke();
+ }
+ #endregion
+
+ //****** debug selection *************
+// if (SelRelease >= 0) {
+// new SolidColor(Color.DarkGreen).SetAsSource(gr);
+// Rectangle R = new Rectangle (
+// rText.X + (int)SelEndCursorPos - 3,
+// rText.Y + (int)(SelRelease.Y * fe.Height),
+// 6,
+// (int)fe.Height);
+// gr.Rectangle (R);
+// gr.Fill ();
+// }
+// if (SelBegin >= 0) {
+// new SolidColor(Color.DarkRed).SetAsSource(gr);
+// Rectangle R = new Rectangle (
+// rText.X + (int)SelStartCursorPos - 3,
+// rText.Y + (int)(SelBegin.Y * fe.Height),
+// 6,
+// (int)fe.Height);
+// gr.Rectangle (R);
+// gr.Fill ();
+// }
+ //*******************
+
+ for (int i = 0; i < lines.Count; i++) {
+ string l = lines [i].Replace ("\t", new String (' ', Interface.TabSize));
+ int lineLength = (int)gr.TextExtents (l).XAdvance;
+ Rectangle lineRect = new Rectangle (
+ rText.X,
+ rText.Y + (int)Math.Ceiling(i * fe.Height),
+ lineLength,
+ (int)Math.Ceiling(fe.Height));
+
+// if (TextAlignment == Alignment.Center ||
+// TextAlignment == Alignment.Top ||
+// TextAlignment == Alignment.Bottom)
+// lineRect.X += (rText.Width - lineLength) / 2;
+// else if (TextAlignment == Alignment.Right ||
+// TextAlignment == Alignment.TopRight ||
+// TextAlignment == Alignment.BottomRight)
+// lineRect.X += (rText.Width - lineLength);
+ if (string.IsNullOrWhiteSpace (l))
+ continue;
+
+ Foreground.SetAsSource (gr);
+ gr.MoveTo (lineRect.X, rText.Y + fe.Ascent + fe.Height * i);
+ gr.ShowText (l);
+ gr.Fill ();
+
+ if (Selectable) {
+ if (SelRelease >= 0 && i >= selectionStart.Y && i <= selectionEnd.Y) {
+ gr.SetSourceColor (selBackground);
+
+ Rectangle selRect = lineRect;
+
+ int cpStart = (int)SelStartCursorPos,
+ cpEnd = (int)SelEndCursorPos;
+
+ if (SelBegin.Y > SelRelease.Y) {
+ cpStart = cpEnd;
+ cpEnd = (int)SelStartCursorPos;
+ }
+
+ if (i == selectionStart.Y) {
+ selRect.Width -= cpStart;
+ selRect.Left += cpStart;
+ }
+ if (i == selectionEnd.Y)
+ selRect.Width -= (lineLength - cpEnd);
+
+ gr.Rectangle (selRect);
+ gr.FillPreserve ();
+ gr.Save ();
+ gr.Clip ();
+ gr.SetSourceColor (SelectionForeground);
+ gr.MoveTo (lineRect.X, rText.Y + fe.Ascent + fe.Height * i);
+ gr.ShowText (l);
+ gr.Fill ();
+ gr.Restore ();
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Mouse handling
+ void updatemouseLocalPos(Point mpos){
+ mouseLocalPos = mpos - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft;
+ if (mouseLocalPos.X < 0)
+ mouseLocalPos.X = 0;
+ if (mouseLocalPos.Y < 0)
+ mouseLocalPos.Y = 0;
+ }
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseEnter (sender, e);
+ if (Selectable)
+ CurrentInterface.MouseCursor = XCursor.Text;
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+ CurrentInterface.MouseCursor = XCursor.Default;
+ }
+ protected override void onFocused (object sender, EventArgs e)
+ {
+ base.onFocused (sender, e);
+
+ if (!_selectable)
+ return;
+ SelBegin = new Point(0,0);
+ SelRelease = new Point (lines.LastOrDefault ().Length, lines.Count-1);
+ RegisterForRedraw ();
+ }
+ protected override void onUnfocused (object sender, EventArgs e)
+ {
+ base.onUnfocused (sender, e);
+
+ SelBegin = -1;
+ SelRelease = -1;
+ RegisterForRedraw ();
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ if (!(SelectionInProgress && HasFocus && _selectable))
+ return;
+
+ updatemouseLocalPos (e.Position);
+
+ RegisterForRedraw();
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ if (this.HasFocus && _selectable){
+ updatemouseLocalPos (e.Position);
+ SelBegin = -1;
+ SelRelease = -1;
+ SelectionInProgress = true;
+ RegisterForRedraw();//TODO:should put it in properties
+ }
+
+ //done at the end to set 'hasFocus' value after testing it
+ base.onMouseDown (sender, e);
+ }
+ public override void onMouseUp (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseUp (sender, e);
+
+ if (!SelectionInProgress)
+ return;
+
+ updatemouseLocalPos (e.Position);
+ SelectionInProgress = false;
+ RegisterForRedraw ();
+ }
+ public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseDoubleClick (sender, e);
+
+ GotoWordStart ();
+ SelBegin = CurrentPosition;
+ GotoWordEnd ();
+ SelRelease = CurrentPosition;
+ SelectionInProgress = false;
+ RegisterForRedraw ();
+ }
+ #endregion
+
+ /// <summary>
+ /// Update Current Column, line and TextCursorPos
+ /// from mouseLocalPos
+ /// </summary>
+ void computeTextCursor(Context gr)
+ {
+ TextExtents te;
+
+ double cPos = 0f;
+
+ CurrentLine = (int)(mouseLocalPos.Y / fe.Height);
+
+ //fix cu
+ if (CurrentLine >= lines.Count)
+ CurrentLine = lines.Count - 1;
+
+ for (int i = 0; i < lines[CurrentLine].Length; i++)
+ {
+ string c = lines [CurrentLine].Substring (i, 1);
+ if (c == "\t")
+ c = new string (' ', Interface.TabSize);
+
+ te = gr.TextExtents(c);
+
+ double halfWidth = te.XAdvance / 2;
+
+ if (mouseLocalPos.X <= cPos + halfWidth)
+ {
+ CurrentColumn = i;
+ textCursorPos = cPos;
+ mouseLocalPos = -1;
+ return;
+ }
+
+ cPos += te.XAdvance;
+ }
+ CurrentColumn = lines[CurrentLine].Length;
+ textCursorPos = cPos;
+
+ //reset mouseLocalPos
+ mouseLocalPos = -1;
+ }
+ /// <summary> Computes offsets in cairo units </summary>
+ void computeTextCursorPosition(Context gr)
+ {
+ if (SelBegin >= 0)
+ SelStartCursorPos = GetXFromTextPointer (gr, SelBegin);
+ if (SelRelease >= 0)
+ SelEndCursorPos = GetXFromTextPointer (gr, SelRelease);
+ textCursorPos = GetXFromTextPointer (gr, new Point(CurrentColumn, CurrentLine));
+ }
+ /// <summary> Compute x offset in cairo unit from text position </summary>
+ double GetXFromTextPointer(Context gr, Point pos)
+ {
+ try {
+ string l = lines [pos.Y].Substring (0, pos.X).
+ Replace ("\t", new String (' ', Interface.TabSize));
+ return gr.TextExtents (l).XAdvance;
+ } catch{
+ return -1;
+ }
+ }
+ }
+}
diff --git a/src/GraphicObjects/Base/ListBox.cs b/src/GraphicObjects/Base/ListBox.cs
new file mode 100644
index 00000000..f66edf61
--- /dev/null
+++ b/src/GraphicObjects/Base/ListBox.cs
@@ -0,0 +1,47 @@
+//
+// ListBox.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+using System.Xml.Serialization;
+using System.ComponentModel;
+using System.IO;
+using System.Diagnostics;
+using System.Xml;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Crow
+{
+ public class ListBox : TemplatedGroup
+ {
+ #region CTOR
+ public ListBox () : base() {}
+ #endregion
+
+ }
+}
+
diff --git a/src/GraphicObjects/Base/Menu.cs b/src/GraphicObjects/Base/Menu.cs
new file mode 100644
index 00000000..50494815
--- /dev/null
+++ b/src/GraphicObjects/Base/Menu.cs
@@ -0,0 +1,76 @@
+//
+// Menu.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+
+namespace Crow
+{
+ public class Menu : TemplatedGroup
+ {
+ #region CTOR
+ public Menu () : base() {}
+ #endregion
+
+ Orientation orientation;
+ bool autoOpen = false;
+
+ #region Public properties
+ [XmlAttributeAttribute][DefaultValue(Orientation.Horizontal)]
+ public Orientation Orientation {
+ get { return orientation; }
+ set {
+ if (orientation == value)
+ return;
+ orientation = value;
+ NotifyValueChanged ("Orientation", orientation);
+ }
+ }
+ [XmlIgnore]public bool AutomaticOpenning
+ {
+ get { return autoOpen; }
+ set { autoOpen = value; }
+ }
+ #endregion
+
+ public override void AddItem (GraphicObject g)
+ {
+ base.AddItem (g);
+
+ if (orientation == Orientation.Horizontal)
+ g.NotifyValueChanged ("PopDirection", Alignment.Bottom);
+ else
+ g.NotifyValueChanged ("PopDirection", Alignment.Right);
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+ AutomaticOpenning = false;
+ }
+ }
+}
+
diff --git a/src/GraphicObjects/Base/MenuItem.cs b/src/GraphicObjects/Base/MenuItem.cs
new file mode 100644
index 00000000..0d99a2eb
--- /dev/null
+++ b/src/GraphicObjects/Base/MenuItem.cs
@@ -0,0 +1,174 @@
+//
+// MenuItem.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+
+namespace Crow
+{
+ public class MenuItem : Menu
+ {
+ #region CTOR
+ public MenuItem () : base() {}
+ #endregion
+
+ public event EventHandler Open;
+ public event EventHandler Close;
+
+ string caption;
+ Command command;
+ Picture icon;
+ bool isOpened;
+ Measure popWidth, popHeight;
+
+ #region Public properties
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public bool IsOpened {
+ get { return isOpened; }
+ set {
+ if (isOpened == value)
+ return;
+ isOpened = value;
+ NotifyValueChanged ("IsOpened", isOpened);
+
+ if (isOpened) {
+ onOpen (this, null);
+ (LogicalParent as Menu).AutomaticOpenning = true;
+ }else
+ onClose (this, null);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(null)]
+ public virtual Command Command {
+ get { return command; }
+ set {
+ if (command == value)
+ return;
+
+ if (command != null) {
+ command.raiseAllValuesChanged ();
+ command.ValueChanged -= Command_ValueChanged;
+ }
+
+ command = value;
+
+ if (command != null) {
+ command.ValueChanged += Command_ValueChanged;
+ command.raiseAllValuesChanged ();
+ }
+
+ NotifyValueChanged ("Command", command);
+ }
+ }
+ [XmlAttributeAttribute]
+ public override bool IsEnabled {
+ get { return Command == null ? base.IsEnabled : Command.CanExecute; }
+ set { base.IsEnabled = value; }
+ }
+ [XmlAttributeAttribute]
+ public override string Caption {
+ get { return Command == null ? base.Caption : Command.Caption; }
+ set { base.Caption = value; }
+ }
+ [XmlAttributeAttribute]
+ public Picture Icon {
+ get { return Command == null ? icon : Command.Icon;; }
+ set {
+ if (icon == value)
+ return;
+ icon = value;
+ if (command == null)
+ NotifyValueChanged ("Icon", icon);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("Fit")]
+ public virtual Measure PopWidth {
+ get { return popWidth; }
+ set {
+ if (popWidth == value)
+ return;
+ popWidth = value;
+ NotifyValueChanged ("PopWidth", popWidth);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("Fit")]
+ public virtual Measure PopHeight {
+ get { return popHeight; }
+ set {
+ if (popHeight == value)
+ return;
+ popHeight = value;
+ NotifyValueChanged ("PopHeight", popHeight);
+ }
+ }
+ #endregion
+
+ public override void AddItem (GraphicObject g)
+ {
+ base.AddItem (g);
+ g.NotifyValueChanged ("PopDirection", Alignment.Right);
+ }
+
+ void Command_ValueChanged (object sender, ValueChangeEventArgs e)
+ {
+ string mName = e.MemberName;
+ if (mName == "CanExecute")
+ mName = "IsEnabled";
+ NotifyValueChanged (mName, e.NewValue);
+ }
+ void onMI_Click (object sender, MouseButtonEventArgs e)
+ {
+ if (command != null)
+ command.Execute ();
+ if(!IsOpened)
+ (LogicalParent as Menu).AutomaticOpenning = false;
+ }
+ protected virtual void onOpen (object sender, EventArgs e){
+ Open.Raise (this, null);
+ }
+ protected virtual void onClose (object sender, EventArgs e){
+ Close.Raise (this, null);
+ }
+ public override bool MouseIsIn (Point m)
+ {
+ return IsEnabled ? base.MouseIsIn (m) || child.MouseIsIn (m) : false;
+ }
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseEnter (sender, e);
+ if ((LogicalParent as Menu).AutomaticOpenning && items.Children.Count>0)
+ IsOpened = true;
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ if (IsOpened)
+ IsOpened = false;
+ base.onMouseLeave (this, e);
+ }
+ }
+}
+
diff --git a/src/GraphicObjects/Base/MessageBox.cs b/src/GraphicObjects/Base/MessageBox.cs
new file mode 100644
index 00000000..d8ce1dae
--- /dev/null
+++ b/src/GraphicObjects/Base/MessageBox.cs
@@ -0,0 +1,150 @@
+//
+// MessageBox.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+
+namespace Crow
+{
+ public class MessageBox : Window
+ {
+ public enum Type {
+ Information,
+ YesNo,
+ Alert,
+ Error
+ }
+ public MessageBox (): base(){}
+
+ protected override void loadTemplate (GraphicObject template)
+ {
+ base.loadTemplate (template);
+ NotifyValueChanged ("MsgIcon", "#Crow.Images.Icons.Informations.svg");
+ }
+ string message, okMessage, cancelMessage;
+ Type msgType = Type.Information;
+
+ public event EventHandler Ok;
+ public event EventHandler Cancel;
+
+ [XmlAttributeAttribute][DefaultValue("Informations")]
+ public virtual string Message
+ {
+ get { return message; }
+ set {
+ if (message == value)
+ return;
+ message = value;
+ NotifyValueChanged ("Message", message);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("Ok")]
+ public virtual string OkMessage
+ {
+ get { return okMessage; }
+ set {
+ if (okMessage == value)
+ return;
+ okMessage = value;
+ NotifyValueChanged ("OkMessage", okMessage);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("Cancel")]
+ public virtual string CancelMessage
+ {
+ get { return cancelMessage; }
+ set {
+ if (cancelMessage == value)
+ return;
+ cancelMessage = value;
+ NotifyValueChanged ("CancelMessage", cancelMessage);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("Information")]
+ public virtual Type MsgType
+ {
+ get { return msgType; }
+ set {
+ if (msgType == value)
+ return;
+ msgType = value;
+ NotifyValueChanged ("MsgType", msgType);
+ switch (msgType) {
+ case Type.Information:
+ NotifyValueChanged ("MsgIcon", "#Crow.Images.Icons.Informations.svg");
+ Caption = "Informations";
+ OkMessage = "Ok";
+ CancelMessage = "Cancel";
+ break;
+ case Type.YesNo:
+ NotifyValueChanged ("MsgIcon", "#Crow.Icons.question.svg");
+ Caption = "Choice";
+ OkMessage = "Yes";
+ CancelMessage = "No";
+ break;
+ case Type.Alert:
+ NotifyValueChanged ("MsgIcon", "#Crow.Images.Icons.IconAlerte.svg");
+ Caption = "Alert";
+ OkMessage = "Ok";
+ CancelMessage = "Cancel";
+ break;
+ case Type.Error:
+ NotifyValueChanged ("MsgIcon", "#Crow.Images.Icons.exit.svg");
+ Caption = "Error";
+ OkMessage = "Ok";
+ CancelMessage = "Cancel";
+ break;
+ }
+ }
+ }
+ void onOkButtonClick (object sender, EventArgs e)
+ {
+ Ok.Raise (this, null);
+ close ();
+ }
+ void onCancelButtonClick (object sender, EventArgs e)
+ {
+ Cancel.Raise (this, null);
+ close ();
+ }
+ public static MessageBox Show (Type msgBoxType, string message, string okMsg = "", string cancelMsg = ""){
+ lock (Interface.CurrentInterface.UpdateMutex) {
+ MessageBox mb = new MessageBox ();
+ mb.Initialize ();
+ mb.CurrentInterface.AddWidget (mb);
+ mb.MsgType = msgBoxType;
+ mb.Message = message;
+ if (!string.IsNullOrEmpty(okMsg))
+ mb.OkMessage = okMsg;
+ if (!string.IsNullOrEmpty(cancelMsg))
+ mb.CancelMessage = cancelMsg;
+ return mb;
+ }
+ }
+ }
+}
+
diff --git a/src/GraphicObjects/Base/NumericControl.cs b/src/GraphicObjects/Base/NumericControl.cs
new file mode 100644
index 00000000..9b511831
--- /dev/null
+++ b/src/GraphicObjects/Base/NumericControl.cs
@@ -0,0 +1,125 @@
+//
+// NumericControl.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+
+namespace Crow
+{
+ public abstract class NumericControl : TemplatedControl
+ {
+ #region CTOR
+ public NumericControl () : base()
+ {
+ }
+ public NumericControl(double minimum, double maximum, double step)
+ : base()
+ {
+ }
+ #endregion
+
+ #region private fields
+ double _actualValue, minValue, maxValue, smallStep, bigStep;
+ #endregion
+
+ #region public properties
+ [XmlAttributeAttribute()][DefaultValue(0.0)]
+ public virtual double Minimum {
+ get { return minValue; }
+ set {
+ if (minValue == value)
+ return;
+
+ minValue = value;
+ NotifyValueChanged ("Minimum", minValue);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(100.0)]
+ public virtual double Maximum
+ {
+ get { return maxValue; }
+ set {
+ if (maxValue == value)
+ return;
+
+ maxValue = value;
+ NotifyValueChanged ("Maximum", maxValue);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(1.0)]
+ public virtual double SmallIncrement
+ {
+ get { return smallStep; }
+ set {
+ if (smallStep == value)
+ return;
+
+ smallStep = value;
+ NotifyValueChanged ("SmallIncrement", smallStep);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(5.0)]
+ public virtual double LargeIncrement
+ {
+ get { return bigStep; }
+ set {
+ if (bigStep == value)
+ return;
+
+ bigStep = value;
+ NotifyValueChanged ("LargeIncrement", bigStep);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(0.0)]
+ public double Value
+ {
+ get { return _actualValue; }
+ set
+ {
+ if (value == _actualValue)
+ return;
+
+ if (value < minValue)
+ _actualValue = minValue;
+ else if (value > maxValue)
+ _actualValue = maxValue;
+ else
+ _actualValue = value;
+
+ NotifyValueChanged("Value", _actualValue);
+ RegisterForGraphicUpdate();
+ }
+ }
+ #endregion
+
+ }
+}
+
diff --git a/src/GraphicObjects/Base/Popper.cs b/src/GraphicObjects/Base/Popper.cs
new file mode 100644
index 00000000..d3f2e7c8
--- /dev/null
+++ b/src/GraphicObjects/Base/Popper.cs
@@ -0,0 +1,245 @@
+//
+// Popper.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 OpenTK.Input;
+
+namespace Crow
+{
+ public class Popper : TemplatedContainer
+ {
+ #region CTOR
+ public Popper() : base()
+ {
+ }
+ #endregion
+
+ bool _isPopped, _canPop;
+ Alignment popDirection;
+ GraphicObject _content;
+ Measure popWidth, popHeight;
+
+ public event EventHandler Popped;
+ public event EventHandler Unpoped;
+
+ #region Public Properties
+ [XmlAttributeAttribute()][DefaultValue("Fit")]
+ public virtual Measure PopWidth {
+ get { return popWidth; }
+ set {
+ if (popWidth == value)
+ return;
+ popWidth = value;
+ NotifyValueChanged ("PopWidth", popWidth);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("Fit")]
+ public virtual Measure PopHeight {
+ get { return popHeight; }
+ set {
+ if (popHeight == value)
+ return;
+ popHeight = value;
+ NotifyValueChanged ("PopHeight", popHeight);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(false)]
+ public bool IsPopped
+ {
+ get { return _isPopped; }
+ set
+ {
+ if (value == _isPopped)
+ return;
+
+ _isPopped = value;
+ NotifyValueChanged ("IsPopped", _isPopped);
+
+ if (_isPopped)
+ onPop (this, null);
+ else
+ onUnpop (this, null);
+
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(true)]
+ public bool CanPop
+ {
+ get { return _canPop; }
+ set
+ {
+ if (value == _canPop)
+ return;
+
+ _canPop = value;
+ NotifyValueChanged ("CanPop", _canPop);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(Alignment.Bottom)]
+ public virtual Alignment PopDirection {
+ get { return popDirection; }
+ set {
+ if (popDirection == value)
+ return;
+ popDirection = value;
+ NotifyValueChanged ("PopDirection", popDirection);
+ }
+ }
+ #endregion
+
+ public override GraphicObject Content {
+ get { return _content; }
+ set {
+ if (_content != null) {
+ _content.LogicalParent = null;
+ _content.LayoutChanged -= _content_LayoutChanged;
+ }
+
+ _content = value;
+
+ if (_content == null)
+ return;
+
+ _content.LogicalParent = this;
+ _content.HorizontalAlignment = HorizontalAlignment.Left;
+ _content.VerticalAlignment = VerticalAlignment.Top;
+ _content.LayoutChanged += _content_LayoutChanged;
+ }
+ }
+ void positionContent(LayoutingType lt){
+ ILayoutable tc = Content.Parent;
+ if (tc == null)
+ return;
+ Rectangle r = this.ScreenCoordinates (this.Slot);
+ if (lt == LayoutingType.X) {
+ if (popDirection.HasFlag (Alignment.Right)) {
+ if (r.Right + Content.Slot.Width > tc.ClientRectangle.Right)
+ Content.Left = r.Left - Content.Slot.Width;
+ else
+ Content.Left = r.Right;
+ } else if (popDirection.HasFlag (Alignment.Left)) {
+ if (r.Left - Content.Slot.Width < tc.ClientRectangle.Left)
+ Content.Left = r.Right;
+ else
+ Content.Left = r.Left - Content.Slot.Width;
+ } else {
+ if (Content.Slot.Width < tc.ClientRectangle.Width) {
+ if (r.Left + Content.Slot.Width > tc.ClientRectangle.Right)
+ Content.Left = tc.ClientRectangle.Right - Content.Slot.Width;
+ else
+ Content.Left = r.Left;
+ } else
+ Content.Left = 0;
+ }
+ }else if (lt == LayoutingType.Y) {
+ if (Content.Slot.Height < tc.ClientRectangle.Height) {
+ if (PopDirection.HasFlag (Alignment.Bottom)) {
+ if (r.Bottom + Content.Slot.Height > tc.ClientRectangle.Bottom)
+ Content.Top = r.Top - Content.Slot.Height;
+ else
+ Content.Top = r.Bottom;
+ } else if (PopDirection.HasFlag (Alignment.Top)) {
+ if (r.Top - Content.Slot.Height < tc.ClientRectangle.Top)
+ Content.Top = r.Bottom;
+ else
+ Content.Top = r.Top - Content.Slot.Height;
+ } else
+ Content.Top = r.Top;
+ }else
+ Content.Top = 0;
+ }
+ }
+ protected void _content_LayoutChanged (object sender, LayoutingEventArgs e)
+ {
+ if (e.LayoutType.HasFlag (LayoutingType.Width))
+ positionContent (LayoutingType.X);
+ if (e.LayoutType.HasFlag(LayoutingType.Height))
+ positionContent (LayoutingType.Y);
+ }
+
+ #region GraphicObject overrides
+ public override void onMouseClick (object sender, MouseButtonEventArgs e)
+ {
+ if (_canPop)
+ IsPopped = !IsPopped;
+ base.onMouseClick (this, e);
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (this, e);
+ IsPopped = false;
+ }
+ public override bool MouseIsIn (Point m)
+ {
+ bool isInContent = false;
+ if (Content != null) {
+ if (Content.Parent != null)
+ isInContent = Content.MouseIsIn (m);
+ }
+ return base.MouseIsIn (m) || isInContent;
+ }
+ public override void checkHoverWidget (MouseMoveEventArgs e)
+ {
+ if (CurrentInterface.HoverWidget != this) {
+ CurrentInterface.HoverWidget = this;
+ onMouseEnter (this, e);
+ }
+ if (Content != null){
+ if (Content.Parent != null) {
+ if (Content.MouseIsIn (e.Position)) {
+ Content.checkHoverWidget (e);
+ return;
+ }
+ }
+ }
+ base.checkHoverWidget (e);
+ }
+ #endregion
+
+ public virtual void onPop(object sender, EventArgs e)
+ {
+ if (Content != null) {
+ Content.Visible = true;
+ if (Content.Parent == null)
+ CurrentInterface.AddWidget (Content, true);
+ if (Content.LogicalParent != this)
+ Content.LogicalParent = this;
+ CurrentInterface.PutOnTop (Content, true);
+ _content_LayoutChanged (this, new LayoutingEventArgs (LayoutingType.Sizing));
+ }
+ Popped.Raise (this, e);
+ }
+ public virtual void onUnpop(object sender, EventArgs e)
+ {
+ if (Content != null) {
+ Content.Visible = false;
+ }
+ Unpoped.Raise (this, e);
+ }
+ }
+}
diff --git a/src/GraphicObjects/Base/PrivateContainer.cs b/src/GraphicObjects/Base/PrivateContainer.cs
new file mode 100644
index 00000000..65eacabb
--- /dev/null
+++ b/src/GraphicObjects/Base/PrivateContainer.cs
@@ -0,0 +1,207 @@
+//
+// PrivateContainer.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Cairo;
+
+namespace Crow
+{
+ /// <summary>
+ /// Implement drawing and layouting for a single child, but
+ /// does not implement IXmlSerialisation to allow reuse of container
+ /// behaviour for widgets that have other xml hierarchy: example
+ /// TemplatedControl may have 3 children (template,templateItem,content) but
+ /// behave exactely as a container for layouting and drawing
+ /// </summary>
+ public class PrivateContainer : GraphicObject
+ {
+ #region CTOR
+ public PrivateContainer()
+ : base()
+ {
+ }
+ #endregion
+
+ protected GraphicObject child;
+
+ internal virtual void SetChild(GraphicObject _child)
+ {
+
+ if (child != null) {
+ contentSize = new Size (0, 0);
+ child.LayoutChanged -= OnChildLayoutChanges;
+ child.Parent = null;
+ this.RegisterForGraphicUpdate ();
+ }
+
+ child = _child as GraphicObject;
+
+ if (child != null) {
+ child.Parent = this;
+ child.LayoutChanged += OnChildLayoutChanges;
+ contentSize = child.Slot.Size;
+ child.RegisteredLayoutings = LayoutingType.None;
+ child.RegisterForLayouting (LayoutingType.Sizing);
+ }
+ }
+
+ #region GraphicObject Overrides
+
+ public override GraphicObject FindByName (string nameToFind)
+ {
+ if (Name == nameToFind)
+ return this;
+
+ return child == null ? null : child.FindByName (nameToFind);
+ }
+ public override bool Contains (GraphicObject goToFind)
+ {
+ return child == goToFind ? true :
+ child == null ? false : child.Contains(goToFind);
+ }
+ public override void OnDataSourceChanged (object sender, DataSourceChangeEventArgs e)
+ {
+ base.OnDataSourceChanged (this, e);
+ if (child != null)
+ if (child.localDataSourceIsNull & child.localLogicalParentIsNull)
+ child.OnDataSourceChanged (sender, e);
+ }
+ public override bool UpdateLayout (LayoutingType layoutType)
+ {
+ if (child != null) {
+ //force sizing to fit if sizing on children and child has stretched size
+ switch (layoutType) {
+ case LayoutingType.Width:
+ if (Width == Measure.Fit && child.Width.Units == Unit.Percent)
+ child.Width = Measure.Fit;
+ break;
+ case LayoutingType.Height:
+ if (Height == Measure.Fit && child.Height.Units == Unit.Percent)
+ child.Height = Measure.Fit;
+ break;
+ }
+ }
+ return base.UpdateLayout (layoutType);
+ }
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+
+ if (child == null)
+ return;
+
+ LayoutingType ltChild = LayoutingType.None;
+
+ if (layoutType == LayoutingType.Width) {
+ if (child.Width.Units == Unit.Percent) {
+ ltChild |= LayoutingType.Width;
+ if (child.Width.Value < 100 && child.Left == 0)
+ ltChild |= LayoutingType.X;
+ } else if (child.Left == 0)
+ ltChild |= LayoutingType.X;
+ } else if (layoutType == LayoutingType.Height) {
+ if (child.Height.Units == Unit.Percent) {
+ ltChild |= LayoutingType.Height;
+ if (child.Height.Value < 100 && child.Top == 0)
+ ltChild |= LayoutingType.Y;
+ } else if (child.Top == 0)
+ ltChild |= LayoutingType.Y;
+ }
+ if (ltChild == LayoutingType.None)
+ return;
+ child.RegisterForLayouting (ltChild);
+ }
+ public virtual void OnChildLayoutChanges (object sender, LayoutingEventArgs arg)
+ {
+ GraphicObject g = sender as GraphicObject;
+
+ if (arg.LayoutType == LayoutingType.Width) {
+ if (Width != Measure.Fit)
+ return;
+ contentSize.Width = g.Slot.Width;
+ this.RegisterForLayouting (LayoutingType.Width);
+ }else if (arg.LayoutType == LayoutingType.Height){
+ if (Height != Measure.Fit)
+ return;
+ contentSize.Height = g.Slot.Height;
+ this.RegisterForLayouting (LayoutingType.Height);
+ }
+ }
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ gr.Save ();
+
+ if (ClipToClientRect) {
+ //clip to client zone
+ CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius);
+ gr.Clip ();
+ }
+
+ if (child != null) {
+ if (child.Visible)
+ child.Paint (ref gr);
+ }
+ gr.Restore ();
+ }
+ protected override void UpdateCache (Context ctx)
+ {
+ Rectangle rb = Slot + Parent.ClientRectangle.Position;
+
+ using (ImageSurface cache = new ImageSurface (bmp, Format.Argb32, Slot.Width, Slot.Height, 4 * Slot.Width)) {
+ Context gr = new Context (cache);
+
+ if (Clipping.count > 0) {
+ Clipping.clearAndClip (gr);
+
+ onDraw (gr);
+ }
+
+ gr.Dispose ();
+
+ ctx.SetSourceSurface (cache, rb.X, rb.Y);
+ ctx.Paint ();
+ }
+ Clipping.Reset();
+ }
+ #endregion
+
+ #region Mouse handling
+ public override void checkHoverWidget (MouseMoveEventArgs e)
+ {
+ base.checkHoverWidget (e);
+ if (child != null)
+ if (child.MouseIsIn (e.Position))
+ child.checkHoverWidget (e);
+ }
+ #endregion
+
+ }
+}
+
diff --git a/src/GraphicObjects/Base/ProgressBar.cs b/src/GraphicObjects/Base/ProgressBar.cs
new file mode 100644
index 00000000..34366ca7
--- /dev/null
+++ b/src/GraphicObjects/Base/ProgressBar.cs
@@ -0,0 +1,67 @@
+//
+// ProgressBar.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Cairo;
+using System.Diagnostics;
+using System.Xml.Serialization;
+using System.ComponentModel;
+
+namespace Crow
+{
+
+ public class ProgressBar : NumericControl
+ {
+ #region CTOR
+ public ProgressBar() : base(){}
+ #endregion
+
+ protected override void loadTemplate (GraphicObject template)
+ {
+
+ }
+
+ #region GraphicObject overrides
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ if (Maximum == 0)
+ return;
+
+ Rectangle rBack = ClientRectangle;
+ rBack.Width = (int)((double)rBack.Width / Maximum * Value);
+ Foreground.SetAsSource (gr, rBack);
+
+ CairoHelpers.CairoRectangle(gr,rBack,CornerRadius);
+ gr.Fill();
+ }
+ #endregion
+ }
+}
diff --git a/src/GraphicObjects/Base/RadioButton.cs b/src/GraphicObjects/Base/RadioButton.cs
new file mode 100644
index 00000000..993ead59
--- /dev/null
+++ b/src/GraphicObjects/Base/RadioButton.cs
@@ -0,0 +1,82 @@
+//
+// RadioButton.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 RadioButton : TemplatedControl
+ {
+ bool isChecked;
+
+ #region CTOR
+ public RadioButton() : base(){}
+ #endregion
+
+ public event EventHandler Checked;
+ public event EventHandler Unchecked;
+
+ #region GraphicObject overrides
+ public override void onMouseClick (object sender, MouseButtonEventArgs e)
+ {
+ Group pg = Parent as Group;
+ if (pg != null) {
+ for (int i = 0; i < pg.Children.Count; i++) {
+ RadioButton c = pg.Children [i] as RadioButton;
+ if (c == null)
+ continue;
+ c.IsChecked = (c == this);
+ }
+ } else
+ IsChecked = !IsChecked;
+
+ base.onMouseClick (sender, e);
+ }
+ #endregion
+
+ [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);
+ }
+ }
+ }
+}
diff --git a/src/GraphicObjects/Base/ScrollBar.cs b/src/GraphicObjects/Base/ScrollBar.cs
new file mode 100644
index 00000000..91ce50ef
--- /dev/null
+++ b/src/GraphicObjects/Base/ScrollBar.cs
@@ -0,0 +1,68 @@
+//
+// ScrollBar.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 OpenTK.Input;
+
+namespace Crow
+{
+ public class ScrollBar : NumericControl
+ {
+ Orientation _orientation;
+
+ #region CTOR
+ public ScrollBar() : base() {}
+ #endregion
+
+ [XmlAttributeAttribute()][DefaultValue(Orientation.Vertical)]
+ public virtual Orientation Orientation
+ {
+ get { return _orientation; }
+ set {
+ if (_orientation == value)
+ return;
+ _orientation = value;
+ NotifyValueChanged ("Orientation", _orientation);
+ RegisterForGraphicUpdate ();
+ }
+ }
+ public void onScrollBack (object sender, MouseButtonEventArgs e)
+ {
+ Value -= SmallIncrement;
+ }
+ public void onScrollForth (object sender, MouseButtonEventArgs e)
+ {
+ Value += SmallIncrement;
+ }
+
+ public void onSliderValueChange(object sender, ValueChangeEventArgs e){
+ if (e.MemberName == "Value")
+ Value = Convert.ToDouble(e.NewValue);
+ }
+ }
+}
diff --git a/src/GraphicObjects/Base/Scroller.cs b/src/GraphicObjects/Base/Scroller.cs
new file mode 100644
index 00000000..640fcfe8
--- /dev/null
+++ b/src/GraphicObjects/Base/Scroller.cs
@@ -0,0 +1,239 @@
+//
+// Scroller.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+using Cairo;
+
+namespace Crow
+{
+ public class Scroller : Container
+ {
+ bool _verticalScrolling;
+ bool _horizontalScrolling;
+ bool _scrollbarVisible;
+ double _scrollX = 0.0;
+ double _scrollY = 0.0;
+ int scrollSpeed;
+
+ public event EventHandler<ScrollingEventArgs> Scrolled;
+
+ #region public properties
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public bool VerticalScrolling {
+ get { return _verticalScrolling; }
+ set { _verticalScrolling = value; }
+ }
+
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public bool HorizontalScrolling {
+ get { return _horizontalScrolling; }
+ set { _horizontalScrolling = value; }
+ }
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public bool ScrollbarVisible {
+ get { return _scrollbarVisible; }
+ set { _scrollbarVisible = value; }
+ }
+ [XmlAttributeAttribute][DefaultValue(0.0)]
+ public double ScrollX {
+ get {
+ return _scrollX;
+ }
+ set {
+ if (_scrollX == value)
+ return;
+ if (value < 0.0)
+ _scrollX = 0.0;
+ else if (value > Child.Slot.Width - ClientRectangle.Width)
+ _scrollX = Math.Max(0.0, Child.Slot.Width - ClientRectangle.Width);
+ else
+ _scrollX = value;
+ NotifyValueChanged("ScrollX", _scrollX);
+ RegisterForRedraw ();
+ Scrolled.Raise (this, new ScrollingEventArgs (Orientation.Horizontal));
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(0.0)]
+ public double ScrollY {
+ get {
+ return _scrollY;
+ }
+ set {
+ if (_scrollY == value)
+ return;
+ if (value < 0.0)
+ _scrollY = 0.0;
+ else if (value > Child.Slot.Height - ClientRectangle.Height)
+ _scrollY = Math.Max(0.0,Child.Slot.Height - ClientRectangle.Height);
+ else
+ _scrollY = value;
+ NotifyValueChanged("ScrollY", _scrollY);
+ RegisterForRedraw ();
+ Scrolled.Raise (this, new ScrollingEventArgs (Orientation.Vertical));
+ }
+ }
+
+ [XmlIgnore]
+ public int MaximumScroll {
+ get {
+ try {
+ return VerticalScrolling ?
+ Math.Max(Child.Slot.Height - ClientRectangle.Height,0) :
+ Math.Max(Child.Slot.Width - ClientRectangle.Width,0);
+ } catch {
+ return 0;
+ }
+ }
+ }
+
+ [XmlAttributeAttribute][DefaultValue(30)]
+ public int ScrollSpeed {
+ get { return scrollSpeed; }
+ set {
+ scrollSpeed = value;
+ NotifyValueChanged("ScrollSpeed", scrollSpeed);
+ }
+ }
+ #endregion
+
+ public Scroller()
+ : base(){}
+
+ #region GraphicObject Overrides
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+
+ NotifyValueChanged("MaximumScroll", MaximumScroll);
+ }
+ void OnChildLayoutChanges (object sender, LayoutingEventArgs arg)
+ {
+ //Debug.WriteLine ("scroller childLayoutChanges");
+ int maxScroll = MaximumScroll;
+ //Debug.WriteLine ("maxscroll={0}", maxScroll);
+ if (_verticalScrolling) {
+ if (arg.LayoutType == LayoutingType.Height) {
+ if (maxScroll < ScrollY) {
+ //Debug.WriteLine ("scrolly={0} maxscroll={1}", ScrollY, maxScroll);
+ ScrollY = maxScroll;
+ }
+ NotifyValueChanged("MaximumScroll", maxScroll);
+ }
+ } else if (arg.LayoutType == LayoutingType.Width) {
+ if (maxScroll < ScrollX) {
+ //Debug.WriteLine ("scrolly={0} maxscroll={1}", ScrollY, maxScroll);
+ ScrollX = maxScroll;
+ }
+ NotifyValueChanged("MaximumScroll", maxScroll);
+ }
+ }
+ void onChildListCleared(object sender, EventArgs e){
+ ScrollY = 0;
+ ScrollX = 0;
+ }
+ public override void SetChild (GraphicObject _child)
+ {
+ GraphicObject c = child as GraphicObject;
+ Group g = child as Group;
+ if (c != null) {
+ c.LayoutChanged -= OnChildLayoutChanges;
+ if (g != null)
+ g.ChildrenCleared -= onChildListCleared;
+ }
+ c = _child as GraphicObject;
+ g = _child as Group;
+ if (c != null) {
+ c.LayoutChanged += OnChildLayoutChanges;
+ if (g != null)
+ g.ChildrenCleared += onChildListCleared;
+ }
+ base.SetChild (_child);
+ }
+ public override Rectangle ScreenCoordinates (Rectangle r)
+ {
+ return base.ScreenCoordinates (r) - new Point((int)ScrollX,(int)ScrollY);
+ }
+ protected override void onDraw (Context gr)
+ {
+ Rectangle rBack = new Rectangle (Slot.Size);
+
+ Background.SetAsSource (gr, rBack);
+ CairoHelpers.CairoRectangle(gr,rBack, CornerRadius);
+ gr.Fill ();
+
+ gr.Save ();
+ if (ClipToClientRect) {
+ //clip to scrolled client zone
+ CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius);
+ gr.Clip ();
+ }
+
+ gr.Translate (-ScrollX, -ScrollY);
+ if (child != null)
+ child.Paint (ref gr);
+ gr.Restore ();
+ }
+
+ #region Mouse handling
+ internal Point savedMousePos;
+ public override bool MouseIsIn (Point m)
+ {
+ return Visible ? base.ScreenCoordinates(Slot).ContainsOrIsEqual (m) : false;
+ }
+ public override void checkHoverWidget (MouseMoveEventArgs e)
+ {
+ savedMousePos = e.Position;
+ Point m = e.Position - new Point ((int)ScrollX, (int)ScrollY);
+ base.checkHoverWidget (new MouseMoveEventArgs(m.X,m.Y,e.XDelta,e.YDelta));
+ }
+ public override void onMouseWheel (object sender, MouseWheelEventArgs e)
+ {
+ if (Child == null)
+ return;
+
+ if (VerticalScrolling )
+ ScrollY -= e.Delta * ScrollSpeed;
+ if (HorizontalScrolling )
+ ScrollX -= e.Delta * ScrollSpeed;
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ savedMousePos.X += e.XDelta;
+ savedMousePos.Y += e.YDelta;
+ base.onMouseMove (sender, new MouseMoveEventArgs(savedMousePos.X,savedMousePos.Y,e.XDelta,e.YDelta));
+ }
+ public override void RegisterClip (Rectangle clip)
+ {
+ base.RegisterClip (clip - new Point((int)ScrollX,(int)ScrollY));
+ }
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/src/GraphicObjects/Base/Slider.cs b/src/GraphicObjects/Base/Slider.cs
new file mode 100644
index 00000000..36f7e731
--- /dev/null
+++ b/src/GraphicObjects/Base/Slider.cs
@@ -0,0 +1,219 @@
+//
+// Slider.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Cairo;
+using System.Xml.Serialization;
+using System.ComponentModel;
+using System.Diagnostics;
+
+namespace Crow
+{
+ public class Slider : NumericControl
+ {
+ #region CTOR
+ public Slider() : base()
+ {}
+ public Slider(double minimum, double maximum, double step)
+ : base(minimum,maximum,step)
+ {
+ }
+ #endregion
+
+ #region implemented abstract members of TemplatedControl
+
+ protected override void loadTemplate (GraphicObject template = null)
+ {
+
+ }
+
+ #endregion
+
+ #region private fields
+ Rectangle cursor;
+ int _cursorSize;
+ Fill _cursorColor;
+ Orientation _orientation;
+ bool holdCursor = false;
+ #endregion
+
+ protected double unity;
+
+ #region Public properties
+ [XmlAttributeAttribute()][DefaultValue("vgradient|0:White|0,1:LightGray|0,9:LightGray|1:DimGray")]
+ public virtual Fill CursorColor {
+ get { return _cursorColor; }
+ set {
+ if (_cursorColor == value)
+ return;
+ _cursorColor = value;
+ RegisterForRedraw ();
+ NotifyValueChanged ("CursorColor", _cursorColor);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(20)]
+ public virtual int CursorSize {
+ get { return _cursorSize; }
+ set {
+ if (_cursorSize == value)
+ return;
+ _cursorSize = value;
+ RegisterForGraphicUpdate ();
+ NotifyValueChanged ("CursorSize", _cursorSize);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(Orientation.Horizontal)]
+ public virtual Orientation Orientation
+ {
+ get { return _orientation; }
+ set {
+ if (_orientation == value)
+ return;
+ _orientation = value;
+
+ RegisterForLayouting (LayoutingType.All);
+ NotifyValueChanged ("Orientation", _orientation);
+ }
+ }
+ #endregion
+
+ [XmlAttributeAttribute()][DefaultValue(10.0)]
+ public override double Maximum {
+ get { return base.Maximum; }
+ set {
+ if (value == base.Maximum)
+ return;
+ base.Maximum = value;
+ LargeIncrement = base.Maximum / 10.0;
+ SmallIncrement = LargeIncrement / 5.0;
+ }
+ }
+
+ #region GraphicObject Overrides
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+ if (Maximum <= 0)
+ return;
+
+ computeCursorPosition ();
+
+ Rectangle r = ClientRectangle;
+ PointD pStart;
+ PointD pEnd;
+ if (_orientation == Orientation.Horizontal) {
+ pStart = r.TopLeft + new Point (_cursorSize / 2, r.Height / 2);
+ pEnd = r.TopRight + new Point (-_cursorSize / 2, r.Height / 2);
+ } else {
+ pStart = r.TopLeft + new Point (r.Width / 2, _cursorSize / 2);
+ pEnd = r.BottomLeft + new Point (r.Width / 2,- _cursorSize / 2);
+ }
+
+ DrawGraduations (gr, pStart,pEnd);
+
+ DrawCursor (gr, cursor);
+ }
+ #endregion
+
+ protected virtual void DrawGraduations(Context gr, PointD pStart, PointD pEnd)
+ {
+ Foreground.SetAsSource (gr);
+
+ gr.LineWidth = 1;
+ gr.MoveTo(pStart);
+ gr.LineTo(pEnd);
+
+ gr.Stroke();
+
+ }
+ protected virtual void DrawCursor(Context gr, Rectangle _cursor)
+ {
+ CursorColor.SetAsSource (gr, _cursor);
+ CairoHelpers.CairoRectangle (gr, _cursor, CornerRadius);
+ gr.Fill();
+ }
+
+ void computeCursorPosition()
+ {
+ Rectangle r = ClientRectangle;
+ PointD p1;
+
+ if (_orientation == Orientation.Horizontal) {
+ cursor = new Rectangle (new Size (_cursorSize, (int)(r.Height)));
+ p1 = r.TopLeft + new Point (_cursorSize / 2, r.Height / 2);
+ unity = (double)(r.Width - _cursorSize) / (Maximum - Minimum);
+ cursor.TopLeft = new Point (r.Left + (int)(Value * unity),
+ (int)(p1.Y - cursor.Height / 2));
+ } else {
+ cursor = new Rectangle (new Size ((int)(r.Width), _cursorSize));
+ p1 = r.TopLeft + new Point (r.Width / 2, _cursorSize / 2);
+ unity = (double)(r.Height - _cursorSize) / (Maximum - Minimum);
+ cursor.TopLeft = new Point ((int)(p1.X - r.Width / 2),
+ r.Top + (int)(Value * unity));
+ }
+ cursor.Inflate (-1);
+ }
+
+ #region mouse handling
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseDown (sender, e);
+
+ Rectangle cursInScreenCoord = ScreenCoordinates (cursor + Slot.Position);
+ if (cursInScreenCoord.ContainsOrIsEqual (e.Position))
+ holdCursor = true;
+ else if (_orientation == Orientation.Horizontal) {
+ if (e.Position.X < cursInScreenCoord.Left)
+ Value -= LargeIncrement;
+ else
+ Value += LargeIncrement;
+ } else {
+ if (e.Position.Y < cursInScreenCoord.Top)
+ Value -= LargeIncrement;
+ else
+ Value += LargeIncrement;
+ }
+ }
+ public override void onMouseUp (object sender,MouseButtonEventArgs e)
+ {
+ base.onMouseUp (sender, e);
+
+ holdCursor = false;
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ if (holdCursor) {
+ if (_orientation == Orientation.Horizontal)
+ Value += (double)e.XDelta / unity;
+ else
+ Value += (double)e.YDelta / unity;
+ }
+
+ base.onMouseMove (sender, e);
+ }
+ #endregion
+ }
+}
diff --git a/src/GraphicObjects/Base/Spinner.cs b/src/GraphicObjects/Base/Spinner.cs
new file mode 100644
index 00000000..1bf93a37
--- /dev/null
+++ b/src/GraphicObjects/Base/Spinner.cs
@@ -0,0 +1,55 @@
+//
+// Spinner.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Spinner : NumericControl
+ {
+ #region CTOR
+ public Spinner () : base()
+ {
+ }
+ public Spinner (double minimum, double maximum, double step) :
+ base (minimum, maximum, step)
+ {
+
+ }
+ #endregion
+
+ void onUp (object sender, MouseButtonEventArgs e)
+ {
+ Value += this.SmallIncrement;
+ }
+ void onDown (object sender, MouseButtonEventArgs e)
+ {
+ Value -= this.SmallIncrement;
+ }
+
+ }
+}
+
diff --git a/src/GraphicObjects/Base/Splitter.cs b/src/GraphicObjects/Base/Splitter.cs
new file mode 100644
index 00000000..2f5dd15b
--- /dev/null
+++ b/src/GraphicObjects/Base/Splitter.cs
@@ -0,0 +1,218 @@
+//
+// Splitter.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+
+namespace Crow
+{
+ public class Splitter : GraphicObject
+ {
+ #region CTOR
+ public Splitter (): base(){}
+ #endregion
+
+ int thickness;
+
+ [XmlAttributeAttribute][DefaultValue(1)]
+ public virtual int Thickness {
+ get { return thickness; }
+ set {
+ if (thickness == value)
+ return;
+ thickness = value;
+ NotifyValueChanged ("Thickness", thickness);
+ RegisterForLayouting (LayoutingType.Sizing);
+ RegisterForGraphicUpdate ();
+ }
+ }
+
+ Unit u1, u2;
+ int init1 = -1, init2 = -1, delta = 0, min1, min2, max1 , max2;
+ GraphicObject go1 = null, go2 = null;
+
+ void initSplit(Measure m1, int size1, Measure m2, int size2){
+ if (m1 != Measure.Stretched) {
+ init1 = size1;
+ u1 = m1.Units;
+ }
+ if (m2 != Measure.Stretched) {
+ init2 = size2;
+ u2 = m2.Units;
+ }
+ }
+ void convertSizeInPix(GraphicObject g1){
+
+ }
+
+ #region GraphicObject override
+ public override ILayoutable Parent {
+ get { return base.Parent; }
+ set {
+ if (value != null) {
+ GenericStack gs = value as GenericStack;
+ if (gs == null)
+ throw new Exception ("Splitter may only be chil of stack");
+
+ }
+ base.Parent = value;
+ }
+ }
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseEnter (sender, e);
+ if ((Parent as GenericStack).Orientation == Orientation.Horizontal)
+ CurrentInterface.MouseCursor = XCursor.H;
+ else
+ CurrentInterface.MouseCursor = XCursor.V;
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+ CurrentInterface.MouseCursor = XCursor.Default;
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseDown (sender, e);
+ go1 = go2 = null;
+ init1 = init2 = -1;
+ delta = 0;
+
+ GenericStack gs = Parent as GenericStack;
+ int ptrSplit = gs.Children.IndexOf (this);
+ if (ptrSplit == 0 || ptrSplit == gs.Children.Count - 1)
+ return;
+
+ go1 = gs.Children [ptrSplit - 1];
+ go2 = gs.Children [ptrSplit + 1];
+
+ if (gs.Orientation == Orientation.Horizontal) {
+ initSplit (go1.Width, go1.Slot.Width, go2.Width, go2.Slot.Width);
+ min1 = go1.MinimumSize.Width;
+ min2 = go2.MinimumSize.Width;
+ max1 = go1.MaximumSize.Width;
+ max2 = go2.MaximumSize.Width;
+ if (init1 >= 0)
+ go1.Width = init1;
+ if (init2 >= 0)
+ go2.Width = init2;
+ } else {
+ initSplit (go1.Height, go1.Slot.Height, go2.Height, go2.Slot.Height);
+ min1 = go1.MinimumSize.Height;
+ min2 = go2.MinimumSize.Height;
+ max1 = go1.MaximumSize.Height;
+ max2 = go2.MaximumSize.Height;
+ if (init1 >= 0)
+ go1.Height = init1;
+ if (init2 >= 0)
+ go2.Height = init2;
+ }
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ if (!IsActive)
+ return;
+
+ GenericStack gs = Parent as GenericStack;
+ int newDelta = delta, size1 = init1 , size2 = init2;
+ if (gs.Orientation == Orientation.Horizontal) {
+ newDelta -= e.XDelta;
+ if (size1 < 0)
+ size1 = go1.Slot.Width + delta;
+ if (size2 < 0)
+ size2 = go2.Slot.Width - delta;
+ } else {
+ newDelta -= e.YDelta;
+ if (size1 < 0)
+ size1 = go1.Slot.Height + delta;
+ if (size2 < 0)
+ size2 = go2.Slot.Height - delta;
+ }
+
+ if (size1 - newDelta < min1 || (max1 > 0 && size1 - newDelta > max1) ||
+ size2 + newDelta < min2 || (max2 > 0 && size2 + newDelta > max2))
+ return;
+
+ delta = newDelta;
+
+ if (gs.Orientation == Orientation.Horizontal) {
+ if (init1 >= 0)
+ go1.Width = init1 - delta;
+ if (init2 >= 0)
+ go2.Width = init2 + delta;
+ } else {
+ if (init1 >= 0)
+ go1.Height = init1 - delta;
+ if (init2 >= 0)
+ go2.Height = init2 + delta;
+ }
+ }
+ public override void onMouseUp (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseUp (sender, e);
+
+ GenericStack gs = Parent as GenericStack;
+
+ if (init1 >= 0 && u1 == Unit.Percent) {
+ if (gs.Orientation == Orientation.Horizontal)
+ go1.Width = new Measure ((int)Math.Ceiling (
+ go1.Width.Value * 100.0 / (double)gs.Slot.Width), Unit.Percent);
+ else
+ go1.Height = new Measure ((int)Math.Ceiling (
+ go1.Height.Value * 100.0 / (double)gs.Slot.Height), Unit.Percent);
+ }
+ if (init2 >= 0 && u2 == Unit.Percent) {
+ if (gs.Orientation == Orientation.Horizontal)
+ go2.Width = new Measure ((int)Math.Floor (
+ go2.Width.Value * 100.0 / (double)gs.Slot.Width), Unit.Percent);
+ else
+ go2.Height = new Measure ((int)Math.Floor (
+ go2.Height.Value * 100.0 / (double)gs.Slot.Height), Unit.Percent);
+ }
+ }
+ public override bool UpdateLayout (LayoutingType layoutType)
+ {
+ GenericStack gs = Parent as GenericStack;
+ if (layoutType == LayoutingType.Width){
+ if (gs.Orientation == Orientation.Horizontal)
+ Width = thickness;
+ else
+ Width = Measure.Stretched;
+ } else if (layoutType == LayoutingType.Height){
+ if (gs.Orientation == Orientation.Vertical)
+ Height = thickness;
+ else
+ Height = Measure.Stretched;
+ }
+ return base.UpdateLayout (layoutType);
+ }
+ #endregion
+ }
+}
+
diff --git a/src/GraphicObjects/Base/TabItem.cs b/src/GraphicObjects/Base/TabItem.cs
new file mode 100644
index 00000000..7dac5472
--- /dev/null
+++ b/src/GraphicObjects/Base/TabItem.cs
@@ -0,0 +1,210 @@
+//
+// TabItem.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 TabItem : TemplatedContainer
+ {
+ #region CTOR
+ public TabItem () : base() {}
+ #endregion
+
+ #region Private fields
+ string caption;
+ Container _contentContainer;
+ GraphicObject _tabTitle;
+ int tabOffset;
+ bool isSelected;
+ Measure tabThickness;
+ #endregion
+
+ #region TemplatedControl overrides
+ public override GraphicObject Content {
+ get {
+ return _contentContainer == null ? null : _contentContainer.Child;
+ }
+ set {
+ if (Content != null) {
+ Content.LogicalParent = null;
+ _contentContainer.SetChild (null);
+ }
+ _contentContainer.SetChild(value);
+ if (value != null)
+ value.LogicalParent = this;
+ }
+ }
+ protected override void loadTemplate(GraphicObject template = null)
+ {
+ base.loadTemplate (template);
+
+ _contentContainer = this.child.FindByName ("Content") as Container;
+ _tabTitle = this.child.FindByName ("TabTitle");
+ }
+ internal GraphicObject TabTitle { get { return _tabTitle; }}
+ #endregion
+
+ [XmlAttributeAttribute][DefaultValue("18")]
+ public virtual Measure TabThickness {
+ get { return tabThickness; }
+ set {
+ if (tabThickness == value)
+ return;
+ tabThickness = value;
+ NotifyValueChanged ("TabThickness", tabThickness);
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(0)]
+ public virtual int TabOffset {
+ get { return tabOffset; }
+ set {
+ if (tabOffset == value)
+ return;
+ tabOffset = value;
+ NotifyValueChanged ("TabOffset", tabOffset);
+
+ RegisterForLayouting (LayoutingType.X);
+ RegisterForGraphicUpdate ();
+ }
+ }
+
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public virtual bool IsSelected {
+ get { return isSelected; }
+ set {
+ if (isSelected == value)
+ return;
+ isSelected = value;
+ NotifyValueChanged ("IsSelected", isSelected);
+ }
+ }
+ protected override void onDraw (Cairo.Context gr)
+ {
+ gr.Save ();
+
+ int spacing = (Parent as TabView).Spacing;
+
+ gr.MoveTo (0.5, TabTitle.Slot.Bottom-0.5);
+ gr.LineTo (TabTitle.Slot.Left - spacing, TabTitle.Slot.Bottom-0.5);
+ gr.CurveTo (
+ TabTitle.Slot.Left - spacing / 2, TabTitle.Slot.Bottom-0.5,
+ TabTitle.Slot.Left - spacing / 2, 0.5,
+ TabTitle.Slot.Left, 0.5);
+ gr.LineTo (TabTitle.Slot.Right, 0.5);
+ gr.CurveTo (
+ TabTitle.Slot.Right + spacing / 2, 0.5,
+ TabTitle.Slot.Right + spacing / 2, TabTitle.Slot.Bottom-0.5,
+ TabTitle.Slot.Right + spacing, TabTitle.Slot.Bottom-0.5);
+ gr.LineTo (Slot.Width-0.5, TabTitle.Slot.Bottom-0.5);
+
+
+ gr.LineTo (Slot.Width-0.5, Slot.Height-0.5);
+ gr.LineTo (0.5, Slot.Height-0.5);
+ gr.ClosePath ();
+ gr.LineWidth = 2;
+ Foreground.SetAsSource (gr);
+ gr.StrokePreserve ();
+
+ gr.Clip ();
+ base.onDraw (gr);
+ gr.Restore ();
+ }
+
+ #region Mouse Handling
+ public override bool MouseIsIn (Point m)
+ {
+ if (!Visible)
+ return false;
+
+ bool mouseIsInTitle = TabTitle.ScreenCoordinates (TabTitle.Slot).ContainsOrIsEqual (m);
+ if (!IsSelected)
+ return mouseIsInTitle;
+
+ return _contentContainer.ScreenCoordinates (_contentContainer.Slot).ContainsOrIsEqual (m)
+ || mouseIsInTitle;
+ }
+ bool holdCursor = false;
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseDown (sender, e);
+ holdCursor = true;
+ }
+ public override void onMouseUp (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseUp (sender, e);
+ holdCursor = false;
+ (Parent as TabView).UpdateLayout (LayoutingType.ArrangeChildren);
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ if (!(HasFocus&&holdCursor))
+ return;
+ TabView tv = Parent as TabView;
+ TabItem previous = null, next = null;
+ int tmp = TabOffset + e.XDelta;
+ if (tmp < tv.Spacing)
+ TabOffset = tv.Spacing;
+ else if (tmp > Parent.getSlot ().Width - TabTitle.Slot.Width - tv.Spacing)
+ TabOffset = Parent.getSlot ().Width - TabTitle.Slot.Width - tv.Spacing;
+ else{
+ int idx = tv.Children.IndexOf (this);
+ if (idx > 0 && e.XDelta < 0) {
+ previous = tv.Children [idx - 1] as TabItem;
+
+ if (tmp < previous.TabOffset + previous.TabTitle.Slot.Width / 2) {
+ tv.Children.RemoveAt (idx);
+ tv.Children.Insert (idx - 1, this);
+ tv.SelectedTab = idx - 1;
+ tv.UpdateLayout (LayoutingType.ArrangeChildren);
+ }
+
+ }else if (idx < tv.Children.Count - 1 && e.XDelta > 0) {
+ next = tv.Children [idx + 1] as TabItem;
+ if (tmp > next.TabOffset - next.TabTitle.Slot.Width / 2){
+ tv.Children.RemoveAt (idx);
+ tv.Children.Insert (idx + 1, this);
+ tv.SelectedTab = idx + 1;
+ tv.UpdateLayout (LayoutingType.ArrangeChildren);
+ }
+ }
+ TabOffset = tmp;
+ }
+ }
+ public void butCloseTabClick (object sender, MouseButtonEventArgs e){
+ (Parent as TabView).RemoveChild(this);
+ }
+ #endregion
+
+ }
+}
+
diff --git a/src/GraphicObjects/Base/TabView.cs b/src/GraphicObjects/Base/TabView.cs
new file mode 100644
index 00000000..ecf38f10
--- /dev/null
+++ b/src/GraphicObjects/Base/TabView.cs
@@ -0,0 +1,210 @@
+//
+// TabView.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Cairo;
+using System.Diagnostics;
+
+namespace Crow
+{
+ public class TabView : Group
+ {
+ #region CTOR
+ public TabView () : base() {}
+ #endregion
+
+ #region Private fields
+ int _spacing;
+ Measure tabThickness;
+ Orientation _orientation;
+ int selectedTab = 0;
+ #endregion
+
+
+ #region public properties
+ [XmlAttributeAttribute()][DefaultValue(Orientation.Horizontal)]
+ public virtual Orientation Orientation
+ {
+ get { return _orientation; }
+ set {
+ if (_orientation == value)
+ return;
+ _orientation = value;
+ NotifyValueChanged ("Orientation", _orientation);
+ if (_orientation == Orientation.Horizontal)
+ NotifyValueChanged ("TabOrientation", Orientation.Vertical);
+ else
+ NotifyValueChanged ("TabOrientation", Orientation.Horizontal);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(16)]
+ public int Spacing
+ {
+ get { return _spacing; }
+ set {
+ if (_spacing == value)
+ return;
+ _spacing = value;
+ NotifyValueChanged ("Spacing", Spacing);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(0)]
+ public virtual int SelectedTab {
+ get { return selectedTab; }
+ set {
+ if (selectedTab < Children.Count && SelectedTab >= 0)
+ (Children [selectedTab] as TabItem).IsSelected = false;
+
+ selectedTab = value;
+
+ if (selectedTab < Children.Count && SelectedTab >= 0)
+ (Children [selectedTab] as TabItem).IsSelected = true;
+
+ NotifyValueChanged ("SelectedTab", selectedTab);
+ RegisterForRedraw ();
+ }
+ }
+ #endregion
+
+ public override void AddChild (GraphicObject child)
+ {
+ TabItem ti = child as TabItem;
+ if (ti == null)
+ throw new Exception ("TabView control accept only TabItem as child.");
+
+ ti.MouseDown += Ti_MouseDown;
+
+ if (Children.Count == 0) {
+ ti.IsSelected = true;
+ SelectedTab = 0;
+ }
+
+ base.AddChild (child);
+ }
+ public override void RemoveChild (GraphicObject child)
+ {
+ base.RemoveChild (child);
+ if (selectedTab > Children.Count - 1)
+ SelectedTab--;
+ else
+ SelectedTab = selectedTab;
+ }
+ public override bool ArrangeChildren { get { return true; } }
+ public override bool UpdateLayout (LayoutingType layoutType)
+ {
+ RegisteredLayoutings &= (~layoutType);
+
+ if (layoutType == LayoutingType.ArrangeChildren) {
+ int curOffset = Spacing;
+ for (int i = 0; i < Children.Count; i++) {
+ if (!Children [i].Visible)
+ continue;
+ TabItem ti = Children [i] as TabItem;
+ ti.TabOffset = curOffset;
+ if (Orientation == Orientation.Horizontal) {
+ if (ti.TabTitle.RegisteredLayoutings.HasFlag (LayoutingType.Width))
+ return false;
+ curOffset += ti.TabTitle.Slot.Width + Spacing;
+ } else {
+ if (ti.TabTitle.RegisteredLayoutings.HasFlag (LayoutingType.Height))
+ return false;
+ curOffset += ti.TabTitle.Slot.Height + Spacing;
+ }
+ }
+
+ //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);
+ }
+ protected override void onDraw (Context gr)
+ {
+ Rectangle rBack = new Rectangle (Slot.Size);
+
+ Background.SetAsSource (gr, rBack);
+ CairoHelpers.CairoRectangle(gr,rBack, CornerRadius);
+ gr.Fill ();
+
+ gr.Save ();
+
+ if (ClipToClientRect) {
+ //clip to client zone
+ CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius);
+ gr.Clip ();
+ }
+
+ for (int i = 0; i < Children.Count; i++) {
+ if (i == SelectedTab)
+ continue;
+ Children [i].Paint (ref gr);
+ }
+
+ if (SelectedTab < Children.Count && SelectedTab >= 0)
+ Children [SelectedTab].Paint (ref gr);
+
+ gr.Restore ();
+ }
+
+ #region Mouse handling
+ public override void checkHoverWidget (MouseMoveEventArgs e)
+ {
+ if (CurrentInterface.HoverWidget != this) {
+ CurrentInterface.HoverWidget = this;
+ onMouseEnter (this, e);
+ }
+
+ if (SelectedTab > Children.Count - 1)
+ return;
+
+ if (((Children[SelectedTab] as TabItem).Content.Parent as GraphicObject).MouseIsIn(e.Position))
+ {
+ Children[SelectedTab].checkHoverWidget (e);
+ return;
+ }
+ for (int i = Children.Count - 1; i >= 0; i--) {
+ TabItem ti = Children [i] as TabItem;
+ if (ti.TabTitle.MouseIsIn(e.Position))
+ {
+ Children[i].checkHoverWidget (e);
+ return;
+ }
+ }
+ }
+ #endregion
+
+ void Ti_MouseDown (object sender, MouseButtonEventArgs e)
+ {
+ SelectedTab = Children.IndexOf (sender as GraphicObject);
+ }
+ }
+}
+
diff --git a/src/GraphicObjects/Base/TemplatedContainer.cs b/src/GraphicObjects/Base/TemplatedContainer.cs
new file mode 100644
index 00000000..1ab8cf7d
--- /dev/null
+++ b/src/GraphicObjects/Base/TemplatedContainer.cs
@@ -0,0 +1,128 @@
+//
+// TemplatedContainer.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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.Xml;
+using System.Reflection;
+
+namespace Crow
+{
+ public abstract class TemplatedContainer : TemplatedControl
+ {
+ #region CTOR
+ public TemplatedContainer () : base(){}
+ #endregion
+
+ [XmlAttributeAttribute]public virtual GraphicObject Content{ get; set;}
+
+ #region GraphicObject overrides
+ public override GraphicObject FindByName (string nameToFind)
+ {
+ if (Name == nameToFind)
+ return this;
+
+ return Content == null ? null : Content.FindByName (nameToFind);
+ }
+ public override bool Contains (GraphicObject goToFind)
+ {
+ if (Content == null)
+ return base.Contains (goToFind);
+
+ if (Content == goToFind)
+ return true;
+ return Content.Contains (goToFind);
+ }
+ #endregion
+
+ #region IXmlSerialisation Overrides
+ public override void ReadXml(System.Xml.XmlReader reader)
+ {
+ using (System.Xml.XmlReader subTree = reader.ReadSubtree ()) {
+ subTree.Read ();
+ string tmp = subTree.ReadOuterXml ();
+
+ //seek for template tag
+ using (XmlReader xr = new XmlTextReader (tmp, XmlNodeType.Element, null)) {
+ xr.Read ();
+ base.ReadXml (xr);
+ }
+ //process content
+ using (XmlReader xr = new XmlTextReader (tmp, XmlNodeType.Element, null)) {
+ xr.Read (); //skip current node
+
+ while (!xr.EOF) {
+ xr.Read (); //read first child
+
+ if (!xr.IsStartElement ())
+ continue;
+
+ if (xr.Name == "Template"){
+ xr.Skip ();
+ if (!xr.IsStartElement ())
+ continue;
+ }
+
+ Type t = Type.GetType ("Crow." + xr.Name);
+ if (t == null) {
+ Assembly a = Assembly.GetEntryAssembly ();
+ foreach (Type expT in a.GetExportedTypes ()) {
+ if (expT.Name == xr.Name) {
+ t = expT;
+ break;
+ }
+ }
+ }
+ if (t == null)
+ throw new Exception (xr.Name + " type not found");
+
+ GraphicObject go = (GraphicObject)Activator.CreateInstance (t);
+
+ (go as IXmlSerializable).ReadXml (xr);
+
+ Content = go;
+
+ xr.Read (); //closing tag
+ }
+
+ }
+ }
+ }
+ public override void WriteXml(System.Xml.XmlWriter writer)
+ {
+ base.WriteXml(writer);
+
+ if (Content == null)
+ return;
+ //TODO: if template is not the default one, we have to save it
+ writer.WriteStartElement(Content.GetType().Name);
+ (Content as IXmlSerializable).WriteXml(writer);
+ writer.WriteEndElement();
+ }
+ #endregion
+ }
+}
+
diff --git a/src/GraphicObjects/Base/TemplatedControl.cs b/src/GraphicObjects/Base/TemplatedControl.cs
new file mode 100644
index 00000000..082d0426
--- /dev/null
+++ b/src/GraphicObjects/Base/TemplatedControl.cs
@@ -0,0 +1,210 @@
+//
+// TemplatedControl.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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.IO;
+using System.Xml;
+using System.Diagnostics;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+using System.Reflection;
+
+namespace Crow
+{
+ public abstract class TemplatedControl : PrivateContainer, IXmlSerializable
+ {
+ #region CTOR
+ public TemplatedControl () : base()
+ {
+ }
+ #endregion
+
+ string _template;
+ string caption;
+
+ [XmlAttributeAttribute][DefaultValue(null)]
+ public string Template {
+ get { return _template; }
+ set {
+ if (Template == value)
+ return;
+ _template = value;
+
+ if (string.IsNullOrEmpty(_template))
+ loadTemplate ();
+ else
+ loadTemplate (CurrentInterface.Load (_template));
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("Templated Control")]
+ public virtual string Caption {
+ get { return caption; }
+ set {
+ if (caption == value)
+ return;
+ caption = value;
+ NotifyValueChanged ("Caption", caption);
+ }
+ }
+ #region GraphicObject overrides
+ public override void Initialize ()
+ {
+ loadTemplate ();
+ base.Initialize ();
+ }
+ public override GraphicObject FindByName (string nameToFind)
+ {
+ //prevent name searching in template
+ return nameToFind == this.Name ? this : null;
+ }
+ protected override void onDraw (Cairo.Context gr)
+ {
+ //onDraw is overrided to prevent default drawing of background, template top container
+ //may have a binding to root background or a fixed one.
+ //this allow applying root background to random template's component
+ gr.Save ();
+
+ if (ClipToClientRect) {
+ //clip to client zone
+ CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius);
+ gr.Clip ();
+ }
+
+ if (child != null)
+ child.Paint (ref gr);
+ gr.Restore ();
+ }
+ #endregion
+
+ protected virtual void loadTemplate(GraphicObject template = null)
+ {
+ if (template == null) {
+ if (!Interface.DefaultTemplates.ContainsKey (this.GetType ().FullName))
+ throw new Exception (string.Format ("No default template found for '{0}'", this.GetType ().FullName));
+ this.SetChild (CurrentInterface.Load (Interface.DefaultTemplates[this.GetType ().FullName]));
+ }else
+ this.SetChild (template);
+ }
+
+ //TODO:IXmlSerializable is not used anymore
+ #region IXmlSerializable
+ public override System.Xml.Schema.XmlSchema GetSchema(){ return null; }
+ public override void ReadXml(System.Xml.XmlReader reader)
+ {
+ //Template could be either an attribute containing path or expressed inlined
+ //as a Template Element
+ using (System.Xml.XmlReader subTree = reader.ReadSubtree())
+ {
+ subTree.Read ();
+
+ string template = reader.GetAttribute ("Template");
+ string tmp = subTree.ReadOuterXml ();
+
+ //Load template from path set as attribute in templated control
+ if (string.IsNullOrEmpty (template)) {
+ //seek for template tag first
+ using (XmlReader xr = new XmlTextReader (tmp, XmlNodeType.Element, null)) {
+ //load template first if inlined
+
+ xr.Read (); //read first child
+ xr.Read (); //skip root node
+
+ while (!xr.EOF) {
+ if (!xr.IsStartElement ()) {
+ xr.Read ();
+ continue;
+ }
+ if (xr.Name == "ItemTemplate") {
+ string dataType = "default", datas = "", itemTmp;
+ while (xr.MoveToNextAttribute ()) {
+ if (xr.Name == "DataType")
+ dataType = xr.Value;
+ else if (xr.Name == "Data")
+ datas = xr.Value;
+ }
+ xr.MoveToElement ();
+ itemTmp = xr.ReadInnerXml ();
+
+// if (ItemTemplates == null)
+// ItemTemplates = new Dictionary<string, ItemTemplate> ();
+//
+// using (IMLReader iTmp = new IMLReader (null, itemTmp)) {
+// ItemTemplates [dataType] =
+// new ItemTemplate (iTmp.RootType, iTmp.GetLoader (), dataType, datas);
+// }
+// if (!string.IsNullOrEmpty (datas))
+// ItemTemplates [dataType].CreateExpandDelegate(this);
+
+ continue;
+ }
+ if (xr.Name == "Template") {
+ xr.Read ();
+
+ Type t = Type.GetType ("Crow." + xr.Name);
+ if (t == null) {
+ Assembly a = Assembly.GetEntryAssembly ();
+ foreach (Type expT in a.GetExportedTypes ()) {
+ if (expT.Name == xr.Name) {
+ t = expT;
+ break;
+ }
+ }
+ }
+ GraphicObject go = (GraphicObject)Activator.CreateInstance (t);
+ (go as IXmlSerializable).ReadXml (xr);
+
+ loadTemplate (go);
+ continue;
+ }
+ xr.ReadInnerXml ();
+ }
+ }
+ } else
+ loadTemplate (CurrentInterface.Load (template));
+
+ //if no template found, load default one
+ if (this.child == null)
+ loadTemplate ();
+
+ //normal xml read
+ using (XmlReader xr = new XmlTextReader (tmp, XmlNodeType.Element, null)) {
+ xr.Read ();
+ base.ReadXml(xr);
+ }
+ }
+ }
+ public override void WriteXml(System.Xml.XmlWriter writer)
+ {
+ //TODO:
+ throw new NotImplementedException();
+ }
+ #endregion
+ }
+}
+
diff --git a/src/GraphicObjects/Base/TemplatedGroup.cs b/src/GraphicObjects/Base/TemplatedGroup.cs
new file mode 100644
index 00000000..e4447168
--- /dev/null
+++ b/src/GraphicObjects/Base/TemplatedGroup.cs
@@ -0,0 +1,489 @@
+//
+// TemplatedGroup.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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.Xml;
+using System.Reflection;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Collections;
+using System.Threading;
+using System.Linq;
+
+namespace Crow
+{
+ public abstract class TemplatedGroup : TemplatedControl
+ {
+ #region CTOR
+ public TemplatedGroup () : base(){}
+ #endregion
+
+ protected Group items;
+ string _itemTemplate, _dataTest;
+
+ #region events
+ public event EventHandler<SelectionChangeEventArgs> SelectedItemChanged;
+ public event EventHandler Loaded;
+ #endregion
+
+ IList data;
+ int _selectedIndex = -1;
+ Color selBackground, selForeground;
+
+ int itemPerPage = 50;
+ CrowThread loadingThread = null;
+ volatile bool cancelLoading = false;
+
+ bool isPaged = false;
+
+ #region Templating
+ //TODO: dont instantiate ItemTemplates if not used
+ //but then i should test if null in msil gen
+ public Dictionary<string, ItemTemplate> ItemTemplates = new Dictionary<string, Crow.ItemTemplate>();
+
+ /// <summary>
+ /// Default item template
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue("#Crow.Templates.ItemTemplate.goml")]
+ public string ItemTemplate {
+ get { return _itemTemplate; }
+ set {
+ if (value == _itemTemplate)
+ return;
+
+ _itemTemplate = value;
+
+ //TODO:reload list with new template?
+ NotifyValueChanged("ItemTemplate", _itemTemplate);
+ }
+ }
+ protected override void loadTemplate(GraphicObject template = null)
+ {
+ base.loadTemplate (template);
+
+ items = this.child.FindByName ("ItemsContainer") as Group;
+ if (items == null)
+ throw new Exception ("TemplatedGroup template Must contain a Group named 'ItemsContainer'");
+ if (items.Children.Count == 0)
+ NotifyValueChanged ("HasChildren", false);
+ else
+ NotifyValueChanged ("HasChildren", true);
+ }
+ /// <summary>
+ /// Use to define condition on Data item for selecting among ItemTemplates.
+ /// Default value is 'TypeOf' for selecting Template depending on Type of Data.
+ /// Other possible values are properties of Data
+ /// </summary>
+ /// <value>The data property test.</value>
+ [XmlAttributeAttribute][DefaultValue("TypeOf")]
+ public string DataTest {
+ get { return _dataTest; }
+ set {
+ if (value == _dataTest)
+ return;
+
+ _dataTest = value;
+
+ NotifyValueChanged("DataTest", _dataTest);
+ }
+ }
+ #endregion
+
+ public virtual List<GraphicObject> Items{
+ get {
+ return isPaged ? items.Children.SelectMany(x => (x as Group).Children).ToList()
+ : items.Children;
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(-1)]public int SelectedIndex{
+ get { return _selectedIndex; }
+ set {
+ if (value == _selectedIndex)
+ return;
+
+ if (_selectedIndex >= 0 && Items.Count > _selectedIndex) {
+ Items[_selectedIndex].Foreground = Color.Transparent;
+ Items[_selectedIndex].Background = Color.Transparent;
+ }
+
+ _selectedIndex = value;
+
+ if (_selectedIndex >= 0 && Items.Count > _selectedIndex) {
+ Items[_selectedIndex].Foreground = SelectionForeground;
+ Items[_selectedIndex].Background = SelectionBackground;
+ }
+
+ NotifyValueChanged ("SelectedIndex", _selectedIndex);
+ NotifyValueChanged ("SelectedItem", SelectedItem);
+ SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
+ }
+ }
+ [XmlIgnore]public virtual object SelectedItem{
+ get { return data == null ? null : _selectedIndex < 0 ? null : data[_selectedIndex]; }
+ }
+ [XmlIgnore]public bool HasItems {
+ get { return Items.Count > 0; }
+ }
+ [XmlAttributeAttribute]public IList Data {
+ get { return data; }
+ set {
+ if (value == data)
+ return;
+
+ cancelLoadingThread ();
+
+ data = value;
+
+ NotifyValueChanged ("Data", data);
+
+ lock (CurrentInterface.LayoutMutex)
+ ClearItems ();
+
+ if (data == null)
+ return;
+
+ loadingThread = new CrowThread (this, loading);
+ loadingThread.Finished += (object sender, EventArgs e) => (sender as TemplatedGroup).Loaded.Raise (sender, e);
+ loadingThread.Start ();
+
+ NotifyValueChanged ("SelectedIndex", _selectedIndex);
+ NotifyValueChanged ("SelectedItem", SelectedItem);
+ SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
+ NotifyValueChanged ("HasItems", HasItems);
+ }
+ }
+
+ [XmlAttributeAttribute][DefaultValue("SteelBlue")]
+ public virtual Color SelectionBackground {
+ get { return selBackground; }
+ set {
+ if (value == selBackground)
+ return;
+ selBackground = value;
+ NotifyValueChanged ("SelectionBackground", selBackground);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("White")]
+ public virtual Color SelectionForeground {
+ get { return selForeground; }
+ set {
+ if (value == selForeground)
+ return;
+ selForeground = value;
+ NotifyValueChanged ("SelectionForeground", selForeground);
+ RegisterForRedraw ();
+ }
+ }
+
+ protected void raiseSelectedItemChanged(){
+ SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
+ }
+
+
+ public virtual void AddItem(GraphicObject g){
+ items.AddChild (g);
+ g.LogicalParent = this;
+ NotifyValueChanged ("HasChildren", true);
+ }
+ public virtual void RemoveItem(GraphicObject g)
+ {
+ g.LogicalParent = null;
+ items.RemoveChild (g);
+ if (items.Children.Count == 0)
+ NotifyValueChanged ("HasChildren", false);
+ }
+
+ public virtual void ClearItems()
+ {
+ _selectedIndex = -1;
+ NotifyValueChanged ("SelectedIndex", _selectedIndex);
+ NotifyValueChanged ("SelectedItem", null);
+
+ items.ClearChildren ();
+ NotifyValueChanged ("HasChildren", false);
+ }
+
+
+ #region GraphicObject overrides
+ public override GraphicObject FindByName (string nameToFind)
+ {
+ if (Name == nameToFind)
+ return this;
+
+ foreach (GraphicObject w in Items) {
+ GraphicObject r = w.FindByName (nameToFind);
+ if (r != null)
+ return r;
+ }
+ return null;
+ }
+ public override bool Contains (GraphicObject goToFind)
+ {
+ foreach (GraphicObject w in Items) {
+ if (w == goToFind)
+ return true;
+ if (w.Contains (goToFind))
+ return true;
+ }
+ return base.Contains(goToFind);
+ }
+// public override void ClearBinding ()
+// {
+// if (items != null)
+// items.ClearBinding ();
+//
+// base.ClearBinding ();
+// }
+// public override void ResolveBindings ()
+// {
+// base.ResolveBindings ();
+// if (items != null)
+// items.ResolveBindings ();
+// }
+ #endregion
+
+ #region IXmlSerialisation Overrides
+ public override void ReadXml(System.Xml.XmlReader reader)
+ {
+ using (System.Xml.XmlReader subTree = reader.ReadSubtree ()) {
+ subTree.Read ();
+ string tmp = subTree.ReadOuterXml ();
+
+ //seek for template tag
+ using (XmlReader xr = new XmlTextReader (tmp, XmlNodeType.Element, null)) {
+ xr.Read ();
+ base.ReadXml (xr);
+ }
+ //process content
+ using (XmlReader xr = new XmlTextReader (tmp, XmlNodeType.Element, null)) {
+ xr.Read (); //skip current node
+
+ while (!xr.EOF) {
+ xr.Read (); //read first child
+
+ if (!xr.IsStartElement ())
+ continue;
+
+ if (xr.Name == "Template" || Name == "ItemTemplate"){
+ xr.Skip ();
+ if (!xr.IsStartElement ())
+ continue;
+ }
+
+ Type t = Type.GetType ("Crow." + xr.Name);
+ if (t == null) {
+ Assembly a = Assembly.GetEntryAssembly ();
+ foreach (Type expT in a.GetExportedTypes ()) {
+ if (expT.Name == xr.Name) {
+ t = expT;
+ break;
+ }
+ }
+ }
+ if (t == null)
+ throw new Exception (xr.Name + " type not found");
+
+ GraphicObject go = (GraphicObject)Activator.CreateInstance (t);
+
+ (go as IXmlSerializable).ReadXml (xr);
+
+ AddItem (go);
+
+ xr.Read (); //closing tag
+ }
+
+ }
+ }
+ }
+ public override void WriteXml(System.Xml.XmlWriter writer)
+ {
+ throw new NotImplementedException ();
+ }
+ #endregion
+
+ void loading(){
+ if (ItemTemplates == null)
+ ItemTemplates = new Dictionary<string, ItemTemplate> ();
+ if (!ItemTemplates.ContainsKey ("default"))
+ ItemTemplates ["default"] = Interface.GetItemTemplate (ItemTemplate);
+
+ for (int i = 1; i <= (data.Count / itemPerPage) + 1; i++) {
+ if (cancelLoading)
+ return;
+ loadPage (i);
+ Thread.Sleep (1);
+ }
+ }
+ void cancelLoadingThread(){
+ if (loadingThread != null)
+ loadingThread.Cancel ();
+ }
+ void loadPage(int pageNum)
+ {
+ #if DEBUG_LOAD
+ Stopwatch loadingTime = new Stopwatch ();
+ loadingTime.Start ();
+ #endif
+
+ Group page;
+ if (typeof(Wrapper).IsAssignableFrom (items.GetType ())) {
+ page = items;
+ itemPerPage = int.MaxValue;
+ } else if (typeof(GenericStack).IsAssignableFrom (items.GetType ())) {
+ GenericStack gs = new GenericStack ();
+ gs.CurrentInterface = items.CurrentInterface;
+ gs.Initialize ();
+ gs.Orientation = (items as GenericStack).Orientation;
+ gs.Width = items.Width;
+ gs.Height = items.Height;
+ gs.VerticalAlignment = items.VerticalAlignment;
+ gs.HorizontalAlignment = items.HorizontalAlignment;
+ page = gs;
+ page.Name = "page" + pageNum;
+ isPaged = true;
+ } else {
+ page = Activator.CreateInstance (items.GetType ()) as Group;
+ page.Name = "page" + pageNum;
+ isPaged = true;
+ }
+
+ for (int i = (pageNum - 1) * itemPerPage; i < pageNum * itemPerPage; i++) {
+ if (i >= data.Count)
+ break;
+ if (cancelLoading)
+ return;
+
+ loadItem (i, page);
+ }
+
+ if (page == items)
+ return;
+ lock (CurrentInterface.LayoutMutex)
+ items.AddChild (page);
+
+ #if DEBUG_LOAD
+ loadingTime.Stop ();
+ Debug.WriteLine("Listbox {2} Loading: {0} ticks \t, {1} ms",
+ loadingTime.ElapsedTicks,
+ loadingTime.ElapsedMilliseconds, this.ToString());
+ #endif
+ }
+ string getItempKey(Type dataType, object o){
+ try {
+ return dataType.GetProperty (_dataTest).GetGetMethod ().Invoke (o, null).ToString();
+ } catch (Exception ex) {
+ return dataType.FullName;
+ }
+ }
+ protected void loadItem(int i, Group page){
+ if (data [i] == null)//TODO:surely a threading sync problem
+ return;
+ GraphicObject g = null;
+ ItemTemplate iTemp = null;
+ Type dataType = data [i].GetType ();
+ string itempKey = dataType.FullName;
+
+ if (_dataTest != "TypeOf")
+ itempKey = getItempKey (dataType, data [i]);
+
+ if (ItemTemplates.ContainsKey (itempKey))
+ iTemp = ItemTemplates [itempKey];
+ else {
+ foreach (string it in ItemTemplates.Keys) {
+ Type t = Type.GetType (it);
+ if (t == null) {
+ Assembly a = Assembly.GetEntryAssembly ();
+ foreach (Type expT in a.GetExportedTypes ()) {
+ if (expT.Name == it) {
+ t = expT;
+ break;
+ }
+ }
+ }
+ if (t == null)
+ continue;
+ if (t.IsAssignableFrom (dataType)) {//TODO:types could be cached
+ iTemp = ItemTemplates [it];
+ break;
+ }
+ }
+ if (iTemp == null)
+ iTemp = ItemTemplates ["default"];
+ }
+
+ lock (CurrentInterface.LayoutMutex) {
+ g = iTemp.CreateInstance(CurrentInterface);
+ page.AddChild (g);
+ //g.LogicalParent = this;
+ registerItemClick (g);
+ }
+
+ if (iTemp.Expand != null && g is Expandable) {
+ (g as Expandable).Expand += iTemp.Expand;
+ (g as Expandable).GetIsExpandable = iTemp.HasSubItems;
+ }
+
+ g.DataSource = data [i];
+ }
+ protected virtual void registerItemClick(GraphicObject g){
+ g.MouseClick += itemClick;
+ }
+// protected void _list_LayoutChanged (object sender, LayoutingEventArgs e)
+// {
+// #if DEBUG_LAYOUTING
+// Debug.WriteLine("list_LayoutChanged");
+// #endif
+// if (_gsList.Orientation == Orientation.Horizontal) {
+// if (e.LayoutType == LayoutingType.Width)
+// _gsList.Width = approxSize;
+// } else if (e.LayoutType == LayoutingType.Height)
+// _gsList.Height = approxSize;
+// }
+ int approxSize
+ {
+ get {
+ if (data == null)
+ return -1;
+ GenericStack page1 = items.FindByName ("page1") as GenericStack;
+ if (page1 == null)
+ return -1;
+
+ return page1.Orientation == Orientation.Horizontal ?
+ data.Count < itemPerPage ?
+ -1:
+ (int)Math.Ceiling ((double)page1.Slot.Width / (double)itemPerPage * (double)(data.Count+1)):
+ data.Count < itemPerPage ?
+ -1:
+ (int)Math.Ceiling ((double)page1.Slot.Height / (double)itemPerPage * (double)(data.Count+1));
+ }
+ }
+ internal virtual void itemClick(object sender, MouseButtonEventArgs e){
+ SelectedIndex = data.IndexOf((sender as GraphicObject).DataSource);
+ }
+ }
+}
diff --git a/src/GraphicObjects/Base/TextBox.cs b/src/GraphicObjects/Base/TextBox.cs
new file mode 100644
index 00000000..0cbee9f8
--- /dev/null
+++ b/src/GraphicObjects/Base/TextBox.cs
@@ -0,0 +1,228 @@
+//
+// TextBox.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Cairo;
+using System.Diagnostics;
+using System.Xml.Serialization;
+
+namespace Crow
+{
+ public class TextBox : Label
+ {
+ #region CTOR
+ public TextBox()
+ { }
+ public TextBox(string _initialValue)
+ : base(_initialValue)
+ {
+
+ }
+ #endregion
+
+ #region GraphicObject overrides
+ [XmlIgnore]public override bool HasFocus //trigger update when lost focus to errase text beam
+ {
+ get
+ {
+ return base.HasFocus;
+ }
+ set
+ {
+ base.HasFocus = value;
+ RegisterForRedraw();
+ }
+ }
+
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+ FontExtents fe = gr.FontExtents;
+ }
+ #endregion
+
+ #region Keyboard handling
+ public override void onKeyDown (object sender, KeyboardKeyEventArgs e)
+ {
+ base.onKeyDown (sender, e);
+
+ Key key = e.Key;
+
+ switch (key)
+ {
+ case Key.Back:
+ if (CurrentPosition == 0)
+ return;
+ this.DeleteChar();
+ break;
+ case Key.Clear:
+ break;
+ case Key.Delete:
+ if (selectionIsEmpty) {
+ if (!MoveRight ())
+ return;
+ }else if (e.Shift)
+ CurrentInterface.Clipboard = this.SelectedText;
+ this.DeleteChar ();
+ break;
+ case Key.Enter:
+ case Key.KeypadEnter:
+ if (!selectionIsEmpty)
+ this.DeleteChar ();
+ if (Multiline)
+ this.InsertLineBreak ();
+ else
+ OnTextChanged(this,new TextChangeEventArgs(Text));
+ break;
+ case Key.Escape:
+ Text = "";
+ CurrentColumn = 0;
+ SelRelease = -1;
+ break;
+ case Key.Home:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = new Point (CurrentColumn, CurrentLine);
+ if (e.Control)
+ CurrentLine = 0;
+ CurrentColumn = 0;
+ SelRelease = new Point (CurrentColumn, CurrentLine);
+ break;
+ }
+ SelRelease = -1;
+ if (e.Control)
+ CurrentLine = 0;
+ CurrentColumn = 0;
+ break;
+ case Key.End:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ if (e.Control)
+ CurrentLine = int.MaxValue;
+ CurrentColumn = int.MaxValue;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ if (e.Control)
+ CurrentLine = int.MaxValue;
+ CurrentColumn = int.MaxValue;
+ break;
+ case Key.Insert:
+ if (e.Shift)
+ this.Insert (CurrentInterface.Clipboard);
+ else if (e.Control && !selectionIsEmpty)
+ CurrentInterface.Clipboard = this.SelectedText;
+ break;
+ case Key.Left:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = new Point(CurrentColumn, CurrentLine);
+ if (e.Control)
+ GotoWordStart ();
+ else if (!MoveLeft ())
+ return;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ if (e.Control)
+ GotoWordStart ();
+ else
+ MoveLeft();
+ break;
+ case Key.Right:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ if (e.Control)
+ GotoWordEnd ();
+ else if (!MoveRight ())
+ return;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ if (e.Control)
+ GotoWordEnd ();
+ else
+ MoveRight ();
+ break;
+ case Key.Up:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ CurrentLine--;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ CurrentLine--;
+ break;
+ case Key.Down:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ CurrentLine++;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ CurrentLine++;
+ break;
+ case Key.Menu:
+ break;
+ case Key.NumLock:
+ break;
+ case Key.PageDown:
+ break;
+ case Key.PageUp:
+ break;
+ case Key.RWin:
+ break;
+ case Key.Tab:
+ this.Insert ("\t");
+ break;
+ default:
+ break;
+ }
+ RegisterForGraphicUpdate();
+ }
+ public override void onKeyPress (object sender, KeyPressEventArgs e)
+ {
+ base.onKeyPress (sender, e);
+
+ this.Insert (e.KeyChar.ToString());
+
+ SelRelease = -1;
+ SelBegin = new Point(CurrentColumn, SelBegin.Y);
+
+ RegisterForGraphicUpdate();
+ }
+ #endregion
+ }
+}
diff --git a/src/GraphicObjects/Base/TextRun.cs b/src/GraphicObjects/Base/TextRun.cs
new file mode 100644
index 00000000..a312ca1e
--- /dev/null
+++ b/src/GraphicObjects/Base/TextRun.cs
@@ -0,0 +1,315 @@
+//
+// TextRun.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 System.Diagnostics;
+using Cairo;
+using System.Text.RegularExpressions;
+using System.Xml.Serialization;
+using System.ComponentModel;
+
+namespace Crow
+{
+ public class TextRun : GraphicObject
+ {
+ #region CTOR
+ public TextRun ()
+ {
+
+ }
+ public TextRun (string _text)
+ : base ()
+ {
+ Text = _text;
+ }
+ #endregion
+
+ //TODO:change protected to private
+
+ #region private and protected fields
+ protected string _text = "label";
+ Alignment _textAlignment = Alignment.Left;
+ bool horizontalStretch = false;
+ bool verticalStretch = false;
+ bool _multiline;
+ bool wordWrap;
+ protected Rectangle rText;
+ protected float widthRatio = 1f;
+ protected float heightRatio = 1f;
+ protected FontExtents fe;
+ protected TextExtents te;
+ #endregion
+
+
+ [XmlAttributeAttribute ()]
+ [DefaultValue (Alignment.Left)]
+ public Alignment TextAlignment {
+ get { return _textAlignment; }
+ set { _textAlignment = value; }
+ }
+ [XmlAttributeAttribute ()]
+ [DefaultValue (false)]
+ public virtual bool HorizontalStretch {
+ get { return horizontalStretch; }
+ set {
+ if (horizontalStretch == value)
+ return;
+ horizontalStretch = value;
+ RegisterForRedraw ();
+ NotifyValueChanged ("HorizontalStretch", horizontalStretch);
+ }
+ }
+ [XmlAttributeAttribute ()]
+ [DefaultValue (false)]
+ public virtual bool VerticalStretch {
+ get { return verticalStretch; }
+ set {
+ if (verticalStretch == value)
+ return;
+ verticalStretch = value;
+ RegisterForRedraw ();
+ NotifyValueChanged ("VerticalStretch", verticalStretch);
+ }
+ }
+ [XmlAttributeAttribute ()]
+ [DefaultValue ("label")]
+ public string Text {
+ get {
+ return lines == null ?
+ _text : lines.Aggregate ((i, j) => i + Interface.LineBreak + j);
+ }
+ set {
+ if (_text == value)
+ return;
+
+ RegisterForGraphicUpdate ();
+
+ _text = value;
+
+ if (string.IsNullOrEmpty (_text))
+ _text = "";
+
+ lines = getLines;
+ }
+ }
+ [XmlAttributeAttribute ()]
+ [DefaultValue (false)]
+ public bool Multiline {
+ get { return _multiline; }
+ set {
+ _multiline = value;
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute ()]
+ [DefaultValue (false)]
+ public bool WordWrap {
+ get {
+ return wordWrap;
+ }
+ set {
+ if (wordWrap == value)
+ return;
+ wordWrap = value;
+ RegisterForGraphicUpdate ();
+ }
+ }
+
+ List<string> lines;
+ List<string> getLines {
+ get {
+ return _multiline ?
+ Regex.Split (_text, "\r\n|\r|\n").ToList () :
+ new List<string> (new string [] { _text });
+ }
+ }
+
+ #region GraphicObject overrides
+ protected override int measureRawSize (LayoutingType lt)
+ {
+ if (lines == null)
+ lines = getLines;
+
+ using (ImageSurface img = new ImageSurface (Format.Argb32, 10, 10)) {
+ using (Context gr = new Context (img)) {
+ //Cairo.FontFace cf = gr.GetContextFontFace ();
+
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+
+
+ fe = gr.FontExtents;
+ te = new TextExtents ();
+
+ if (lt == LayoutingType.Height) {
+ int lc = lines.Count;
+ //ensure minimal height = text line height
+ if (lc == 0)
+ lc = 1;
+
+ return (int)(fe.Height * lc) + Margin * 2;
+ }
+
+ foreach (string s in lines) {
+ string l = s.Replace("\t", new String (' ', Interface.TabSize));
+ TextExtents tmp = gr.TextExtents (l);
+ if (tmp.XAdvance > te.XAdvance)
+ te = tmp;
+ }
+ return (int)Math.Ceiling (te.XAdvance) + Margin * 2;
+ }
+ }
+ }
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+ gr.FontOptions = Interface.FontRenderingOptions;
+ gr.Antialias = Interface.Antialias;
+
+ rText = new Rectangle (new Size (
+ measureRawSize (LayoutingType.Width), measureRawSize (LayoutingType.Height)));
+ rText.Width -= 2 * Margin;
+ rText.Height -= 2 * Margin;
+
+ widthRatio = 1f;
+ heightRatio = 1f;
+
+ Rectangle cb = ClientRectangle;
+
+ //ignore text alignment if size to content = true
+ //or if text size is larger than client bounds
+ if (Width < 0 || Height < 0 || rText.Width > cb.Width) {
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+ } else {
+ if (horizontalStretch) {
+ widthRatio = (float)cb.Width / rText.Width;
+ if (!verticalStretch)
+ heightRatio = widthRatio;
+ }
+ if (verticalStretch) {
+ heightRatio = (float)cb.Height / rText.Height;
+ if (!horizontalStretch)
+ widthRatio = heightRatio;
+ }
+
+ rText.Width = (int)(widthRatio * cb.Width);
+ rText.Height = (int)(heightRatio * cb.Height);
+
+ switch (TextAlignment) {
+ case Alignment.TopLeft: //ok
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+ break;
+ case Alignment.Top: //ok
+ rText.Y = cb.Y;
+ rText.X = cb.X + cb.Width / 2 - rText.Width / 2;
+ break;
+ case Alignment.TopRight: //ok
+ rText.Y = cb.Y;
+ rText.X = cb.Right - rText.Width;
+ break;
+ case Alignment.Left://ok
+ rText.X = cb.X;
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ break;
+ case Alignment.Right://ok
+ rText.X = cb.X + cb.Width - rText.Width;
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ break;
+ case Alignment.Bottom://ok
+ rText.X = cb.Width / 2 - rText.Width / 2;
+ rText.Y = cb.Height - rText.Height;
+ break;
+ case Alignment.BottomLeft://ok
+ rText.X = cb.X;
+ rText.Y = cb.Bottom - rText.Height;
+ break;
+ case Alignment.BottomRight://ok
+ rText.Y = cb.Bottom - rText.Height;
+ rText.X = cb.Right - rText.Width;
+ break;
+ case Alignment.Center://ok
+ rText.X = cb.X + cb.Width / 2 - rText.Width / 2;
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ break;
+ }
+ }
+
+ gr.FontMatrix = new Matrix (widthRatio * Font.Size, 0, 0, heightRatio * Font.Size, 0, 0);
+
+
+ int curLineCount = 0;
+ for (int i = 0; i < lines.Count; i++) {
+ string l = lines [i].Replace ("\t", new String (' ', Interface.TabSize));
+ List<string> wl = new List<string> ();
+ int lineLength = (int)gr.TextExtents (l).XAdvance;
+
+ if (wordWrap && lineLength > cb.Width) {
+ string tmpLine = "";
+ int curChar = 0;
+ while (curChar < l.Length) {
+ tmpLine += l [curChar];
+ if ((int)gr.TextExtents (tmpLine).XAdvance > cb.Width) {
+ tmpLine = tmpLine.Remove (tmpLine.Length - 1);
+ wl.Add (tmpLine);
+ tmpLine = "";
+ continue;
+ }
+ curChar++;
+ }
+ wl.Add (tmpLine);
+ } else
+ wl.Add (l);
+
+ foreach (string ll in wl) {
+ lineLength = (int)gr.TextExtents (ll).XAdvance;
+
+
+ if (string.IsNullOrWhiteSpace (ll)) {
+ curLineCount++;
+ continue;
+ }
+
+ Foreground.SetAsSource (gr);
+ gr.MoveTo (rText.X, rText.Y + fe.Ascent + fe.Height * curLineCount);
+
+ gr.ShowText (ll);
+ gr.Fill ();
+
+ curLineCount++;
+ }
+ }
+ }
+ #endregion
+ }
+}
diff --git a/src/GraphicObjects/Base/TreeView.cs b/src/GraphicObjects/Base/TreeView.cs
new file mode 100644
index 00000000..6549f148
--- /dev/null
+++ b/src/GraphicObjects/Base/TreeView.cs
@@ -0,0 +1,120 @@
+//
+// TreeView.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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.Diagnostics;
+using System.ComponentModel;
+
+namespace Crow
+{
+ //treeview expect expandable child (or not)
+ //if their are expandable, some functions and events are added
+ public class TreeView : TemplatedGroup
+ {
+ GraphicObject selectedItemContainer = null;
+ bool isRoot;
+
+ #region CTOR
+ public TreeView () : base()
+ {
+ }
+ #endregion
+
+ [XmlAttributeAttribute()][DefaultValue(false)]
+ public virtual bool IsRoot {
+ get { return isRoot; }
+ set {
+ if (isRoot == value)
+ return;
+ isRoot = value;
+ NotifyValueChanged ("IsRoot", isRoot);
+ }
+ }
+ [XmlIgnore]public override object SelectedItem {
+ get {
+ return selectedItemContainer == null ?
+ "" : selectedItemContainer.DataSource;
+ }
+ }
+
+ protected override void registerItemClick (GraphicObject g)
+ {
+ //register ItemClick on the Root node
+ TreeView tv = this as TreeView;
+ while (!tv.IsRoot) {
+ ILayoutable tmp = tv.Parent;
+ while (!(tmp is TreeView)) {
+ tmp = tmp.Parent;
+ }
+ tv = tmp as TreeView;
+ }
+ g.MouseClick += tv.itemClick;
+ }
+ internal override void itemClick (object sender, MouseButtonEventArgs e)
+ {
+ GraphicObject tmp = sender as GraphicObject;
+ if (!tmp.HasFocus)
+ return;
+ if (selectedItemContainer != null) {
+ selectedItemContainer.Foreground = Color.Transparent;
+ selectedItemContainer.Background = Color.Transparent;
+ }
+ selectedItemContainer = tmp;
+ selectedItemContainer.Foreground = SelectionForeground;
+ selectedItemContainer.Background = SelectionBackground;
+ NotifyValueChanged ("SelectedItem", SelectedItem);
+ raiseSelectedItemChanged ();
+ }
+
+ void onExpandAll_MouseClick (object sender, MouseButtonEventArgs e)
+ {
+ ExpandAll ();
+ }
+
+ public void ExpandAll(){
+ foreach (Group grp in items.Children) {
+ foreach (GraphicObject go in grp.Children) {
+ Expandable exp = go as Expandable;
+ if (exp == null)
+ continue;
+ TreeView subTV = exp.FindByName ("List") as TreeView;
+ if (subTV == null)
+ continue;
+ EventHandler handler = null;
+ handler = delegate(object sender, EventArgs e) {
+ TreeView tv = sender as TreeView;
+ tv.Loaded -= handler;
+ tv.ExpandAll ();
+ };
+ subTV.Loaded += handler;
+ exp.IsExpanded = true;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/GraphicObjects/Base/VerticalStack.cs b/src/GraphicObjects/Base/VerticalStack.cs
new file mode 100644
index 00000000..d5d0236b
--- /dev/null
+++ b/src/GraphicObjects/Base/VerticalStack.cs
@@ -0,0 +1,49 @@
+//
+// VerticalStack.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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;
+
+namespace Crow
+{
+ public class VerticalStack : GenericStack
+ {
+ public VerticalStack()
+ : base()
+ {
+ }
+
+ [System.Xml.Serialization.XmlIgnore]
+ public override Orientation Orientation
+ {
+ get { return Orientation.Vertical; }
+ }
+
+
+ }
+}
diff --git a/src/GraphicObjects/Base/Window.cs b/src/GraphicObjects/Base/Window.cs
new file mode 100644
index 00000000..e2820528
--- /dev/null
+++ b/src/GraphicObjects/Base/Window.cs
@@ -0,0 +1,399 @@
+//
+// Window.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Window : TemplatedContainer
+ {
+ enum Direction
+ {
+ None,
+ N,
+ S,
+ E,
+ W,
+ NW,
+ NE,
+ SW,
+ SE,
+ }
+
+ string _icon;
+ bool _resizable;
+ bool _movable;
+ bool hoverBorder = false;
+ bool alwaysOnTop = false;
+
+ Rectangle savedBounds;
+ bool _minimized = false;
+
+ Container _contentContainer;
+ Direction currentDirection = Direction.None;
+
+ #region Events
+ public event EventHandler Closing;
+ public event EventHandler Maximized;
+ public event EventHandler Unmaximized;
+ public event EventHandler Minimize;
+ #endregion
+
+ #region CTOR
+ public Window () : base() {
+
+ }
+ #endregion
+
+ #region TemplatedContainer overrides
+ public override GraphicObject Content {
+ get { return _contentContainer == null ? null : _contentContainer.Child; }
+ set { _contentContainer.SetChild(value); }
+ }
+ protected override void loadTemplate(GraphicObject template = null)
+ {
+ base.loadTemplate (template);
+ _contentContainer = this.child.FindByName ("Content") as Container;
+
+ NotifyValueChanged ("ShowNormal", false);
+ NotifyValueChanged ("ShowMinimize", true);
+ NotifyValueChanged ("ShowMaximize", true);
+ }
+ #endregion
+
+ #region public properties
+ [XmlAttributeAttribute][DefaultValue("#Crow.Images.Icons.crow.png")]
+ public string Icon {
+ get { return _icon; }
+ set {
+ if (_icon == value)
+ return;
+ _icon = value;
+ NotifyValueChanged ("Icon", _icon);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public bool Resizable {
+ get {
+ return _resizable;
+ }
+ set {
+ if (_resizable == value)
+ return;
+ _resizable = value;
+ NotifyValueChanged ("Resizable", _resizable);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public bool Movable {
+ get {
+ return _movable;
+ }
+ set {
+ if (_movable == value)
+ return;
+ _movable = value;
+ NotifyValueChanged ("Movable", _movable);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public bool IsMinimized {
+ get { return _minimized; }
+ set{
+ if (value == IsMinimized)
+ return;
+
+ _minimized = value;
+ _contentContainer.Visible = !_minimized;
+
+ NotifyValueChanged ("IsMinimized", _minimized);
+ }
+ }
+ [XmlIgnore]public bool IsMaximized {
+ get { return Width == Measure.Stretched & Height == Measure.Stretched & !_minimized; }
+ }
+ [XmlIgnore]public bool IsNormal {
+ get { return !(IsMaximized|_minimized); }
+ }
+ [XmlAttributeAttribute][DefaultValue(false)]
+ public bool AlwaysOnTop {
+ get {
+ return alwaysOnTop;
+ }
+ set {
+ if (alwaysOnTop == value)
+ return;
+ alwaysOnTop = value;
+ if (alwaysOnTop) {
+ CurrentInterface.PutOnTop (this);
+ CurrentInterface.TopWindows++;
+ }else
+ CurrentInterface.TopWindows--;
+ NotifyValueChanged ("AlwaysOnTop", alwaysOnTop);
+ }
+ }
+// [XmlAttributeAttribute()][DefaultValue(WindowState.Normal)]
+// public virtual WindowState State {
+// get { return _state; }
+// set {
+// if (_state == value)
+// return;
+// _state = value;
+// NotifyValueChanged ("State", _state);
+// NotifyValueChanged ("IsNormal", IsNormal);
+// NotifyValueChanged ("IsMaximized", IsMaximized);
+// NotifyValueChanged ("IsMinimized", IsMinimized);
+// NotifyValueChanged ("IsNotMinimized", IsNotMinimized);
+// }
+// }
+ #endregion
+
+ #region GraphicObject Overrides
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ Interface otkgw = CurrentInterface;
+
+ if (!hoverBorder) {
+ currentDirection = Direction.None;
+ CurrentInterface.MouseCursor = XCursor.Default;
+ return;
+ }
+
+ if (this.HasFocus && _movable) {
+ if (e.Mouse.IsButtonDown (MouseButton.Left)) {
+ int currentLeft = this.Left;
+ int currentTop = this.Top;
+ int currentWidth, currentHeight;
+
+ if (currentLeft == 0) {
+ currentLeft = this.Slot.Left;
+ this.Left = currentLeft;
+ }
+ if (currentTop == 0) {
+ currentTop = this.Slot.Top;
+ this.Top = currentTop;
+ }
+ if (this.Width.IsFixed)
+ currentWidth = this.Width;
+ else
+ currentWidth = this.Slot.Width;
+
+ if (this.Height.IsFixed)
+ currentHeight = this.Height;
+ else
+ currentHeight = this.Slot.Height;
+
+ switch (currentDirection) {
+ case Direction.None:
+ this.Left = currentLeft + e.XDelta;
+ this.Top = currentTop + e.YDelta;
+ break;
+ case Direction.N:
+ this.Height = currentHeight - e.YDelta;
+ if (this.Height == currentHeight - e.YDelta)
+ this.Top = currentTop + e.YDelta;
+ break;
+ case Direction.S:
+ this.Height = currentHeight + e.YDelta;
+ break;
+ case Direction.W:
+ this.Width = currentWidth - e.XDelta;
+ if (this.Width == currentWidth - e.XDelta)
+ this.Left = currentLeft + e.XDelta;
+ break;
+ case Direction.E:
+ this.Width = currentWidth + e.XDelta;
+ break;
+ case Direction.NW:
+ this.Height = currentHeight - e.YDelta;
+ if (this.Height == currentHeight - e.YDelta)
+ this.Top = currentTop + e.YDelta;
+ this.Width = currentWidth - e.XDelta;
+ if (this.Width == currentWidth - e.XDelta)
+ this.Left = currentLeft + e.XDelta;
+ break;
+ case Direction.NE:
+ this.Height = currentHeight - e.YDelta;
+ if (this.Height == currentHeight - e.YDelta)
+ this.Top = currentTop + e.YDelta;
+ this.Width = currentWidth + e.XDelta;
+ break;
+ case Direction.SW:
+ this.Width = currentWidth - e.XDelta;
+ if (this.Width == currentWidth - e.XDelta)
+ this.Left = currentLeft + e.XDelta;
+ this.Height = currentHeight + e.YDelta;
+ break;
+ case Direction.SE:
+ this.Height = currentHeight + e.YDelta;
+ this.Width = currentWidth + e.XDelta;
+ break;
+ }
+ return;
+ }
+ }
+ if (Resizable) {
+ Direction lastDir = currentDirection;
+
+ if (Math.Abs (e.Position.Y - this.Slot.Y) < Interface.BorderThreshold) {
+ if (Math.Abs (e.Position.X - this.Slot.X) < Interface.BorderThreshold)
+ currentDirection = Direction.NW;
+ else if (Math.Abs (e.Position.X - this.Slot.Right) < Interface.BorderThreshold)
+ currentDirection = Direction.NE;
+ else
+ currentDirection = Direction.N;
+ } else if (Math.Abs (e.Position.Y - this.Slot.Bottom) < Interface.BorderThreshold) {
+ if (Math.Abs (e.Position.X - this.Slot.X) < Interface.BorderThreshold)
+ currentDirection = Direction.SW;
+ else if (Math.Abs (e.Position.X - this.Slot.Right) < Interface.BorderThreshold)
+ currentDirection = Direction.SE;
+ else
+ currentDirection = Direction.S;
+ } else if (Math.Abs (e.Position.X - this.Slot.X) < Interface.BorderThreshold)
+ currentDirection = Direction.W;
+ else if (Math.Abs (e.Position.X - this.Slot.Right) < Interface.BorderThreshold)
+ currentDirection = Direction.E;
+ else
+ currentDirection = Direction.None;
+
+ if (currentDirection != lastDir) {
+ switch (currentDirection) {
+ case Direction.None:
+ otkgw.MouseCursor = XCursor.Default;
+ break;
+ case Direction.N:
+ otkgw.MouseCursor = XCursor.V;
+ break;
+ case Direction.S:
+ otkgw.MouseCursor = XCursor.V;
+ break;
+ case Direction.E:
+ otkgw.MouseCursor = XCursor.H;
+ break;
+ case Direction.W:
+ otkgw.MouseCursor = XCursor.H;
+ break;
+ case Direction.NW:
+ otkgw.MouseCursor = XCursor.NW;
+ break;
+ case Direction.NE:
+ otkgw.MouseCursor = XCursor.NE;
+ break;
+ case Direction.SW:
+ otkgw.MouseCursor = XCursor.SW;
+ break;
+ case Direction.SE:
+ otkgw.MouseCursor = XCursor.SE;
+ break;
+ }
+ }
+ }
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseDown (sender, e);
+ }
+ #endregion
+
+ protected void onMaximized (object sender, EventArgs e){
+ lock (CurrentInterface.LayoutMutex) {
+ if (!IsMinimized)
+ savedBounds = this.LastPaintedSlot;
+ this.Left = this.Top = 0;
+ this.RegisterForLayouting (LayoutingType.Positioning);
+ this.Width = this.Height = Measure.Stretched;
+ IsMinimized = false;
+ Resizable = false;
+ NotifyValueChanged ("ShowNormal", true);
+ NotifyValueChanged ("ShowMinimize", true);
+ NotifyValueChanged ("ShowMaximize", false);
+ }
+
+ Maximized.Raise (sender, e);
+
+
+
+ }
+ protected void onUnmaximized (object sender, EventArgs e){
+ lock (CurrentInterface.LayoutMutex) {
+ this.Left = savedBounds.Left;
+ this.Top = savedBounds.Top;
+ this.Width = savedBounds.Width;
+ this.Height = savedBounds.Height;
+ IsMinimized = false;
+ Resizable = true;
+ NotifyValueChanged ("ShowNormal", false);
+ NotifyValueChanged ("ShowMinimize", true);
+ NotifyValueChanged ("ShowMaximize", true);
+ }
+
+ Unmaximized.Raise (sender, e);
+ }
+ protected void onMinimized (object sender, EventArgs e){
+ lock (CurrentInterface.LayoutMutex) {
+ if (IsNormal)
+ savedBounds = this.LastPaintedSlot;
+ Width = 200;
+ Height = 20;
+ Resizable = false;
+ IsMinimized = true;
+ NotifyValueChanged ("ShowNormal", true);
+ NotifyValueChanged ("ShowMinimize", false);
+ NotifyValueChanged ("ShowMaximize", true);
+ }
+
+ Minimize.Raise (sender, e);
+ }
+ protected void onBorderMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ hoverBorder = false;
+ currentDirection = Direction.None;
+ CurrentInterface.MouseCursor = XCursor.Default;
+ }
+ protected void onBorderMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ hoverBorder = true;
+ }
+
+
+ protected void butQuitPress (object sender, MouseButtonEventArgs e)
+ {
+ CurrentInterface.MouseCursor = XCursor.Default;
+ close ();
+ }
+
+ protected void close(){
+ Closing.Raise (this, null);
+ CurrentInterface.DeleteWidget (this);
+ }
+ }
+}
+
diff --git a/src/GraphicObjects/Base/Wrapper.cs b/src/GraphicObjects/Base/Wrapper.cs
new file mode 100644
index 00000000..8b282ebb
--- /dev/null
+++ b/src/GraphicObjects/Base/Wrapper.cs
@@ -0,0 +1,233 @@
+//
+// Wrapper.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 Wrapper : GenericStack
+ {
+ public Wrapper () : base()
+ {}
+
+ #region Group Overrides
+ public override void ChildrenLayoutingConstraints (ref LayoutingType layoutType)
+ {
+ layoutType &= (~LayoutingType.Positioning);
+ }
+ public override void ComputeChildrenPositions()
+ {
+ int dx = 0;
+ int dy = 0;
+
+ if (Orientation == Orientation.Vertical) {
+ int tallestChild = 0;
+ foreach (GraphicObject c in Children) {
+ if (!c.Visible)
+ continue;
+ if (dx + c.Slot.Width > ClientRectangle.Width) {
+ dx = 0;
+ dy += tallestChild + Spacing;
+ c.Slot.X = dx;
+ c.Slot.Y = dy;
+ tallestChild = c.Slot.Height;
+ } else {
+ if (tallestChild < c.Slot.Height)
+ tallestChild = c.Slot.Height;
+ c.Slot.X = dx;
+ c.Slot.Y = dy;
+ }
+ dx += c.Slot.Width + Spacing;
+ }
+ } else {
+ int largestChild = 0;
+ foreach (GraphicObject c in Children) {
+ if (!c.Visible)
+ continue;
+ if (dy + c.Slot.Height > ClientRectangle.Height) {
+ dy = 0;
+ dx += largestChild + Spacing;
+ c.Slot.X = dx;
+ c.Slot.Y = dy;
+ largestChild = c.Slot.Width;
+ } else {
+ if (largestChild < c.Slot.Width)
+ largestChild = c.Slot.Width;
+ c.Slot.X = dx;
+ c.Slot.Y = dy;
+ }
+ dy += c.Slot.Height + Spacing;
+ }
+ }
+ IsDirty = true;
+ }
+ public override void OnChildLayoutChanges (object sender, LayoutingEventArgs arg)
+ {
+ //children can't stretch in a wrapper
+ GraphicObject go = sender as GraphicObject;
+ //System.Diagnostics.Debug.WriteLine ("wrapper child layout change: " + go.LastSlots.ToString() + " => " + go.Slot.ToString());
+ switch (arg.LayoutType) {
+ case LayoutingType.Width:
+ if (Orientation == Orientation.Horizontal && go.Width.Units == Unit.Percent){
+ go.Width = Measure.Fit;
+ return;
+ }
+ this.RegisterForLayouting (LayoutingType.Width);
+ break;
+ case LayoutingType.Height:
+ if (Orientation == Orientation.Vertical && go.Height.Units == Unit.Percent) {
+ go.Height = Measure.Fit;
+ return;
+ }
+ this.RegisterForLayouting (LayoutingType.Height);
+ break;
+ default:
+ return;
+ }
+ this.RegisterForLayouting (LayoutingType.ArrangeChildren);
+ }
+ #endregion
+
+ #region GraphicObject Overrides
+ protected override int measureRawSize (LayoutingType lt)
+ {
+ int tmp = 0;
+ //Wrapper can't fit in the opposite direction of the wrapper, this func is called only if Fit
+ if (lt == LayoutingType.Width) {
+ if (Orientation == Orientation.Vertical) {
+ Width = Measure.Stretched;
+ return -1;
+ } else if (RegisteredLayoutings.HasFlag (LayoutingType.Height))
+ return -1;
+ else {
+ int dy = 0;
+ int largestChild = 0;
+ lock (Children) {
+ foreach (GraphicObject c in Children) {
+ if (!c.Visible)
+ continue;
+ if (c.Height.Units == Unit.Percent &&
+ c.RegisteredLayoutings.HasFlag (LayoutingType.Height))
+ return -1;
+ if (dy + c.Slot.Height > ClientRectangle.Height) {
+ dy = 0;
+ tmp += largestChild + Spacing;
+ largestChild = c.Slot.Width;
+ } else if (largestChild < c.Slot.Width)
+ largestChild = c.Slot.Width;
+
+ dy += c.Slot.Height + Spacing;
+ }
+ if (dy == 0)
+ tmp -= Spacing;
+ return tmp + largestChild + 2 * Margin;
+ }
+ }
+ } else if (Orientation == Orientation.Horizontal) {
+ Height = Measure.Stretched;
+ return -1;
+ } else if (RegisteredLayoutings.HasFlag (LayoutingType.Width))
+ return -1;
+ else {
+ int dx = 0;
+ int tallestChild = 0;
+ lock (Children) {
+ foreach (GraphicObject c in Children) {
+ if (!c.Visible)
+ continue;
+ if (c.Width.Units == Unit.Percent &&
+ c.RegisteredLayoutings.HasFlag (LayoutingType.Width))
+ return -1;
+ if (dx + c.Slot.Width > ClientRectangle.Width) {
+ dx = 0;
+ tmp += tallestChild + Spacing;
+ tallestChild = c.Slot.Height;
+ } else if (tallestChild < c.Slot.Height)
+ tallestChild = c.Slot.Height;
+
+ dx += c.Slot.Width + Spacing;
+ }
+ if (dx == 0)
+ tmp -= Spacing;
+ return tmp + tallestChild + 2 * Margin;
+ }
+ }
+ }
+
+ public override bool UpdateLayout (LayoutingType layoutType)
+ {
+ RegisteredLayoutings &= (~layoutType);
+
+ if (layoutType == LayoutingType.ArrangeChildren) {
+ if ((RegisteredLayoutings & LayoutingType.Sizing) != 0)
+ return false;
+
+ 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 OnLayoutChanges (LayoutingType layoutType)
+ {
+ #if DEBUG_LAYOUTING
+ CurrentInterface.currentLQI.Slot = LastSlots;
+ CurrentInterface.currentLQI.Slot = Slot;
+ #endif
+ switch (layoutType) {
+ case LayoutingType.Width:
+ foreach (GraphicObject c in Children) {
+ if (c.Width.Units == Unit.Percent)
+ c.RegisterForLayouting (LayoutingType.Width);
+ }
+ if (Height == Measure.Fit)
+ RegisterForLayouting (LayoutingType.Height);
+ RegisterForLayouting (LayoutingType.X);
+ break;
+ case LayoutingType.Height:
+ foreach (GraphicObject c in Children) {
+ if (c.Height.Units == Unit.Percent)
+ c.RegisterForLayouting (LayoutingType.Height);
+ }
+ if (Width == Measure.Fit)
+ RegisterForLayouting (LayoutingType.Width);
+ RegisterForLayouting (LayoutingType.Y);
+ break;
+ default:
+ return;
+ }
+ RegisterForLayouting (LayoutingType.ArrangeChildren);
+ raiseLayoutChanged (new LayoutingEventArgs (layoutType));
+ }
+ #endregion
+ }
+}
+