From: Jean-Philippe Bruyère Date: Mon, 5 Feb 2018 19:51:26 +0000 (+0100) Subject: :book:, ItemTemplate files handling with multime root, Content made mandatory in... X-Git-Tag: 0.6.0~7 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=41f95a2a2d9d6d5d12a47566f5b7d74a382fd406;p=jp%2Fcrow.git :book:, ItemTemplate files handling with multime root, Content made mandatory in TemplatedContainer --- diff --git a/Crow.Test/Crow.Test.csproj b/Crow.Test/Crow.Test.csproj index 94ec07b8..fe1bbca7 100644 --- a/Crow.Test/Crow.Test.csproj +++ b/Crow.Test/Crow.Test.csproj @@ -10,6 +10,7 @@ Crow.Test Crow.Test v4.6.1 + 0.5 true diff --git a/Crow.csproj b/Crow.csproj index b58634db..e6cf6864 100644 --- a/Crow.csproj +++ b/Crow.csproj @@ -370,6 +370,7 @@ Crow.DockingView.template + diff --git a/CrowIDE/CrowIDE.csproj b/CrowIDE/CrowIDE.csproj index 90cd9290..c2a44269 100644 --- a/CrowIDE/CrowIDE.csproj +++ b/CrowIDE/CrowIDE.csproj @@ -40,6 +40,12 @@ $(SolutionDir)build\obj\$(Configuration) $(SolutionDir)build\$(Configuration) + + Program + %24{TargetName} + %24{SolutionDir}\build\%24{ProjectConfigName} + false + @@ -47,7 +53,6 @@ $(SolutionDir)packages\OpenTK.2.0.0\lib\net20\OpenTK.dll - opentk @@ -58,7 +63,6 @@ - @@ -67,6 +71,9 @@ + + + diff --git a/CrowIDE/OpenGL/Extensions.cs b/CrowIDE/OpenGL/Extensions.cs new file mode 100644 index 00000000..7b26f76b --- /dev/null +++ b/CrowIDE/OpenGL/Extensions.cs @@ -0,0 +1,41 @@ +// +// Extensions.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2017 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +using OpenTK; +using Crow; + +namespace Crow +{ + public static partial class Extensions { + public static Vector4 ToVector4(this Color c){ + float[] f = c.floatArray; + return new Vector4 (f [0], f [1], f [2], f [3]); + } + public static Vector3 Transform(this Vector3 v, Matrix4 m){ + return Vector4.Transform(new Vector4(v, 1), m).Xyz; + } + public static bool IsInBetween(this int v, int min, int max){ + return v >= min & v <= max; + } + + } +} + diff --git a/CrowIDE/OpenTKGameWindow.cs b/CrowIDE/OpenTKGameWindow.cs deleted file mode 100644 index b3851904..00000000 --- a/CrowIDE/OpenTKGameWindow.cs +++ /dev/null @@ -1,334 +0,0 @@ -// -// OpenTKGameWindow.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2016 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -using System; -using System.Threading; -using OpenTK; -using OpenTK.Graphics.OpenGL; - -namespace Crow -{ - public class OpenTKGameWindow : GameWindow, IValueChange - { - #region IValueChange implementation - public event EventHandler ValueChanged; - public virtual void NotifyValueChanged(string MemberName, object _value) - { - if (ValueChanged != null) - ValueChanged.Invoke(this, new ValueChangeEventArgs(MemberName, _value)); - } - #endregion - - public Interface CrowInterface; - - #region FPS - int frameCpt = 0; - int _fps = 0; - - public int fps { - get { return _fps; } - set { - if (_fps == value) - return; - - _fps = value; - - if (_fps > fpsMax) { - fpsMax = _fps; - ValueChanged.Raise(this, new ValueChangeEventArgs ("fpsMax", fpsMax)); - } else if (_fps < fpsMin) { - fpsMin = _fps; - ValueChanged.Raise(this, new ValueChangeEventArgs ("fpsMin", fpsMin)); - } - - ValueChanged.Raise(this, new ValueChangeEventArgs ("fps", _fps)); - #if MEASURE_TIME - ValueChanged.Raise (this, new ValueChangeEventArgs ("update", - this.CrowInterface.updateTime.ElapsedTicks.ToString () + " ticks")); - ValueChanged.Raise (this, new ValueChangeEventArgs ("layouting", - this.CrowInterface.layoutTime.ElapsedTicks.ToString () + " ticks")); - ValueChanged.Raise (this, new ValueChangeEventArgs ("drawing", - this.CrowInterface.drawingTime.ElapsedTicks.ToString () + " ticks")); - ValueChanged.Raise (this, new ValueChangeEventArgs ("clipping", - this.CrowInterface.clippingTime.ElapsedTicks.ToString () + " ticks")); - #endif - } - } - - public int fpsMin = int.MaxValue; - public int fpsMax = 0; - - void resetFps () - { - fpsMin = int.MaxValue; - fpsMax = 0; - _fps = 0; - } - public string update = ""; - public string drawing = ""; - public string layouting = ""; - public string clipping = ""; - #endregion - - #region ctor - public OpenTKGameWindow(int _width = 800, int _height = 600, string _title="Crow", - int colors = 32, int depth = 24, int stencil = 0, int samples = 1, - int major=3, int minor=3) - : this(_width, _height, new OpenTK.Graphics.GraphicsMode(colors, depth, stencil, samples), - _title,GameWindowFlags.Default,DisplayDevice.Default, - major,minor,OpenTK.Graphics.GraphicsContextFlags.Default) - { - } - public OpenTKGameWindow (int width, int height, OpenTK.Graphics.GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device, int major, int minor, OpenTK.Graphics.GraphicsContextFlags flags) - : base(width,height,mode,title,options,device,major,minor,flags) - { - CrowInterface = new Interface (); - - Thread t = new Thread (interfaceThread); - t.IsBackground = true; - t.Start (); - } - - #endregion - - void interfaceThread() - { - CrowInterface.Quit += Quit; - CrowInterface.MouseCursorChanged += CrowInterface_MouseCursorChanged; - while (CrowInterface.ClientRectangle.Size.Width == 0) - Thread.Sleep (5); - - while (true) { - CrowInterface.Update (); - Thread.Sleep (1); - } - } - - public void Quit (object sender, EventArgs e) - { - this.Exit (); - } - void CrowInterface_MouseCursorChanged (object sender, MouseCursorChangedEventArgs e) - { - this.Cursor = new MouseCursor( - (int)e.NewCursor.Xhot, - (int)e.NewCursor.Yhot, - (int)e.NewCursor.Width, - (int)e.NewCursor.Height, - e.NewCursor.data); - } - - #region Events - //those events are raised only if mouse isn't in a graphic object - public event EventHandler MouseWheelChanged; - public event EventHandler MouseButtonUp; - public event EventHandler MouseButtonDown; - public event EventHandler MouseClick; - public event EventHandler MouseMove; - public event EventHandler KeyboardKeyDown; - public event EventHandler KeyboardKeyUp; - - #endregion - - #region graphic context - public int texID; - public Shader shader; - public vaoMesh quad; - public Matrix4 projection; - - void createContext() - { - #region Create texture - if (GL.IsTexture(texID)) - GL.DeleteTexture (texID); - GL.GenTextures(1, out texID); - GL.ActiveTexture (TextureUnit.Texture0); - GL.BindTexture(TextureTarget.Texture2D, texID); - - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, - ClientRectangle.Width, ClientRectangle.Height, 0, - OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, CrowInterface.bmp); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - - GL.BindTexture(TextureTarget.Texture2D, 0); - #endregion - } - void OpenGLDraw() - { - bool blend, depthTest; - GL.GetBoolean (GetPName.Blend, out blend); - GL.GetBoolean (GetPName.DepthTest, out depthTest); - GL.Enable (EnableCap.Blend); - GL.Disable (EnableCap.DepthTest); - - shader.Enable (); - shader.SetMVP (projection); - GL.ActiveTexture (TextureUnit.Texture0); - GL.BindTexture (TextureTarget.Texture2D, texID); - lock (CrowInterface.RenderMutex) { - if (CrowInterface.IsDirty) { - GL.TexSubImage2D (TextureTarget.Texture2D, 0, - CrowInterface.DirtyRect.Left, CrowInterface.DirtyRect.Top, - CrowInterface.DirtyRect.Width, CrowInterface.DirtyRect.Height, - OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, CrowInterface.dirtyBmp); - CrowInterface.IsDirty = false; - } - } - quad.Render (BeginMode.TriangleStrip); - GL.BindTexture(TextureTarget.Texture2D, 0); - - if (!blend) - GL.Disable (EnableCap.Blend); - if (depthTest) - GL.Enable (EnableCap.DepthTest); - } - #endregion - - /// - /// Override this method for your OpenGL rendering calls - /// - public virtual void OnRender(FrameEventArgs e) - { - } - /// - /// Override this method to customize clear method between frames - /// - public virtual void GLClear() - { - GL.Clear (ClearBufferMask.ColorBufferBit|ClearBufferMask.DepthBufferBit); - } - - #region Game win overrides - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - - this.KeyPress += new EventHandler(OpenTKGameWindow_KeyPress); - Keyboard.KeyDown += new EventHandler(Keyboard_KeyDown); - Keyboard.KeyUp += new EventHandler(Keyboard_KeyUp); - Mouse.WheelChanged += new EventHandler(Mouse_WheelChanged); - Mouse.ButtonDown += new EventHandler(Mouse_ButtonDown); - Mouse.ButtonUp += new EventHandler(Mouse_ButtonUp); - Mouse.Move += new EventHandler(Mouse_Move); - - GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - Console.WriteLine("\n\n*************************************"); - Console.WriteLine("GL version: " + GL.GetString (StringName.Version)); - Console.WriteLine("GL vendor: " + GL.GetString (StringName.Vendor)); - Console.WriteLine("GLSL version: " + GL.GetString (StringName.ShadingLanguageVersion)); - Console.WriteLine("*************************************\n"); - - projection = OpenTK.Matrix4.CreateOrthographicOffCenter (-0.5f, 0.5f, -0.5f, 0.5f, 1, -1); - - shader = new Shader (); - quad = new Crow.vaoMesh (0, 0, 0, 1, 1, 1, -1); - } - - protected override void OnUpdateFrame(FrameEventArgs e) - { - base.OnUpdateFrame(e); - fps = (int)RenderFrequency; - - - if (frameCpt > 50) { - resetFps (); - frameCpt = 0; - GC.Collect(); - GC.WaitForPendingFinalizers(); - NotifyValueChanged("memory", GC.GetTotalMemory (false).ToString()); - } - frameCpt++; - } - protected override void OnRenderFrame(FrameEventArgs e) - { - GLClear (); - - base.OnRenderFrame(e); - - OnRender (e); - OpenGLDraw (); - - SwapBuffers (); - } - - protected override void OnResize(EventArgs e) - { - base.OnResize (e); - CrowInterface.ProcessResize( - new Rectangle( - 0, - 0, - this.ClientRectangle.Width, - this.ClientRectangle.Height)); - createContext (); - GL.Viewport (0, 0, ClientRectangle.Width, ClientRectangle.Height); - } - #endregion - - #region Mouse Handling - void update_mouseButtonStates(ref MouseState e, OpenTK.Input.MouseState otk_e){ - for (int i = 0; i < MouseState.MaxButtons; i++) { - if (otk_e.IsButtonDown ((OpenTK.Input.MouseButton)i)) - e.EnableBit (i); - } - } - void Mouse_Move(object sender, OpenTK.Input.MouseMoveEventArgs otk_e) - { - if (!CrowInterface.ProcessMouseMove (otk_e.X, otk_e.Y)) - MouseMove.Raise (sender, otk_e); - } - void Mouse_ButtonUp(object sender, OpenTK.Input.MouseButtonEventArgs otk_e) - { - if (!CrowInterface.ProcessMouseButtonUp ((int)otk_e.Button)) - MouseButtonUp.Raise (sender, otk_e); - } - void Mouse_ButtonDown(object sender, OpenTK.Input.MouseButtonEventArgs otk_e) - { - if (!CrowInterface.ProcessMouseButtonDown ((int)otk_e.Button)) - MouseButtonDown.Raise (sender, otk_e); - } - void Mouse_WheelChanged(object sender, OpenTK.Input.MouseWheelEventArgs otk_e) - { - if (!CrowInterface.ProcessMouseWheelChanged (otk_e.DeltaPrecise)) - MouseWheelChanged.Raise (sender, otk_e); - } - #endregion - - #region keyboard Handling - void Keyboard_KeyDown(object sender, OpenTK.Input.KeyboardKeyEventArgs otk_e) - { - if (!CrowInterface.ProcessKeyDown((int)otk_e.Key)) - KeyboardKeyDown.Raise (this, otk_e); - } - void Keyboard_KeyUp(object sender, OpenTK.Input.KeyboardKeyEventArgs otk_e) - { - if (!CrowInterface.ProcessKeyUp((int)otk_e.Key)) - KeyboardKeyUp.Raise (this, otk_e); - } - void OpenTKGameWindow_KeyPress (object sender, OpenTK.KeyPressEventArgs e) - { - CrowInterface.ProcessKeyPress (e.KeyChar); - } - #endregion - } -} diff --git a/CrowIDE/src/CrowIDE.cs b/CrowIDE/src/CrowIDE.cs index 4c557603..de082acc 100644 --- a/CrowIDE/src/CrowIDE.cs +++ b/CrowIDE/src/CrowIDE.cs @@ -31,7 +31,7 @@ using System.IO; namespace CrowIDE { - class CrowIDE : OpenTKGameWindow + class CrowIDE : CrowWindow { public Command CMDLoad = new Command(new Action(() => System.Diagnostics.Debug.WriteLine("Open"))) { Caption = "Open", Icon = new SvgPicture("#Crow.Icons.open-file.svg")}; public Command CMDSave = new Command(new Action(() => System.Diagnostics.Debug.WriteLine("Save"))) { Caption = "Save", Icon = new SvgPicture("#Crow.Icons.open-file.svg")}; @@ -72,7 +72,7 @@ namespace CrowIDE //this.CrowInterface.LoadInterface ("#CrowIDE.ui.imlEditor.crow").DataSource = this; //GraphicObject go = this.CrowInterface.LoadInterface (@"ui/test.crow"); - GraphicObject go = this.CrowInterface.LoadInterface (@"#CrowIDE.ui.imlEditor.crow"); + GraphicObject go = CurrentInterface.LoadInterface (@"#CrowIDE.ui.imlEditor.crow"); imlVE = go.FindByName ("crowContainer") as ImlVisualEditor; go.DataSource = this; } @@ -94,10 +94,10 @@ namespace CrowIDE } void loadWindow(string path){ try { - GraphicObject g = CrowInterface.FindByName (path); + GraphicObject g = CurrentInterface.FindByName (path); if (g != null) return; - g = CrowInterface.LoadInterface (path); + g = CurrentInterface.LoadInterface (path); g.Name = path; g.DataSource = imlVE; } catch (Exception ex) { @@ -105,9 +105,9 @@ namespace CrowIDE } } void closeWindow (string path){ - GraphicObject g = CrowInterface.FindByName (path); + GraphicObject g = CurrentInterface.FindByName (path); if (g != null) - CrowInterface.DeleteWidget (g); + CurrentInterface.DeleteWidget (g); } protected void onCommandSave(object sender, MouseButtonEventArgs e){ diff --git a/CrowIDE/src/CrowWindow.cs b/CrowIDE/src/CrowWindow.cs new file mode 100644 index 00000000..d4447a1f --- /dev/null +++ b/CrowIDE/src/CrowWindow.cs @@ -0,0 +1,413 @@ +// +// CrowWindow.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Threading; +using OpenTK; +using OpenTK.Graphics.OpenGL; +using System.Collections.Generic; + +namespace Crow +{ + public class CrowWindow : GameWindow, IValueChange + { + #region IValueChange implementation + public event EventHandler ValueChanged; + public virtual void NotifyValueChanged(string MemberName, object _value) + { + if (ValueChanged != null) + ValueChanged.Invoke(this, new ValueChangeEventArgs(MemberName, _value)); + } + #endregion + + #region FPS + int frameCpt = 0; + int _fps = 0; + + public int fps { + get { return _fps; } + set { + if (_fps == value) + return; + + _fps = value; + #if MEASURE_TIME + if (_fps > fpsMax) { + fpsMax = _fps; + ValueChanged.Raise(this, new ValueChangeEventArgs ("fpsMax", fpsMax)); + } else if (_fps < fpsMin) { + fpsMin = _fps; + ValueChanged.Raise(this, new ValueChangeEventArgs ("fpsMin", fpsMin)); + } + #endif + if (frameCpt % 3 == 0) { + ValueChanged.Raise (this, new ValueChangeEventArgs ("fps", _fps)); + #if MEASURE_TIME + foreach (PerformanceMeasure m in ifaceControl[0].PerfMeasures) + m.NotifyChanges (); + #endif + } + } + } + + #if MEASURE_TIME + public PerformanceMeasure glDrawMeasure = new PerformanceMeasure("OpenGL Draw", 10); + + public int fpsMin = int.MaxValue; + public int fpsMax = 0; + + void resetFps () + { + fpsMin = int.MaxValue; + fpsMax = 0; + _fps = 0; + } + #endif + + #endregion + + #region ctor + public CrowWindow(int _width = 800, int _height = 600, string _title="Crow", + int colors = 32, int depth = 24, int stencil = 0, int samples = 1, + int major=3, int minor=3) + : this(_width, _height, new OpenTK.Graphics.GraphicsMode(colors, depth, stencil, samples), + _title,GameWindowFlags.Default,DisplayDevice.Default, + major,minor,OpenTK.Graphics.GraphicsContextFlags.Default) + { + } + public CrowWindow (int width, int height, OpenTK.Graphics.GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device, int major, int minor, OpenTK.Graphics.GraphicsContextFlags flags) + : base(width,height,mode,title,options,device,major,minor,flags) + { + } + + #endregion + + protected Shader shader; + public List ifaceControl = new List(); + int focusedIdx = -1, activeIdx = -2; + + // TODO:We should be able to set the current interface programmaticaly + /// + /// Gets the currently focused interface, focus could have been given by creation of new iface controler and + /// not only by the mouse + /// + public Interface CurrentInterface { + get { + if (ifaceControl.Count == 0) {//create default orthogonal interface + addInterfaceControler (new InterfaceControler ( + new Rectangle (0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height))); + focusedIdx = 0; + } + return ifaceControl [focusedIdx].CrowInterface; + } + } + + void addInterfaceControler(InterfaceControler ifaceControler) + { + ifaceControler.CrowInterface.Quit += Quit; + ifaceControler.CrowInterface.MouseCursorChanged += CrowInterface_MouseCursorChanged; + + ifaceControl.Add (ifaceControler); + focusedIdx = ifaceControl.Count - 1; + } + void openGLDraw(){ + //save GL states + bool blend, depthTest, cullFace; + GL.GetBoolean (GetPName.Blend, out blend); + GL.GetBoolean (GetPName.DepthTest, out depthTest); + GL.GetBoolean (GetPName.CullFace, out cullFace); + GL.Enable (EnableCap.Blend); + GL.Disable (EnableCap.DepthTest); + GL.Disable (EnableCap.CullFace); + + #if MEASURE_TIME + glDrawMeasure.StartCycle(); + #endif + + shader.Enable (); + for (int i = 0; i < ifaceControl.Count; i++) { + shader.SetMVP (ifaceControl [i].InterfaceMVP); + ifaceControl [i].OpenGLDraw (); + } + + #if MEASURE_TIME + glDrawMeasure.StopCycle(); + #endif + + //restore GL states + if (!blend) + GL.Disable (EnableCap.Blend); + if (depthTest) + GL.Enable (EnableCap.DepthTest); + if (cullFace) + GL.Enable (EnableCap.CullFace); + } + + public void Quit (object sender, EventArgs e) + { + foreach (InterfaceControler ic in ifaceControl) { + ic.Dispose (); + } + this.Exit (); + } + void CrowInterface_MouseCursorChanged (object sender, MouseCursorChangedEventArgs e) + { + this.Cursor = new MouseCursor( + (int)e.NewCursor.Xhot, + (int)e.NewCursor.Yhot, + (int)e.NewCursor.Width, + (int)e.NewCursor.Height, + e.NewCursor.data); + } + + #region Events + //those events are raised only if mouse isn't in a graphic object + public event EventHandler CrowMouseWheel; + public event EventHandler CrowMouseUp; + public event EventHandler CrowMouseDown; + public event EventHandler CrowMouseClick; + public event EventHandler CrowMouseMove; + public event EventHandler CrowKeyDown; + public event EventHandler CrowKeyUp; + + #endregion + + public ProjectiveIFaceControler Add3DInterface(int width, int height, Matrix4 ifaceModelMat){ + ProjectiveIFaceControler tmp = new ProjectiveIFaceControler (new Rectangle (0, 0, width, height), ifaceModelMat); + addInterfaceControler (tmp); + return tmp; + } + public GraphicObject AddWidget (GraphicObject g, int interfaceIdx = 0){ + if (ifaceControl.Count == 0)//create default orthogonal interface + addInterfaceControler (new InterfaceControler ( + new Rectangle (0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height))); + ifaceControl [interfaceIdx].CrowInterface.AddWidget (g); + return g; + } + + public void DeleteWidget (GraphicObject g, int interfaceIdx = 0){ + ifaceControl [interfaceIdx].CrowInterface.DeleteWidget (g); + } + /// + /// check if a default interface exists, create one if not + /// + void checkDefaultIFace (){ + if (ifaceControl.Count == 0)//create default orthogonal interface + addInterfaceControler (new InterfaceControler ( + new Rectangle (0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height))); + } + /// + /// Load the content of the IML file pointed by path and add it to the current interface + /// graphic tree. + /// + /// the path of the IML file to load + /// interface index to bind to, a default one is created if none exists + public GraphicObject Load (string path, int interfaceIdx = 0){ + checkDefaultIFace(); + return ifaceControl [interfaceIdx].CrowInterface.LoadInterface (path); + } + /// + /// Load the content of the IML string passed as first argument and add it to the current interface + /// graphic tree. + /// + /// a valid IML string + /// interface index to bind to, a default one is created if none exists + public void LoadIMLFragment (string imlFragment, int interfaceIdx = 0){ + checkDefaultIFace(); + ifaceControl [interfaceIdx].CrowInterface.LoadIMLFragment (imlFragment); + } + + public GraphicObject FindByName (string nameToFind){ + for (int i = 0; i < ifaceControl.Count; i++) { + GraphicObject tmp = ifaceControl [i].CrowInterface.FindByName (nameToFind); + if (tmp != null) + return tmp; + } + return null; + } + public void ClearInterface (int interfaceIdx = 0){ + ifaceControl [interfaceIdx].CrowInterface.ClearInterface (); + } + /// Override this method for your OpenGL rendering calls + public virtual void OnRender(FrameEventArgs e) + { + } + /// Override this method to customize clear method between frames + public virtual void GLClear() + { + GL.Clear (ClearBufferMask.ColorBufferBit|ClearBufferMask.DepthBufferBit); + } + + #region Game win overrides + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + this.KeyPress += new EventHandler(OpenTKGameWindow_KeyPress); + KeyDown += new EventHandler(Keyboard_KeyDown); + KeyUp += new EventHandler(Keyboard_KeyUp); + + MouseWheel += new EventHandler(GL_Mouse_WheelChanged); + MouseDown += new EventHandler(GL_Mouse_ButtonDown); + MouseUp += new EventHandler(GL_Mouse_ButtonUp); + MouseMove += new EventHandler(GL_Mouse_Move); + + #if DEBUG + Console.WriteLine("\n\n*************************************"); + Console.WriteLine("GL version: " + GL.GetString (StringName.Version)); + Console.WriteLine("GL vendor: " + GL.GetString (StringName.Vendor)); + Console.WriteLine("GLSL version: " + GL.GetString (StringName.ShadingLanguageVersion)); + Console.WriteLine("*************************************\n"); + #endif + + shader = new Shader (); + shader.Enable (); + + GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f); + } + protected override void OnUpdateFrame(FrameEventArgs e) + { + base.OnUpdateFrame(e); + fps = (int)RenderFrequency; + + #if MEASURE_TIME + if (frameCpt > 500) { + resetFps (); + frameCpt = 0; +// #if DEBUG +// GC.Collect(); +// GC.WaitForPendingFinalizers(); +// NotifyValueChanged("memory", GC.GetTotalMemory (false).ToString()); +// #endif + } + #endif + + frameCpt++; + } + protected override void OnRenderFrame(FrameEventArgs e) + { + GLClear (); + + base.OnRenderFrame(e); + + OnRender (e); + openGLDraw (); + + + SwapBuffers (); + } + protected override void OnResize(EventArgs e) + { + base.OnResize (e); + for (int i = 0; i < ifaceControl.Count; i++) { + ifaceControl[i].ProcessResize( + new Rectangle( + 0, + 0, + this.ClientRectangle.Width, + this.ClientRectangle.Height)); + } + } + #endregion + + #region Mouse and Keyboard Handling + void update_mouseButtonStates(ref MouseState e, OpenTK.Input.MouseState otk_e){ + for (int i = 0; i < MouseState.MaxButtons; i++) { + if (otk_e.IsButtonDown ((OpenTK.Input.MouseButton)i)) + e.EnableBit (i); + } + } + protected virtual void GL_Mouse_Move(object sender, OpenTK.Input.MouseMoveEventArgs otk_e) + { + if (activeIdx == -2) { + focusedIdx = -1; + for (int i = 0; i < ifaceControl.Count; i++) { + if (ifaceControl [i].ProcessMouseMove (otk_e.X, otk_e.Y)) { + focusedIdx = i; + return; + } + } + } else if (focusedIdx >= 0) { + ifaceControl [focusedIdx].ProcessMouseMove (otk_e.X, otk_e.Y); + return; + } + if (focusedIdx < 0) + CrowMouseMove.Raise (sender, otk_e); + } + protected virtual void GL_Mouse_ButtonUp(object sender, OpenTK.Input.MouseButtonEventArgs otk_e) + { + activeIdx = -2; + if (focusedIdx >= 0) { + if (ifaceControl [focusedIdx].ProcessMouseButtonUp ((int)otk_e.Button)) + return; + } + CrowMouseUp.Raise (sender, otk_e); + } + protected virtual void GL_Mouse_ButtonDown(object sender, OpenTK.Input.MouseButtonEventArgs otk_e) + { + activeIdx = focusedIdx; + if (focusedIdx >= 0) { + if (ifaceControl [focusedIdx].ProcessMouseButtonDown ((int)otk_e.Button)) + return; + } + CrowMouseDown.Raise (sender, otk_e); + } + protected virtual void GL_Mouse_WheelChanged(object sender, OpenTK.Input.MouseWheelEventArgs otk_e) + { + if (focusedIdx >= 0) { + if (ifaceControl [focusedIdx].ProcessMouseWheelChanged (otk_e.DeltaPrecise)) + return; + } + CrowMouseWheel.Raise (sender, otk_e); + } + + protected virtual void Keyboard_KeyDown(object sender, OpenTK.Input.KeyboardKeyEventArgs otk_e) + { + if (focusedIdx >= 0) { + if (ifaceControl [focusedIdx].ProcessKeyDown((int)otk_e.Key)) + return; + } + CrowKeyDown.Raise (this, otk_e); + } + protected virtual void Keyboard_KeyUp(object sender, OpenTK.Input.KeyboardKeyEventArgs otk_e) + { + if (focusedIdx >= 0) { + if (ifaceControl [focusedIdx].ProcessKeyUp((int)otk_e.Key)) + return; + } + CrowKeyUp.Raise (this, otk_e); + } + protected virtual void OpenTKGameWindow_KeyPress (object sender, OpenTK.KeyPressEventArgs e) + { + if (focusedIdx >= 0) { + if (ifaceControl [focusedIdx].ProcessKeyPress (e.KeyChar)) + return; + } + //TODO:create keyboardkeypress evt + } + #endregion + } +} diff --git a/CrowIDE/src/Extensions.cs b/CrowIDE/src/Extensions.cs index 5e8981df..0d12fec8 100644 --- a/CrowIDE/src/Extensions.cs +++ b/CrowIDE/src/Extensions.cs @@ -23,7 +23,7 @@ using System.Collections.Generic; namespace Crow { - public static class Extensions + public static partial class Extensions { public static List GetChildren(this GraphicObject go){ Type goType = go.GetType(); diff --git a/CrowIDE/src/ImlVisualEditor.cs b/CrowIDE/src/ImlVisualEditor.cs index 61a1e8c8..49a20a64 100644 --- a/CrowIDE/src/ImlVisualEditor.cs +++ b/CrowIDE/src/ImlVisualEditor.cs @@ -25,6 +25,7 @@ using System.Xml.Serialization; using System.ComponentModel; using System.IO; using System.Collections.Generic; +using Crow.IML; namespace CrowIDE { diff --git a/CrowIDE/src/InterfaceControler.cs b/CrowIDE/src/InterfaceControler.cs new file mode 100644 index 00000000..5098e119 --- /dev/null +++ b/CrowIDE/src/InterfaceControler.cs @@ -0,0 +1,267 @@ +// +// InterfaceControler.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using OpenTK; +using OpenTK.Graphics.OpenGL; +using System.Threading; +using System.Collections.Generic; + +namespace Crow +{ + public class ProjectiveIFaceControler : InterfaceControler { + Matrix4 modelview; + int[] viewport = new int[4]; + Vector3 vEyePosition; + + public Matrix4 ifaceModelMat; + Point localMousePos; + + public ProjectiveIFaceControler(Rectangle ifaceBounds, Matrix4 _ifaceModelMat) + : base(ifaceBounds){ + ifaceModelMat = _ifaceModelMat; + } + + public override Matrix4 InterfaceMVP { + get { return ifaceModelMat * modelview * projection; } + } + + public override void initGL(){ + quad = new Crow.vaoMesh (0, 0, 0, 1, 1, 1, -1); + //ifaceModelMat = Matrix4.CreateRotationX(MathHelper.PiOver2) * Matrix4.CreateTranslation(Vector3.UnitY); + CrowInterface.ProcessResize(iRect); + createContext (); + //CrowInterface.ProcessResize (iRect); + } + public override void ProcessResize (Rectangle newSize) + { + } + public override void OpenGLDraw () + { + GL.Enable (EnableCap.DepthTest); + base.OpenGLDraw (); + GL.Disable (EnableCap.DepthTest); + } + public void UpdateView (Matrix4 _projection, Matrix4 _modelview, int[] _viewport, Vector3 _vEyePosition) + { + projection = _projection; + modelview = _modelview; + viewport = _viewport; + vEyePosition = _vEyePosition; + } + public override bool ProcessMouseMove (int x, int y) + { + Matrix4 mv = ifaceModelMat * modelview; + Vector3 vMouse = UnProject(ref projection, ref mv, viewport, new Vector2 (x, y)).Xyz; + Vector3 vE = vEyePosition.Transform (ifaceModelMat.Inverted()); + Vector3 vMouseRay = Vector3.Normalize(vMouse - vE); + float a = vE.Z / vMouseRay.Z; + vMouse = vE - vMouseRay * a; + //vMouse = vMouse.Transform (interfaceModelView.Inverted()); + localMousePos = new Point ((int)Math.Truncate ((vMouse.X + 0.5f) * iRect.Width), + iRect.Height - (int)Math.Truncate ((vMouse.Y + 0.5f) * iRect.Height)); + mouseIsInInterface = localMousePos.X.IsInBetween (0, iRect.Width) & localMousePos.Y.IsInBetween (0, iRect.Height); + + return mouseIsInInterface ? CrowInterface.ProcessMouseMove (localMousePos.X, localMousePos.Y) : false; + } + Vector4 UnProject(ref Matrix4 projection, ref Matrix4 view, int[] viewport, Vector2 mouse) + { + Vector4 vec; + + vec.X = 2.0f * mouse.X / (float)viewport[2] - 1; + vec.Y = -(2.0f * mouse.Y / (float)viewport[3] - 1); + vec.Z = 0f; + vec.W = 1.0f; + + Matrix4 viewInv = Matrix4.Invert(view); + Matrix4 projInv = Matrix4.Invert(projection); + + Vector4.Transform(ref vec, ref projInv, out vec); + Vector4.Transform(ref vec, ref viewInv, out vec); + + if (vec.W > float.Epsilon || vec.W < float.Epsilon) + { + vec.X /= vec.W; + vec.Y /= vec.W; + vec.Z /= vec.W; + } + + return vec; + } + } + public class InterfaceControler : IDisposable { + public Interface CrowInterface; + public int texID; + public vaoMesh quad; + public Rectangle iRect = new Rectangle(0,0,2048,2048); + public bool mouseIsInInterface = false; + + protected Matrix4 projection; + public virtual Matrix4 InterfaceMVP { + get { return projection; } + } + + #if MEASURE_TIME + public List PerfMeasures; + public PerformanceMeasure glDrawMeasure = new PerformanceMeasure("OpenGL Draw", 10); + #endif + + #region CTOR + public InterfaceControler(Rectangle ifaceBounds){ + iRect = ifaceBounds; + + CrowInterface = new Interface (); + + #if MEASURE_TIME + PerfMeasures = new List ( + new PerformanceMeasure[] { + this.CrowInterface.updateMeasure, + this.CrowInterface.layoutingMeasure, + this.CrowInterface.clippingMeasure, + this.CrowInterface.drawingMeasure, + this.glDrawMeasure + } + ); + #endif + + Thread t = new Thread (interfaceThread); + t.IsBackground = true; + t.Start (); + + initGL (); + } + #endregion + + void interfaceThread() + { + while (CrowInterface.ClientRectangle.Size.Width == 0) + Thread.Sleep (5); + + while (true) { + CrowInterface.Update (); + Thread.Sleep (2); + } + } + + #region Mouse And Keyboard handling + public virtual void ProcessResize(Rectangle newSize){ + iRect = newSize; + CrowInterface.ProcessResize(newSize); + createContext (); + GL.Viewport (0, 0, newSize.Width, newSize.Height);//TODO:find a better place for this + } + public virtual bool ProcessMouseMove(int x, int y){ + return CrowInterface.ProcessMouseMove (x, y); + } + public virtual bool ProcessMouseButtonUp(int button) + { + return CrowInterface.ProcessMouseButtonUp (button); + } + public virtual bool ProcessMouseButtonDown(int button) + { + return CrowInterface.ProcessMouseButtonDown (button); + } + public virtual bool ProcessMouseWheelChanged(float delta) + { + return CrowInterface.ProcessMouseWheelChanged (delta); + } + public virtual bool ProcessKeyDown(int Key){ + return CrowInterface.ProcessKeyDown(Key); + } + public virtual bool ProcessKeyUp(int Key){ + return CrowInterface.ProcessKeyUp(Key); + } + public virtual bool ProcessKeyPress(char Key){ + return CrowInterface.ProcessKeyPress(Key); + } + #endregion + + #region graphic context + public virtual void initGL(){ + projection = OpenTK.Matrix4.CreateOrthographicOffCenter (-0.5f, 0.5f, -0.5f, 0.5f, 1, -1); + quad = new Crow.vaoMesh (0, 0, 0, 1, 1, 1, -1); + createContext (); + } + /// Create the texture for the interface redering + public virtual void createContext() + { + if (GL.IsTexture(texID)) + GL.DeleteTexture (texID); + GL.GenTextures(1, out texID); + GL.ActiveTexture (TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, texID); + + GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, + iRect.Width, iRect.Height, 0, + OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, CrowInterface.bmp); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + + GL.BindTexture(TextureTarget.Texture2D, 0); + } + /// Rendering of the interface + public virtual void OpenGLDraw() + { + #if MEASURE_TIME + glDrawMeasure.StartCycle(); + #endif + + GL.ActiveTexture (TextureUnit.Texture0); + GL.BindTexture (TextureTarget.Texture2D, texID); + if (Monitor.TryEnter(CrowInterface.RenderMutex)) { + if (CrowInterface.IsDirty) { + GL.TexSubImage2D (TextureTarget.Texture2D, 0, + CrowInterface.DirtyRect.Left, CrowInterface.DirtyRect.Top, + CrowInterface.DirtyRect.Width, CrowInterface.DirtyRect.Height, + OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, CrowInterface.dirtyBmp); + CrowInterface.IsDirty = false; + } + Monitor.Exit (CrowInterface.RenderMutex); + } + quad.Render (BeginMode.TriangleStrip); + GL.BindTexture(TextureTarget.Texture2D, 0); + + #if MEASURE_TIME + glDrawMeasure.StopCycle(); + #endif + } + #endregion + + #region IDisposable implementation + + public void Dispose () + { + if (GL.IsTexture(texID)) + GL.DeleteTexture (texID); + if (quad != null) + quad.Dispose (); + } + + #endregion + } +} + diff --git a/README.md b/README.md index 17ecb240..cfd9cde4 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@

+## Presentation **C.R.O.W.** is a [widget toolkit](https://en.wikipedia.org/wiki/Widget_toolkit) and rendering engine entirely developed in **C#**, offering a nice trade-off between complexity of language and performances. Crow provides a declarative interface language @@ -36,16 +37,25 @@ for easy c# code linking.

-For **documentation** and **tutorials** visit the [Wiki](https://github.com/jpbruyere/Crow/wiki) -or the [Project Site](https://jpbruyere.github.io/Crow/). +#### Features +- [Declarative interface definition](interface-markup-language). +- [Templates](Templates) +- [Styling](Styling) +- [Dynamic binding system](The-binding-system) +- SVG rendering (with [rsvg library](https://developer.gnome.org/rsvg/)) + +#### Documentation +* [Introduction](Global-architecture) +* [Classes documentation autogenerated from doxygen](index) +* [Tutorials](Tutorials) Please report bugs and issues on [GitHub](https://github.com/jpbruyere/Crow/issues) ## Getting Start ### Requirements -- [mono > 5.0](http://www.mono-project.com/download/) -- [Cairo Graphic Library](https://cairographics.org/) >= 1.10 +- [mono >= 5.0](http://www.mono-project.com/download/) +- [Cairo Graphic Library](https://cairographics.org/) >= 1.20 - [rsvg library](https://developer.gnome.org/rsvg/) for svg rendering - [nuget](https://www.nuget.org/). diff --git a/Templates/FileDialog.template b/Templates/FileDialog.template index 8305dfc9..7eba6828 100644 --- a/Templates/FileDialog.template +++ b/Templates/FileDialog.template @@ -28,7 +28,8 @@ diff --git a/Templates/FileItems.template b/Templates/FileItems.template new file mode 100644 index 00000000..99d7b6a9 --- /dev/null +++ b/Templates/FileItems.template @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/src/GraphicObjects/Button.cs b/src/GraphicObjects/Button.cs index 5967d94e..d4b95457 100644 --- a/src/GraphicObjects/Button.cs +++ b/src/GraphicObjects/Button.cs @@ -54,24 +54,6 @@ namespace Crow public event EventHandler Pressed; public event EventHandler Released; - #region TemplatedContainer overrides - public override GraphicObject Content { - get { - return _contentContainer == null ? null : _contentContainer.Child; - } - set { - if (_contentContainer != null) - _contentContainer.SetChild(value); - } - } - protected override void loadTemplate(GraphicObject template = null) - { - base.loadTemplate (template); - - _contentContainer = this.child.FindByName ("Content") as Container; - } - #endregion - #region GraphicObject Overrides public override void onMouseDown (object sender, MouseButtonEventArgs e) { diff --git a/src/GraphicObjects/Expandable.cs b/src/GraphicObjects/Expandable.cs index 2328fd5f..43f44a93 100644 --- a/src/GraphicObjects/Expandable.cs +++ b/src/GraphicObjects/Expandable.cs @@ -65,25 +65,6 @@ namespace Crow { IsExpanded = !IsExpanded; } - /// - /// Implement the abstract Content property of TemplatedControl - /// - public override GraphicObject Content { - get { - return _contentContainer == null ? null : _contentContainer.Child; - } - set { - _contentContainer.SetChild(value); - NotifyValueChanged ("HasContent", HasContent); - } - } - //TODO: move loadTemplate and ResolveBinding in TemplatedContainer - protected override void loadTemplate(GraphicObject template = null) - { - base.loadTemplate (template); - - _contentContainer = this.child.FindByName ("Content") as Container; - } #region Public properties [XmlAttributeAttribute][DefaultValue("#Crow.Images.Icons.expandable.svg")] @@ -120,9 +101,6 @@ namespace Crow onCollapse (this, null); } } - [XmlIgnore]public bool HasContent { - get { return _contentContainer == null ? false : _contentContainer.Child != null; } - } [XmlIgnore]public bool IsExpandable { get { try { diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index b7ca11f6..3da8330d 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -205,10 +205,10 @@ namespace Crow public Rectangle LastSlots; /// /// keep last slot painted on screen to clear traces if moved or resized - /// TODO: we should ensure the whole parsed widget tree is the last painted /// version to clear effective oldslot if parents have been moved or resized. /// IDEA is to add a ScreenCoordinates function that use only lastPaintedSlots /// + //TODO: we should ensure the whole parsed widget tree is the last painted public Rectangle LastPaintedSlot; /// Prevent requeuing multiple times the same widget public bool IsQueueForRedraw = false; diff --git a/src/GraphicObjects/GroupBox.cs b/src/GraphicObjects/GroupBox.cs index e5a170ae..61e7f2af 100644 --- a/src/GraphicObjects/GroupBox.cs +++ b/src/GraphicObjects/GroupBox.cs @@ -39,22 +39,5 @@ namespace Crow public GroupBox () : base(){} public GroupBox(Interface iface) : base(iface){} #endregion - - #region TemplatedContainer implementation - public override GraphicObject Content { - get { - return _contentContainer == null ? null : _contentContainer.Child; - } - set { - _contentContainer.SetChild(value); - } - } - protected override void loadTemplate(GraphicObject template = null) - { - base.loadTemplate (template); - - _contentContainer = this.child.FindByName ("Content") as Container; - } - #endregion } } diff --git a/src/GraphicObjects/Popper.cs b/src/GraphicObjects/Popper.cs index 175c99fd..9f60aa40 100644 --- a/src/GraphicObjects/Popper.cs +++ b/src/GraphicObjects/Popper.cs @@ -39,7 +39,7 @@ namespace Crow bool _isPopped, _canPop; Alignment popDirection; - GraphicObject _contentContainer; + GraphicObject _content; Measure popWidth, popHeight; public event EventHandler Popped; @@ -111,22 +111,22 @@ namespace Crow #endregion public override GraphicObject Content { - get { return _contentContainer; } + get { return _content; } set { - if (_contentContainer != null) { - _contentContainer.LogicalParent = null; - _contentContainer.LayoutChanged -= _content_LayoutChanged; + if (_content != null) { + _content.LogicalParent = null; + _content.LayoutChanged -= _content_LayoutChanged; } - _contentContainer = value; + _content = value; - if (_contentContainer == null) + if (_content == null) return; - _contentContainer.LogicalParent = this; - _contentContainer.HorizontalAlignment = HorizontalAlignment.Left; - _contentContainer.VerticalAlignment = VerticalAlignment.Top; - _contentContainer.LayoutChanged += _content_LayoutChanged; + _content.LogicalParent = this; + _content.HorizontalAlignment = HorizontalAlignment.Left; + _content.VerticalAlignment = VerticalAlignment.Top; + _content.LayoutChanged += _content_LayoutChanged; } } void positionContent(LayoutingType lt){ @@ -242,9 +242,9 @@ namespace Crow protected override void Dispose (bool disposing) { - if (_contentContainer != null && disposing) { - if (_contentContainer.Parent == null) - _contentContainer.Dispose (); + if (_content != null && disposing) { + if (_content.Parent == null) + _content.Dispose (); } base.Dispose (disposing); } diff --git a/src/GraphicObjects/TabItem.cs b/src/GraphicObjects/TabItem.cs index 9dfff864..b19d14e8 100644 --- a/src/GraphicObjects/TabItem.cs +++ b/src/GraphicObjects/TabItem.cs @@ -65,7 +65,6 @@ namespace Crow { base.loadTemplate (template); - _contentContainer = this.child.FindByName ("Content") as Container; titleWidget = this.child.FindByName ("TabTitle"); } internal GraphicObject TabTitle { get { return titleWidget; }} diff --git a/src/GraphicObjects/TemplatedContainer.cs b/src/GraphicObjects/TemplatedContainer.cs index 02c14d38..a2e35eab 100644 --- a/src/GraphicObjects/TemplatedContainer.cs +++ b/src/GraphicObjects/TemplatedContainer.cs @@ -31,7 +31,12 @@ using System.Reflection; namespace Crow { - public abstract class TemplatedContainer : TemplatedControl + /// + /// base class for new containers that will use templates. + /// + /// TemplatedControl's **must** provide a widget of the [`Container`](Container) class named **_'Content'_** inside their template tree + /// + public class TemplatedContainer : TemplatedControl { #region CTOR public TemplatedContainer() : base(){} @@ -40,7 +45,27 @@ namespace Crow protected Container _contentContainer; - [XmlAttributeAttribute]public virtual GraphicObject Content{ get; set;} + /// + /// Single child of this templated container. + /// + public virtual GraphicObject Content { + get { + return _contentContainer == null ? null : _contentContainer.Child; + } + set { + _contentContainer.SetChild(value); + NotifyValueChanged ("HasContent", HasContent); + } + } + [XmlIgnore]public bool HasContent { + get { return _contentContainer?.Child != null; } + } + //TODO: move loadTemplate and ResolveBinding in TemplatedContainer + protected override void loadTemplate(GraphicObject template = null) + { + base.loadTemplate (template); + _contentContainer = this.child.FindByName ("Content") as Container; + } #region GraphicObject overrides public override GraphicObject FindByName (string nameToFind) diff --git a/src/GraphicObjects/TemplatedControl.cs b/src/GraphicObjects/TemplatedControl.cs index 6e236c8d..a3a6b142 100644 --- a/src/GraphicObjects/TemplatedControl.cs +++ b/src/GraphicObjects/TemplatedControl.cs @@ -54,6 +54,7 @@ namespace Crow /// /// Template path /// + //TODO: this property should be renamed 'TemplatePath' [XmlAttributeAttribute][DefaultValue(null)] public string Template { get { return _template; } @@ -69,7 +70,7 @@ namespace Crow } } /// - /// caption property being recurrent in templated widget, it is declared here. + /// a caption being recurrent need in templated widget, it is declared here. /// [XmlAttributeAttribute()][DefaultValue("Templated Control")] public virtual string Caption { diff --git a/src/GraphicObjects/TemplatedGroup.cs b/src/GraphicObjects/TemplatedGroup.cs index aaeb77f6..81ea25fb 100644 --- a/src/GraphicObjects/TemplatedGroup.cs +++ b/src/GraphicObjects/TemplatedGroup.cs @@ -67,7 +67,11 @@ namespace Crow public Dictionary ItemTemplates = new Dictionary(); /// - /// Default item template + /// Item templates file path, on disk or embedded. + /// + /// ItemTemplate file may contains either a single template without the + /// ItemTemplate enclosing tag, or several item templates each enclosed + /// in a separate tag /// [XmlAttributeAttribute][DefaultValue("#Crow.Templates.ItemTemplate.goml")] public string ItemTemplate { @@ -272,10 +276,8 @@ namespace Crow /// Items loading thread /// void loading(){ - if (ItemTemplates == null) - ItemTemplates = new Dictionary (); - if (!ItemTemplates.ContainsKey ("default")) - ItemTemplates ["default"] = Interface.GetItemTemplate (ItemTemplate); + //if (!ItemTemplates.ContainsKey ("default")) + // ItemTemplates ["default"] = Interface.GetItemTemplate (ItemTemplate); for (int i = 1; i <= (data.Count / itemPerPage) + 1; i++) { if ((bool)loadingThread?.cancelRequested) { diff --git a/src/GraphicObjects/Window.cs b/src/GraphicObjects/Window.cs index d1f34374..efc8f4e3 100644 --- a/src/GraphicObjects/Window.cs +++ b/src/GraphicObjects/Window.cs @@ -70,14 +70,9 @@ namespace Crow #endregion #region TemplatedContainer overrides - public override GraphicObject Content { - get { return _contentContainer == null ? null : _contentContainer.Child; } - set { _contentContainer.SetChild(value); } - } protected override void loadTemplate(GraphicObject template = null) { base.loadTemplate (template); - _contentContainer = this.child.FindByName ("Content") as Container; NotifyValueChanged ("ShowNormal", false); NotifyValueChanged ("ShowMinimize", true); diff --git a/src/Instantiator.cs b/src/Instantiator.cs index 902092e2..5f187c58 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -82,15 +82,23 @@ namespace Crow.IML Stopwatch loadingTime = new Stopwatch (); loadingTime.Start (); #endif - using (XmlTextReader itr = new XmlTextReader (stream)) { + using (XmlReader itr = XmlReader.Create (stream)) { parseIML (itr); } + stream.Dispose (); #if DEBUG_LOAD loadingTime.Stop (); Debug.WriteLine ("IML Instantiator creation '{2}' : {0} ticks, {1} ms", loadingTime.ElapsedTicks, loadingTime.ElapsedMilliseconds, imlPath); #endif } + /// + /// Initializes a new instance of the Instantiator class with an already openned xml reader + /// positionned on the start tag inside the itemTemplate + /// + public Instantiator (XmlReader itr){ + parseIML (itr); + } //TODO:check if still used public Instantiator (Type _root, InstanciatorInvoker _loader) { @@ -144,7 +152,7 @@ namespace Crow.IML /// /// Parses IML and build a dynamic method that will be used to instanciate one or multiple occurence of the IML file or fragment /// - void parseIML (XmlTextReader reader) { + void parseIML (XmlReader reader) { IMLContext ctx = new IMLContext (findRootType (reader)); ctx.nodesStack.Push (new Node (ctx.RootType)); @@ -169,21 +177,21 @@ namespace Crow.IML /// read first node to set GraphicObject class for loading /// and let reader position on that node /// - Type findRootType (XmlTextReader reader) + Type findRootType (XmlReader reader) { string root = "Object"; - while (reader.Read ()) { - if (reader.NodeType == XmlNodeType.Element) { - root = reader.Name; - break; - } - } + while (reader.NodeType != XmlNodeType.Element) + reader.Read (); + root = reader.Name; Type t = tryGetGOType (root); if (t == null) throw new Exception ("IML parsing error: undefined root type (" + root + ")"); return t; } - void emitLoader (XmlTextReader reader, IMLContext ctx) + /// + /// main parsing entry point + /// + void emitLoader (XmlReader reader, IMLContext ctx) { string tmpXml = reader.ReadOuterXml (); @@ -195,6 +203,41 @@ namespace Crow.IML //emitCheckAndBindValueChanged (ctx); } /// + /// Parses the item template tag. + /// + /// the string triplet dataType, itemTmpID read as attribute of this tag + /// current xml text reader + /// /// file containing the templates if its a dedicated one + string[] parseItemTemplateTag (XmlReader reader, string itemTemplatePath = "") { + string dataType = "default", datas = "", path = ""; + while (reader.MoveToNextAttribute ()) { + if (reader.Name == "DataType") + dataType = reader.Value; + else if (reader.Name == "Data") + datas = reader.Value; + else if (reader.Name == "Path") + path = reader.Value; + } + reader.MoveToElement (); + + string itemTmpID = itemTemplatePath; + + if (string.IsNullOrEmpty (path)) { + itemTmpID += Guid.NewGuid ().ToString (); + Interface.Instantiators [itemTmpID] = + new ItemTemplate (new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataType, datas); + + } else { + if (!reader.IsEmptyElement) + throw new Exception ("ItemTemplate with Path attribute set may not include sub nodes"); + itemTmpID += path+dataType+datas; + if (!Interface.Instantiators.ContainsKey (itemTmpID)) + Interface.Instantiators [itemTmpID] = + new ItemTemplate (Interface.GetStreamFromPath (path), dataType, datas); + } + return new string [] { dataType, itemTmpID, datas }; + } + /// /// process template and item template definition prior to /// other attributes or childs processing /// @@ -208,6 +251,7 @@ namespace Crow.IML reader.Read (); string templatePath = reader.GetAttribute ("Template"); + string itemTemplatePath = reader.GetAttribute ("ItemTemplate"); int depth = reader.Depth + 1; while (reader.Read ()) { @@ -217,35 +261,8 @@ namespace Crow.IML inlineTemplate = true; reader.Read (); readChildren (reader, ctx, -1); - } else if (reader.Name == "ItemTemplate") { - string dataType = "default", datas = "", path = ""; - while (reader.MoveToNextAttribute ()) { - if (reader.Name == "DataType") - dataType = reader.Value; - else if (reader.Name == "Data") - datas = reader.Value; - else if (reader.Name == "Path") - path = reader.Value; - } - reader.MoveToElement (); - - string itemTmpID; - - if (string.IsNullOrEmpty (path)) { - itemTmpID = Guid.NewGuid ().ToString (); - Interface.Instantiators [itemTmpID] = - new ItemTemplate (new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataType, datas); - - } else { - if (!reader.IsEmptyElement) - throw new Exception ("ItemTemplate with Path attribute may not include sub nodes"); - itemTmpID = path+dataType+datas; - if (!Interface.Instantiators.ContainsKey (itemTmpID)) - Interface.Instantiators [itemTmpID] = - new ItemTemplate (Interface.GetStreamFromPath (itemTmpID), dataType, datas); - } - itemTemplateIds.Add (new string [] { dataType, itemTmpID, datas }); - } + } else if (reader.Name == "ItemTemplate") + itemTemplateIds.Add (parseItemTemplateTag (reader)); } if (!inlineTemplate) {//load from path or default template @@ -260,6 +277,36 @@ namespace Crow.IML } ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadTmp);//load template } + if (itemTemplateIds.Count == 0) { + //try to load ItemTemplate(s) from ItemTemplate attribute of TemplatedGroup + if (!string.IsNullOrEmpty (itemTemplatePath)) { + //check if it is already loaded in cache as a single itemTemplate instantiator + if (Interface.Instantiators.ContainsKey (itemTemplatePath)) { + itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "" }); + } else { + using (Stream stream = Interface.GetStreamFromPath (itemTemplatePath)) { + //itemtemplate files may have multiple root nodes + XmlReaderSettings itrSettings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment }; + using (XmlReader itr = XmlReader.Create (stream, itrSettings)) { + while (itr.Read ()) { + if (!itr.IsStartElement ()) + continue; + if (itr.NodeType == XmlNodeType.Element) { + if (itr.Name != "ItemTemplate") { + //the file contains a single template to use as default + Interface.Instantiators [itemTemplatePath] = + new ItemTemplate (itr); + itemTemplateIds.Add (new string [] { "default", itemTemplatePath, "" }); + break;//we should be at the end of the file + } + itemTemplateIds.Add (parseItemTemplateTag (itr, itemTemplatePath)); + } + } + } + } + } + } + } //copy item templates (review this) foreach (string [] iTempId in itemTemplateIds) { ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref diff --git a/src/ItemTemplate.cs b/src/ItemTemplate.cs index d866bc19..d0c6873e 100644 --- a/src/ItemTemplate.cs +++ b/src/ItemTemplate.cs @@ -63,6 +63,12 @@ namespace Crow strDataType = _dataType; fetchMethodName = _fetchDataMethod; } + public ItemTemplate (XmlReader reader, string _dataType = null, string _fetchDataMethod = null) + :base(reader) + { + strDataType = _dataType; + fetchMethodName = _fetchDataMethod; + } #endregion public void CreateExpandDelegate (TemplatedGroup host){