aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/Interface.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Interface.cs')
-rw-r--r--src/Interface.cs471
1 files changed, 454 insertions, 17 deletions
diff --git a/src/Interface.cs b/src/Interface.cs
index 3dd20a25..352f2a1a 100644
--- a/src/Interface.cs
+++ b/src/Interface.cs
@@ -30,11 +30,20 @@ using System.Reflection.Emit;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
+using System.Threading;
+using Cairo;
namespace Crow
{
- public static class Interface
+ public class Interface : ILayoutable
{
+ #region CTOR
+ static Interface(){
+ Interface.LoadCursors ();
+ }
+ #endregion
+
+ #region Static and constants
/// <summary> Used to prevent spurious loading of templates </summary>
internal static bool XmlSerializerInit = false;
/// <summary> keep ressource path for debug msg </summary>
@@ -55,10 +64,13 @@ namespace Crow
public static int BorderThreshold = 5;
public const int MaxCacheSize = 2048;
public const int MaxLayoutingTries = 50;
+ #endregion
public static Queue<LayoutingQueueItem> LayoutingQueue = new Queue<LayoutingQueueItem>();
+ #region default values loading helpers
public delegate void loadDefaultInvoker(object instance);
public static Dictionary<String, loadDefaultInvoker> DefaultValuesLoader = new Dictionary<string, loadDefaultInvoker>();
+ #endregion
public static void LoadCursors(){
//Load cursors
@@ -73,19 +85,6 @@ namespace Crow
}
#region Load/Save
-
- 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 Stream GetStreamFromPath (string path)
{
Stream stream = null;
@@ -132,7 +131,17 @@ namespace Crow
return t;
}
+ 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 GraphicObject Load (string path, object hostClass = null)
{
Interface.XmlLoaderCount ++;
@@ -144,9 +153,6 @@ namespace Crow
Interface.XmlLoaderCount --;
return tmp;
}
-
-
-
public static GraphicObject Load (Stream stream, Type type, object hostClass = null)
{
#if DEBUG_LOAD
@@ -181,7 +187,438 @@ namespace Crow
return result;
}
+ public GraphicObject LoadInterface (string path)
+ {
+ GraphicObject tmp = Interface.Load (path, this);
+ AddWidget (tmp);
+ return tmp;
+ }
+
+ #endregion
+
+ #if MEASURE_TIME
+ public Stopwatch updateTime = new Stopwatch ();
+ public Stopwatch layoutTime = new Stopwatch ();
+ public Stopwatch guTime = new Stopwatch ();
+ public Stopwatch drawingTime = new Stopwatch ();
+ #endif
+
+ public List<GraphicObject> GraphicObjects = new List<GraphicObject>();
+ public Color Background = Color.Transparent;
+
+ internal static Interface currentWindow;
+ public static Interface CurrentInterface;
+
+ Rectangles _redrawClip = new Rectangles();//should find another way to access it from child
+ List<GraphicObject> _gobjsToRedraw = new List<GraphicObject>();
+
+ Context ctx;
+ Surface surf;
+ public byte[] bmp;
+ public bool IsDirty = false;
+ public Rectangle DirtyRect;
+
+ #region focus
+ GraphicObject _activeWidget; //button is pressed on widget
+ GraphicObject _hoverWidget; //mouse is over
+ GraphicObject _focusedWidget; //has keyboard (or other perif) focus
+
+ public GraphicObject activeWidget
+ {
+ get { return _activeWidget; }
+ set
+ {
+ if (_activeWidget == value)
+ return;
+
+ if (_activeWidget != null)
+ _activeWidget.IsActive = false;
+
+ _activeWidget = value;
+
+ if (_activeWidget != null)
+ _activeWidget.IsActive = true;
+ }
+ }
+ public GraphicObject hoverWidget
+ {
+ get { return _hoverWidget; }
+ set {
+ if (_hoverWidget == value)
+ return;
+ _hoverWidget = value;
+ }
+ }
+ public GraphicObject FocusedWidget {
+ get { return _focusedWidget; }
+ set {
+ if (_focusedWidget == value)
+ return;
+ if (_focusedWidget != null)
+ _focusedWidget.onUnfocused (this, null);
+ _focusedWidget = value;
+ if (_focusedWidget != null)
+ _focusedWidget.onFocused (this, null);
+ }
+ }
+ #endregion
+
+
+ public void Update(){
+ if (mouseRepeatCount > 0) {
+ int mc = mouseRepeatCount;
+ mouseRepeatCount -= mc;
+ for (int i = 0; i < mc; i++) {
+ FocusedWidget.onMouseClick (this, new MouseButtonEventArgs (Mouse.X, Mouse.Y, MouseButton.Left, true));
+ }
+ }
+ #if MEASURE_TIME
+ layoutTime.Reset ();
+ guTime.Reset ();
+ drawingTime.Reset ();
+ updateTime.Restart ();
+ #endif
+
+ GraphicObject[] invGOList = new GraphicObject[GraphicObjects.Count];
+ GraphicObjects.CopyTo (invGOList, 0);
+ invGOList = invGOList.Reverse ().ToArray ();
+
+ #if MEASURE_TIME
+ layoutTime.Start ();
+ #endif
+ //Debug.WriteLine ("======= Layouting queue start =======");
+
+ while (Interface.LayoutingQueue.Count > 0) {
+ LayoutingQueueItem lqi = Interface.LayoutingQueue.Dequeue ();
+ lqi.ProcessLayouting ();
+ }
+
+ #if MEASURE_TIME
+ layoutTime.Stop ();
+ #endif
+
+ //Debug.WriteLine ("otd:" + gobjsToRedraw.Count.ToString () + "-");
+ //final redraw clips should be added only when layout is completed among parents,
+ //that's why it take place in a second pass
+ GraphicObject[] gotr = new GraphicObject[gobjsToRedraw.Count];
+ gobjsToRedraw.CopyTo (gotr);
+ gobjsToRedraw.Clear ();
+ foreach (GraphicObject p in gotr) {
+ p.IsQueuedForRedraw = false;
+ p.Parent.RegisterClip (p.LastPaintedSlot);
+ p.Parent.RegisterClip (p.getSlot());
+ }
+
+ #if MEASURE_TIME
+ updateTime.Stop ();
+ drawingTime.Start ();
+ #endif
+
+ using (surf = new ImageSurface (bmp, Format.Argb32, ClientRectangle.Width, ClientRectangle.Height, ClientRectangle.Width * 4)) {
+ using (ctx = new Context (surf)){
+
+
+ if (clipping.count > 0) {
+ //Link.draw (ctx);
+ clipping.clearAndClip(ctx);
+
+ foreach (GraphicObject p in invGOList) {
+ if (!p.Visible)
+ continue;
+ if (!clipping.intersect (p.Slot))
+ continue;
+ ctx.Save ();
+
+ p.Paint (ref ctx);
+
+ ctx.Restore ();
+ }
+
+ #if DEBUG_CLIP_RECTANGLE
+ clipping.stroke (ctx, Color.Red.AdjustAlpha(0.5));
+ #endif
+
+ if (IsDirty)
+ DirtyRect += clipping.Bounds;
+ else
+ DirtyRect = clipping.Bounds;
+ IsDirty = true;
+
+ DirtyRect.Left = Math.Max (0, DirtyRect.Left);
+ DirtyRect.Top = Math.Max (0, DirtyRect.Top);
+ DirtyRect.Width = Math.Min (ClientRectangle.Width - DirtyRect.Left, DirtyRect.Width);
+ DirtyRect.Height = Math.Min (ClientRectangle.Height - DirtyRect.Top, DirtyRect.Height);
+
+ clipping.Reset ();
+ }
+
+ #if MEASURE_TIME
+ drawingTime.Stop ();
+ #endif
+ //surf.WriteToPng (@"/mnt/data/test.png");
+ }
+ }
+ // if (ToolTip.isVisible) {
+ // ToolTip.panel.processkLayouting();
+ // if (ToolTip.panel.layoutIsValid)
+ // ToolTip.panel.Paint(ref ctx);
+ // }
+ // Debug.WriteLine("INTERFACE: layouting: {0} ticks \t graphical update {1} ticks \t drawing {2} ticks",
+ // layoutTime.ElapsedTicks,
+ // guTime.ElapsedTicks,
+ // drawingTime.ElapsedTicks);
+ // Debug.WriteLine("INTERFACE: layouting: {0} ms \t graphical update {1} ms \t drawing {2} ms",
+ // layoutTime.ElapsedMilliseconds,
+ // guTime.ElapsedMilliseconds,
+ // drawingTime.ElapsedMilliseconds);
+
+ // Debug.WriteLine("UPDATE: {0} ticks \t, {1} ms",
+ // updateTime.ElapsedTicks,
+ // updateTime.ElapsedMilliseconds);
+ }
+ public Rectangles clipping {
+ get { return _redrawClip; }
+ set { _redrawClip = value; }
+ }
+ public List<GraphicObject> gobjsToRedraw {
+ get { return _gobjsToRedraw; }
+ set { _gobjsToRedraw = value; }
+ }
+ public void AddWidget(GraphicObject g)
+ {
+ g.Parent = this;
+ GraphicObjects.Insert (0, g);
+
+ g.RegisterForLayouting (LayoutingType.Sizing);
+ }
+ public void DeleteWidget(GraphicObject g)
+ {
+ g.Visible = false;//trick to ensure clip is added to refresh zone
+ g.ClearBinding();
+ GraphicObjects.Remove (g);
+ }
+ public void PutOnTop(GraphicObject g)
+ {
+ if (GraphicObjects.IndexOf(g) > 0)
+ {
+ GraphicObjects.Remove(g);
+ GraphicObjects.Insert(0, g);
+ //g.registerClipRect ();
+ }
+ }
+ /// <summary> Remove all Graphic objects from top container </summary>
+ public void ClearInterface()
+ {
+ int i = 0;
+ while (GraphicObjects.Count>0) {
+ //TODO:parent is not reset to null because object will be added
+ //to ObjectToRedraw list, and without parent, it fails
+ GraphicObject g = GraphicObjects [i];
+ g.Visible = false;
+ g.ClearBinding ();
+ GraphicObjects.RemoveAt (0);
+ }
+ }
+ public GraphicObject FindByName (string nameToFind)
+ {
+ foreach (GraphicObject w in GraphicObjects) {
+ GraphicObject r = w.FindByName (nameToFind);
+ if (r != null)
+ return r;
+ }
+ return null;
+ }
+ public void ProcessResize(Rectangle bounds){
+ clientRectangle = bounds;
+
+ int stride = 4 * ClientRectangle.Width;
+ int bmpSize = Math.Abs (stride) * ClientRectangle.Height;
+ bmp = new byte[bmpSize];
+
+ foreach (GraphicObject g in GraphicObjects)
+ g.RegisterForLayouting (LayoutingType.All);
+ }
+
+ XCursor cursor = XCursor.Default;
+ public MouseState Mouse;
+ public KeyboardState Keyboard;
+ Rectangle clientRectangle;
+
+ public event EventHandler<MouseCursorChangedEventArgs> MouseCursorChanged;
+ public event EventHandler Quit;
+
+ #region Mouse Handling
+
+ public XCursor MouseCursor {
+ set {
+ if (value == cursor)
+ return;
+ cursor = value;
+ MouseCursorChanged.Raise (this,new MouseCursorChangedEventArgs(value));
+ }
+ }
+ public bool ProcessMouseMove(int x, int y)
+ {
+ int deltaX = x - Mouse.X;
+ int deltaY = y - Mouse.Y;
+ Mouse.X = x;
+ Mouse.Y = y;
+ MouseMoveEventArgs e = new MouseMoveEventArgs (x, y, deltaX, deltaY);
+ e.Mouse = Mouse;
+
+ if (_activeWidget != null) {
+ //TODO, ensure object is still in the graphic tree
+ //send move evt even if mouse move outside bounds
+ _activeWidget.onMouseMove (this, e);
+ return true;
+ }
+
+ if (hoverWidget != null) {
+ //TODO, ensure object is still in the graphic tree
+ //check topmost graphicobject first
+ GraphicObject tmp = hoverWidget;
+ GraphicObject topc = null;
+ while (tmp is GraphicObject) {
+ topc = tmp;
+ tmp = tmp.Parent as GraphicObject;
+ }
+ int idxhw = GraphicObjects.IndexOf (topc);
+ if (idxhw != 0) {
+ int i = 0;
+ while (i < idxhw) {
+ if (GraphicObjects [i].MouseIsIn (e.Position)) {
+ hoverWidget.onMouseLeave (this, e);
+ GraphicObjects [i].checkHoverWidget (e);
+ return true;
+ }
+ i++;
+ }
+ }
+
+
+ if (hoverWidget.MouseIsIn (e.Position)) {
+ hoverWidget.checkHoverWidget (e);
+ return true;
+ } else {
+ hoverWidget.onMouseLeave (this, e);
+ //seek upward from last focused graph obj's
+ while (hoverWidget.Parent as GraphicObject != null) {
+ hoverWidget = hoverWidget.Parent as GraphicObject;
+ if (hoverWidget.MouseIsIn (e.Position)) {
+ hoverWidget.checkHoverWidget (e);
+ return true;
+ } else
+ hoverWidget.onMouseLeave (this, e);
+ }
+ }
+ }
+
+ //top level graphic obj's parsing
+ for (int i = 0; i < GraphicObjects.Count; i++) {
+ GraphicObject g = GraphicObjects[i];
+ if (g.MouseIsIn (e.Position)) {
+ g.checkHoverWidget (e);
+ PutOnTop (g);
+ return true;
+ }
+ }
+ hoverWidget = null;
+ return false;
+ }
+ public bool ProcessMouseButtonUp(int button)
+ {
+ Mouse.DisableBit (button);
+ MouseButtonEventArgs e = new MouseButtonEventArgs () { Mouse = Mouse };
+
+ if (_activeWidget == null)
+ return false;
+
+ if (mouseRepeatThread != null) {
+ mouseRepeatOn = false;
+ mouseRepeatThread.Abort();
+ mouseRepeatThread.Join ();
+ }
+
+ _activeWidget.onMouseUp (this, e);
+ activeWidget = null;
+ return true;
+ }
+ public bool ProcessMouseButtonDown(int button)
+ {
+ Mouse.EnableBit (button);
+ MouseButtonEventArgs e = new MouseButtonEventArgs () { Mouse = Mouse };
+
+ if (hoverWidget == null)
+ return false;
+
+ hoverWidget.onMouseDown(hoverWidget,new BubblingMouseButtonEventArg(e));
+
+ if (FocusedWidget == null)
+ return true;
+ if (!FocusedWidget.MouseRepeat)
+ return true;
+ mouseRepeatThread = new Thread (mouseRepeatThreadFunc);
+ mouseRepeatThread.Start ();
+ return true;
+ }
+// public bool ProcessMouseWheelChanged(int delta)
+// {
+// if (hoverWidget == null)
+// return false;
+// hoverWidget.onMouseWheel (this, e);
+// return true;
+// }
+
+ volatile bool mouseRepeatOn;
+ volatile int mouseRepeatCount;
+ Thread mouseRepeatThread;
+ void mouseRepeatThreadFunc()
+ {
+ mouseRepeatOn = true;
+ Thread.Sleep (Interface.DeviceRepeatDelay);
+ while (mouseRepeatOn) {
+ mouseRepeatCount++;
+ Thread.Sleep (Interface.DeviceRepeatInterval);
+ }
+ mouseRepeatCount = 0;
+ }
+ #endregion
+ #region ILayoutable implementation
+ public void RegisterClip(Rectangle r){
+ clipping.AddRectangle (r);
+ }
+ public bool ArrangeChildren { get { return false; }}
+ public int LayoutingTries {
+ get { throw new NotImplementedException (); }
+ set { throw new NotImplementedException (); }
+ }
+ public LayoutingType RegisteredLayoutings {
+ get { return LayoutingType.None; }
+ set { throw new NotImplementedException (); }
+ }
+ public void RegisterForLayouting (LayoutingType layoutType) { throw new NotImplementedException (); }
+ public bool UpdateLayout (LayoutingType layoutType) { throw new NotImplementedException (); }
+ public Rectangle ContextCoordinates (Rectangle r) => r;
+ public Rectangle ScreenCoordinates (Rectangle r) => r;
+
+ public ILayoutable Parent {
+ get { return null; }
+ set { throw new NotImplementedException (); }
+ }
+ public ILayoutable LogicalParent {
+ get { return null; }
+ set { throw new NotImplementedException (); }
+ }
+
+ public Rectangle ClientRectangle {
+ get { return clientRectangle; }
+ }
+ public Interface HostContainer {
+ get { return this; }
+ }
+ public Rectangle getSlot () => ClientRectangle;
+ public Rectangle getBounds () => ClientRectangle;
#endregion
}
}