--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <dllmap os="!windows,osx" dll="libgio-2.0-0.dll" target="libgio-2.0.so.0"/>
+ <dllmap os="!windows,osx" dll="libglib-2.0-0.dll" target="libglib-2.0.so.0"/>
+ <dllmap os="!windows,osx" dll="libgobject-2.0-0.dll" target="libgobject-2.0.so.0"/>
+ <dllmap os="!windows,osx" dll="libgdk-3-0.dll" target="libgdk-3.so.0"/>
+ <dllmap os="!windows,osx" dll="libgdk_pixbuf-2.0-0.dll" target="libgdk_pixbuf-2.0.so.0"/>
+ <dllmap os="!windows,osx" dll="rsvg-2" target="librsvg-2.so.2"/>
+
+
+ <dllmap os="windows" dll="rsvg-2" target="librsvg-2-2.dll"/>
+
+ <dllmap os="osx" dll="libgio-2.0-0.dll" target="libgio-2.0.0.dylib"/>
+ <dllmap os="osx" dll="libgio-2.0-0.dll" target="libgio-2.0.0.dylib"/>
+ <dllmap os="osx" dll="libglib-2.0-0.dll" target="libglib-2.0.0.dylib"/>
+ <dllmap os="osx" dll="libgobject-2.0-0.dll" target="libgobject-2.0.0.dylib"/>
+ <dllmap os="osx" dll="libgdk-3.0.dll" target="libgdk-quartz-3.0.0.dylib"/>
+ <dllmap os="osx" dll="libgdk_pixbuf-2.0-0.dll" target="libgdk_pixbuf-2.0.0.dylib"/>
+</configuration>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml" />
- <Reference Include="cairo-sharp">
- <Package>gtk-sharp-3.0</Package>
- </Reference>
<Reference Include="Crow">
- <HintPath>packages\Crow.OpenTK.0.5.1\lib\net45\Crow.dll</HintPath>
+ <HintPath>packages\Crow.OpenTK.0.5.4\lib\net45\Crow.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
+ <Compile Include="src\CrowEdit.cs" />
+ <Compile Include="src\CrowEditExtentions.cs" />
+ <Compile Include="src\SourceLine.cs" />
+ <Compile Include="src\Token.cs" />
+ <Compile Include="src\CodeTextBuffer.cs" />
+ <Compile Include="CrowWindow.cs" />
+ <Compile Include="InterfaceControler.cs" />
<Compile Include="OpenGL\Extensions.cs" />
<Compile Include="OpenGL\Shader.cs" />
<Compile Include="OpenGL\Texture.cs" />
<Compile Include="OpenGL\vaoMesh.cs" />
- <Compile Include="src\CrowEdit.cs" />
- <Compile Include="src\CrowEditExtentions.cs" />
- <Compile Include="src\CrowWindow.cs" />
- <Compile Include="src\InterfaceControler.cs" />
- <Compile Include="src\ScrollingObject.cs" />
- <Compile Include="src\TextBuffer.cs" />
- <Compile Include="src\ScrollingTextBox.cs" />
+ <Compile Include="src\SourceEditor.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="ui\" />
<EmbeddedResource Include="ui\icons\outbox.svg" />
<EmbeddedResource Include="ui\saveFile.crow" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
--- /dev/null
+//
+// CrowWindow.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2013-2017 Jean-Philippe Bruyère
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Threading;
+using OpenTK;
+using OpenTK.Graphics.OpenGL;
+using System.Collections.Generic;
+
+namespace Crow
+{
+ public class CrowWindow : GameWindow, IValueChange
+ {
+ #region IValueChange implementation
+ public event EventHandler<ValueChangeEventArgs> 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 % 20 == 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<InterfaceControler> ifaceControl = new List<InterfaceControler>();
+ int focusedIdx = -1, activeIdx = -2;
+
+ void addInterfaceControler(InterfaceControler ifaceControler)
+ {
+ ifaceControler.CrowInterface.Quit += Quit;
+ ifaceControler.CrowInterface.MouseCursorChanged += CrowInterface_MouseCursorChanged;
+
+ ifaceControl.Add (ifaceControler);
+ }
+ 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<OpenTK.Input.MouseWheelEventArgs> MouseWheelChanged;
+ public event EventHandler<OpenTK.Input.MouseButtonEventArgs> MouseButtonUp;
+ public event EventHandler<OpenTK.Input.MouseButtonEventArgs> MouseButtonDown;
+ public event EventHandler<OpenTK.Input.MouseButtonEventArgs> MouseClick;
+ public event EventHandler<OpenTK.Input.MouseMoveEventArgs> MouseMove;
+ public event EventHandler<OpenTK.Input.KeyboardKeyEventArgs> KeyboardKeyDown;
+ public event EventHandler<OpenTK.Input.KeyboardKeyEventArgs> KeyboardKeyUp;
+
+ #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);
+ }
+ public GraphicObject Load (string path, int interfaceIdx = 0){
+ if (ifaceControl.Count == 0)//create default orthogonal interface
+ addInterfaceControler (new InterfaceControler (
+ new Rectangle (0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height)));
+ return ifaceControl [interfaceIdx].CrowInterface.LoadInterface (path);
+ }
+ 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 ();
+ }
+ /// <summary>Override this method for your OpenGL rendering calls</summary>
+ public virtual void OnRender(FrameEventArgs e)
+ {
+ }
+ /// <summary>Override this method to customize clear method between frames</summary>
+ 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<OpenTK.KeyPressEventArgs>(OpenTKGameWindow_KeyPress);
+ Keyboard.KeyDown += new EventHandler<OpenTK.Input.KeyboardKeyEventArgs>(Keyboard_KeyDown);
+ Keyboard.KeyUp += new EventHandler<OpenTK.Input.KeyboardKeyEventArgs>(Keyboard_KeyUp);
+ Mouse.WheelChanged += new EventHandler<OpenTK.Input.MouseWheelEventArgs>(GL_Mouse_WheelChanged);
+ Mouse.ButtonDown += new EventHandler<OpenTK.Input.MouseButtonEventArgs>(GL_Mouse_ButtonDown);
+ Mouse.ButtonUp += new EventHandler<OpenTK.Input.MouseButtonEventArgs>(GL_Mouse_ButtonUp);
+ Mouse.Move += new EventHandler<OpenTK.Input.MouseMoveEventArgs>(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)
+ MouseMove.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;
+ }
+ MouseButtonUp.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;
+ }
+ MouseButtonDown.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;
+ }
+ MouseWheelChanged.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;
+ }
+ KeyboardKeyDown.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;
+ }
+ KeyboardKeyUp.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
+ }
+}
--- /dev/null
+//
+// InterfaceControler.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2013-2017 Jean-Philippe Bruyère
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using 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<PerformanceMeasure> 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<PerformanceMeasure> (
+ 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 (1);
+ }
+ }
+
+ #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 ();
+ }
+ /// <summary>Create the texture for the interface redering</summary>
+ 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);
+ }
+ /// <summary>Rendering of the interface</summary>
+ 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
+ }
+}
+
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="Crow.OpenTK" version="0.5.1" targetFramework="net45" />
+ <package id="Crow.OpenTK" version="0.5.4" targetFramework="net45" />
<package id="OpenTK" version="2.0.0" targetFramework="net45" />
</packages>
--- /dev/null
+//
+// CodeTextBuffer.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Crow
+{
+ public class CodeTextBuffer : List<SourceLine>
+ {
+ public CodeTextBuffer () : base()
+ {
+ }
+
+ public int longestLineIdx = 0;
+ public int longestLineCharCount = 0;
+
+ public CodeTextBuffer (string rawSource) : this (){
+ if (string.IsNullOrEmpty (rawSource))
+ return;
+ string[] lines = Regex.Split (rawSource, "\r\n|\r|\n|\\\\n");
+ for (int i = 0; i < lines.Length; i++) {
+ if (lines [i].Length > longestLineCharCount) {
+ longestLineCharCount = lines [i].Length;
+ longestLineIdx = i;
+ }
+ this.Add (new SourceLine ( lines [i] ));
+ }
+ }
+
+ /// <summary>
+ /// return all lines with linebreaks
+ /// </summary>
+ public string FullText{
+ get {
+ string tmp = "";
+ foreach (SourceLine sl in this)
+ tmp += sl.RawText + Interface.LineBreak;
+ return tmp;
+ }
+ }
+
+ public void Tokenize (int lineIndex) {
+ //handle multiline block comments
+ if (lineIndex > 0){
+ if (this [lineIndex - 1].Tokens?.LastOrDefault ().Type == TokenType.BlockComment)
+ this [lineIndex].PresetCurrentToken (TokenType.BlockComment);
+ }
+ this [lineIndex].Tokenize ();
+ }
+
+ public void InsertLine(int index, SourceLine line){
+ base.Insert (index, line);
+ }
+ public void RemoveLine (int index) {
+ base.RemoveAt (index);
+ }
+
+ //public void Tokenize (int lineIndex) {
+ // //handle multiline block comments
+ // if (lineIndex > 0){
+ // if (this [lineIndex - 1].Tokens?.LastOrDefault ().Type == TokenType.BlockComment)
+ // this [lineIndex].PresetCurrentToken (TokenType.BlockComment);
+ // }
+ // this [lineIndex].Tokenize ();
+ //}
+ }
+}
+
public bool IsDirty { get { return _text != _origText; }}
public string CurrentDir {
- get { return _curDir; }
+ get { return Configuration.Get<string>("CurrentDir"); }
set {
- if (_curDir == value)
+ if (CurrentDir == value)
return;
- _curDir = value;
+ Configuration.Set ("CurrentDir", value);
NotifyValueChanged ("CurrentDir", _curDir);
}
}
{
base.OnLoad (e);
+ if (CurrentDir == null)
+ CurrentDir = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
+
this.ValueChanged += CrowEdit_ValueChanged;
initCommands ();
+++ /dev/null
-//
-// CrowWindow.cs
-//
-// Author:
-// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// Copyright (c) 2013-2017 Jean-Philippe Bruyère
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-using System;
-using System.Threading;
-using OpenTK;
-using OpenTK.Graphics.OpenGL;
-using System.Collections.Generic;
-
-namespace Crow
-{
- public class CrowWindow : GameWindow, IValueChange
- {
- #region IValueChange implementation
- public event EventHandler<ValueChangeEventArgs> 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 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<InterfaceControler> ifaceControl = new List<InterfaceControler>();
- int focusedIdx = -1, activeIdx = -2;
-
- void addInterfaceControler(InterfaceControler ifaceControler)
- {
- ifaceControler.CrowInterface.Quit += Quit;
- ifaceControler.CrowInterface.MouseCursorChanged += CrowInterface_MouseCursorChanged;
-
- ifaceControl.Add (ifaceControler);
- }
- 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<OpenTK.Input.MouseWheelEventArgs> MouseWheelChanged;
- public event EventHandler<OpenTK.Input.MouseButtonEventArgs> MouseButtonUp;
- public event EventHandler<OpenTK.Input.MouseButtonEventArgs> MouseButtonDown;
- public event EventHandler<OpenTK.Input.MouseButtonEventArgs> MouseClick;
- public event EventHandler<OpenTK.Input.MouseMoveEventArgs> MouseMove;
- public event EventHandler<OpenTK.Input.KeyboardKeyEventArgs> KeyboardKeyDown;
- public event EventHandler<OpenTK.Input.KeyboardKeyEventArgs> KeyboardKeyUp;
-
- #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);
- }
- public GraphicObject Load (string path, int interfaceIdx = 0){
- if (ifaceControl.Count == 0)//create default orthogonal interface
- addInterfaceControler (new InterfaceControler (
- new Rectangle (0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height)));
- return ifaceControl [interfaceIdx].CrowInterface.LoadInterface (path);
- }
- 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 ();
- }
- /// <summary>Override this method for your OpenGL rendering calls</summary>
- public virtual void OnRender(FrameEventArgs e)
- {
- }
- /// <summary>Override this method to customize clear method between frames</summary>
- 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<OpenTK.KeyPressEventArgs>(OpenTKGameWindow_KeyPress);
- Keyboard.KeyDown += new EventHandler<OpenTK.Input.KeyboardKeyEventArgs>(Keyboard_KeyDown);
- Keyboard.KeyUp += new EventHandler<OpenTK.Input.KeyboardKeyEventArgs>(Keyboard_KeyUp);
- Mouse.WheelChanged += new EventHandler<OpenTK.Input.MouseWheelEventArgs>(GL_Mouse_WheelChanged);
- Mouse.ButtonDown += new EventHandler<OpenTK.Input.MouseButtonEventArgs>(GL_Mouse_ButtonDown);
- Mouse.ButtonUp += new EventHandler<OpenTK.Input.MouseButtonEventArgs>(GL_Mouse_ButtonUp);
- Mouse.Move += new EventHandler<OpenTK.Input.MouseMoveEventArgs>(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)
- MouseMove.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;
- }
- MouseButtonUp.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;
- }
- MouseButtonDown.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;
- }
- MouseWheelChanged.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;
- }
- KeyboardKeyDown.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;
- }
- KeyboardKeyUp.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
- }
-}
+++ /dev/null
-//
-// InterfaceControler.cs
-//
-// Author:
-// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// Copyright (c) 2013-2017 Jean-Philippe Bruyère
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-using System;
-using 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<PerformanceMeasure> 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<PerformanceMeasure> (
- 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 (1);
- }
- }
-
- #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 ();
- }
- /// <summary>Create the texture for the interface redering</summary>
- 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);
- }
- /// <summary>Rendering of the interface</summary>
- 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
- }
-}
-
+++ /dev/null
-//
-// ScrollingObject.cs
-//
-// Author:
-// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// Copyright (c) 2013-2017 Jean-Philippe Bruyère
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-using System;
-using System.Xml.Serialization;
-using System.ComponentModel;
-using System.Collections;
-using Cairo;
-
-
-namespace Crow
-{
- public class ScrollingObject : GraphicObject
- {
- #region CTOR
- public ScrollingObject ():base()
- {
- }
- #endregion
-
- int scrollX, scrollY, maxScrollX, maxScrollY, mouseWheelSpeed;
-
- /// <summary> Horizontal Scrolling Position </summary>
- [XmlAttributeAttribute][DefaultValue(0)]
- public virtual int ScrollX {
- get { return scrollX; }
- set {
- if (scrollX == value)
- return;
-
- int newS = value;
- if (newS < 0)
- newS = 0;
- else if (newS > maxScrollX)
- newS = maxScrollX;
-
- if (newS == scrollX)
- return;
-
- scrollX = value;
-
- NotifyValueChanged ("ScrollX", scrollX);
- RegisterForGraphicUpdate ();
- }
- }
- /// <summary> Vertical Scrolling Position </summary>
- [XmlAttributeAttribute][DefaultValue(0)]
- public virtual int ScrollY {
- get { return scrollY; }
- set {
- if (scrollY == value)
- return;
-
- int newS = value;
- if (newS < 0)
- newS = 0;
- else if (newS > maxScrollY)
- newS = maxScrollY;
-
- if (newS == scrollY)
- return;
-
- scrollY = value;
-
- NotifyValueChanged ("ScrollY", scrollY);
- RegisterForGraphicUpdate ();
- }
- }
- /// <summary> Horizontal Scrolling maximum value </summary>
- [XmlAttributeAttribute][DefaultValue(0)]
- public virtual int MaxScrollX {
- get { return maxScrollX; }
- set {
- if (maxScrollX == value)
- return;
-
- maxScrollX = value;
-
- if (scrollX > maxScrollX)
- ScrollX = maxScrollX;
-
- NotifyValueChanged ("MaxScrollX", maxScrollX);
- RegisterForGraphicUpdate ();
- }
- }
- /// <summary> Vertical Scrolling maximum value </summary>
- [XmlAttributeAttribute][DefaultValue(0)]
- public virtual int MaxScrollY {
- get { return maxScrollY; }
- set {
- if (maxScrollY == value)
- return;
-
- maxScrollY = value;
-
- if (scrollY > maxScrollY)
- ScrollY = maxScrollY;
-
- NotifyValueChanged ("MaxScrollY", maxScrollY);
- RegisterForGraphicUpdate ();
- }
- }
- /// <summary> Mouse Wheel Scrolling multiplier </summary>
- [XmlAttributeAttribute][DefaultValue(1)]
- public virtual int MouseWheelSpeed {
- get { return mouseWheelSpeed; }
- set {
- if (mouseWheelSpeed == value)
- return;
-
- mouseWheelSpeed = value;
-
- NotifyValueChanged ("MouseWheelSpeed", mouseWheelSpeed);
- }
- }
-
- /// <summary> Process scrolling vertically, or if shift is down, vertically </summary>
- public override void onMouseWheel (object sender, MouseWheelEventArgs e)
- {
- base.onMouseWheel (sender, e);
- if (CurrentInterface.Keyboard.IsKeyDown (Key.ShiftLeft))
- ScrollX -= e.Delta * MouseWheelSpeed;
- else
- ScrollY -= e.Delta * MouseWheelSpeed;
-
- }
- /// <summary> Process scrolling with arrow keys, home and end keys. </summary>
- public override void onKeyDown (object sender, KeyboardKeyEventArgs e)
- {
- base.onKeyDown (sender, e);
-
- switch (e.Key) {
- case Key.Up:
- ScrollY--;
- break;
- case Key.Down:
- ScrollY++;
- break;
- case Key.Left:
- ScrollX--;
- break;
- case Key.Right:
- ScrollX++;
- break;
- case Key.Home:
- ScrollX = 0;
- ScrollY = 0;
- break;
- case Key.End:
- ScrollX = MaxScrollX;
- ScrollY = MaxScrollY;
- break;
- }
- }
- }
-}
-
+++ /dev/null
-//
-// ScrollingTextBox.cs
-//
-// Author:
-// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// Copyright (c) 2013-2017 Jean-Philippe Bruyère
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-using System;
-using System.Xml.Serialization;
-using System.ComponentModel;
-using System.Collections;
-using Cairo;
-using System.Text;
-using System.Collections.Generic;
-using System.Text.RegularExpressions;
-using System.Linq;
-
-namespace Crow
-{
- /// <summary>
- /// Scrolling text box optimized for monospace fonts, for coding
- /// </summary>
- public class ScrollingTextBox : ScrollingObject
- {
- #region CTOR
- public ScrollingTextBox ():base()
- {
-
-
- }
- #endregion
-
- public event EventHandler TextChanged;
-
- public virtual void OnTextChanged(Object sender, EventArgs e)
- {
- TextChanged.Raise (this, e);
- }
-
- #region private and protected fields
- string lineBreak = Interface.LineBreak;
- int visibleLines = 1;
- List<string> lines;
- string _text = "label";
- Color selBackground;
- Color selForeground;
- Point mouseLocalPos = 0;//mouse coord in widget space
- int _currentCol; //0 based cursor position in string
- int _currentLine;
- Point _selBegin = -1; //selection start (row,column)
- Point _selRelease = -1; //selection end (row,column)
-
- protected Rectangle rText;
- protected FontExtents fe;
- protected TextExtents te;
- #endregion
-
- [XmlAttributeAttribute][DefaultValue("label")]
- public string Text
- {
- get {
- return lines == null ?
- _text : lines.Aggregate((i, j) => i + Interface.LineBreak + j);
- }
- set
- {
- if (string.Equals (value, _text, StringComparison.Ordinal))
- return;
-
- _text = value;
-
- if (string.IsNullOrEmpty(_text))
- _text = "";
-
- lines = getLines;
- MaxScrollY = Math.Max (0, lines.Count - visibleLines);
-
- OnTextChanged (this, null);
- RegisterForGraphicUpdate ();
- }
- }
-
-
- [XmlAttributeAttribute][DefaultValue("BlueGray")]
- public virtual Color SelectionBackground {
- get { return selBackground; }
- set {
- if (value == selBackground)
- return;
- selBackground = value;
- NotifyValueChanged ("SelectionBackground", selBackground);
- RegisterForRedraw ();
- }
- }
- [XmlAttributeAttribute][DefaultValue("White")]
- public virtual Color SelectionForeground {
- get { return selForeground; }
- set {
- if (value == selForeground)
- return;
- selForeground = value;
- NotifyValueChanged ("SelectionForeground", selForeground);
- RegisterForRedraw ();
- }
- }
- [XmlAttributeAttribute][DefaultValue(0)]
- public int CurrentColumn{
- get { return _currentCol; }
- set {
- if (value == _currentCol)
- return;
- if (value < 0)
- _currentCol = 0;
- else if (value > lines [_currentLine].Length)
- _currentCol = lines [_currentLine].Length;
- else
- _currentCol = value;
- NotifyValueChanged ("CurrentColumn", _currentCol);
- }
- }
- [XmlAttributeAttribute][DefaultValue(0)]
- public int CurrentLine{
- get { return _currentLine; }
- set {
- if (value == _currentLine)
- return;
- if (value >= lines.Count)
- _currentLine = lines.Count-1;
- else if (value < 0)
- _currentLine = 0;
- else
- _currentLine = value;
- //force recheck of currentCol for bounding
- int cc = _currentCol;
- _currentCol = 0;
- CurrentColumn = cc;
- NotifyValueChanged ("CurrentLine", _currentLine);
- }
- }
- [XmlIgnore]public Point CurrentPosition {
- get { return new Point(CurrentColumn, CurrentLine); }
- }
- //TODO:using HasFocus for drawing selection cause SelBegin and Release binding not to work
- /// <summary>
- /// Selection begin position in char units (line, column)
- /// </summary>
- [XmlAttributeAttribute][DefaultValue("-1")]
- public Point SelBegin {
- get { return _selBegin; }
- set {
- if (value == _selBegin)
- return;
- _selBegin = value;
- System.Diagnostics.Debug.WriteLine ("SelBegin=" + _selBegin);
- NotifyValueChanged ("SelBegin", _selBegin);
- NotifyValueChanged ("SelectedText", SelectedText);
- }
- }
- /// <summary>
- /// Selection release position in char units (line, column)
- /// </summary>
- [XmlAttributeAttribute][DefaultValue("-1")]
- public Point SelRelease {
- get {
- return _selRelease;
- }
- set {
- if (value == _selRelease)
- return;
- _selRelease = value;
- NotifyValueChanged ("SelRelease", _selRelease);
- NotifyValueChanged ("SelectedText", SelectedText);
- }
- }
- /// <summary>
- /// return char at CurrentLine, CurrentColumn
- /// </summary>
- [XmlIgnore]protected Char CurrentChar
- {
- get {
- return lines [CurrentLine] [CurrentColumn];
- }
- }
- /// <summary>
- /// ordered selection start and end positions in char units
- /// </summary>
- [XmlIgnore]protected Point selectionStart
- {
- get {
- return SelRelease < 0 || SelBegin.Y < SelRelease.Y ? SelBegin :
- SelBegin.Y > SelRelease.Y ? SelRelease :
- SelBegin.X < SelRelease.X ? SelBegin : SelRelease;
- }
- }
- [XmlIgnore]public Point selectionEnd
- {
- get {
- return SelRelease < 0 || SelBegin.Y > SelRelease.Y ? SelBegin :
- SelBegin.Y < SelRelease.Y ? SelRelease :
- SelBegin.X > SelRelease.X ? SelBegin : SelRelease;
- }
- }
- [XmlIgnore]public string SelectedText
- {
- get {
-
- if (SelRelease < 0 || SelBegin < 0)
- return "";
- if (selectionStart.Y == selectionEnd.Y)
- return lines [selectionStart.Y].Substring (selectionStart.X, selectionEnd.X - selectionStart.X);
- string tmp = "";
- tmp = lines [selectionStart.Y].Substring (selectionStart.X);
- for (int l = selectionStart.Y + 1; l < selectionEnd.Y; l++) {
- tmp += Interface.LineBreak + lines [l];
- }
- tmp += Interface.LineBreak + lines [selectionEnd.Y].Substring (0, selectionEnd.X);
- return tmp;
- }
- }
- [XmlIgnore]public bool selectionIsEmpty
- { get { return SelRelease == SelBegin; } }
-
- List<string> getLines {
- get {
- return Regex.Split (_text, "\r\n|\r|\n|\\\\n").ToList();
- }
- }
- /// <summary>
- /// Moves cursor one char to the left.
- /// </summary>
- /// <returns><c>true</c> if move succeed</returns>
- public bool MoveLeft(){
- int tmp = _currentCol - 1;
- if (tmp < 0) {
- if (_currentLine == 0)
- return false;
- CurrentLine--;
- CurrentColumn = int.MaxValue;
- } else
- CurrentColumn = tmp;
- return true;
- }
- /// <summary>
- /// Moves cursor one char to the right.
- /// </summary>
- /// <returns><c>true</c> if move succeed</returns>
- public bool MoveRight(){
- int tmp = _currentCol + 1;
- if (tmp > lines [_currentLine].Length){
- if (CurrentLine == lines.Count - 1)
- return false;
- CurrentLine++;
- CurrentColumn = 0;
- } else
- CurrentColumn = tmp;
- return true;
- }
- public void GotoWordStart(){
- if (lines[CurrentLine].Length == 0)
- return;
- CurrentColumn--;
- //skip white spaces
- while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0)
- CurrentColumn--;
- while (char.IsLetterOrDigit (lines [CurrentLine] [CurrentColumn]) && CurrentColumn > 0)
- CurrentColumn--;
- if (!char.IsLetterOrDigit (this.CurrentChar))
- CurrentColumn++;
- }
- public void GotoWordEnd(){
- //skip white spaces
- if (CurrentColumn >= lines [CurrentLine].Length - 1)
- return;
- while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < lines [CurrentLine].Length-1)
- CurrentColumn++;
- while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < lines [CurrentLine].Length-1)
- CurrentColumn++;
- if (char.IsLetterOrDigit (this.CurrentChar))
- CurrentColumn++;
- }
- public void DeleteChar()
- {
- if (selectionIsEmpty) {
- if (CurrentColumn == 0) {
- if (CurrentLine == 0 && lines.Count == 1)
- return;
- CurrentLine--;
- CurrentColumn = lines [CurrentLine].Length;
- lines [CurrentLine] += lines [CurrentLine + 1];
- lines.RemoveAt (CurrentLine + 1);
- OnTextChanged (this, null);
- return;
- }
- CurrentColumn--;
- lines [CurrentLine] = lines [CurrentLine].Remove (CurrentColumn, 1);
- } else {
- int linesToRemove = selectionEnd.Y - selectionStart.Y + 1;
- int l = selectionStart.Y;
-
- if (linesToRemove > 0) {
- lines [l] = lines [l].Remove (selectionStart.X, lines [l].Length - selectionStart.X) +
- lines [selectionEnd.Y].Substring (selectionEnd.X, lines [selectionEnd.Y].Length - selectionEnd.X);
- l++;
- for (int c = 0; c < linesToRemove-1; c++)
- lines.RemoveAt (l);
- CurrentLine = selectionStart.Y;
- CurrentColumn = selectionStart.X;
- } else
- lines [l] = lines [l].Remove (selectionStart.X, selectionEnd.X - selectionStart.X);
- CurrentColumn = selectionStart.X;
- SelBegin = -1;
- SelRelease = -1;
- }
- OnTextChanged (this, null);
- }
-
- #region GraphicObject overrides
- public override Font Font {
- get { return base.Font; }
- set {
- base.Font = value;
-
- using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) {
- using (Context gr = new Context (img)) {
- gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
- gr.SetFontSize (Font.Size);
-
- fe = gr.FontExtents;
- }
- }
- MaxScrollY = 0;
- RegisterForGraphicUpdate ();
- }
- }
- protected override int measureRawSize(LayoutingType lt)
- {
- if (lt == LayoutingType.Height)
- return (int)Math.Ceiling(fe.Height * lines.Count) + Margin * 2;
-
- string txt = _text.Replace("\t", new String (' ', Interface.TabSize));
-
-
- int maxChar = 0;
- foreach (string s in Regex.Split (txt, "\r\n|\r|\n|\\\\n")) {
- if (maxChar < s.Length)
- maxChar = s.Length;
- }
- return (int)(fe.MaxXAdvance * maxChar) + Margin * 2;
- }
- public override void OnLayoutChanges (LayoutingType layoutType)
- {
- base.OnLayoutChanges (layoutType);
-
- if (layoutType == LayoutingType.Height)
- updateVisibleLines ();
- }
- protected override void onDraw (Context gr)
- {
- base.onDraw (gr);
-
- gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
- gr.SetFontSize (Font.Size);
- gr.FontOptions = Interface.FontRenderingOptions;
- gr.Antialias = Interface.Antialias;
-
- Rectangle cb = ClientRectangle;
-
- Foreground.SetAsSource (gr);
-
- bool selectionInProgress = false;
-
- #region draw text cursor
- if (SelBegin != SelRelease)
- selectionInProgress = true;
- else if (HasFocus){
- gr.SetSourceColor(Color.Red);
- gr.LineWidth = 2.0;
- double cursorX = cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance;
- gr.MoveTo (0.5 + cursorX, cb.Y + (CurrentLine - ScrollY) * fe.Height);
- gr.LineTo (0.5 + cursorX, cb.Y + (CurrentLine + 1 - ScrollY) * fe.Height);
- gr.Stroke();
- }
- #endregion
-
- Foreground.SetAsSource (gr);
-
- for (int i = 0; i < visibleLines; i++) {
- int curL = i + ScrollY;
- if (curL >= lines.Count)
- break;
- string lstr = lines[curL];
-
- gr.MoveTo (cb.X, cb.Y + fe.Ascent + fe.Height * i);
- gr.ShowText (lstr);
- gr.Fill ();
-
- if (selectionInProgress && curL >= selectionStart.Y && curL <= selectionEnd.Y) {
-
- double rLineX = cb.X,
- rLineY = cb.Y + i * fe.Height,
- rLineW = lstr.Length * fe.MaxXAdvance;
-
- System.Diagnostics.Debug.WriteLine ("sel start: " + selectionStart + " sel end: " + selectionEnd);
- if (curL == selectionStart.Y) {
- rLineX += selectionStart.X * fe.MaxXAdvance;
- rLineW -= selectionStart.X * fe.MaxXAdvance;
- }
- if (curL == selectionEnd.Y)
- rLineW -= (lstr.Length - selectionEnd.X) * fe.MaxXAdvance;
-
- gr.Save ();
- gr.Operator = Operator.Source;
- gr.Rectangle (rLineX, rLineY, rLineW, fe.Height);
- gr.SetSourceColor (SelectionBackground);
- gr.FillPreserve ();
- gr.Clip ();
- gr.Operator = Operator.Over;
- gr.SetSourceColor (SelectionForeground);
- gr.MoveTo (cb.X, cb.Y + fe.Ascent + fe.Height * i);
- gr.ShowText (lstr);
- gr.Fill ();
- gr.Restore ();
- }
- }
- }
- #endregion
-
- #region Mouse handling
- void updatemouseLocalPos(Point mpos){
- mouseLocalPos = mpos - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft;
- if (mouseLocalPos.X < 0)
- mouseLocalPos.X = 0;
- if (mouseLocalPos.Y < 0)
- mouseLocalPos.Y = 0;
-
- CurrentLine = ScrollY + (int)Math.Floor (mouseLocalPos.Y / fe.Height);
- CurrentColumn = ScrollX + (int)Math.Round (mouseLocalPos.X / fe.MaxXAdvance);
- }
- public override void onMouseEnter (object sender, MouseMoveEventArgs e)
- {
- base.onMouseEnter (sender, e);
- CurrentInterface.MouseCursor = XCursor.Text;
- }
- public override void onMouseLeave (object sender, MouseMoveEventArgs e)
- {
- base.onMouseLeave (sender, e);
- CurrentInterface.MouseCursor = XCursor.Default;
- }
- protected override void onFocused (object sender, EventArgs e)
- {
- base.onFocused (sender, e);
-
-// SelBegin = new Point(0,0);
-// SelRelease = new Point (lines.LastOrDefault ().Length, lines.Count-1);
- RegisterForRedraw ();
- }
- protected override void onUnfocused (object sender, EventArgs e)
- {
- base.onUnfocused (sender, e);
-
-// SelBegin = -1;
-// SelRelease = -1;
- RegisterForRedraw ();
- }
- public override void onMouseMove (object sender, MouseMoveEventArgs e)
- {
- base.onMouseMove (sender, e);
-
- if (!e.Mouse.IsButtonDown (MouseButton.Left))
- return;
- if (!HasFocus || SelBegin < 0)
- return;
-
- updatemouseLocalPos (e.Position);
- SelRelease = CurrentPosition;
-
- RegisterForRedraw();
- }
- public override void onMouseDown (object sender, MouseButtonEventArgs e)
- {
- if (this.HasFocus){
- updatemouseLocalPos (e.Position);
- SelBegin = SelRelease = CurrentPosition;
- RegisterForRedraw();//TODO:should put it in properties
- }
-
- //done at the end to set 'hasFocus' value after testing it
- base.onMouseDown (sender, e);
- }
- public override void onMouseUp (object sender, MouseButtonEventArgs e)
- {
- base.onMouseUp (sender, e);
-
- if (SelBegin == SelRelease)
- SelBegin = SelRelease = -1;
-
- updatemouseLocalPos (e.Position);
- RegisterForRedraw ();
- }
- public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e)
- {
- base.onMouseDoubleClick (sender, e);
-
- GotoWordStart ();
- SelBegin = CurrentPosition;
- GotoWordEnd ();
- SelRelease = CurrentPosition;
- RegisterForRedraw ();
- }
- #endregion
-
- #region Keyboard handling
- public override void onKeyDown (object sender, KeyboardKeyEventArgs e)
- {
- base.onKeyDown (sender, e);
-
- Key key = e.Key;
-
- switch (key)
- {
- case Key.Back:
- if (CurrentPosition == 0)
- return;
- this.DeleteChar();
- break;
- case Key.Clear:
- break;
- case Key.Delete:
- if (selectionIsEmpty) {
- if (!MoveRight ())
- return;
- }else if (e.Shift)
- CurrentInterface.Clipboard = this.SelectedText;
- this.DeleteChar ();
- break;
- case Key.Enter:
- case Key.KeypadEnter:
- if (!selectionIsEmpty)
- this.DeleteChar ();
- this.InsertLineBreak ();
- break;
- case Key.Escape:
- Text = "";
- CurrentColumn = 0;
- SelRelease = -1;
- break;
- case Key.Home:
- if (e.Shift) {
- if (selectionIsEmpty)
- SelBegin = new Point (CurrentColumn, CurrentLine);
- if (e.Control)
- CurrentLine = 0;
- CurrentColumn = 0;
- SelRelease = new Point (CurrentColumn, CurrentLine);
- break;
- }
- SelRelease = -1;
- if (e.Control)
- CurrentLine = 0;
- CurrentColumn = 0;
- break;
- case Key.End:
- if (e.Shift) {
- if (selectionIsEmpty)
- SelBegin = CurrentPosition;
- if (e.Control)
- CurrentLine = int.MaxValue;
- CurrentColumn = int.MaxValue;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- if (e.Control)
- CurrentLine = int.MaxValue;
- CurrentColumn = int.MaxValue;
- break;
- case Key.Insert:
- if (e.Shift)
- this.Insert (CurrentInterface.Clipboard);
- else if (e.Control && !selectionIsEmpty)
- CurrentInterface.Clipboard = this.SelectedText;
- break;
- case Key.Left:
- if (e.Shift) {
- if (selectionIsEmpty)
- SelBegin = new Point(CurrentColumn, CurrentLine);
- if (e.Control)
- GotoWordStart ();
- else if (!MoveLeft ())
- return;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- if (e.Control)
- GotoWordStart ();
- else
- MoveLeft();
- break;
- case Key.Right:
- if (e.Shift) {
- if (selectionIsEmpty)
- SelBegin = CurrentPosition;
- if (e.Control)
- GotoWordEnd ();
- else if (!MoveRight ())
- return;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- if (e.Control)
- GotoWordEnd ();
- else
- MoveRight ();
- break;
- case Key.Up:
- if (e.Shift) {
- if (selectionIsEmpty)
- SelBegin = CurrentPosition;
- CurrentLine--;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- CurrentLine--;
- break;
- case Key.Down:
- if (e.Shift) {
- if (selectionIsEmpty)
- SelBegin = CurrentPosition;
- CurrentLine++;
- SelRelease = CurrentPosition;
- break;
- }
- SelRelease = -1;
- CurrentLine++;
- break;
- case Key.Menu:
- break;
- case Key.NumLock:
- break;
- case Key.PageDown:
- break;
- case Key.PageUp:
- break;
- case Key.RWin:
- break;
- case Key.Tab:
- this.Insert ("\t");
- break;
- default:
- break;
- }
- RegisterForGraphicUpdate();
- }
- public override void onKeyPress (object sender, KeyPressEventArgs e)
- {
- base.onKeyPress (sender, e);
-
- this.Insert (e.KeyChar.ToString());
-
- SelRelease = -1;
- SelBegin = -1; //new Point(CurrentColumn, SelBegin.Y);
-
- RegisterForGraphicUpdate();
- }
- #endregion
-
-
- /// <summary> Compute x offset in cairo unit from text position </summary>
- double GetXFromTextPointer(Context gr, Point pos)
- {
- try {
- string l = lines [pos.Y].Substring (0, pos.X).
- Replace ("\t", new String (' ', Interface.TabSize));
- return gr.TextExtents (l).XAdvance;
- } catch{
- return -1;
- }
- }
-
- /// <summary> line break could be '\r' or '\n' or '\r\n' </summary>
- string detectLineBreakKind(){
- string strLB = "";
-
- if (string.IsNullOrEmpty(_text))
- return Interface.LineBreak;
- int i = 0;
- while ( i < _text.Length) {
- if (_text [i] == '\r') {
- strLB += '\r';
- i++;
- }
- if (i < _text.Length) {
- if (_text [i] == '\r')
- return "\r";
- if (_text [i] == '\n')
- strLB += '\n';
- }
- if (!string.IsNullOrEmpty (strLB))
- return strLB;
- i++;
- }
- return Interface.LineBreak;
- }
-
- void updateVisibleLines(){
- visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height);
- MaxScrollY = Math.Max (0, lines.Count - visibleLines);
-
- System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines);
- System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY);
- }
-
-
- /// <summary>
- /// Insert new string at caret position, should be sure no line break is inside.
- /// </summary>
- /// <param name="str">String.</param>
- protected void Insert(string str)
- {
- if (!selectionIsEmpty)
- this.DeleteChar ();
- string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray();
- lines [CurrentLine] = lines [CurrentLine].Insert (CurrentColumn, strLines[0]);
- CurrentColumn += strLines[0].Length;
- for (int i = 1; i < strLines.Length; i++) {
- InsertLineBreak ();
- lines [CurrentLine] = lines [CurrentLine].Insert (CurrentColumn, strLines[i]);
- CurrentColumn += strLines[i].Length;
- }
- OnTextChanged (this, null);
- RegisterForGraphicUpdate();
- }
-
- /// <summary>
- /// Insert a line break.
- /// </summary>
- protected void InsertLineBreak()
- {
- lines.Insert(CurrentLine + 1, lines[CurrentLine].Substring(CurrentColumn));
- lines [CurrentLine] = lines [CurrentLine].Substring (0, CurrentColumn);
- CurrentLine++;
- CurrentColumn = 0;
- OnTextChanged (this, null);
- }
- }
-}
\ No newline at end of file
--- /dev/null
+//
+// ScrollingTextBox.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2013-2017 Jean-Philippe Bruyère
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Xml.Serialization;
+using System.ComponentModel;
+using System.Collections;
+using Cairo;
+using System.Text;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Linq;
+using System.Diagnostics;
+
+namespace Crow
+{
+ /// <summary>
+ /// Scrolling text box optimized for monospace fonts, for coding
+ /// </summary>
+ public class SourceEditor : ScrollingObject
+ {
+ #region CTOR
+ public SourceEditor ():base()
+ {
+
+
+ }
+ #endregion
+
+ public event EventHandler TextChanged;
+
+ public virtual void OnTextChanged(Object sender, EventArgs e)
+ {
+ TextChanged.Raise (this, e);
+ }
+
+ #region private and protected fields
+ string lineBreak = Interface.LineBreak;
+ int visibleLines = 1;
+ int visibleColumns = 1;
+ CodeTextBuffer buffer;
+ string _text = "label";
+ Color selBackground;
+ Color selForeground;
+ int _currentCol; //0 based cursor position in string
+ int _currentLine;
+ Point _selBegin = -1; //selection start (row,column)
+ Point _selRelease = -1; //selection end (row,column)
+
+ protected Rectangle rText;
+ protected FontExtents fe;
+ protected TextExtents te;
+ #endregion
+
+ [XmlAttributeAttribute][DefaultValue("label")]
+ public string Text
+ {
+ get {
+ return buffer == null ?
+ _text : buffer.FullText;
+ }
+ set
+ {
+ if (string.Equals (value, _text, StringComparison.Ordinal))
+ return;
+
+ _text = value;
+
+ if (string.IsNullOrEmpty(_text))
+ _text = "";
+
+ buffer = new CodeTextBuffer (_text);
+ MaxScrollY = Math.Max (0, buffer.Count - visibleLines);
+ MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns);
+
+ OnTextChanged (this, null);
+ RegisterForGraphicUpdate ();
+ }
+ }
+
+
+ [XmlAttributeAttribute][DefaultValue("BlueGray")]
+ public virtual Color SelectionBackground {
+ get { return selBackground; }
+ set {
+ if (value == selBackground)
+ return;
+ selBackground = value;
+ NotifyValueChanged ("SelectionBackground", selBackground);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("White")]
+ public virtual Color SelectionForeground {
+ get { return selForeground; }
+ set {
+ if (value == selForeground)
+ return;
+ selForeground = value;
+ NotifyValueChanged ("SelectionForeground", selForeground);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(0)]
+ public int CurrentColumn{
+ get { return _currentCol; }
+ set {
+ if (value == _currentCol)
+ return;
+ if (value < 0)
+ _currentCol = 0;
+ else if (value > buffer [_currentLine].Length)
+ _currentCol = buffer [_currentLine].Length;
+ else
+ _currentCol = value;
+
+ if (_currentCol < ScrollX)
+ ScrollX = _currentCol;
+ else if (_currentCol >= ScrollX + visibleColumns)
+ ScrollX = _currentCol - visibleColumns + 1;
+
+ NotifyValueChanged ("CurrentColumn", _currentCol);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(0)]
+ public int CurrentLine{
+ get { return _currentLine; }
+ set {
+ if (value == _currentLine)
+ return;
+ if (value >= buffer.Count)
+ _currentLine = buffer.Count-1;
+ else if (value < 0)
+ _currentLine = 0;
+ else
+ _currentLine = value;
+ //force recheck of currentCol for bounding
+ int cc = _currentCol;
+ _currentCol = 0;
+ CurrentColumn = cc;
+ //System.Diagnostics.Debug.WriteLine ("Scroll:{0} visibleLines:{1} CurLine:{2}", ScrollY, visibleLines, CurrentLine);
+ if (_currentLine < ScrollY)
+ ScrollY = _currentLine;
+ else if (_currentLine >= ScrollY + visibleLines)
+ ScrollY = _currentLine - visibleLines + 1;
+ NotifyValueChanged ("CurrentLine", _currentLine);
+ }
+ }
+ [XmlIgnore]public Point CurrentPosition {
+ get { return new Point(CurrentColumn, CurrentLine); }
+ }
+ //TODO:using HasFocus for drawing selection cause SelBegin and Release binding not to work
+ /// <summary>
+ /// Selection begin position in char units (line, column)
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue("-1")]
+ public Point SelBegin {
+ get { return _selBegin; }
+ set {
+ if (value == _selBegin)
+ return;
+ _selBegin = value;
+ System.Diagnostics.Debug.WriteLine ("SelBegin=" + _selBegin);
+ NotifyValueChanged ("SelBegin", _selBegin);
+ NotifyValueChanged ("SelectedText", SelectedText);
+ }
+ }
+ /// <summary>
+ /// Selection release position in char units (line, column)
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue("-1")]
+ public Point SelRelease {
+ get {
+ return _selRelease;
+ }
+ set {
+ if (value == _selRelease)
+ return;
+ _selRelease = value;
+ NotifyValueChanged ("SelRelease", _selRelease);
+ NotifyValueChanged ("SelectedText", SelectedText);
+ }
+ }
+ /// <summary>
+ /// return char at CurrentLine, CurrentColumn
+ /// </summary>
+ [XmlIgnore]protected Char CurrentChar
+ {
+ get {
+ return buffer [CurrentLine] [CurrentColumn];
+ }
+ }
+ /// <summary>
+ /// ordered selection start and end positions in char units
+ /// </summary>
+ [XmlIgnore]protected Point selectionStart
+ {
+ get {
+ return SelRelease < 0 || SelBegin.Y < SelRelease.Y ? SelBegin :
+ SelBegin.Y > SelRelease.Y ? SelRelease :
+ SelBegin.X < SelRelease.X ? SelBegin : SelRelease;
+ }
+ }
+ [XmlIgnore]public Point selectionEnd
+ {
+ get {
+ return SelRelease < 0 || SelBegin.Y > SelRelease.Y ? SelBegin :
+ SelBegin.Y < SelRelease.Y ? SelRelease :
+ SelBegin.X > SelRelease.X ? SelBegin : SelRelease;
+ }
+ }
+ [XmlIgnore]public string SelectedText
+ {
+ get {
+
+ if (SelRelease < 0 || SelBegin < 0)
+ return "";
+ if (selectionStart.Y == selectionEnd.Y)
+ return buffer [selectionStart.Y].RawText.Substring (selectionStart.X, selectionEnd.X - selectionStart.X);
+ string tmp = "";
+ tmp = buffer [selectionStart.Y].RawText.Substring (selectionStart.X);
+ for (int l = selectionStart.Y + 1; l < selectionEnd.Y; l++) {
+ tmp += Interface.LineBreak + buffer [l];
+ }
+ tmp += Interface.LineBreak + buffer [selectionEnd.Y].RawText.Substring (0, selectionEnd.X);
+ return tmp;
+ }
+ }
+ [XmlIgnore]public bool selectionIsEmpty
+ { get { return SelRelease == SelBegin; } }
+
+ List<string> getLines {
+ get {
+ return Regex.Split (_text, "\r\n|\r|\n|\\\\n").ToList();
+ }
+ }
+ /// <summary>
+ /// Moves cursor one char to the left.
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ public bool MoveLeft(){
+ int tmp = _currentCol - 1;
+ if (tmp < 0) {
+ if (_currentLine == 0)
+ return false;
+ CurrentLine--;
+ CurrentColumn = int.MaxValue;
+ } else
+ CurrentColumn = tmp;
+ return true;
+ }
+ /// <summary>
+ /// Moves cursor one char to the right.
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ public bool MoveRight(){
+ int tmp = _currentCol + 1;
+ if (tmp > buffer [_currentLine].Length){
+ if (CurrentLine == buffer.Count - 1)
+ return false;
+ CurrentLine++;
+ CurrentColumn = 0;
+ } else
+ CurrentColumn = tmp;
+ return true;
+ }
+ public void GotoWordStart(){
+ if (buffer[CurrentLine].Length == 0)
+ return;
+ CurrentColumn--;
+ //skip white spaces
+ while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0)
+ CurrentColumn--;
+ while (char.IsLetterOrDigit (buffer [CurrentLine] [CurrentColumn]) && CurrentColumn > 0)
+ CurrentColumn--;
+ if (!char.IsLetterOrDigit (this.CurrentChar))
+ CurrentColumn++;
+ }
+ public void GotoWordEnd(){
+ //skip white spaces
+ if (CurrentColumn >= buffer [CurrentLine].Length - 1)
+ return;
+ while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < buffer [CurrentLine].Length-1)
+ CurrentColumn++;
+ while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < buffer [CurrentLine].Length-1)
+ CurrentColumn++;
+ if (char.IsLetterOrDigit (this.CurrentChar))
+ CurrentColumn++;
+ }
+ public void DeleteChar()
+ {
+ if (selectionIsEmpty) {
+ if (CurrentColumn == 0) {
+ if (CurrentLine == 0 && buffer.Count == 1)
+ return;
+ CurrentLine--;
+ CurrentColumn = buffer [CurrentLine].Length;
+ buffer [CurrentLine].RawText += buffer [CurrentLine + 1].RawText;
+ buffer.RemoveLine (CurrentLine + 1);
+ OnTextChanged (this, null);
+ return;
+ }
+ CurrentColumn--;
+ buffer [CurrentLine].RawText = buffer [CurrentLine].RawText.Remove (CurrentColumn, 1);
+ } else {
+ int linesToRemove = selectionEnd.Y - selectionStart.Y + 1;
+ int l = selectionStart.Y;
+
+ if (linesToRemove > 0) {
+ buffer [l].RawText = buffer [l].RawText.Remove (selectionStart.X, buffer [l].Length - selectionStart.X) +
+ buffer [selectionEnd.Y].RawText.Substring (selectionEnd.X, buffer [selectionEnd.Y].Length - selectionEnd.X);
+ l++;
+ for (int c = 0; c < linesToRemove-1; c++)
+ buffer.RemoveLine (l);
+ CurrentLine = selectionStart.Y;
+ CurrentColumn = selectionStart.X;
+ } else
+ buffer [l].RawText = buffer [l].RawText.Remove (selectionStart.X, selectionEnd.X - selectionStart.X);
+ CurrentColumn = selectionStart.X;
+ SelBegin = -1;
+ SelRelease = -1;
+ }
+ OnTextChanged (this, null);
+ }
+
+ #region GraphicObject overrides
+ public override Font Font {
+ get { return base.Font; }
+ set {
+ base.Font = value;
+
+ using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) {
+ using (Context gr = new Context (img)) {
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+
+ fe = gr.FontExtents;
+ }
+ }
+ MaxScrollY = 0;
+ RegisterForGraphicUpdate ();
+ }
+ }
+ protected override int measureRawSize(LayoutingType lt)
+ {
+ if (lt == LayoutingType.Height)
+ return (int)Math.Ceiling(fe.Height * buffer.Count) + Margin * 2;
+
+ string txt = _text.Replace("\t", new String (' ', Interface.TabSize));
+
+
+ int maxChar = 0;
+ foreach (string s in Regex.Split (txt, "\r\n|\r|\n|\\\\n")) {
+ if (maxChar < s.Length)
+ maxChar = s.Length;
+ }
+ return (int)(fe.MaxXAdvance * maxChar) + Margin * 2;
+ }
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+
+ if (layoutType == LayoutingType.Height)
+ updateVisibleLines ();
+ else if (layoutType == LayoutingType.Width)
+ updateVisibleColumns ();
+ }
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+ gr.FontOptions = Interface.FontRenderingOptions;
+ gr.Antialias = Interface.Antialias;
+
+ Rectangle cb = ClientRectangle;
+
+ Foreground.SetAsSource (gr);
+
+ bool selectionInProgress = false;
+
+ Foreground.SetAsSource (gr);
+
+ #region draw text cursor
+ if (SelBegin != SelRelease)
+ selectionInProgress = true;
+ else if (HasFocus){
+ gr.LineWidth = 1.0;
+ double cursorX = cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance;
+ gr.MoveTo (0.5 + cursorX, cb.Y + (CurrentLine - ScrollY) * fe.Height);
+ gr.LineTo (0.5 + cursorX, cb.Y + (CurrentLine + 1 - ScrollY) * fe.Height);
+ gr.Stroke();
+ }
+ #endregion
+
+ for (int i = 0; i < visibleLines; i++) {
+ int curL = i + ScrollY;
+ if (curL >= buffer.Count)
+ break;
+ string lstr = buffer[curL].RawText;
+ if (ScrollX < lstr.Length)
+ lstr = lstr.Substring (ScrollX);
+ else
+ lstr = "";
+
+ gr.MoveTo (cb.X, cb.Y + fe.Ascent + fe.Height * i);
+ gr.ShowText (lstr);
+ gr.Fill ();
+
+ if (selectionInProgress && curL >= selectionStart.Y && curL <= selectionEnd.Y) {
+
+ double rLineX = cb.X,
+ rLineY = cb.Y + i * fe.Height,
+ rLineW = lstr.Length * fe.MaxXAdvance;
+
+ System.Diagnostics.Debug.WriteLine ("sel start: " + selectionStart + " sel end: " + selectionEnd);
+ if (curL == selectionStart.Y) {
+ rLineX += (selectionStart.X - ScrollX) * fe.MaxXAdvance;
+ rLineW -= selectionStart.X * fe.MaxXAdvance;
+ }
+ if (curL == selectionEnd.Y)
+ rLineW -= (lstr.Length - selectionEnd.X) * fe.MaxXAdvance;
+
+ gr.Save ();
+ gr.Operator = Operator.Source;
+ gr.Rectangle (rLineX, rLineY, rLineW, fe.Height);
+ gr.SetSourceColor (SelectionBackground);
+ gr.FillPreserve ();
+ gr.Clip ();
+ gr.Operator = Operator.Over;
+ gr.SetSourceColor (SelectionForeground);
+ gr.MoveTo (cb.X, cb.Y + fe.Ascent + fe.Height * i);
+ gr.ShowText (lstr);
+ gr.Fill ();
+ gr.Restore ();
+ }
+ }
+ }
+ #endregion
+
+ #region Mouse handling
+ void updatemouseLocalPos(Point mpos){
+ Point mouseLocalPos = mpos - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft;
+ if (mouseLocalPos.X < 0)
+ CurrentColumn--;
+ else
+ CurrentColumn = ScrollX + (int)Math.Round (mouseLocalPos.X / fe.MaxXAdvance);
+
+ if (mouseLocalPos.Y < 0)
+ CurrentLine--;
+ else
+ CurrentLine = ScrollY + (int)Math.Floor (mouseLocalPos.Y / fe.Height);
+ }
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseEnter (sender, e);
+ currentInterface.MouseCursor = XCursor.Text;
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+ currentInterface.MouseCursor = XCursor.Default;
+ }
+ protected override void onFocused (object sender, EventArgs e)
+ {
+ base.onFocused (sender, e);
+
+ // SelBegin = new Point(0,0);
+ // SelRelease = new Point (lines.LastOrDefault ().Length, lines.Count-1);
+ RegisterForRedraw ();
+ }
+ protected override void onUnfocused (object sender, EventArgs e)
+ {
+ base.onUnfocused (sender, e);
+
+ // SelBegin = -1;
+ // SelRelease = -1;
+ RegisterForRedraw ();
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ if (!e.Mouse.IsButtonDown (MouseButton.Left))
+ return;
+ if (!HasFocus || SelBegin < 0)
+ return;
+
+ updatemouseLocalPos (e.Position);
+ SelRelease = CurrentPosition;
+
+ RegisterForRedraw();
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ if (this.HasFocus){
+ updatemouseLocalPos (e.Position);
+ SelBegin = SelRelease = CurrentPosition;
+ RegisterForRedraw();//TODO:should put it in properties
+ }
+
+ //done at the end to set 'hasFocus' value after testing it
+ base.onMouseDown (sender, e);
+ }
+ public override void onMouseUp (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseUp (sender, e);
+
+ if (SelBegin == SelRelease)
+ SelBegin = SelRelease = -1;
+
+ updatemouseLocalPos (e.Position);
+ RegisterForRedraw ();
+ }
+ public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseDoubleClick (sender, e);
+
+ GotoWordStart ();
+ SelBegin = CurrentPosition;
+ GotoWordEnd ();
+ SelRelease = CurrentPosition;
+ RegisterForRedraw ();
+ }
+ #endregion
+
+ #region Keyboard handling
+ public override void onKeyDown (object sender, KeyboardKeyEventArgs e)
+ {
+ //base.onKeyDown (sender, e);
+
+ Key key = e.Key;
+
+ switch (key)
+ {
+ case Key.Back:
+ if (CurrentPosition == 0)
+ return;
+ this.DeleteChar();
+ break;
+ case Key.Clear:
+ break;
+ case Key.Delete:
+ if (selectionIsEmpty) {
+ if (!MoveRight ())
+ return;
+ }else if (e.Shift)
+ currentInterface.Clipboard = this.SelectedText;
+ this.DeleteChar ();
+ break;
+ case Key.Enter:
+ case Key.KeypadEnter:
+ if (!selectionIsEmpty)
+ this.DeleteChar ();
+ this.InsertLineBreak ();
+ break;
+ case Key.Escape:
+ Text = "";
+ CurrentColumn = 0;
+ SelRelease = -1;
+ break;
+ case Key.Home:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = new Point (CurrentColumn, CurrentLine);
+ if (e.Control)
+ CurrentLine = 0;
+ CurrentColumn = 0;
+ SelRelease = new Point (CurrentColumn, CurrentLine);
+ break;
+ }
+ SelRelease = -1;
+ if (e.Control)
+ CurrentLine = 0;
+ CurrentColumn = 0;
+ break;
+ case Key.End:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ if (e.Control)
+ CurrentLine = int.MaxValue;
+ CurrentColumn = int.MaxValue;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ if (e.Control)
+ CurrentLine = int.MaxValue;
+ CurrentColumn = int.MaxValue;
+ break;
+ case Key.Insert:
+ if (e.Shift)
+ this.Insert (currentInterface.Clipboard);
+ else if (e.Control && !selectionIsEmpty)
+ currentInterface.Clipboard = this.SelectedText;
+ break;
+ case Key.Left:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = new Point(CurrentColumn, CurrentLine);
+ if (e.Control)
+ GotoWordStart ();
+ else if (!MoveLeft ())
+ return;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ if (e.Control)
+ GotoWordStart ();
+ else
+ MoveLeft();
+ break;
+ case Key.Right:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ if (e.Control)
+ GotoWordEnd ();
+ else if (!MoveRight ())
+ return;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ if (e.Control)
+ GotoWordEnd ();
+ else
+ MoveRight ();
+ break;
+ case Key.Up:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ CurrentLine--;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ CurrentLine--;
+ break;
+ case Key.Down:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ CurrentLine++;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ CurrentLine++;
+ break;
+ case Key.Menu:
+ break;
+ case Key.NumLock:
+ break;
+ case Key.PageDown:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ CurrentLine += visibleLines;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ SelRelease = -1;
+ CurrentLine += visibleLines;
+ break;
+ case Key.PageUp:
+ if (e.Shift) {
+ if (selectionIsEmpty)
+ SelBegin = CurrentPosition;
+ CurrentLine -= visibleLines;
+ SelRelease = CurrentPosition;
+ break;
+ }
+ CurrentLine -= visibleLines;
+ break;
+ case Key.RWin:
+ break;
+ case Key.Tab:
+ this.Insert ("\t");
+ break;
+ default:
+ break;
+ }
+ RegisterForGraphicUpdate();
+ }
+ public override void onKeyPress (object sender, KeyPressEventArgs e)
+ {
+ base.onKeyPress (sender, e);
+
+ this.Insert (e.KeyChar.ToString());
+
+ SelRelease = -1;
+ SelBegin = -1; //new Point(CurrentColumn, SelBegin.Y);
+
+ RegisterForGraphicUpdate();
+ }
+ #endregion
+
+
+ /// <summary> Compute x offset in cairo unit from text position </summary>
+ double GetXFromTextPointer(Context gr, Point pos)
+ {
+ try {
+ string l = buffer [pos.Y].RawText.Substring (0, pos.X).
+ Replace ("\t", new String (' ', Interface.TabSize));
+ return gr.TextExtents (l).XAdvance;
+ } catch{
+ return -1;
+ }
+ }
+
+ /// <summary> line break could be '\r' or '\n' or '\r\n' </summary>
+ string detectLineBreakKind(){
+ string strLB = "";
+
+ if (string.IsNullOrEmpty(_text))
+ return Interface.LineBreak;
+ int i = 0;
+ while ( i < _text.Length) {
+ if (_text [i] == '\r') {
+ strLB += '\r';
+ i++;
+ }
+ if (i < _text.Length) {
+ if (_text [i] == '\r')
+ return "\r";
+ if (_text [i] == '\n')
+ strLB += '\n';
+ }
+ if (!string.IsNullOrEmpty (strLB))
+ return strLB;
+ i++;
+ }
+ return Interface.LineBreak;
+ }
+
+ void updateVisibleLines(){
+ visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height);
+ MaxScrollY = Math.Max (0, buffer.Count - visibleLines);
+
+ System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines);
+ System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY);
+ }
+ void updateVisibleColumns(){
+ visibleColumns = (int)Math.Floor ((double)ClientRectangle.Width / fe.MaxXAdvance);
+ MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns);
+
+ System.Diagnostics.Debug.WriteLine ("update visible columns: " + visibleColumns);
+ System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX);
+ }
+
+
+
+ /// <summary>
+ /// Insert new string at caret position, should be sure no line break is inside.
+ /// </summary>
+ /// <param name="str">String.</param>
+ protected void Insert(string str)
+ {
+ if (!selectionIsEmpty)
+ this.DeleteChar ();
+ string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray();
+ buffer [CurrentLine].RawText = buffer [CurrentLine].RawText.Insert (CurrentColumn, strLines[0]);
+ CurrentColumn += strLines[0].Length;
+ for (int i = 1; i < strLines.Length; i++) {
+ InsertLineBreak ();
+ buffer [CurrentLine].RawText = buffer [CurrentLine].RawText.Insert (CurrentColumn, strLines[i]);
+ CurrentColumn += strLines[i].Length;
+ }
+ OnTextChanged (this, null);
+ RegisterForGraphicUpdate();
+ }
+
+ /// <summary>
+ /// Insert a line break.
+ /// </summary>
+ protected void InsertLineBreak()
+ {
+ buffer.InsertLine(CurrentLine + 1, new SourceLine (buffer[CurrentLine].RawText.Substring(CurrentColumn)));
+ buffer [CurrentLine].RawText = buffer [CurrentLine].RawText.Substring (0, CurrentColumn);
+ CurrentLine++;
+ CurrentColumn = 0;
+ OnTextChanged (this, null);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+//
+// SourceLine.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+using System;
+using System.Collections.Generic;
+
+namespace Crow
+{
+ /// <summary>
+ /// basic structure for line of source code
+ /// </summary>
+ public class SourceLine
+ {
+ public string RawText;
+ public List<Token> Tokens = null;
+
+ public int Length {
+ get { return string.IsNullOrEmpty (RawText)? 0 : RawText.Length; }
+ }
+ public char this[int index]{
+ get { return RawText [index]; }
+
+ }
+ int ptr; //character pointer in the source string
+ Token tok; //current token parsed before addition to the token list
+
+ public SourceLine ()
+ {
+ }
+ public SourceLine (string rawText){
+ RawText = rawText;
+ }
+
+ /// <summary>
+ /// Tokenize this instance.
+ /// This tokenization step is used for display mainly, so literals are not interpreted
+ /// </summary>
+ public bool Tokenize(){
+ Tokens = new List<Token>();
+ ptr = 0;
+
+ while (!eol) {
+ Char c = readChar ();
+
+ //block comments
+ if (tok?.Type == TokenType.BlockComment) {
+ tok.Content += c;
+ if (c == '*') {
+ if (peekChar () == '/') {
+ tok.Content += readChar ();
+ saveCurTok ();
+ }
+ }
+ continue;
+ } else if (tok?.Type == TokenType.StringLiteral) {
+ tok.Content += c;
+ if (c == '\\')//may escape " char, so next char is read;
+ tok.Content += readChar ();
+ else if (c == '"')
+ saveCurTok ();
+ continue;
+ } else if (tok?.Type == TokenType.CharacterLiteral) {
+ tok.Content += c;
+ if (c == '\\')//may escape ' char, so next char is read;
+ tok.Content += readChar ();
+ else if (c == '\'')
+ saveCurTok ();
+ continue;
+ } else if (tok?.Type == TokenType.WhiteSpace) {
+ if (char.IsWhiteSpace (c)) {
+ tok.Content += c;
+ continue;
+ }
+ saveCurTok ();
+ //if (char.IsLetter (c))
+
+ }
+
+ //single char tokens
+ if (c == '{')
+ tok.Type = TokenType.OpenBlock;
+ else if (c == '}')
+ tok.Type = TokenType.CloseBlock;
+ else if (c == '(')
+ tok.Type = TokenType.OpenParenth;
+ else if (c == ')')
+ tok.Type = TokenType.CloseParenth;
+
+
+ if (tok == null) {
+ tok = new Token () { Content = new string (c, 1) };
+
+ if (char.IsWhiteSpace (c))
+ tok.Type = TokenType.WhiteSpace;
+ else if (char.IsDigit (c))
+ tok.Type = TokenType.DigitalLiteral;
+ else if (char.IsLetter (c))
+ tok.Type = TokenType.Unknown;
+ else if (c == '"')
+ tok.Type = TokenType.StringLiteral;
+ else {//put here all single step parsing token, reseting tok directely
+ saveCurTok ();
+ }
+ }
+
+
+
+ ptr++;
+ }
+ return true;
+ }
+ /// <summary> add tok to token list and reset it to null </summary>
+ void saveCurTok(){
+ Tokens.Add (tok);
+ tok = null;
+ }
+ public void PresetCurrentToken (TokenType tokType, string content = null){
+ tok = new Token (tokType,content);
+ }
+
+ bool eol { get { return ptr < RawText.Length; }}
+ char readChar() {
+ char c = RawText [ptr];
+ ptr++;
+ return c;
+ }
+ char peekChar() {
+ return RawText [ptr];
+ }
+
+// public static implicit operator SourceLine(string rawText){
+// return new SourceLine() { RawText = rawText };
+// }
+// public static implicit operator string(SourceLine sl){
+// return sl?.RawText;
+// }
+ }
+}
+
+++ /dev/null
-//
-// TextBuffer.cs
-//
-// Author:
-// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// 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 <http://www.gnu.org/licenses/>.
-using System;
-
-namespace Crow
-{
- public class TextBuffer
- {
- public TextBuffer ()
- {
- }
- }
-}
-
--- /dev/null
+//
+// Token.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+using System;
+
+namespace Crow
+{
+ public enum TokenType {
+ Unknown,
+ WhiteSpace,
+ OpenParenth,
+ CloseParenth,
+ OpenBlock,
+ CloseBlock,
+ StatementEnding,
+ UnaryOp,
+ BinaryOp,
+ Affectation,
+ StringLiteral,
+ CharacterLiteral,
+ DigitalLiteral,
+ Literal,
+ Identifier,
+ Indexer,
+ Type,
+ LineComment,
+ BlockComment,
+ }
+ public class Token
+ {
+ public TokenType Type;
+ public string Content;
+
+ public Token ()
+ {
+ }
+ public Token (TokenType tokType, string content = null){
+ Type = tokType;
+ Content = content;
+ }
+ }
+}
+
<MenuItem Command="{CMDHelp}"/>
</MenuItem>
</Menu>
- <HorizontalStack Height="Stretched" Background="Ivory">
- <ScrollingTextBox Focusable="true" Name="editor" Font="couriernew, 16" VerticalAlignment="Top" Margin="10"
- Foreground="Jet"
+ <HorizontalStack Height="Stretched" >
+ <SourceEditor Focusable="true" Name="editor" Font="couriernew, 16" VerticalAlignment="Top" Margin="10"
+ Foreground="Jet" Background="Ivory" Width="Stretched" Height="Stretched"
Text="{Text}" KeyDown="textView_KeyDown"/>
<ScrollBar Name="scrollbarY" Value="{²../editor.ScrollY}"
Maximum="{../editor.MaxScrollY}" Orientation="Vertical"
<Label Text="{../../editor.CurrentColumn}"/>
<Label Text="Line:"/>
<Label Text="{../../editor.CurrentLine}"/>
+ <Label Text="ScrollX:"/>
+ <Label Text="{../../editor.ScrollX}"/>
</HorizontalStack>
</VerticalStack>
</Window>
\ No newline at end of file