diff options
Diffstat (limited to 'src/Interface.cs')
| -rw-r--r-- | src/Interface.cs | 471 |
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 } } |