aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/GraphicObjects
diff options
context:
space:
mode:
authorjpbruyere <jp.bruyere@hotmail.com>2014-12-18 22:41:10 +0100
committerjpbruyere <jp.bruyere@hotmail.com>2014-12-18 22:41:10 +0100
commitd5a5d9516969d7a0fb665a72be7269e7bd51ad58 (patch)
tree64807d55e1994b3c700e9628ac7a6ba602a4426f /src/GraphicObjects
parent02fed0f59e37aa66381023ec1543cca9a54ac118 (diff)
misa a jour
Diffstat (limited to 'src/GraphicObjects')
-rw-r--r--src/GraphicObjects/Button.cs266
-rw-r--r--src/GraphicObjects/Container.cs214
-rw-r--r--src/GraphicObjects/GenericStack.cs163
-rw-r--r--src/GraphicObjects/GraduatedSlider.cs51
-rw-r--r--src/GraphicObjects/GraphicObject.cs951
-rw-r--r--src/GraphicObjects/Group.cs324
-rw-r--r--src/GraphicObjects/GroupBox.cs159
-rw-r--r--src/GraphicObjects/HorizontalStack.cs27
-rw-r--r--src/GraphicObjects/ILayoutable.cs28
-rw-r--r--src/GraphicObjects/Image.cs115
-rw-r--r--src/GraphicObjects/Label.cs294
-rw-r--r--src/GraphicObjects/NumericControl.cs119
-rw-r--r--src/GraphicObjects/ProgressBar.cs110
-rw-r--r--src/GraphicObjects/Scroller.cs219
-rw-r--r--src/GraphicObjects/Slider.cs156
-rw-r--r--src/GraphicObjects/TextBox.cs420
-rw-r--r--src/GraphicObjects/VerticalStack.cs25
17 files changed, 3641 insertions, 0 deletions
diff --git a/src/GraphicObjects/Button.cs b/src/GraphicObjects/Button.cs
new file mode 100644
index 00000000..c426d51d
--- /dev/null
+++ b/src/GraphicObjects/Button.cs
@@ -0,0 +1,266 @@
+using System;
+
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+//using OpenTK.Graphics.OpenGL;
+
+using Cairo;
+
+using winColors = System.Drawing.Color;
+using System.Diagnostics;
+using System.Xml.Serialization;
+using OpenTK.Input;
+
+namespace go
+{
+ public delegate void GOEvent(GraphicObject sender);
+
+ public enum ButtonStates
+ {
+ normal,
+ mouseOver,
+ mouseDown,
+ Disable
+ }
+
+ public class Button : Container, IXmlSerializable
+ {
+ public GOEvent Click;
+
+ bool _isCheckable = false;
+ bool _isChecked = false;
+ bool _affectMultiSelectState = false;
+ bool _border3D = false;
+ Color _checkedColor;
+ ButtonStates _CurrentState = ButtonStates.normal;
+
+ public Button() : base()
+ {}
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(true)]
+ public override bool Focusable
+ {
+ get { return base.Focusable; }
+ set { base.Focusable = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ public Color CheckedColor
+ {
+ get { return _checkedColor; }
+ set
+ {
+ _checkedColor = value;
+ registerForGraphicUpdate();
+ }
+ }
+
+ [System.Xml.Serialization.XmlIgnore]
+ public ButtonStates CurrentState
+ {
+ get { return _CurrentState; }
+ set
+ {
+ if (value == _CurrentState)
+ return;
+ _CurrentState = value;
+ registerForRedraw();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(false)]
+ public bool IsChecked
+ {
+ get { return _isChecked; }
+ set
+ {
+ if (value == _isChecked)
+ return;
+
+ _isChecked = value;
+
+ if (Click != null)
+ Click(this);
+
+ registerForGraphicUpdate();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(false)]
+ public bool IsCheckable
+ {
+ get { return _isCheckable; }
+ set { _isCheckable = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(false)]
+ public bool AffectMultiSelectState
+ {
+ get { return _affectMultiSelectState; }
+ set { _affectMultiSelectState = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(false)]
+ public bool Border3d
+ {
+ get { return _border3D; }
+ set { _border3D = value; }
+ }
+
+ public override void Paint(ref Context ctx, Rectangles clip = null)
+ {
+ base.Paint(ref ctx, clip);
+
+ Rectangle r = Parent.ContextCoordinates (Slot);
+
+ ctx.Save();
+ //ctx.ResetClip();
+
+ if (IsCheckable){
+ if (IsChecked){
+ ctx.Color = CheckedColor;
+ ctx.Rectangle(r);
+ ctx.Fill();
+
+ if (Border3d)
+ CairoHelpers.StrokeLoweredRectangle(ctx, r);
+ }
+ else{
+ if (Border3d)
+ CairoHelpers.StrokeRaisedRectangle(ctx, r);
+ }
+ }
+
+ switch (CurrentState){
+ case ButtonStates.normal:
+ break;
+ case ButtonStates.mouseOver:
+ CairoHelpers.CairoRectangle(ctx,r,CornerRadius);
+ ctx.Operator = Operator.Add;
+ ctx.Color = new Color(0.2, 0.2, 0.2, 1.0);
+ ctx.Fill();
+ ctx.Operator = Operator.Over;
+ break;
+ case ButtonStates.mouseDown:
+ CairoHelpers.CairoRectangle(ctx,r,CornerRadius);
+ ctx.Operator = Operator.Add;
+ ctx.Color = Color.Red;
+ ctx.Fill();
+ ctx.Operator = Operator.Over;
+ break;
+ case ButtonStates.Disable:
+ CairoHelpers.CairoRectangle(ctx,r,CornerRadius);
+ ctx.Color = new Color(0.2, 0.2, 0.2, 0.7);
+ ctx.Fill();
+ break;
+
+ }
+ ctx.Restore();
+ }
+
+
+ #region mouse handling
+ public override void onMouseButtonDown (object sender,MouseButtonEventArgs e)
+ {
+ if (CurrentState != ButtonStates.Disable)
+ {
+ if (IsCheckable)
+ {
+ if (IsChecked)
+ IsChecked = false;
+ else
+ {
+ Group wg = Parent as Group;
+ if (wg != null)
+ {
+ if (AffectMultiSelectState && !wg.MultiSelect)
+ {
+ foreach (Button but in wg.Children.OfType<Button>())
+ {
+ if (but.IsCheckable)
+ {
+ if (but.IsChecked && but.AffectMultiSelectState)
+ {
+ but.IsChecked = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ IsChecked = true;
+ }
+ }
+ //else if (Click != null)
+ // Click(this);
+
+ CurrentState = ButtonStates.mouseDown;
+ }
+
+ base.onMouseButtonDown (sender, e);
+ }
+ public override void onMouseButtonUp (object sender, OpenTK.Input.MouseButtonEventArgs e)
+ {
+ if (CurrentState != ButtonStates.Disable) {
+ if (MouseIsIn (e.Position))
+ CurrentState = ButtonStates.mouseOver;
+ else
+ CurrentState = ButtonStates.normal;
+
+ }
+
+ base.onMouseButtonUp (sender, e);
+ }
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ if (CurrentState == ButtonStates.normal)
+ CurrentState = ButtonStates.mouseOver;
+
+ base.onMouseEnter (sender, e);
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ if (CurrentState == ButtonStates.mouseOver)
+ CurrentState = ButtonStates.normal;
+
+ base.onMouseLeave (sender, e);
+ }
+ #endregion
+
+ #region IXmlSerializable
+ public override System.Xml.Schema.XmlSchema GetSchema()
+ {
+ return null;
+ }
+ public override void ReadXml(System.Xml.XmlReader reader)
+ {
+ string handler = reader.GetAttribute("OnClick");
+ GraphicObject.EventsToResolve.Add(new EventSource
+ {
+ Source = this,
+ Handler = handler,
+ EventName = "OnClick"
+ });
+
+ //Container c = this as Container;
+ base.ReadXml(reader);
+ }
+ public override void WriteXml(System.Xml.XmlWriter writer)
+ {
+ if (Click != null)
+ {
+ writer.WriteAttributeString("OnClick", Click.Method.Name);
+ }
+
+ base.WriteXml(writer);
+ }
+ #endregion
+ }
+}
diff --git a/src/GraphicObjects/Container.cs b/src/GraphicObjects/Container.cs
new file mode 100644
index 00000000..ef843cbc
--- /dev/null
+++ b/src/GraphicObjects/Container.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Xml.Serialization;
+using System.Reflection;
+using OpenTK.Input;
+
+namespace go
+{
+ public class Container : GraphicObject, IXmlSerializable
+ {
+ public GraphicObject child;
+
+ public Container()
+ : base()
+ {
+ }
+ public Container(Rectangle _bounds)
+ : base(_bounds)
+ {
+ }
+
+ public T setChild<T>(T _child)
+ {
+
+ if (child != null)
+ child.Parent = null;
+
+ child = _child as GraphicObject;
+
+ if (child != null)
+ child.Parent = this;
+
+ return (T)_child;
+ }
+ public override GraphicObject FindByName (string nameToFind)
+ {
+ if (Name == nameToFind)
+ return this;
+
+ return child == null ? null : child.FindByName (nameToFind);
+ }
+ public override void InvalidateLayout()
+ {
+ base.InvalidateLayout();
+
+ if (child != null)
+ child.InvalidateLayout();
+ }
+ public override bool LayoutIsValid
+ {
+ get
+ {
+ if (!Visible)
+ return true;
+
+ return !base.LayoutIsValid || child == null ?
+ base.LayoutIsValid :
+ child.LayoutIsValid;
+ }
+ set { base.LayoutIsValid = value; }
+ }
+ public override Size measureRawSize ()
+ {
+ Size raw = Bounds.Size;
+
+ if (child != null) {
+ if (Bounds.Width < 0 && child.WIsValid)
+ raw.Width = child.Width + 2 * (Margin + BorderWidth);
+ if (Bounds.Height < 0 && child.HIsValid)
+ raw.Height = child.Height + 2 * (Margin + BorderWidth);
+ }
+
+ return raw;
+ }
+ public override void UpdateLayout()
+ {
+ if (LayoutIsValid)
+ return;
+
+ if (Width < 0 && child.Width == 0)
+ child.Width = -1;
+ if (Height < 0 && child.Height == 0)
+ child.Height = -1;
+
+ if (!(base.LayoutIsValid))
+ base.UpdateLayout();
+
+ if (child != null)
+ {
+ if (!child.LayoutIsValid) {
+ child.UpdateLayout ();
+
+ if (!WIsValid) {
+ if (Width < 0 && child.WIsValid) {
+ Slot.Width = child.Slot.Width + 2 * Margin + 2 * BorderWidth;
+ WIsValid = true;
+ }
+ }
+ if (!HIsValid) {
+ if (Height < 0 && child.HIsValid) {
+ Slot.Height = child.Slot.Height + 2 * Margin + 2 * BorderWidth;
+ HIsValid = true;
+ }
+ }
+ }
+ }
+
+ if (LayoutIsValid)
+ registerForRedraw();
+ }
+// public override void onDraw (Cairo.Context gr)
+// {
+// base.onDraw (gr);
+//
+// if (child == null)
+// return;
+// if (!child.Visible)
+// return;
+//
+// child.Paint (ref gr);
+// }
+ public override Rectangle ContextCoordinates (Rectangle r)
+ {
+ return
+ Parent.ContextCoordinates(r) + getSlot().Position + ClientRectangle.Position;
+
+ }
+ public override void Paint(ref Cairo.Context ctx, Rectangles clip = null)
+ {
+ if (!Visible)//check if necessary??
+ return;
+
+ ctx.Save();
+
+// ctx.Rectangle(ContextCoordinates(Slot));
+// ctx.Clip();
+//
+ if (clip != null)
+ clip.clip(ctx);
+
+ base.Paint(ref ctx, clip);
+
+ //clip to client zone
+ ctx.Rectangle(Parent.ContextCoordinates(ClientRectangle + Slot.Position));
+ ctx.Clip();
+
+// if (clip != null)
+// clip.Rebase(this);
+
+ if (child != null)
+ child.Paint(ref ctx, clip);
+
+ ctx.Restore();
+ }
+
+ #region Mouse handling
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ if (child != null)
+ if (child.MouseIsIn (e.Position))
+ child.onMouseMove (sender, 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();
+ subTree.Read(); //move to first child
+
+ if (!subTree.IsStartElement())
+ return;
+
+ Type t = Type.GetType("go." + subTree.Name);
+ GraphicObject go = (GraphicObject)Activator.CreateInstance(t);
+
+ (go as IXmlSerializable).ReadXml(subTree);
+
+ setChild(go);
+
+ subTree.Read();
+
+ if (!subTree.IsStartElement())
+ return;
+
+ }
+ }
+ 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/GenericStack.cs b/src/GraphicObjects/GenericStack.cs
new file mode 100644
index 00000000..90e0d132
--- /dev/null
+++ b/src/GraphicObjects/GenericStack.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Serialization;
+using System.ComponentModel;
+
+namespace go
+{
+ public class GenericStack : Group
+ {
+ int currentXForWidget = 0;
+ int currentYForWidget = 0;
+ bool childrenArePositionned = false;
+ int _widgetSpacing;
+ Orientation _orientation;
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(-1)]
+ public override int Width {
+ get { return base.Width; }
+ set { base.Width = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(-1)]
+ public override int Height {
+ get { return base.Height; }
+ set { base.Height = value; }
+ }
+
+ [XmlAttributeAttribute()]
+ [DefaultValue(2)]
+ public int WidgetSpacing
+ {
+ get { return _widgetSpacing; }
+ set { _widgetSpacing = value; }
+ }
+
+ [XmlAttributeAttribute()]
+ [DefaultValue(Orientation.Horizontal)]
+ public virtual Orientation Orientation
+ {
+ get { return _orientation; }
+ set { _orientation = value; }
+ }
+
+ public GenericStack()
+ : base()
+ {
+ }
+
+ public override bool LayoutIsValid {
+ get { return childrenArePositionned && base.LayoutIsValid; }
+ set { base.LayoutIsValid = value; }
+ }
+ public override void InvalidateLayout ()
+ {
+ childrenArePositionned = false;
+ base.InvalidateLayout ();
+ }
+ public override Size measureRawSize ()
+ {
+ Size raw = Bounds.Size;
+ Size tmp = new Size ();
+
+ if (raw.Width >= 0 && raw.Height >= 0)
+ return raw;
+
+ foreach (GraphicObject c in Children) {
+ if (raw.Width < 0) {
+ if (c.WIsValid) {
+ if (Orientation == Orientation.Horizontal && c.Bounds.Width != 0)
+ tmp.Width += c.Slot.Width + WidgetSpacing;
+ else
+ tmp.Width = Math.Max (tmp.Width, c.Slot.Right);
+ }else
+ return raw;
+ }
+ if (raw.Height < 0) {
+ if (c.HIsValid) {
+ if (Orientation == Orientation.Vertical && c.Bounds.Height != 0)
+ tmp.Height += c.Slot.Height + WidgetSpacing;
+ else
+ tmp.Height = Math.Max (tmp.Height, c.Slot.Bottom);
+ }else
+ return raw;
+ }
+ }
+
+ if (raw.Width < 0)
+ tmp.Width += 2*(BorderWidth+Margin);
+ if (raw.Height < 0)
+ tmp.Height += 2*(BorderWidth+Margin);
+
+ return tmp;
+ }
+ public virtual void ComputeChildrenPositions()
+ {
+ int d = 0;
+ if (Orientation == Orientation.Horizontal) {
+ foreach (GraphicObject c in Children) {
+ if (c.Bounds.Width == 0)
+ continue;
+
+ c.Slot.X = d;
+ c.XIsValid = true;
+ d += c.Slot.Width + WidgetSpacing;
+ }
+ } else {
+ foreach (GraphicObject c in Children) {
+ if (c.Bounds.Height == 0)
+ continue;
+
+ c.Slot.Y = d;
+ c.YIsValid = true;
+ d += c.Slot.Height + WidgetSpacing;
+ }
+ }
+ childrenArePositionned = true;
+ }
+
+ public override void UpdateLayout()
+ {
+ base.UpdateLayout();
+
+ ComputeChildrenPositions ();
+
+ if (LayoutIsValid)
+ registerForRedraw();
+ }
+//
+// bool enoughtSpaceForWidget(GraphicObject w)
+// {
+// if (!SizeToContent)
+// {
+// int nextXForWidget = 0;
+// int nextYForWidget = 0;
+//
+// if (Orientation == Orientation.Horizontal)
+// nextXForWidget = currentXForWidget + w.Slot.Width;
+// else
+// nextYForWidget = nextYForWidget + w.Slot.Height;
+//
+//
+// if (nextXForWidget > clientBounds.Right )
+// return false;
+// if (currentYForWidget > clientBounds.Bottom )
+// return false;
+// }
+// return true;
+// }
+// void advance(GraphicObject w)
+// {
+// if (Orientation == Orientation.Horizontal)
+// currentXForWidget = currentXForWidget + WidgetSpacing + w.Slot.Width;
+// else
+// currentYForWidget = currentYForWidget + WidgetSpacing + w.Slot.Height;
+//
+// }
+ }
+}
diff --git a/src/GraphicObjects/GraduatedSlider.cs b/src/GraphicObjects/GraduatedSlider.cs
new file mode 100644
index 00000000..9eb0ec7a
--- /dev/null
+++ b/src/GraphicObjects/GraduatedSlider.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Cairo;
+using System.Xml.Serialization;
+
+namespace go
+{
+ public class GraduatedSlider : Slider
+ {
+ public GraduatedSlider() : base()
+ {}
+ public GraduatedSlider(double minimum, double maximum, double step)
+ : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ SmallIncrement = step;
+ LargeIncrement = step * 5;
+ }
+ public override void DrawGraduations(Context gr, PointD pStart, PointD pEnd)
+ {
+ Rectangle r = clientBounds;
+ gr.Color = Foreground;
+
+ gr.LineWidth = 2;
+ gr.MoveTo(pStart);
+ gr.LineTo(pEnd);
+
+ gr.Stroke();
+ gr.LineWidth = 1;
+
+ double sst = unity * SmallIncrement;
+ double bst = unity * LargeIncrement;
+
+
+ PointD vBar = new PointD(0, sst);
+ for (double x = Minimum; x <= Maximum - Minimum; x += SmallIncrement)
+ {
+ double lineLength = r.Height / 3;
+ if (x % LargeIncrement != 0)
+ lineLength /= 3;
+ PointD p = new PointD(pStart.X + x * unity, pStart.Y);
+ gr.MoveTo(p);
+ gr.LineTo(new PointD(p.X, p.Y + lineLength));
+ }
+ gr.Stroke();
+ }
+ }
+}
diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs
new file mode 100644
index 00000000..06f52da3
--- /dev/null
+++ b/src/GraphicObjects/GraphicObject.cs
@@ -0,0 +1,951 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+//using OpenTK.Graphics.OpenGL;
+using System.Drawing.Imaging;
+using System.Diagnostics;
+using OpenTK.Input;
+
+using Cairo;
+
+using System.Xml.Serialization;
+using System.Reflection;
+using System.ComponentModel;
+using System.IO;
+
+namespace go
+{
+ public class GraphicObject : IXmlSerializable, ILayoutable
+ {
+
+ #region private properties
+
+ ILayoutable _parent;
+ string _name;
+ Color _background;
+ Color _foreground;
+ Color _borderColor;
+ int _borderWidth;
+ double _cornerRadius;
+ int _margin;
+ bool _focusable = false;
+ bool _hasFocus = false;
+ protected bool _isVisible = true;
+ VerticalAlignment _verticalAlignment;
+ HorizontalAlignment _horizontalAlignment;
+
+ bool hIsValid = false;
+ bool wIsValid = false;
+ bool xIsValid = false;
+ bool yIsValid = false;
+
+ #endregion
+
+ public Rectangle Bounds;
+ public Rectangle Slot = new Rectangle ();
+ public object Tag;
+ public byte[] bmp;
+
+ #region ILayoutable
+
+ [System.Xml.Serialization.XmlIgnore]
+ public ILayoutable Parent {
+ get { return _parent; }
+ set { _parent = value; }
+ }
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool SizeIsValid {
+ get { return hIsValid & wIsValid; }
+ set {
+ hIsValid = value;
+ wIsValid = value;
+ }
+ }
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool WIsValid {
+ get { return wIsValid; }
+ set { wIsValid = value; }
+ }
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool HIsValid {
+ get { return hIsValid; }
+ set { hIsValid = value; }
+ }
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool XIsValid {
+ get { return xIsValid; }
+ set { xIsValid = value; }
+ }
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool YIsValid {
+ get { return yIsValid; }
+ set { yIsValid = value; }
+ }
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool PositionIsValid {
+ get { return xIsValid & yIsValid; }
+ set {
+ xIsValid = value;
+ yIsValid = value;
+ }
+ }
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool LayoutIsValid {
+ get { return SizeIsValid & PositionIsValid; }
+ set {
+ if (value == SizeIsValid & PositionIsValid)
+ return;
+
+ SizeIsValid = value;
+ PositionIsValid = value;
+
+ //if (!layoutIsValid && Parent != null)
+ // Parent.layoutIsValid = false;
+ }
+ }
+
+ public virtual void InvalidateLayout ()
+ {
+ bmp = null;
+ LayoutIsValid = false;
+ }
+
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual Rectangle ClientRectangle {
+ get {
+ Rectangle cb = Slot.Size;
+ cb.Inflate ( - BorderWidth - Margin);
+ return cb;
+ }
+ }
+ public virtual Rectangle ContextCoordinates(Rectangle r){
+ return
+ Parent.ContextCoordinates (r);// + ClientRectangle.Position;
+ }
+ public virtual Rectangle ScreenCoordinates (Rectangle r){
+ //r += Slot.Position;
+
+ return
+ Parent.ScreenCoordinates(r) + Parent.getSlot().Position + Parent.ClientRectangle.Position;
+ }
+ #endregion
+
+ #region event handlers
+
+ public event EventHandler<MouseWheelEventArgs> MouseWheelChanged = delegate { };
+ public event EventHandler<MouseButtonEventArgs> MouseButtonUp = delegate { };
+ public event EventHandler<MouseButtonEventArgs> MouseButtonDown = delegate { };
+ public event EventHandler<MouseButtonEventArgs> MouseClicked = delegate { };
+ public event EventHandler<MouseMoveEventArgs> MouseMove = delegate { };
+ public event EventHandler<MouseMoveEventArgs> MouseEnter = delegate { };
+ public event EventHandler<MouseMoveEventArgs> MouseLeave = delegate { };
+ public event EventHandler<KeyboardKeyEventArgs> KeyDown = delegate { };
+ public event EventHandler<KeyboardKeyEventArgs> KeyUp = delegate { };
+ public event EventHandler Focused = delegate { };
+ public event EventHandler Unfocused = delegate { };
+
+ #endregion
+
+ #region public properties
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue("unamed")]
+ public virtual string Name {
+ get { return _name; }
+ set { _name = value; }
+ }
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(VerticalAlignment.Center)]
+ public virtual VerticalAlignment VerticalAlignment {
+ get { return _verticalAlignment; }
+ set {
+ if (_verticalAlignment == value)
+ return;
+
+ _verticalAlignment = value;
+
+// if (Top > 0)
+ }
+ }
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(HorizontalAlignment.Center)]
+ public virtual HorizontalAlignment HorizontalAlignment {
+ get { return _horizontalAlignment; }
+ set { _horizontalAlignment = value; }
+ }
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public virtual int Left {
+ get { return Bounds.X; }
+ set {
+ if (Bounds.X == value)
+ return;
+
+ Bounds.X = value;
+
+ LayoutIsValid = false;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public virtual int Top {
+ get { return Bounds.Y; }
+ set {
+ if (Bounds.Y == value)
+ return;
+
+ Bounds.Y = value;
+
+ LayoutIsValid = false;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public virtual int Width {
+ get { return Bounds.Width; }
+ set {
+ if (Bounds.Width == value)
+ return;
+
+ Bounds.Width = value;
+
+ InvalidateLayout ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public virtual int Height {
+ get { return Bounds.Height; }
+ set {
+ if (Bounds.Height == value)
+ return;
+
+ Bounds.Height = value;
+
+ InvalidateLayout ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(false)]
+ public virtual bool Focusable {
+ get { return _focusable; }
+ set { _focusable = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue("Transparent")]
+ public virtual Color Background {
+ get { return _background; }
+ set {
+ _background = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue("White")]
+ public virtual Color Foreground {
+ get { return _foreground; }
+ set {
+ _foreground = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue("White")]
+ public virtual Color BorderColor {
+ get { return _borderColor; }
+ set {
+ _borderColor = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public virtual int BorderWidth {
+ get { return _borderWidth; }
+ set {
+ _borderWidth = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(5)]
+ public virtual double CornerRadius {
+ get { return _cornerRadius; }
+ set {
+ _cornerRadius = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public virtual int Margin {
+ get { return _margin; }
+ set {
+ _margin = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(true)]
+ public virtual bool Visible {
+ get { return _isVisible; }
+ set {
+ if (value == _isVisible)
+ return;
+
+ _isVisible = value;
+ if (Parent != null)
+ Parent.InvalidateLayout ();
+ //else
+ // registerForRedraw();
+ }
+ }
+ //should maybe test only focusedWidget in higher container (opentkwin for now)
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool HasFocus {
+ get { return _hasFocus; }
+ set { _hasFocus = value; }
+ }
+
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool isCached {
+ get { return false; }
+ }
+
+ [System.Xml.Serialization.XmlIgnore]
+ public virtual bool cachingInProgress {
+ get { return false; }
+ set { return; }
+ }
+
+ public virtual Rectangle clientBounds {
+ get {
+ Rectangle cb = Slot.Size;
+ cb.Inflate (-(BorderWidth + Margin));
+ return cb;
+ }
+ }
+
+
+
+
+
+
+ public virtual Rectangle getSlot()
+ {
+ return Slot;
+ }
+ [XmlIgnore]
+ public virtual bool DrawingIsValid
+ { get { return bmp == null ?
+ false :
+ true; } }
+ #endregion
+
+ public GraphicObject ()
+ {
+ loadDefaultValues ();
+ registerForGraphicUpdate ();
+ }
+ public GraphicObject (Rectangle _bounds)
+ {
+ loadDefaultValues ();
+ Bounds = _bounds;
+ registerForGraphicUpdate ();
+ }
+ /// <summary>
+ /// Loads the default values from XML attributes default
+ /// </summary>
+ void loadDefaultValues()
+ {
+ foreach (PropertyInfo pi in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
+ if (pi.GetSetMethod () == null)
+ continue;
+
+ bool isAttribute = false;
+ string name = "";
+ Type valueType = null;
+
+ MemberInfo mi = pi.GetGetMethod ();
+
+ 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)
+ continue;
+
+ DefaultValueAttribute dv = o as DefaultValueAttribute;
+ if (dv != null) {
+ if (pi.PropertyType == typeof(Color))
+ pi.SetValue (this, Color.Parse ((string)dv.Value), null);
+ else
+ pi.SetValue (this, dv.Value, null);
+ continue;
+ }
+
+ //Debug.WriteLine (o.ToString ());
+ }
+ }
+
+ }
+
+ public virtual GraphicObject FindByName(string nameToFind){
+ return nameToFind == _name ? this : null;
+ }
+
+
+ /// <summary>
+ /// Clear chached object and add clipping region in redraw list of interface
+ /// </summary>
+ public virtual void registerForGraphicUpdate ()
+ {
+ bmp = null;
+ registerForRedraw ();
+ //Interface.registerForGraphicUpdate(this);
+ }
+ /// <summary>
+ /// Add clipping region in redraw list of interface, dont update cached object content
+ /// </summary>
+ public virtual void registerForRedraw ()
+ {
+ if (LayoutIsValid && Visible)
+ OpenTKGameWindow.gobjsToRedraw.Add (this);
+ }
+ public virtual void registerClipRect()
+ {
+ OpenTKGameWindow.redrawClip.AddRectangle (ScreenCoordinates(Slot));
+ }
+ public virtual Size measureRawSize ()
+ {
+ return Bounds.Size;
+ }
+
+ public virtual void ComputeSize()
+ {
+ Size rawSize = measureRawSize ();
+
+ if (!wIsValid) {
+ wIsValid = true;
+ if (Width > 0)
+ Slot.Width = Width;
+ else if (Width < 0) {
+ if (rawSize.Width > 0)
+ Slot.Width = rawSize.Width;
+ else
+ wIsValid = false;
+ } else if (Parent.WIsValid)
+ Slot.Width = Parent.ClientRectangle.Width;
+ else
+ wIsValid = false;
+ }
+
+ if (!hIsValid) {
+ hIsValid = true;
+ if (Height > 0)
+ Slot.Height = Height;
+ else if (Height < 0) {
+ if (rawSize.Height > 0)
+ Slot.Height = rawSize.Height;
+ else
+ hIsValid = false;
+ } else if (Parent.HIsValid)
+ Slot.Height = Parent.ClientRectangle.Height;
+ else
+ hIsValid = false;
+ }
+ }
+ public virtual void ComputePosition()
+ {
+ if (!xIsValid) {
+ xIsValid = true;
+ if (Width == 0)
+ Slot.X = 0;
+ else if (Left != 0)
+ Slot.X = Left;
+ else if (Parent.WIsValid && WIsValid) {
+ 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
+ xIsValid = false;
+ }
+
+ if (!yIsValid) {
+ yIsValid = true;
+ if (Height== 0)
+ Slot.Y = 0;
+ else if (Top != 0)
+ Slot.Y = Top;
+ else if (Parent.HIsValid && HIsValid) {
+ switch (VerticalAlignment) {
+ case VerticalAlignment.Top:
+ 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
+ yIsValid = false;
+ }
+ }
+ public virtual void UpdateLayout ()
+ {
+// if (LayoutIsValid)
+// return;
+//
+ //should maybe invalidate drawing
+
+ //Rectangle oldRenderBounds = Slot;
+
+ if (!SizeIsValid)
+ ComputeSize ();
+ if (!PositionIsValid)
+ ComputePosition ();
+ if (LayoutIsValid)
+ registerForRedraw ();
+
+ }
+ public virtual void onDraw(Context gr)
+ {
+ Rectangle rBack = new Rectangle (Slot.Size);
+
+ if (BorderWidth > 0)
+ rBack.Inflate (-BorderWidth / 2);
+
+ gr.Color = Background;
+ CairoHelpers.CairoRectangle(gr,rBack,_cornerRadius);
+ gr.Fill ();
+
+ if (BorderWidth > 0) {
+
+ gr.LineWidth = BorderWidth;
+ gr.Color = BorderColor;
+ CairoHelpers.CairoRectangle(gr,rBack,_cornerRadius);
+ gr.Stroke ();
+ }
+ }
+
+ internal virtual void UpdateGraphic ()
+ {
+ int stride = 4 * Slot.Width;
+
+ int bmpSize = Math.Abs (stride) * Slot.Height;
+ bmp = new byte[bmpSize];
+
+ using (ImageSurface draw =
+ new ImageSurface(bmp, Format.Argb32, Slot.Width, Slot.Height, stride)) {
+ using (Context gr = new Context (draw)) {
+ gr.Antialias = Antialias.Subpixel;
+ onDraw (gr);
+ }
+ draw.Flush ();
+ }
+ }
+
+ //could maybe be internal
+ public virtual void Paint (ref Context ctx, Rectangles clip = null)
+ {
+ if (!Visible)
+ return;
+
+ if (bmp == null)
+ UpdateGraphic ();
+
+ Rectangle rb = Parent.ContextCoordinates(Slot);
+
+ using (ImageSurface source = new ImageSurface(bmp, Format.Argb32, rb.Width, rb.Height, 4 * Slot.Width)) {
+ ctx.SetSourceSurface (source, rb.X, rb.Y);
+ ctx.Paint ();
+ }
+ }
+
+ #region Keyboard handling
+ public virtual void onKeyDown(object sender, KeyboardKeyEventArgs e){
+ if (KeyDown != null)
+ KeyDown (sender, e);
+ }
+ #endregion
+
+ #region Mouse handling
+
+ public virtual bool MouseIsIn(Point m)
+ {
+ return Visible ? ScreenCoordinates(Slot).ContainsOrIsEqual (m) : false;
+ }
+ public virtual void onMouseMove(object sender, MouseMoveEventArgs e)
+ {
+ OpenTKGameWindow w = sender as OpenTKGameWindow;
+
+ if (w.hoverWidget != this) {
+ w.hoverWidget = this;
+ onMouseEnter (sender, e);
+ }
+
+ if (MouseMove != null)
+ MouseMove (sender, e);
+ }
+ public virtual void onMouseButtonUp(object sender, MouseButtonEventArgs e){
+ if (MouseIsIn (e.Position))
+ onMouseClicked (sender, e);
+ if (MouseButtonUp != null)
+ MouseButtonUp (sender, e);
+ }
+ public virtual void onMouseButtonDown(object sender, MouseButtonEventArgs e){
+ (sender as OpenTKGameWindow).FocusedWidget = this;
+ if (MouseButtonDown!=null)
+ MouseButtonDown (sender, e);
+ }
+ public virtual void onMouseClicked(object sender, MouseButtonEventArgs e){
+ if (MouseClicked!=null)
+ MouseClicked(sender,e);
+ }
+ public virtual void onMouseWheel(object sender, MouseWheelEventArgs e){
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseWheel(this,e);
+
+ if (MouseWheelChanged!=null)
+ MouseWheelChanged (this, e);
+ }
+ public virtual void onFocused(object sender, EventArgs e){
+ if (Focused!=null)
+ Focused (sender, e);
+ this.HasFocus = true;
+ }
+ public virtual void onUnfocused(object sender, EventArgs e){
+ if (Unfocused!=null)
+ Unfocused (sender, e);
+ this.HasFocus = false;
+ }
+ public virtual void onMouseEnter(object sender, MouseMoveEventArgs e)
+ {
+ //Debug.WriteLine ("Mouse Enter: " + this.ToString ());
+ if (MouseEnter != null)
+ MouseEnter (this, e);
+ }
+ public virtual void onMouseLeave(object sender, MouseMoveEventArgs e)
+ {
+ if (MouseLeave != null)
+ MouseLeave (this, e);
+ }
+ #endregion
+
+ #region Load/Save
+
+ internal static List<EventSource> EventsToResolve;
+
+ public static void Save<T>(string file, T graphicObject)
+ {
+ XmlSerializerNamespaces xn = new XmlSerializerNamespaces();
+ xn.Add("", "");
+ XmlSerializer xs = new XmlSerializer(typeof(T));
+
+ xs = new XmlSerializer(typeof(T));
+ using (Stream s = new FileStream(file, FileMode.Create))
+ {
+ xs.Serialize(s, graphicObject, xn);
+ }
+ }
+ public static void Load<T>(string file, out T result, object ClassContainingHandlers = null)
+ {
+ EventsToResolve = new List<EventSource>();
+
+ XmlSerializerNamespaces xn = new XmlSerializerNamespaces();
+ xn.Add("", "");
+ XmlSerializer xs = new XmlSerializer(typeof(T));
+
+ using (Stream s = new FileStream(file, FileMode.Open))
+ {
+ result = (T)xs.Deserialize(s);
+ }
+
+ if (ClassContainingHandlers == null)
+ return;
+
+ foreach (EventSource es in EventsToResolve)
+ {
+ if (es.Handler == null)
+ continue;
+
+ MethodInfo mi = ClassContainingHandlers.GetType().GetMethod(es.Handler, BindingFlags.NonPublic | BindingFlags.Public
+ | BindingFlags.Instance);
+
+ if (mi == null) {
+ Debug.WriteLine ("Handler Method not found: " + es.Handler);
+ continue;
+ }
+ FieldInfo fi;
+ Type ty = es.Source.GetType ();
+ do {
+ fi = ty.GetField (es.EventName,
+ BindingFlags.NonPublic |
+ BindingFlags.Instance |
+ BindingFlags.GetField);
+ ty = ty.BaseType;
+ if (ty == null)
+ break;
+ } while(fi == null);
+ Delegate del = Delegate.CreateDelegate(fi.FieldType, ClassContainingHandlers, mi);
+ fi.SetValue(es.Source, del);
+
+ }
+
+ }
+
+ #endregion
+
+ public override string ToString ()
+ {
+ return Name == "unamed" ? this.GetType ().ToString() : Name;
+// string tmp = this.GetType ().ToString ().Split (new char[] { '.' }).Last () + ":-";
+// if (!LayoutIsValid)
+// tmp += "L-";
+// //if (Interface.graphicUpdateList.Contains(this))
+// // tmp += "GU-";
+// if (OpenTKGameWindow.redrawClip.intersect (this.renderBoundsInBackendSurfaceCoordonate))
+// tmp += "D-";
+// return tmp + string.Format ("rb:{0}", Slot);
+ }
+
+ #region IXmlSerializable
+
+ public virtual System.Xml.Schema.XmlSchema GetSchema ()
+ {
+ return null;
+ }
+ //TODO: allert or inform when unknow attribute name in XML, very confusing...
+ public virtual void ReadXml (System.Xml.XmlReader reader)
+ {
+ foreach (EventInfo ei in this.GetType().GetEvents()) {
+// FieldInfo fi = this.GetType().GetField(ei.Name,
+// BindingFlags.NonPublic |
+// BindingFlags.Instance |
+// BindingFlags.GetField);
+ string handler = reader.GetAttribute(ei.Name);
+ if (string.IsNullOrEmpty (handler))
+ continue;
+ GraphicObject.EventsToResolve.Add(new EventSource
+ {
+ Source = this,
+ Handler = handler,
+ EventName = ei.Name
+ });
+ }
+ foreach (PropertyInfo pi in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
+ if (pi.GetSetMethod () == null)
+ continue;
+
+ bool isAttribute = false;
+
+ string name = "";
+ object value = null;
+ object defaultValue = null;
+
+ Type valueType = null;
+
+
+ MemberInfo mi = pi.GetGetMethod ();
+
+ //if (mi.MemberType == MemberTypes.Property)
+ //{
+ // PropertyInfo pi = mi as PropertyInfo;
+ // value = pi.GetValue(this, null);
+ // valueType = pi.PropertyType;
+ //}
+ //else if (mi.MemberType == MemberTypes.Field)
+ //{
+ // FieldInfo fi = mi as FieldInfo;
+ // value = fi.GetValue(this);
+ // valueType = fi.FieldType;
+ //}
+
+ 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)
+ continue;
+
+ DefaultValueAttribute dv = o as DefaultValueAttribute;
+ if (dv != null) {
+ defaultValue = dv.Value;
+ continue;
+ }
+ }
+
+
+ if (isAttribute) {
+ string v = reader.GetAttribute (name);
+
+ if (string.IsNullOrEmpty (v)) {
+ //TODO: maybe find another way to convert correctely colors default values
+ if (pi.PropertyType == typeof(Color))
+ pi.SetValue (this, Color.Parse ((string)defaultValue), null);
+ else
+ pi.SetValue (this, defaultValue, null);
+ } else {
+ if (pi.PropertyType == typeof(string)) {
+ pi.SetValue (this, v, null);
+ continue;
+ }
+
+ object o = null;
+
+ if (pi.PropertyType.IsEnum) {
+ o = Enum.Parse (pi.PropertyType, v);
+ } else {
+ MethodInfo me = pi.PropertyType.GetMethod ("Parse", new Type[] { typeof(string) });
+ o = me.Invoke (null, new string[] { v });
+ }
+
+ pi.SetValue (this, o, null);
+ }
+ } else {
+ ////if member type is not serializable, cancel
+ //if (valueType.GetInterface("IXmlSerializable") == null)
+ // continue;
+
+ //(pi.GetValue(this, null) as IXmlSerializable).WriteXml(writer);
+ }
+ }
+
+ }
+
+ 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
+ }
+}
diff --git a/src/GraphicObjects/Group.cs b/src/GraphicObjects/Group.cs
new file mode 100644
index 00000000..d65e07af
--- /dev/null
+++ b/src/GraphicObjects/Group.cs
@@ -0,0 +1,324 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using System.Diagnostics;
+using Cairo;
+using System.Xml.Serialization;
+using System.ComponentModel;
+
+namespace go
+{
+ public class Group : GraphicObject, IXmlSerializable
+ {
+ bool _multiSelect = false;
+
+ public GraphicObject activeWidget;
+
+ public List<GraphicObject> Children = new List<GraphicObject>();
+
+ [XmlAttributeAttribute()]
+ [DefaultValue(false)]
+ public bool MultiSelect
+ {
+ get { return _multiSelect; }
+ set { _multiSelect = value; }
+ }
+
+
+ public Group()
+ : base()
+ {
+ }
+
+ public virtual T addChild<T>(T child)
+ {
+ GraphicObject g = child as GraphicObject;
+ Children.Add(g);
+ g.Parent = this as GraphicObject;
+ LayoutIsValid = false;
+ return (T)child;
+ }
+ public void removeChild(GraphicObject child)
+ {
+ Children.Remove(child);
+ child.Parent = null;
+ LayoutIsValid = false;
+ }
+
+ #region GraphicObject overrides
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(true)]//overiden to get default to true
+ public override bool Focusable
+ {
+ get { return base.Focusable; }
+ set { base.Focusable = value; }
+ }
+ [XmlIgnore]
+ public override bool DrawingIsValid {
+ get {
+ if (!base.DrawingIsValid)
+ return false;
+ foreach (GraphicObject g in Children) {
+ if (!g.DrawingIsValid)
+ return false;
+ }
+ return true;
+ }
+ }
+ [XmlIgnore]
+ public override bool LayoutIsValid
+ {
+ get
+ {
+ if (!Visible)
+ return true;
+
+ if (!base.LayoutIsValid)
+ return false;
+ else//le layout n'est valide que si tous les enfents sont validés aussi
+ {
+ foreach (GraphicObject w in Children)
+ if (!w.LayoutIsValid)
+ return false;
+ }
+
+ return true;
+ }
+ set { base.LayoutIsValid = value; }
+ }
+
+ public override GraphicObject FindByName (string nameToFind)
+ {
+ if (Name == nameToFind)
+ return this;
+
+ foreach (GraphicObject w in Children) {
+ GraphicObject r = w.FindByName (nameToFind);
+ if (r != null)
+ return r;
+ }
+ return null;
+ }
+ public override void InvalidateLayout()
+ {
+ base.InvalidateLayout();
+ foreach (GraphicObject w in Children)
+ w.InvalidateLayout();
+ }
+ public override Size measureRawSize ()
+ {
+ Size raw = Bounds.Size;
+ Size tmp = new Size ();
+
+ if (raw.Width >= 0 && raw.Height >= 0)
+ return raw;
+
+ foreach (GraphicObject c in Children) {
+ if (raw.Width < 0) {
+ if (c.XIsValid & c.WIsValid)
+ tmp.Width = Math.Max (tmp.Width, c.Slot.Right);
+ else
+ return raw;
+ }
+ if (raw.Height < 0) {
+ if (c.YIsValid & c.HIsValid)
+ tmp.Height = Math.Max (tmp.Height, c.Slot.Bottom);
+ else
+ return raw;
+ }
+ }
+
+ if (raw.Width < 0)
+ tmp.Width += 2*(BorderWidth+Margin);
+ if (raw.Height < 0)
+ tmp.Height += 2*(BorderWidth+Margin);
+
+ return tmp;
+ }
+ public override void UpdateLayout()
+ {
+ if (LayoutIsValid)
+ return;
+
+ bool atLeastOneChildHasWNotDependingOnParent = false;
+ bool atLeastOneChildHasHNotDependingOnParent = false;
+
+ foreach (GraphicObject c in Children) {
+ if (c.LayoutIsValid)
+ continue;
+
+ if (Width < 0 && c.Width != 0)
+ atLeastOneChildHasWNotDependingOnParent = true;
+ if (Height < 0 && c.Height != 0)
+ atLeastOneChildHasHNotDependingOnParent = true;
+
+ c.UpdateLayout ();
+ }
+
+ if (Width < 0 && !atLeastOneChildHasWNotDependingOnParent)
+ Debug.WriteLine ("ERROR: no child has fixed width and parent width is set to content!");
+ if (Height < 0 && !atLeastOneChildHasHNotDependingOnParent)
+ Debug.WriteLine ("ERROR: no child has fixed height and parent height is set to content!");
+
+ base.UpdateLayout ();
+ }
+
+ public override Rectangle ContextCoordinates(Rectangle r){
+ return r + ClientRectangle.Position;
+ }
+
+ public override void onDraw (Context gr)
+ {
+ Rectangle rBack = new Rectangle (Slot.Size);
+ gr.Color = Background;
+ CairoHelpers.CairoRectangle(gr,rBack,CornerRadius);
+ gr.Fill ();
+
+ foreach (GraphicObject g in Children) {
+ g.Paint (ref gr);
+ }
+ }
+
+ public override void Paint(ref Context ctx, Rectangles clip = null)
+ {
+ if ( !(Visible & LayoutIsValid) )
+ return;
+
+ // ctx.Save ();
+
+// if (clip!=null)
+// clip.clearAndClip (ctx);//n'était pas actif
+
+ if (bmp == null)
+ UpdateGraphic ();
+ else {
+
+ Rectangle rb = Parent.ContextCoordinates (Slot);
+
+ if (clip != null) {
+ clip.Rebase (this);
+ //localClip = clip.intersectingRects (Slot.Size);
+ } else
+ clip = new Rectangles ();
+
+ if (!DrawingIsValid || clip != null) {//false when 1 child has changed
+ //child having their content changed has to be repainted
+ //and those with slot intersecting clip rectangle have also to be repainted
+
+ using (ImageSurface cache =
+ new ImageSurface (bmp, Format.Argb32, Slot.Width, Slot.Height, Slot.Width * 4)) {
+ Context gr = new Context (cache);
+ clip.clearAndClip (gr);
+
+ Rectangle rBack = Slot.Size;
+ gr.Color = Background;
+ CairoHelpers.CairoRectangle(gr,rBack,CornerRadius);
+ gr.Fill ();
+ #if DEBUG_CLIP_RECTANGLE
+ clip.stroke (gr, Color.Amaranth.AdjustAlpha (0.8));
+ #endif
+ foreach (GraphicObject c in Children) {
+ Rectangles childClip = clip.intersectingRects (ContextCoordinates(c.Slot));
+ if (!c.DrawingIsValid || childClip.count > 0)
+ c.Paint (ref gr,childClip);//, localClip);
+ }
+
+ gr.Dispose ();
+ }
+ }
+ }
+
+ base.Paint (ref ctx, clip);
+
+ // ctx.Restore();
+ }
+ #endregion
+
+
+ #region Mouse handling
+ public override void onMouseMove (object sender, OpenTK.Input.MouseMoveEventArgs e)
+ {
+ foreach (GraphicObject g in Children)
+ {
+ if (g.MouseIsIn(e.Position))
+ {
+ g.onMouseMove (sender, e);
+ return;
+ }
+ }
+ base.onMouseMove (sender, e);
+ }
+ #endregion
+
+ public void putWidgetOnTop(GraphicObject w)
+ {
+ if (Children.Contains(w))
+ {
+ Children.Remove(w);
+ Children.Add(w);
+ }
+ }
+ public void putWidgetOnBottom(GraphicObject w)
+ {
+ if (Children.Contains(w))
+ {
+ Children.Remove(w);
+ Children.Insert(0, w);
+ }
+ }
+
+ public override string ToString()
+ {
+ string tmp = base.ToString();
+ foreach (GraphicObject w in Children)
+ {
+ tmp += "\n" + w.ToString();
+ }
+ return tmp;
+ }
+
+ #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("go." + subTree.Name);
+ 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/GroupBox.cs b/src/GraphicObjects/GroupBox.cs
new file mode 100644
index 00000000..77419f5e
--- /dev/null
+++ b/src/GraphicObjects/GroupBox.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Cairo;
+using System.Xml.Serialization;
+using System.ComponentModel;
+
+namespace go
+{
+
+ public class GroupBox : Container
+ {
+ string _title;
+ int _fontSize = 10;
+
+ [XmlAttributeAttribute]
+ [DefaultValue("GroupBox")]
+ public string Text
+ {
+ get { return _title; }
+ set
+ {
+ _title = value;
+ registerForGraphicUpdate();
+ }
+ }
+
+ [XmlAttributeAttribute]
+ [DefaultValue(10)]
+ public int fontSize
+ {
+ get { return _fontSize; }
+ set
+ {
+ _fontSize = value;
+ registerForGraphicUpdate();
+ }
+ }
+
+ public Size titleSize()
+ {
+#if _WIN32 || _WIN64
+ byte[] txt = System.Text.UTF8Encoding.UTF8.GetBytes(_title);
+#endif
+
+ Size s;
+
+ using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) {
+ using (Context gr = new Context (img)) {
+ gr.SetFontSize (fontSize);
+ TextExtents te;
+#if _WIN32 || _WIN64
+ te = gr.TextExtents(txt);
+#elif __linux__
+ te = gr.TextExtents (Text);
+#endif
+ FontExtents fe = gr.FontExtents;
+ s = new Size ((int)Math.Ceiling (te.XAdvance), (int)Math.Ceiling (fe.Height));
+ }
+ }
+ return s;// +borderWidth;
+ }
+
+ public GroupBox() : base (){
+ }
+ public GroupBox(Rectangle _bounds, string _title = "GroupBox")
+ : base(_bounds)
+ {
+ Text = _title;
+ }
+
+ public GroupBox(string _title = "GroupBox")
+ : base()
+ {
+ Text = _title;
+ }
+
+ public override Rectangle clientBounds
+ {
+ get
+ {
+ Size ts = titleSize();
+
+ Rectangle cb = Slot;
+ cb.X = 0;
+ cb.Y = ts.Height;
+ cb.Height -= ts.Height;
+ cb.Inflate(-BorderWidth - Margin);
+
+ return cb;
+ }
+ }
+
+ public override void onDraw (Context gr)
+ {
+ //base.onDraw (gr);
+
+ byte[] txt = System.Text.UTF8Encoding.UTF8.GetBytes(Text);
+ Rectangle r = new Rectangle(Slot.Size);
+ //gr.Rotate(Math.PI);
+ gr.SetFontSize(fontSize);
+ FontExtents fe = gr.FontExtents;
+ TextExtents te = gr.TextExtents(Text);
+
+ gr.Antialias = Antialias.Subpixel;
+ gr.LineWidth = BorderWidth;
+ gr.Color = Background;
+
+ CairoHelpers.CairoRectangle (gr, r, CornerRadius);
+ gr.Fill();
+
+ Rectangle rTitle = r;
+
+ int th = (int)Math.Ceiling(fe.Height / 2);
+ r.Y += th;
+ r.Height -= th;
+
+ const int titleGap = 5;
+
+ if (BorderWidth > 0)
+ {
+ gr.Color = BorderColor;
+ gr.LineWidth = BorderWidth;
+
+ r.Inflate(-BorderWidth / 2, -BorderWidth / 2);
+
+ rTitle.X = r.X + titleGap;
+ rTitle.Width = (int)Math.Ceiling(te.XAdvance) + 2 * titleGap;
+ rTitle.Height = (int)Math.Ceiling(fe.Height);
+ gr.Save();
+ gr.FillRule = FillRule.EvenOdd;
+ gr.Rectangle(new Rectangle(Slot.Size));
+ gr.Rectangle(rTitle);
+ gr.Clip();
+
+ CairoHelpers.CairoRectangle (gr, r, CornerRadius);
+
+ gr.Stroke();
+ gr.Restore();
+ }
+
+ gr.MoveTo(rTitle.X + titleGap, rTitle.Y + (int)Math.Ceiling(fe.Height - fe.Descent));
+
+ #if _WIN32 || _WIN64
+ gr.ShowText(txt);
+ #elif __linux__
+ gr.ShowText(Text);
+ #endif
+ gr.Color = Foreground;
+ gr.Fill();
+ }
+
+ public override string ToString()
+ {
+ return this.Text + ":" + base.ToString();
+ }
+ }
+}
+
diff --git a/src/GraphicObjects/HorizontalStack.cs b/src/GraphicObjects/HorizontalStack.cs
new file mode 100644
index 00000000..bdfc136e
--- /dev/null
+++ b/src/GraphicObjects/HorizontalStack.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Serialization;
+
+namespace go
+{
+ public class HorizontalStack : GenericStack
+ {
+ public HorizontalStack()
+ : base()
+ {
+ Orientation = go.Orientation.Horizontal;
+ }
+
+ [XmlIgnore]
+ public override Orientation Orientation
+ {
+ get { return Orientation.Horizontal; }
+ //set { }
+ }
+
+
+
+ }
+}
diff --git a/src/GraphicObjects/ILayoutable.cs b/src/GraphicObjects/ILayoutable.cs
new file mode 100644
index 00000000..6560eb6c
--- /dev/null
+++ b/src/GraphicObjects/ILayoutable.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace go
+{
+ public interface ILayoutable
+ {
+ ILayoutable Parent { get; set; }
+
+ bool SizeIsValid { get; set; }
+ bool WIsValid { get; set; }
+ bool HIsValid { get; set; }
+ bool PositionIsValid { get; set; }
+ bool XIsValid { get; set; }
+ bool YIsValid { get; set; }
+ bool LayoutIsValid { get; set; }
+
+ Rectangle ClientRectangle { get; }
+ Rectangle getSlot();
+
+ void InvalidateLayout ();
+
+
+ Rectangle ContextCoordinates(Rectangle r);
+ Rectangle ScreenCoordinates (Rectangle r);
+
+ }
+}
+
diff --git a/src/GraphicObjects/Image.cs b/src/GraphicObjects/Image.cs
new file mode 100644
index 00000000..ce60a8eb
--- /dev/null
+++ b/src/GraphicObjects/Image.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+//using OpenTK.Graphics.OpenGL;
+using Cairo;
+using System.IO;
+
+namespace go
+{
+ public class Image : GraphicObject
+ {
+
+ byte[] image;
+ Size imgSize;
+ string _imgPath;
+
+ [System.Xml.Serialization.XmlIgnore]
+ public System.Drawing.Bitmap Bitmap {
+ set {
+ loadImage (value);
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute("Path")]
+ public string ImagePath {
+ get { return _imgPath; }
+ set {
+ _imgPath = value;
+ loadImage (_imgPath);
+ }
+ }
+
+ public Image () : base()
+ {
+ }
+
+ public Image (string ImagePath, Rectangle _bounds)
+ : base(_bounds)
+ {
+ _imgPath = ImagePath;
+ loadImage(_imgPath);
+ }
+
+ public Image (string ImagePath)
+ : base()
+ {
+ _imgPath = ImagePath;
+ loadImage(_imgPath);
+ }
+
+ public Image (System.Drawing.Bitmap _bitmap)
+ : base()
+ {
+ Bitmap = _bitmap;
+ }
+
+ public override Size measureRawSize ()
+ {
+ if (image == null)
+ loadImage (@"Images/Icons/icon_alert.gif");
+
+ return imgSize + (BorderWidth + Margin)*2;
+ }
+
+ //load image via System.Drawing.Bitmap, cairo load png only
+ public void loadImage (string path)
+ {
+ if (!File.Exists(path))
+ return;
+
+ loadImage (new System.Drawing.Bitmap (path));
+ _imgPath = path;
+ }
+
+ public void loadImage (System.Drawing.Bitmap bitmap)
+ {
+ if (bitmap == null)
+ return;
+
+ System.Drawing.Imaging.BitmapData data = bitmap.LockBits
+ (new System.Drawing.Rectangle (0, 0, bitmap.Width, bitmap.Height),
+ System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
+
+ imgSize = new Size (bitmap.Width, bitmap.Height);
+
+ int stride = data.Stride;
+ int bitmapSize = Math.Abs (data.Stride) * bitmap.Height;
+
+ image = new byte[bitmapSize];
+ System.Runtime.InteropServices.Marshal.Copy (data.Scan0, image, 0, bitmapSize);
+
+ bitmap.UnlockBits (data);
+ //bitmap.Dispose();
+ }
+ public override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+ float ratio = 1f;
+ float widthRatio = (float)clientBounds.Width / imgSize.Width;
+ float heightRatio = (float)clientBounds.Height / imgSize.Height;
+
+ ratio = Math.Min (widthRatio, heightRatio);
+ Rectangle rImg = clientBounds;
+
+ gr.Scale (widthRatio, heightRatio);
+ using (ImageSurface imgSurf = new ImageSurface(image, Format.Argb32,
+ imgSize.Width, imgSize.Height, 4 * imgSize.Width)) {
+ gr.SetSourceSurface (imgSurf, (int)(rImg.X / widthRatio), (int)(rImg.Y / heightRatio));
+ gr.Paint ();
+ }
+ }
+ }
+}
diff --git a/src/GraphicObjects/Label.cs b/src/GraphicObjects/Label.cs
new file mode 100644
index 00000000..c29050a1
--- /dev/null
+++ b/src/GraphicObjects/Label.cs
@@ -0,0 +1,294 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+using Cairo;
+using System.Text.RegularExpressions;
+
+namespace go
+{
+ [Serializable]
+ public class Label : GraphicObject
+ {
+ //TODO:change protected to private
+
+ protected string _text = "label";
+ Alignment _textAlignment = Alignment.LeftCenter;
+ int _fontSize = 12;
+ bool _multiline = false;
+
+ protected Rectangle rText;
+ protected float widthRatio = 1f;
+ protected float heightRatio = 1f;
+ protected FontExtents fe;
+ protected TextExtents te;
+
+ //public string FontFace = "MagicMedieval";
+ public string FontFace = "droid";
+
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(-1)]
+ public override int Width {
+ get { return base.Width; }
+ set { base.Width = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(-1)]
+ public override int Height {
+ get { return base.Height; }
+ set { base.Height = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(2)]
+ public virtual int Margin {
+ get { return base.Margin; }
+ set { base.Margin = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(Alignment.LeftCenter)]
+ public Alignment TextAlignment
+ {
+ get { return _textAlignment; }
+ set { _textAlignment = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ public string Text
+ {
+ get { return _text; }
+ set
+ {
+ if (_text == value)
+ return;
+
+
+ registerForGraphicUpdate();
+ InvalidateLayout();
+
+ _text = value;
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(10)]
+ public int FontSize
+ {
+ get { return _fontSize; }
+ set
+ {
+ _fontSize = value;
+ registerForGraphicUpdate();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(false)]
+ public bool Multiline
+ {
+ get { return _multiline; }
+ set
+ {
+ _multiline = value;
+ registerForGraphicUpdate();
+ }
+ }
+
+ public Label()
+ {
+
+ }
+
+ public Label(string _text)
+ : base()
+ {
+ //updateFont();
+ Text = _text;
+ }
+
+ void updateFont()
+ {
+ //TextFont = new Font(fontFamily, fontSize, fontStyle, GraphicsUnit.Pixel);
+
+ bmp = null;
+ }
+
+ string[] getLines {
+ get {
+ return _multiline ?
+ Regex.Split (_text, "\r\n|\r|\n") :
+ new string[] { _text };
+ }
+ }
+
+ public override Size measureRawSize()
+ {
+ Size s;
+
+
+ if (string.IsNullOrEmpty(_text))
+ _text = "";
+
+ string[] lines = getLines;
+
+ using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) {
+ using (Context gr = new Context (img)) {
+ //Cairo.FontFace cf = gr.GetContextFontFace ();
+ gr.SelectFontFace (FontFace, FontSlant.Normal, FontWeight.Normal);
+ gr.SetFontSize (FontSize);
+
+ te = new TextExtents();
+
+ foreach (string str in lines) {
+#if _WIN32 || _WIN64
+ TextExtents tmp = gr.TextExtents(str.ToUtf8());
+#elif __linux__
+ TextExtents tmp = gr.TextExtents (str);
+#endif
+ if (tmp.XAdvance > te.XAdvance)
+ te = tmp;
+ }
+ fe = gr.FontExtents;
+ s = new Size ((int)Math.Ceiling (te.XAdvance) + (BorderWidth + Margin) * 2, (int)Math.Ceiling (fe.Height) * lines.Length + (BorderWidth + Margin) * 2);
+ }
+ }
+ return s;// +borderWidth;
+ }
+ public override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+ string[] lines = getLines;
+
+ gr.SelectFontFace(FontFace, FontSlant.Normal, FontWeight.Normal);
+
+ gr.LineWidth = BorderWidth;
+ gr.Antialias = Antialias.Subpixel;
+ //gr.FontOptions.Antialias = Antialias.Subpixel;
+ //gr.FontOptions.HintMetrics = HintMetrics.On;
+ gr.SetFontSize(FontSize);
+
+ rText = new Rectangle(new Point(0, 0),new Size((int)te.Width,(int)fe.Height * lines.Length));
+
+ widthRatio = 1f;
+ heightRatio = 1f;
+
+ Rectangle cb = clientBounds;
+
+ //ignore text alignment if size to content = true
+ if (Bounds.Size < 0)
+ {
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+ }else{
+ switch (TextAlignment)
+ {
+ case Alignment.None:
+ break;
+ case Alignment.TopLeft: //ok
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+ break;
+ case Alignment.TopCenter: //ok
+ rText.Y = cb.Y;
+ rText.X = cb.X + cb.Width / 2 - rText.Width / 2;
+ break;
+ case Alignment.TopRight: //ok
+ rText.X = cb.Right - rText.Width;
+ rText.Y = cb.Y;
+ break;
+ case Alignment.TopStretch://ok
+ heightRatio = widthRatio = (float)cb.Width / rText.Width;
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+ rText.Width = cb.Width;
+ rText.Height = (int)(rText.Height * heightRatio);
+ break;
+ case Alignment.LeftCenter://ok
+ rText.X = cb.X;
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ break;
+ case Alignment.LeftStretch://ok
+ heightRatio = widthRatio = (float)cb.Height / rText.Height;
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+ rText.Height = cb.Height;
+ rText.Width = (int)(widthRatio * cb.Width);
+ break;
+ case Alignment.RightCenter://ok
+ rText.X = cb.X + cb.Width - rText.Width;
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ break;
+ case Alignment.RightStretch://ok
+ heightRatio = widthRatio = (float)cb.Height / rText.Height;
+ rText.Height = cb.Height;
+ rText.Width = (int)(widthRatio * cb.Width);
+ rText.X = cb.X;
+ rText.Y = cb.Y;
+ break;
+ case Alignment.BottomCenter://ok
+ rText.X = cb.Width / 2 - rText.Width / 2;
+ rText.Y = cb.Height - rText.Height;
+ break;
+ case Alignment.BottomStretch://ok
+ heightRatio = widthRatio = (float)cb.Width / rText.Width;
+ rText.Width = cb.Width;
+ rText.Height = (int)(rText.Height * heightRatio);
+ rText.Y = cb.Bottom - rText.Height;
+ rText.X = cb.X;
+ 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;
+ case Alignment.Fit://ok, peut être mieu aligné
+ widthRatio = (float)cb.Width / rText.Width;
+ heightRatio = (float)cb.Height / rText.Height;
+ rText = cb;
+ break;
+ case Alignment.HorizontalStretch://ok
+ heightRatio = widthRatio = (float)cb.Width / rText.Width;
+ rText.Width = cb.Width;
+ rText.Height = (int)(heightRatio * rText.Height);
+ rText.Y = cb.Y + cb.Height / 2 - rText.Height / 2;
+ rText.X = cb.X;
+ break;
+ case Alignment.VerticalStretch://ok
+ heightRatio = widthRatio = (float)cb.Height / rText.Height;
+ rText.Height = cb.Height;
+ rText.Width = (int)(widthRatio * rText.Width);
+ rText.X = cb.X + cb.Width / 2 - rText.Width / 2;
+ rText.Y = cb.Y;
+ break;
+ default:
+ break;
+ }
+ }
+
+ gr.Color = Foreground;
+
+ gr.FontMatrix = new Matrix(widthRatio * FontSize, 0, 0, heightRatio * FontSize, 0, 0);
+
+ for (int i = 0; i < lines.Length; i++) {
+ gr.MoveTo(rText.X, rText.Y + fe.Ascent + fe.Height * i);
+ #if _WIN32 || _WIN64
+ gr.ShowText(lines[i].ToUtf8());
+ #elif __linux__
+ gr.ShowText(lines[i]);
+ #endif
+ }
+ gr.Fill();
+ }
+ }
+}
diff --git a/src/GraphicObjects/NumericControl.cs b/src/GraphicObjects/NumericControl.cs
new file mode 100644
index 00000000..c8aa48d2
--- /dev/null
+++ b/src/GraphicObjects/NumericControl.cs
@@ -0,0 +1,119 @@
+using System;
+
+namespace go
+{
+ public class NumericControl : GraphicObject
+ {
+ public NumericControl () : base()
+ {
+ }
+ public NumericControl(double minimum, double maximum, double step)
+ : base()
+ {
+ _minValue = minimum;
+ _maxValue = maximum;
+ _smallStep = step;
+ _bigStep = step * 5;
+ }
+
+ #region event handlers
+ public EventHandler<ValueChangeEventArgs> ValueChanged;
+
+ public virtual void onValueChanged(object sender, ValueChangeEventArgs e)
+ {
+ if (ValueChanged != null)
+ ValueChanged (sender, e);
+ }
+ #endregion
+
+ double _actualValue, _minValue, _maxValue, _smallStep, _bigStep;
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public virtual double Minimum {
+ get { return _minValue; }
+ set {
+ if (_minValue == value)
+ return;
+
+ _minValue = value;
+
+ LayoutIsValid = false;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(10.0)]
+ public virtual double Maximum
+ {
+ get { return _maxValue; }
+ set {
+ if (_maxValue == value)
+ return;
+
+ _maxValue = value;
+
+ LayoutIsValid = false;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0.5)]
+ public virtual double SmallIncrement
+ {
+ get { return _smallStep; }
+ set {
+ if (_smallStep == value)
+ return;
+
+ _smallStep = value;
+
+ LayoutIsValid = false;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(2.0)]
+ public virtual double LargeIncrement
+ {
+ get { return _bigStep; }
+ set {
+ if (_bigStep == value)
+ return;
+
+ _bigStep = value;
+
+ LayoutIsValid = false;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public double Value
+ {
+ get { return _actualValue; }
+ set
+ {
+ if (value == _actualValue)
+ return;
+
+ Decimal oldV = (Decimal)_actualValue;
+
+ if (value < _minValue)
+ _actualValue = _minValue;
+ else if (value > _maxValue)
+ _actualValue = _maxValue;
+ else
+ _actualValue = value;
+
+ onValueChanged(this,new ValueChangeEventArgs(oldV,(Decimal)_actualValue));
+ registerForGraphicUpdate();
+ }
+ }
+ }
+}
+
diff --git a/src/GraphicObjects/ProgressBar.cs b/src/GraphicObjects/ProgressBar.cs
new file mode 100644
index 00000000..b9743c5a
--- /dev/null
+++ b/src/GraphicObjects/ProgressBar.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Cairo;
+using System.Diagnostics;
+
+namespace go
+{
+ [Serializable]
+ public class ProgressBar : GraphicObject
+ {
+ int _currentValue = 0;
+ int _minimum = 0;
+ int _maximum = 100;
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue("Gray")]
+ public override Color Background {
+ get { return base.Background; }
+ set { base.Background = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public override int BorderWidth {
+ get { return base.BorderWidth; }
+ set { base.BorderWidth = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue("BlueCrayola")]
+ public virtual Color Foreground {
+ get { return base.Foreground; }
+ set { base.Foreground = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public int Value
+ {
+ get { return _currentValue; }
+ set
+ {
+ if (_currentValue == value)
+ return;
+
+ _currentValue = value;
+
+ if (_currentValue > Maximum)
+ _currentValue = Maximum;
+
+ registerForGraphicUpdate();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(0)]
+ public int Minimum {
+ get {return _minimum;}
+ set {
+ if (_minimum == value || _minimum >= Maximum)
+ return;
+ _minimum = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(100)]
+ public int Maximum {
+ get {return _maximum;}
+ set {
+ if (_maximum == value || _maximum <= _minimum)
+ return;
+ _maximum = value;
+ registerForGraphicUpdate();
+ }
+ }
+
+ public ProgressBar() : base(){}
+
+ public ProgressBar(int _max) : base()
+ {
+ Maximum = _max;
+ }
+
+ public override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ Rectangle rBack = Slot.Size;
+
+ if (BorderWidth > 0)
+ rBack.Inflate(-BorderWidth, -BorderWidth);
+ rBack.Width = (int)((double)rBack.Width / Maximum * Value);
+
+ gr.Color = Foreground;
+
+ CairoHelpers.CairoRectangle(gr,rBack,CornerRadius);
+ gr.Fill();
+ }
+ public override void Paint (ref Context ctx, Rectangles clip)
+ {
+ base.Paint (ref ctx, clip);
+// if (clip != null)
+// clip.stroke (ctx, Color.Amethyst);
+ }
+ }
+}
diff --git a/src/GraphicObjects/Scroller.cs b/src/GraphicObjects/Scroller.cs
new file mode 100644
index 00000000..74300dba
--- /dev/null
+++ b/src/GraphicObjects/Scroller.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using Cairo;
+using System.Xml.Serialization;
+using OpenTK.Input;
+
+namespace go
+{
+ public class Scroller : Container
+ {
+ bool _verticalScrolling;
+ bool _horizontalScrolling;
+
+ #region public properties
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(false)]
+ public bool VerticalScrolling {
+ get { return _verticalScrolling; }
+ set { _verticalScrolling = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(false)]
+ public bool HorizontalScrolling {
+ get { return _horizontalScrolling; }
+ set { _horizontalScrolling = value; }
+ }
+
+ [XmlIgnore]
+ public int scrollX = 0;
+ [XmlIgnore]
+ public int scrollY = 0;
+
+ public static int ScrollSpeed = 10;
+
+ #endregion
+
+
+ public Scroller()
+ : base()
+ {
+ }
+
+ #region GraphicObject Overrides
+
+ #endregion
+
+ #region Mouse handling
+ public override bool MouseIsIn (Point m)
+ {
+ //Debug.WriteLine ("scroller mouse is in");
+ return Visible ? base.ScreenCoordinates(Slot).ContainsOrIsEqual (m) : false;
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ Point m = e.Position + new Point (scrollX, scrollY);
+ base.onMouseMove (sender, new MouseMoveEventArgs(m.X,m.Y,e.XDelta,e.YDelta));
+ }
+ public override void onMouseWheel (object sender, MouseWheelEventArgs e)
+ {
+ //base.onMouseWheel (sender, e); base event buble to the top
+// if (MouseWheelChanged!=null)
+// MouseWheelChanged (this, e);
+
+ if (child == null)
+ return;
+
+ if (VerticalScrolling )
+ {
+ Debug.WriteLine ("scroll");
+ //add redraw call with old bounds to errase old position
+ registerForRedraw();
+
+ scrollY += e.Delta * ScrollSpeed;
+
+ if (scrollY > 0)
+ scrollY = 0;
+ else if (scrollY < -child.Slot.Height + clientBounds.Height)
+ scrollY = -child.Slot.Height + clientBounds.Height;
+
+ }
+ if (HorizontalScrolling )
+ {
+ //add redraw call with old bounds to errase old position
+ registerForRedraw();
+
+ scrollX += e.Delta * ScrollSpeed;
+
+ if (scrollX > 0)
+ scrollX = 0;
+ else if (scrollX < -child.Slot.Width + clientBounds.Width)
+ scrollX = -child.Slot.Width + clientBounds.Width;
+ }
+
+
+ //renderBounds.Y = -scrollY;
+ registerForRedraw();
+ //Parent.registerForGraphicUpdate ();
+ }
+ #endregion
+
+ public override Rectangle ContextCoordinates (Rectangle r)
+ {
+ return base.ContextCoordinates (r) + new Point(scrollX,scrollY);
+ }
+ public override Rectangle ScreenCoordinates (Rectangle r)
+ {
+ return base.ScreenCoordinates (r) + new Point(scrollX,scrollY);
+ }
+
+ public override void registerClipRect ()
+ {
+ OpenTKGameWindow.redrawClip.AddRectangle (base.ScreenCoordinates(Slot));
+ }
+ public override void UpdateLayout ()
+ {
+ base.UpdateLayout ();
+
+ if (!LayoutIsValid)
+ return;
+
+ //override child positionning inside scroller in the
+ //scrolling direction
+ if (child == null)
+ return;
+ if (VerticalScrolling)
+ child.Slot.Y = 0;
+ if (HorizontalScrolling)
+ child.Slot.X = 0;
+ }
+ public override void Paint(ref Cairo.Context ctx, Rectangles clip = null)
+ {
+ if (!Visible)//check if necessary??
+ return;
+
+ ctx.Save();
+
+ // ctx.Rectangle(ContextCoordinates(Slot));
+ // ctx.Clip();
+ //
+// if (clip != null)
+// clip.clip(ctx);
+ //clip.Srcoll (this);
+
+ base.Paint (ref ctx, clip);
+
+ //clip to client zone
+ ctx.Rectangle(Parent.ContextCoordinates(ClientRectangle + Slot.Position));
+ ctx.Clip();
+
+// if (clip != null)
+// clip.Srcoll (this);
+ // if (clip != null)
+ // clip.Rebase(this);
+
+ if (child != null)
+ child.Paint(ref ctx, clip);
+
+ ctx.Restore();
+ }
+
+//
+// public override void Paint(ref Cairo.Context ctx, Rectangles clip = null)
+// {
+// if (!Visible)//check if necessary??
+// return;
+//
+// ctx.Save();
+//
+// Rectangle tmp;
+// tmp = Parent.ContextCoordinates(Slot);
+//// ctx.Rectangle(tmp);
+//// ctx.Clip();
+//
+//// if (clip != null)
+//// clip.clip(ctx);
+//
+// if (bmp == null)
+// UpdateGraphic ();
+//
+//
+//
+// int stride = 4 * Slot.Width;
+// using (ImageSurface source = new ImageSurface(bmp, Format.Argb32, tmp.Width, tmp.Height, stride)) {
+// ctx.SetSourceSurface (source, tmp.X, tmp.Y);
+// ctx.Paint ();
+// }
+//
+//
+// //clip to client zone
+//// ctx.Rectangle(Parent.ContextCoordinates(ClientRectangle + Slot.Position));
+//// ctx.Clip();
+//
+// //ctx.Translate (scrollX, scrollY);
+//
+// if (clip != null)
+// clip.Srcoll (this);
+////
+//// //clip.stroke (ctx, Color.Green);
+////
+////// if (scrollY < 0)
+////// Debug.WriteLine ("..");
+//// }
+//
+//
+//
+// if (child != null)
+// child.Paint(ref ctx, clip);
+//
+// ctx.Restore();
+// }
+
+ }
+}
diff --git a/src/GraphicObjects/Slider.cs b/src/GraphicObjects/Slider.cs
new file mode 100644
index 00000000..bb295f2c
--- /dev/null
+++ b/src/GraphicObjects/Slider.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Cairo;
+using System.Xml.Serialization;
+
+namespace go
+{
+ public class Slider : NumericControl
+ {
+ Rectangle cursor;
+ int _cursorWidth;
+ Color _cursorColor;
+ //Point mouseDownPos;
+ bool holdCursor = false;//may use maybe active go equality
+
+ protected double unity;
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue("Gray")]
+ public override Color Background {
+ get { return base.Background; }
+ set { base.Background = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(2)]
+ public override int BorderWidth {
+ get { return base.BorderWidth; }
+ set { base.BorderWidth = value; }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue("BlueGray")]
+ public virtual Color CursorColor {
+ get { return _cursorColor; }
+ set {
+ _cursorColor = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(20)]
+ public virtual int CursorWidth {
+ get { return _cursorWidth; }
+ set {
+ _cursorWidth = value;
+ registerForGraphicUpdate ();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(true)]
+ public override bool Focusable
+ {
+ get { return base.Focusable; }
+ set { base.Focusable = value; }
+ }
+
+
+
+ public Slider() : base()
+ {}
+ public Slider(double minimum, double maximum, double step)
+ : base(minimum,maximum,step)
+ {
+ }
+
+ void computeCursorPosition()
+ {
+ Rectangle r = clientBounds;
+
+ cursor = new Rectangle(new Size(_cursorWidth,(int) (r.Height)));
+
+ PointD p1 = r.TopLeft + new Point(cursor.Width/2, r.Height / 2);
+
+ unity = (double)(r.Width - cursor.Width) / (Maximum - Minimum);
+
+ cursor.TopLeft = new Point(r.Left + (int)(Value * unity),
+ (int)(p1.Y - cursor.Height / 2));
+ }
+
+
+ #region mouse handling
+ public override void onMouseButtonDown (object sender, OpenTK.Input.MouseButtonEventArgs e)
+ {
+ base.onMouseButtonDown (sender, e);
+
+ Rectangle cursInScreenCoord = ScreenCoordinates(cursor+Slot.Position);
+ if (cursInScreenCoord.ContainsOrIsEqual(e.Position))
+ holdCursor = true;
+ else if (e.Position.X < cursInScreenCoord.Left)
+ Value -= LargeIncrement;
+ else
+ Value += LargeIncrement;
+ }
+ public override void onMouseButtonUp (object sender, OpenTK.Input.MouseButtonEventArgs e)
+ {
+ base.onMouseButtonUp (sender, e);
+
+ holdCursor = false;
+ }
+ public override void onMouseMove (object sender, OpenTK.Input.MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ if (!holdCursor)
+ return;
+
+ Value += (double)e.XDelta / unity;
+
+ }
+ #endregion
+
+ internal override void UpdateGraphic ()
+ {
+ computeCursorPosition();
+ base.UpdateGraphic ();
+ }
+
+ public override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ Rectangle r = clientBounds;
+ PointD pStart = r.TopLeft + new Point(cursor.Width/2, r.Height / 2);
+ PointD pEnd = r.TopRight + new Point(-cursor.Width/2, r.Height / 2);
+
+ DrawGraduations (gr, pStart,pEnd);
+
+ DrawCursor (gr, cursor);
+ }
+
+ public virtual void DrawGraduations(Context gr, PointD pStart, PointD pEnd)
+ {
+ gr.Color = Foreground;
+
+ gr.LineWidth = 1;
+ gr.MoveTo(pStart);
+ gr.LineTo(pEnd);
+
+ gr.Stroke();
+
+ }
+ public virtual void DrawCursor(Context gr, Rectangle _cursor)
+ {
+ gr.Color = CursorColor;
+ CairoHelpers.CairoRectangle (gr, _cursor, CornerRadius);
+ gr.FillPreserve();
+ gr.Color = BorderColor;
+ gr.Stroke ();
+ }
+ }
+}
diff --git a/src/GraphicObjects/TextBox.cs b/src/GraphicObjects/TextBox.cs
new file mode 100644
index 00000000..4df0b3f7
--- /dev/null
+++ b/src/GraphicObjects/TextBox.cs
@@ -0,0 +1,420 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenTK.Input;
+using Cairo;
+using System.Diagnostics;
+using System.Xml.Serialization;
+using System.Globalization;
+
+namespace go
+{
+ public class TextBoxWidget : Label
+ {
+ public static KeyboardDevice Keyboard;
+ public static char decimalSeparator = '.';//????? should follow locale policy
+ static bool _capitalOn = false; //????????????????
+ public static bool capitalOn
+ {
+ get
+ {
+ return _capitalOn;
+ //Keyboard[Key.ShiftLeft] || Keyboard[Key.ShiftRight] ?
+ //!_capitalOn : _capitalOn;
+ }
+ set { _capitalOn = value; }
+ }
+
+ Color selColor = Color.BlueBell;
+ Color selFontColor = Color.WhiteSmoke;
+ Point mouseLocalPos; //mouse coord in widget space, filled only when clicked
+ int _currentPos; //0 based cursor position in string
+ double textCursorPos; //cursor position in cairo units in widget client coord.
+ double SelStartCursorPos = -1;
+ double SelEndCursorPos = -1;
+ bool SelectionInProgress = false;
+
+ [XmlIgnore]
+ public override bool HasFocus //trigger update when lost focus to errase text beam
+ {
+ get
+ {
+ return base.HasFocus;
+ }
+ set
+ {
+ base.HasFocus = value;
+ registerForGraphicUpdate();
+ }
+ }
+
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ [System.ComponentModel.DefaultValue(true)]
+ public override bool Focusable
+ {
+ get { return base.Focusable; }
+ set { base.Focusable = value; }
+ }
+
+ [XmlIgnore]
+ public GOEvent onTextChanged;
+
+ [XmlIgnore]
+ public int currentPos
+ {
+ get { return _currentPos; }
+ set { _currentPos = value; }
+ }
+
+ [XmlIgnore]
+ public int selBeginPos;
+ [XmlIgnore]
+ public int selReleasePos;
+ [XmlIgnore]
+ public int selectionStart //ordered selection start and end positions
+ {
+ get { return selReleasePos < 0 ? selBeginPos : Math.Min(selBeginPos, selReleasePos); }
+ }
+ [XmlIgnore]
+ public int selectionEnd
+ { get { return selReleasePos < 0 ? selReleasePos : Math.Max(selBeginPos, selReleasePos); } }
+ [XmlIgnore]
+ public string selectedText
+ { get { return selectionEnd < 0 ? null : Text.Substring(selectionStart, selectionEnd - selectionStart); } }
+ [XmlIgnore]
+ public bool selectionIsEmpty
+ { get { return string.IsNullOrEmpty(selectedText); } }
+
+ public TextBoxWidget(string _initialValue, GOEvent _onTextChanged = null)
+ : base(_initialValue)
+ {
+ onTextChanged = _onTextChanged;
+ Focusable = true;
+ selColor = Color.SkyBlue;
+ selFontColor = Color.Black;
+ Foreground = Color.Black;
+ Background = Color.White;
+ TextAlignment = Alignment.LeftCenter;
+ }
+
+ public TextBoxWidget()
+ { }
+
+ public override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+// gr.FontOptions.Antialias = Antialias.Subpixel;
+// gr.FontOptions.HintMetrics = HintMetrics.On;
+ gr.SetFontSize(FontSize);
+ FontExtents fe = gr.FontExtents;
+
+ #region draw text cursor
+ if (mouseLocalPos > 0)
+ {
+ computeTextCursor(gr);
+
+ if (SelectionInProgress)
+ {
+ selReleasePos = currentPos;
+ SelEndCursorPos = textCursorPos;
+ }
+ else if (selBeginPos < 0)
+ {
+ selBeginPos = currentPos;
+ SelStartCursorPos = textCursorPos;
+ selReleasePos = -1;
+ }
+ else
+ computeTextCursorPosition(gr);
+
+ }
+ else
+ computeTextCursorPosition(gr);
+
+ if (HasFocus)
+ {
+ //TODO:
+ gr.Color = Color.White;
+ gr.LineWidth = 2;
+ gr.MoveTo(new PointD(textCursorPos + rText.X, rText.Y ));
+ gr.LineTo(new PointD(textCursorPos + rText.X, rText.Y + fe.Height));
+ gr.Stroke();
+ }
+
+ #endregion
+
+ if (selReleasePos >= 0)
+ {
+ gr.Color = selColor;
+ Rectangle selRect =
+ new Rectangle((int)SelStartCursorPos + rText.X, (int)(rText.Y), (int)(SelEndCursorPos - SelStartCursorPos), (int)fe.Height);
+ gr.Rectangle(selRect);
+ gr.Fill();
+
+ gr.Color = selFontColor;
+ }
+ else
+ gr.Color = Foreground;
+
+ gr.MoveTo(rText.X, rText.Y + fe.Ascent);
+ #if _WIN32 || _WIN64
+ gr.ShowText(txt);
+ #elif __linux__
+ gr.ShowText(Text);
+ #endif
+ gr.Fill();
+
+ }
+
+ #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 (!selectionIsEmpty)
+ {
+ Text = Text.Remove(selectionStart, selectionEnd - selectionStart);
+ selReleasePos = -1;
+ currentPos = selBeginPos;
+ }
+ else if (currentPos > 0)
+ {
+ currentPos--;
+ Text = Text.Remove(currentPos, 1);
+ }
+ break;
+ case Key.Clear:
+ break;
+ case Key.Delete:
+ if (!selectionIsEmpty)
+ {
+ Text = Text.Remove(selectionStart, selectionEnd - selectionStart);
+ selReleasePos = -1;
+ currentPos = selBeginPos;
+ }
+ else if (currentPos < Text.Length)
+ Text = Text.Remove(currentPos, 1);
+ break;
+ case Key.Down:
+ break;
+ case Key.End:
+ currentPos = Text.Length;
+ break;
+ case Key.Enter:
+ case Key.KeypadEnter:
+ if (onTextChanged != null)
+ onTextChanged(this);
+ break;
+ case Key.Escape:
+ Text = "";
+ currentPos = 0;
+ selReleasePos = -1;
+ break;
+ case Key.Home:
+ currentPos = 0;
+ break;
+ case Key.Insert:
+ break;
+ case Key.Left:
+ if (currentPos > 0)
+ currentPos--;
+ break;
+ case Key.Menu:
+ break;
+ case Key.NumLock:
+ break;
+ case Key.PageDown:
+ break;
+ case Key.PageUp:
+ break;
+ case Key.RWin:
+ break;
+ case Key.Right:
+ if (currentPos < Text.Length)
+ currentPos++;
+ break;
+ case Key.Tab:
+ Text = Text.Insert(currentPos, "\t");
+ currentPos++;
+ break;
+ case Key.Up:
+ break;
+ case Key.KeypadDecimal:
+ Text = Text.Insert(currentPos, new string(new char[]
+ { Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) }));
+ currentPos++;
+ break;
+ case Key.Space:
+ Text = Text.Insert(currentPos, " ");
+ currentPos++;
+ break;
+ case Key.KeypadDivide:
+ case Key.Slash:
+ Text = Text.Insert(currentPos, "/");
+ currentPos++;
+ break;
+ case Key.KeypadMultiply:
+ Text = Text.Insert(currentPos, "*");
+ currentPos++;
+ break;
+ case Key.KeypadMinus:
+ case Key.Minus:
+ Text = Text.Insert(currentPos, "-");
+ currentPos++;
+ break;
+ case Key.KeypadPlus:
+ case Key.Plus:
+ Text = Text.Insert(currentPos, "+");
+ currentPos++;
+ break;
+ default:
+ if (!selectionIsEmpty)
+ {
+ Text = Text.Remove(selectionStart, selectionEnd - selectionStart);
+ currentPos = selBeginPos;
+ }
+
+ string k = "?";
+ if ((char)key >= 67 && (char)key <= 76)
+ k = ((int)key - 67).ToString();
+ else if ((char)key >= 109 && (char)key <= 118)
+ k = ((int)key - 109).ToString();
+ else if (capitalOn)
+ k = key.ToString();
+ else
+ k = key.ToString().ToLower();
+
+ Text = Text.Insert(currentPos, k);
+ currentPos++;
+
+ selReleasePos = -1;
+ selBeginPos = currentPos;
+
+ break;
+ }
+ registerForGraphicUpdate();
+ }
+ #endregion
+
+ #region Mouse handling
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+// SelectionInProgress = true;
+// mouseLocalPos = e.Position - ScreenCoordBounds.TopLeft - rText.TopLeft;
+// registerForGraphicUpdate();
+
+ base.onMouseEnter (sender, e);
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ if ((sender as OpenTKGameWindow).activeWidget != this)
+ return;
+
+ SelectionInProgress = true;
+ mouseLocalPos = e.Position - ScreenCoordinates(rText).TopLeft;
+ registerForGraphicUpdate();
+
+ }
+ public override void onMouseButtonDown (object sender, MouseButtonEventArgs e)
+ {
+ if (this.HasFocus){
+ mouseLocalPos = Mouse.Position - ScreenCoordinates(rText).TopLeft - rText.TopLeft;
+ selBeginPos = -1;
+ selReleasePos = -1;
+ }else{
+ selBeginPos = 0;
+ selReleasePos = Text.Length;
+ }
+
+ //done at the end to set 'hasFocus' value after testing it
+ base.onMouseButtonDown (sender, e);
+
+ registerForGraphicUpdate();
+ }
+ public override void onMouseButtonUp (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseButtonUp (sender, e);
+ SelectionInProgress = false;
+ }
+ #endregion
+
+ void computeTextCursor(Context gr)
+ {
+ FontExtents fe = gr.FontExtents;
+ TextExtents te;
+
+ double cPos = 0f;
+ for (int i = 0; i < _text.Length; i++)
+ {
+#if _WIN32 || _WIN64
+ byte[] c = System.Text.UTF8Encoding.UTF8.GetBytes(Text.Substring(i, 1));
+ te = gr.TextExtents(c);
+#elif __linux__
+ te = gr.TextExtents(Text.Substring(i, 1));
+#endif
+ double halfWidth = te.XAdvance / 2;
+
+ if (mouseLocalPos.X <= cPos + halfWidth)
+ {
+ currentPos = i;
+ textCursorPos = cPos;
+ mouseLocalPos = -1;
+ return;
+ }
+
+ cPos += te.XAdvance;
+ }
+ currentPos = _text.Length;
+ textCursorPos = cPos;
+
+ //reset mouseLocalPos
+ mouseLocalPos = -1;
+ }
+ void computeTextCursorPosition(Context gr)
+ {
+ FontExtents fe = gr.FontExtents;
+ TextExtents te;
+
+ double cPos = 0f;
+
+ int limit = currentPos;
+
+ if (selectionEnd > 0)
+ limit = Math.Max(currentPos, selectionEnd);
+
+ for (int i = 0; i <= limit; i++)
+ {
+ if (i == currentPos)
+ textCursorPos = cPos;
+ if (i == selectionStart)
+ SelStartCursorPos = cPos;
+ if (i == selectionEnd)
+ SelEndCursorPos = cPos;
+
+ if (i < Text.Length)
+ {
+#if _WIN32 || _WIN64
+ byte[] c = System.Text.UTF8Encoding.UTF8.GetBytes(Text.Substring(i, 1));
+ te = gr.TextExtents(c);
+#elif __linux__
+ te = gr.TextExtents(Text.Substring(i,1));
+#endif
+ cPos += te.XAdvance;
+ }
+ }
+
+ }
+ }
+}
diff --git a/src/GraphicObjects/VerticalStack.cs b/src/GraphicObjects/VerticalStack.cs
new file mode 100644
index 00000000..0fd063a0
--- /dev/null
+++ b/src/GraphicObjects/VerticalStack.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace go
+{
+ public class VerticalStack : GenericStack
+ {
+ public VerticalStack()
+ : base()
+ {
+ Orientation = go.Orientation.Vertical;
+ }
+
+ [System.Xml.Serialization.XmlIgnore]
+ public override Orientation Orientation
+ {
+ get { return Orientation.Vertical; }
+ //set { }
+ }
+
+
+ }
+}