From 137749098ac20b4ea84ced1151dbd6d7f56766ed Mon Sep 17 00:00:00 2001 From: jpbruyere Date: Wed, 15 Apr 2015 12:02:25 +0200 Subject: [PATCH] Multiline TextBox --- GOLib.csproj | 274 +++++++++++---- GOLib.sln | 10 +- MonoDevelop.GOLib/MonoDevelop.GOLib.csproj | 45 ++- Tests/GOLIBTest_0.cs | 2 +- Tests/GOLIBTest_1.cs | 2 +- Tests/GOLIBTest_2.cs | 8 +- Tests/GOLIBTest_3.cs | 2 +- Tests/GOLIBTest_4.cs | 2 +- Tests/GOLIBTest_5.cs | 2 +- Tests/Interfaces/test2.goml | 10 +- Tests/Interfaces/test4.goml | 2 +- Tests/Tests.csproj | 67 +++- src/GraphicObjects/GraphicObject.cs | 3 + src/GraphicObjects/Group.cs | 16 +- src/GraphicObjects/Label.cs | 387 +++++++++++++++++++-- src/GraphicObjects/Scroller.cs | 4 +- src/GraphicObjects/TextBox.cs | 337 ++++-------------- 17 files changed, 753 insertions(+), 420 deletions(-) diff --git a/GOLib.csproj b/GOLib.csproj index 3b8ed8b9..af75b9e2 100644 --- a/GOLib.csproj +++ b/GOLib.csproj @@ -2,7 +2,7 @@ Debug - Linux_x86 + {C2980F9B-4798-4C05-99E2-E174810F7C7B} Library Properties @@ -22,10 +22,11 @@ 8.0.30703 2.0 v4.5 + AnyCPU true - TRACE;DEBUG + TRACE;DEBUG;__linux__ False true Full @@ -37,61 +38,187 @@ None 4194304 - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -99,12 +226,6 @@ - - dependencies\OpenTK.Compatibility.dll - - - dependencies\OpenTK.dll - rsvg2-sharp-2.0 @@ -113,6 +234,9 @@ + + ..\..\src\opentk-git\Binaries\OpenTK\Release\OpenTK.dll + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + diff --git a/GOLib.sln b/GOLib.sln index 6a99e7da..8fc87a39 100644 --- a/GOLib.sln +++ b/GOLib.sln @@ -16,19 +16,17 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {232716B4-D19D-4FD7-B310-94A98FD926F0}.Debug|Linux_x86.ActiveCfg = Debug|Any CPU - {232716B4-D19D-4FD7-B310-94A98FD926F0}.Debug|Linux_x86.Build.0 = Debug|Any CPU {232716B4-D19D-4FD7-B310-94A98FD926F0}.Release|Linux_x86.ActiveCfg = Release|Linux_x86 {232716B4-D19D-4FD7-B310-94A98FD926F0}.Release|Linux_x86.Build.0 = Release|Linux_x86 - {74289092-9F70-4941-AFCB-DFD7BE2140B6}.Debug|Linux_x86.ActiveCfg = Debug|Linux_x86 - {74289092-9F70-4941-AFCB-DFD7BE2140B6}.Debug|Linux_x86.Build.0 = Debug|Linux_x86 + {74289092-9F70-4941-AFCB-DFD7BE2140B6}.Debug|Linux_x86.ActiveCfg = Debug|Any CPU + {74289092-9F70-4941-AFCB-DFD7BE2140B6}.Debug|Linux_x86.Build.0 = Debug|Any CPU {74289092-9F70-4941-AFCB-DFD7BE2140B6}.Release|Linux_x86.ActiveCfg = Release|Linux_x86 {74289092-9F70-4941-AFCB-DFD7BE2140B6}.Release|Linux_x86.Build.0 = Release|Linux_x86 - {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Debug|Linux_x86.ActiveCfg = Debug|Linux_x86 - {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Debug|Linux_x86.Build.0 = Debug|Linux_x86 + {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Debug|Linux_x86.ActiveCfg = Debug|Any CPU + {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Debug|Linux_x86.Build.0 = Debug|Any CPU {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Release|Linux_x86.ActiveCfg = Release|Linux_x86 {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Release|Linux_x86.Build.0 = Release|Linux_x86 {E9E14DB5-3C67-4E01-B5C3-4D90D7E31A2E}.Debug|Linux_x86.ActiveCfg = Debug|Any CPU - {E9E14DB5-3C67-4E01-B5C3-4D90D7E31A2E}.Debug|Linux_x86.Build.0 = Debug|Any CPU {E9E14DB5-3C67-4E01-B5C3-4D90D7E31A2E}.Release|Linux_x86.ActiveCfg = Release|Any CPU {E9E14DB5-3C67-4E01-B5C3-4D90D7E31A2E}.Release|Linux_x86.Build.0 = Release|Any CPU EndGlobalSection diff --git a/MonoDevelop.GOLib/MonoDevelop.GOLib.csproj b/MonoDevelop.GOLib/MonoDevelop.GOLib.csproj index 009c9a02..b780e2d7 100644 --- a/MonoDevelop.GOLib/MonoDevelop.GOLib.csproj +++ b/MonoDevelop.GOLib/MonoDevelop.GOLib.csproj @@ -29,6 +29,11 @@ prompt 4 false + + + + + full @@ -37,6 +42,11 @@ prompt 4 false + + + + + @@ -76,16 +86,37 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + + + + diff --git a/Tests/GOLIBTest_0.cs b/Tests/GOLIBTest_0.cs index 3cc4e834..6d479219 100644 --- a/Tests/GOLIBTest_0.cs +++ b/Tests/GOLIBTest_0.cs @@ -27,7 +27,7 @@ namespace test protected override void OnLoad (EventArgs e) { base.OnLoad (e); - LoadInterface("Interfaces/test0.xml", out g); + LoadInterface("Interfaces/test0.goml", out g); } protected override void OnRenderFrame (FrameEventArgs e) diff --git a/Tests/GOLIBTest_1.cs b/Tests/GOLIBTest_1.cs index 91692b66..f92cd16c 100644 --- a/Tests/GOLIBTest_1.cs +++ b/Tests/GOLIBTest_1.cs @@ -27,7 +27,7 @@ namespace test protected override void OnLoad (EventArgs e) { base.OnLoad (e); - LoadInterface("Interfaces/test1.xml", out g); + LoadInterface("Interfaces/test1.goml", out g); } protected override void OnRenderFrame (FrameEventArgs e) diff --git a/Tests/GOLIBTest_2.cs b/Tests/GOLIBTest_2.cs index f92cd16c..6b9f832a 100644 --- a/Tests/GOLIBTest_2.cs +++ b/Tests/GOLIBTest_2.cs @@ -16,9 +16,9 @@ using System.Threading; namespace test { - class GOLIBTest_1 : OpenTKGameWindow + class GOLIBTest_2 : OpenTKGameWindow { - public GOLIBTest_1 () + public GOLIBTest_2 () : base(1024, 600,"test") {} @@ -27,7 +27,7 @@ namespace test protected override void OnLoad (EventArgs e) { base.OnLoad (e); - LoadInterface("Interfaces/test1.goml", out g); + LoadInterface("Interfaces/test2.goml", out g); } protected override void OnRenderFrame (FrameEventArgs e) @@ -47,7 +47,7 @@ namespace test { Console.WriteLine ("starting example"); - using (GOLIBTest_1 win = new GOLIBTest_1( )) { + using (GOLIBTest_2 win = new GOLIBTest_2( )) { win.Run (30.0); } } diff --git a/Tests/GOLIBTest_3.cs b/Tests/GOLIBTest_3.cs index f0dc3de8..ae233369 100755 --- a/Tests/GOLIBTest_3.cs +++ b/Tests/GOLIBTest_3.cs @@ -27,7 +27,7 @@ namespace test protected override void OnLoad (EventArgs e) { base.OnLoad (e); - g = LoadInterface("Interfaces/test3.xml"); + g = LoadInterface("Interfaces/test3.goml"); } protected override void OnRenderFrame (FrameEventArgs e) diff --git a/Tests/GOLIBTest_4.cs b/Tests/GOLIBTest_4.cs index f43880de..2482a66e 100644 --- a/Tests/GOLIBTest_4.cs +++ b/Tests/GOLIBTest_4.cs @@ -60,7 +60,7 @@ namespace test { base.OnLoad (e); - LoadInterface("Interfaces/test4.xml", out c); + LoadInterface("Interfaces/test4.goml", out c); //LoadInterface("golibtests/test4.xml", out c2); //c2.HorizontalAlignment = HorizontalAlignment.Left; //c2.VerticalAlignment = VerticalAlignment.Top; diff --git a/Tests/GOLIBTest_5.cs b/Tests/GOLIBTest_5.cs index ce515ba8..87fb5276 100644 --- a/Tests/GOLIBTest_5.cs +++ b/Tests/GOLIBTest_5.cs @@ -27,7 +27,7 @@ namespace test { base.OnLoad (e); - LoadInterface("Interfaces/test5.xml", out c); + LoadInterface("Interfaces/test5.goml", out c); gl.Add (c.FindByName ("g0")); ll.Add (c.FindByName ("lab0")as Label); } diff --git a/Tests/Interfaces/test2.goml b/Tests/Interfaces/test2.goml index 43524e85..69ac77ec 100755 --- a/Tests/Interfaces/test2.goml +++ b/Tests/Interfaces/test2.goml @@ -1,9 +1,9 @@  - - - + + + \ No newline at end of file diff --git a/Tests/Interfaces/test4.goml b/Tests/Interfaces/test4.goml index fdcd988d..4118e075 100755 --- a/Tests/Interfaces/test4.goml +++ b/Tests/Interfaces/test4.goml @@ -29,7 +29,7 @@ Width="100" Height="10" Value="30"/> - + diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 4570715f..d5880c46 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -2,17 +2,17 @@ Debug - Linux_x86 8.0.30703 2.0 {74289092-9F70-4941-AFCB-DFD7BE2140B6} Exe Tests Tests - test.GOLIBTest_0 + test.GOLIBTest_3 v4.5 ..\bin\$(configuration) obj\$(configuration) + AnyCPU ..\bin\Debug @@ -31,13 +31,6 @@ 0 false - - __linux__ - x86 - - - _WIN32 - @@ -53,35 +46,77 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + PreserveNewest + + PreserveNewest + + PreserveNewest + + PreserveNewest + + PreserveNewest + + + + + PreserveNewest + + - - + + + + + + + + diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index 86cb3009..aa108e22 100755 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -54,6 +54,9 @@ namespace go bool yIsValid = false; #endregion + public static int TabSize = 4; + public static string LineBreak = "\r\n"; + public static bool ReplaceTabsWithSpace = false; public static bool DesignerMode = false; #region public fields diff --git a/src/GraphicObjects/Group.cs b/src/GraphicObjects/Group.cs index 4e8e5492..d2c48904 100644 --- a/src/GraphicObjects/Group.cs +++ b/src/GraphicObjects/Group.cs @@ -140,13 +140,13 @@ namespace go foreach (GraphicObject c in Children) { if (raw.Width < 0) { - if (c.XIsValid & c.WIsValid) + if (c.WIsValid) tmp.Width = Math.Max (tmp.Width, c.Slot.Right); else return raw; } if (raw.Height < 0) { - if (c.YIsValid & c.HIsValid) + if (c.HIsValid) tmp.Height = Math.Max (tmp.Height, c.Slot.Bottom); else return raw; @@ -172,12 +172,12 @@ namespace go if (c.LayoutIsValid) continue; - if (Width < 0 && c.Width != 0) { + if (Width < 0 && c.WIsValid) { if (!atLeastOneChildHasWNotDependingOnParent && !(this is GenericStack)) c.XIsValid = true; atLeastOneChildHasWNotDependingOnParent = true; } - if (Height < 0 && c.Height != 0) { + if (Height < 0 && c.HIsValid) { if (!atLeastOneChildHasHNotDependingOnParent && !(this is GenericStack)) c.YIsValid = true; atLeastOneChildHasHNotDependingOnParent = true; @@ -186,10 +186,10 @@ namespace go c.UpdateLayout (); } - if (Width < 0 && !atLeastOneChildHasWNotDependingOnParent) - Debug.WriteLine ("ERROR: no child has fixed width and parent width is set to content!"); - if (Height < 0 && !atLeastOneChildHasHNotDependingOnParent) - Debug.WriteLine ("ERROR: no child has fixed height and parent height is set to content!"); +// if (Width < 0 && !atLeastOneChildHasWNotDependingOnParent) +// Debug.WriteLine ("ERROR: no child has fixed width and parent width is set to content!"); +// if (Height < 0 && !atLeastOneChildHasHNotDependingOnParent) +// Debug.WriteLine ("ERROR: no child has fixed height and parent height is set to content!"); base.UpdateLayout (); } diff --git a/src/GraphicObjects/Label.cs b/src/GraphicObjects/Label.cs index 37e2b8c7..aa93a7e9 100755 --- a/src/GraphicObjects/Label.cs +++ b/src/GraphicObjects/Label.cs @@ -7,6 +7,7 @@ using Cairo; using System.Text.RegularExpressions; using System.Xml.Serialization; using System.ComponentModel; +using OpenTK.Input; namespace go { @@ -32,6 +33,15 @@ namespace go Alignment _textAlignment = Alignment.LeftCenter; Font _font; bool _multiline = false; + Color selColor; + Color selFontColor; + Point mouseLocalPos; //mouse coord in widget space, filled only when clicked + int _currentCol; //0 based cursor position in string + int _currentLine; + double textCursorPos; //cursor position in cairo units in widget client coord. + double SelStartCursorPos = -1; + double SelEndCursorPos = -1; + bool SelectionInProgress = false; protected Rectangle rText; protected float widthRatio = 1f; @@ -40,7 +50,22 @@ namespace go protected TextExtents te; #endregion - //public string FontFace = "MagicMedieval"; + [XmlAttributeAttribute()][DefaultValue("SkyBlue")] + public virtual Color SelectionBackground { + get { return selColor; } + set { + selColor = value; + registerForGraphicUpdate (); + } + } + [XmlAttributeAttribute()][DefaultValue("Black")] + public virtual Color SelectionForeground { + get { return selFontColor; } + set { + selFontColor = value; + registerForGraphicUpdate (); + } + } [XmlAttributeAttribute()][DefaultValue(Alignment.LeftCenter)] public Alignment TextAlignment @@ -51,17 +76,24 @@ namespace go [XmlAttributeAttribute()][DefaultValue("label")] public string Text { - get { return _text; } + get { + return lines == null ? + _text : lines.Aggregate((i, j) => i + GraphicObject.LineBreak + j); + } set { if (_text == value) return; - - + registerForGraphicUpdate(); InvalidateLayout(); _text = value; + + if (string.IsNullOrEmpty(_text)) + _text = ""; + + lines = getLines; } } [XmlAttributeAttribute()][DefaultValue("droid,10")] @@ -79,15 +111,108 @@ namespace go registerForGraphicUpdate(); } } - - string[] getLines { - get { + [XmlIgnore]public int currentCol{ + get { return _currentCol; } + set { + if (value < 0) + _currentCol = 0; + else if (value > lines [_currentLine].Count ()) + _currentCol = lines [_currentLine].Count (); + else + _currentCol = value; + } + } + [XmlIgnore]public int currentLine{ + get { return _currentLine; } + set { + if (value > lines.Count) + _currentLine = lines.Count; + else if (value < 0) + _currentLine = 0; + else + _currentLine = value; + } + } + [XmlIgnore]protected Point selBegin; + [XmlIgnore]protected Point selRelease; + [XmlIgnore]protected Point selectionStart //ordered selection start and end positions + { + 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 { + return null; + //selectionEnd < 0 ? null : + //Text.Substring(selectionStart, selectionEnd - selectionStart); + } + } + [XmlIgnore]public bool selectionIsEmpty + { get { return selRelease < 0; } } + + List lines; + List getLines { + get { return _multiline ? - Regex.Split (_text, "\r\n|\r|\n") : - new string[] { _text }; + Regex.Split (_text, "\r\n|\r|\n").ToList() : + new List(new string[] { _text }); } } + public void DeleteChar() + { + if (selectionIsEmpty) { + if (currentCol == 0) { + if (currentLine == 0) + return; + currentLine--; + currentCol = lines [currentLine].Count (); + lines [currentLine] += lines [currentLine + 1]; + lines.RemoveAt (currentLine + 1); + return; + } + currentCol--; + lines [currentLine] = lines [currentLine].Remove (currentCol, 1); + } else { + Debug.WriteLine (selectionEnd.ToString()); + int linesToRemove = selectionEnd.Y - selectionStart.Y; + 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); + } else + lines [l] = lines [l].Remove (selectionStart.X, selectionEnd.X - selectionStart.X); + currentCol = selectionStart.X; + selBegin = -1; + selRelease = -1; + } + } + /// + /// Insert new string at caret position, should be sure no line break is inside. + /// + /// String. + protected void Insert(string str) + { + lines [currentLine] = lines [currentLine].Insert (currentCol, str); + currentCol += str.Length; + } + #region GraphicObject overrides [XmlAttributeAttribute()][DefaultValue(-1)] public override int Width { @@ -107,13 +232,10 @@ namespace go protected override Size measureRawSize() { - Size s; - + Size size; - if (string.IsNullOrEmpty(_text)) - _text = ""; - - string[] lines = getLines; + if (lines == null) + lines = getLines; using (ImageSurface img = new ImageSurface (Format.Argb32, 10, 10)) { using (Context gr = new Context (img)) { @@ -124,25 +246,26 @@ namespace go te = new TextExtents(); - foreach (string str in lines) { + foreach (string s in lines) { + string l = s.Replace("\t", new String (' ', GraphicObject.TabSize)); + #if _WIN32 || _WIN64 - TextExtents tmp = gr.TextExtents(str.ToUtf8()); + TextExtents tmp = gr.TextExtents(str.ToUtf8()); #elif __linux__ - TextExtents tmp = gr.TextExtents (str); + TextExtents tmp = gr.TextExtents (l); #endif if (tmp.XAdvance > te.XAdvance) te = tmp; } fe = gr.FontExtents; - s = new Size ((int)Math.Ceiling (te.XAdvance) + Margin * 2, (int)Math.Ceiling (fe.Height) * lines.Length + Margin*2); + size = new Size ((int)Math.Ceiling (te.XAdvance) + Margin * 2, (int)(fe.Height * lines.Count) + Margin*2); } } - return s;// +borderWidth; + return size;// +borderWidth; } protected override void onDraw (Context gr) { base.onDraw (gr); - string[] lines = getLines; gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); gr.SetFontSize (Font.Size); @@ -151,7 +274,7 @@ namespace go //gr.FontOptions.Antialias = Antialias.Subpixel; //gr.FontOptions.HintMetrics = HintMetrics.On; - rText = new Rectangle(new Point(0, 0),new Size((int)te.Width,(int)fe.Height * lines.Length)); + rText = ClientRectangle; widthRatio = 1f; heightRatio = 1f; @@ -256,20 +379,228 @@ namespace go } } - gr.Color = Foreground; + #region draw text cursor + if (mouseLocalPos > 0) + { + computeTextCursor(gr); + + if (SelectionInProgress) + { + selRelease = new Point(currentCol, currentLine); + SelEndCursorPos = textCursorPos; + } + else if (selBegin < 0) + { + selBegin = new Point(currentCol, currentLine); + SelStartCursorPos = textCursorPos; + selRelease = -1; + } + else + computeTextCursorPosition(gr); + } + else + computeTextCursorPosition(gr); + + if (HasFocus) + { + //TODO: + gr.Color = Color.White; + gr.LineWidth = 1.5; + gr.MoveTo(new PointD(textCursorPos + rText.X, rText.Y + currentLine * fe.Height)); + gr.LineTo(new PointD(textCursorPos + rText.X, rText.Y + (currentLine + 1) * fe.Height)); + gr.Stroke(); + } + #endregion + +// gr.Color = Color.Green; +// Rectangle R = new Rectangle( +// rText.X + (int)SelEndCursorPos-10, +// rText.Y + (int)(selRelease.Y * fe.Height), +// 20, +// (int)fe.Height); +// gr.Rectangle(R); +// gr.Fill(); +// gr.Color = Color.UnmellowYellow; +// R = new Rectangle( +// rText.X + (int)SelStartCursorPos-10, +// rText.Y + (int)(selBegin.Y * fe.Height), +// 20, +// (int)fe.Height); +// gr.Rectangle(R); +// gr.Fill(); gr.FontMatrix = new Matrix(widthRatio * Font.Size, 0, 0, heightRatio * Font.Size, 0, 0); - for (int i = 0; i < lines.Length; i++) { - gr.MoveTo(rText.X, rText.Y + fe.Ascent + fe.Height * i); + for (int i = 0; i < lines.Count; i++) { + string l = lines [i].Replace ("\t", new String (' ', GraphicObject.TabSize)); + if (selRelease >= 0 && i >= selectionStart.Y && i <= selectionEnd.Y) { + gr.Color = selColor; + int lineLength = (int)gr.TextExtents (l).XAdvance; + Rectangle selRect = new Rectangle ( + rText.X, + rText.Y + (int)(i * fe.Height), + lineLength, + (int)fe.Height); + + int cpStart = (int)SelStartCursorPos, + cpEnd = (int)SelEndCursorPos; + + if (selBegin.Y > selRelease.Y) { + cpStart = cpEnd; + cpEnd = (int)SelStartCursorPos; + } + + if (i == selectionStart.Y) { + selRect.Width -= cpStart; + selRect.Left += cpStart; + } + if (i == selectionEnd.Y) + selRect.Width -= (lineLength - cpEnd); + + gr.Rectangle (selRect); + gr.Fill (); + } + + if (string.IsNullOrWhiteSpace (l)) + continue; + + gr.Color = Foreground; + gr.MoveTo (rText.X, rText.Y + fe.Ascent + fe.Height * i); + #if _WIN32 || _WIN64 - gr.ShowText(lines[i].ToUtf8()); + gr.ShowText(l.ToUtf8()); #elif __linux__ - gr.ShowText(lines[i]); + gr.ShowText (l); #endif + gr.Fill (); + } - gr.Fill(); } #endregion + + #region Mouse handling + public override void onMouseEnter (object sender, MouseMoveEventArgs e) + { + // SelectionInProgress = true; + // mouseLocalPos = e.Position - ScreenCoordBounds.TopLeft - rText.TopLeft; + // registerForGraphicUpdate(); + + base.onMouseEnter (sender, e); + } + public override void onMouseLeave (object sender, MouseMoveEventArgs e) + { + base.onMouseLeave (sender, e); + } + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + base.onMouseMove (sender, e); + + if ((sender as OpenTKGameWindow).activeWidget != this) + return; + + SelectionInProgress = true; + mouseLocalPos = e.Position - ScreenCoordinates(ClientRectangle).TopLeft; + registerForGraphicUpdate(); + + } + public override void onMouseButtonDown (object sender, MouseButtonEventArgs e) + { + if (this.HasFocus){ + mouseLocalPos = e.Position - ScreenCoordinates(ClientRectangle).TopLeft; + selBegin = -1; + selRelease = -1; + }else{ +// selBeginPos = 0; +// selReleasePos = new Point(lines[lines.Count].Length-1, lines.Count-1); + } + + //done at the end to set 'hasFocus' value after testing it + base.onMouseButtonDown (sender, e); + + registerForGraphicUpdate(); + } + public override void onMouseButtonUp (object sender, MouseButtonEventArgs e) + { + base.onMouseButtonUp (sender, e); + SelectionInProgress = false; + } + #endregion + + + void computeTextCursor(Context gr) + { + //FontExtents fe = gr.FontExtents; + TextExtents te; + + double cPos = 0f; + + currentLine = (int)(mouseLocalPos.Y / fe.Height); + + for (int i = 0; i < lines[currentLine].Length; i++) + { + string c = lines [currentLine].Substring (i, 1); + if (c == "\t") + c = new string (' ', GraphicObject.TabSize); + + #if _WIN32 || _WIN64 + byte[] c = System.Text.UTF8Encoding.UTF8.GetBytes(Text.Substring(i, 1)); + te = gr.TextExtents(c); + #elif __linux__ + te = gr.TextExtents(c); + #endif + double halfWidth = te.XAdvance / 2; + + if (mouseLocalPos.X <= cPos + halfWidth) + { + currentCol = i; + textCursorPos = cPos; + mouseLocalPos = -1; + return; + } + + cPos += te.XAdvance; + } + currentCol = lines[currentLine].Length; + textCursorPos = cPos; + + //reset mouseLocalPos + mouseLocalPos = -1; + } + void computeTextCursorPosition(Context gr) + { + TextExtents te; + + double cPos = 0f; + + int limit = currentCol; + + if (selectionEnd > 0) + limit = Math.Max(currentCol, selectionEnd.X); + + for (int i = 0; i <= limit; i++) + { + if (i == currentCol) + textCursorPos = cPos; + if (i == selectionStart.X) + SelStartCursorPos = cPos; + if (i == selectionEnd.X) + SelEndCursorPos = cPos; + + if (i < lines[currentLine].Length) + { + string c = lines [currentLine].Substring (i, 1); + if (c == "\t") + c = new string (' ', GraphicObject.TabSize); + #if _WIN32 || _WIN64 + byte[] c = System.Text.UTF8Encoding.UTF8.GetBytes(Text.Substring(i, 1)); + te = gr.TextExtents(c); + #elif __linux__ + te = gr.TextExtents(c); + #endif + cPos += te.XAdvance; + } + } + + } } } diff --git a/src/GraphicObjects/Scroller.cs b/src/GraphicObjects/Scroller.cs index 3d952fd0..45a8a8b3 100644 --- a/src/GraphicObjects/Scroller.cs +++ b/src/GraphicObjects/Scroller.cs @@ -52,8 +52,7 @@ namespace go #region Mouse handling public override bool MouseIsIn (Point m) - { - //Debug.WriteLine ("scroller mouse is in"); + { return Visible ? base.ScreenCoordinates(Slot).ContainsOrIsEqual (m) : false; } public override void onMouseMove (object sender, MouseMoveEventArgs e) @@ -72,7 +71,6 @@ namespace go if (VerticalScrolling ) { - Debug.WriteLine ("scroll"); //add redraw call with old bounds to errase old position registerForRedraw(); diff --git a/src/GraphicObjects/TextBox.cs b/src/GraphicObjects/TextBox.cs index 70d841aa..be7cdab9 100644 --- a/src/GraphicObjects/TextBox.cs +++ b/src/GraphicObjects/TextBox.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Xml.Serialization; using System.Globalization; using System.ComponentModel; +using System.Runtime.InteropServices; namespace go { @@ -36,33 +37,6 @@ namespace go set { _capitalOn = value; } } - #region private fields - Color selColor; - Color selFontColor; - Point mouseLocalPos; //mouse coord in widget space, filled only when clicked - int _currentPos; //0 based cursor position in string - double textCursorPos; //cursor position in cairo units in widget client coord. - double SelStartCursorPos = -1; - double SelEndCursorPos = -1; - bool SelectionInProgress = false; - #endregion - - [XmlAttributeAttribute()][DefaultValue("SkyBlue")] - public virtual Color SelectionBackground { - get { return selColor; } - set { - selColor = value; - registerForGraphicUpdate (); - } - } - [XmlAttributeAttribute()][DefaultValue("Black")] - public virtual Color SelectionForeground { - get { return selFontColor; } - set { - selFontColor = value; - registerForGraphicUpdate (); - } - } #region GraphicObject overrides [XmlIgnore]public override bool HasFocus //trigger update when lost focus to errase text beam @@ -84,12 +58,12 @@ namespace go set { base.Focusable = value; } } [XmlAttributeAttribute()][DefaultValue("White")] - public virtual Color Background { + public override Color Background { get { return base.Background; } set { base.Background = value; } } [XmlAttributeAttribute()][DefaultValue("Black")] - public virtual Color Foreground { + public override Color Foreground { get { return base.Foreground; } set { base.Foreground = value; } } @@ -99,66 +73,20 @@ namespace go base.onDraw (gr); // gr.FontOptions.Antialias = Antialias.Subpixel; // gr.FontOptions.HintMetrics = HintMetrics.On; - gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); - gr.SetFontSize (Font.Size); +// gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); +// gr.SetFontSize (Font.Size); FontExtents fe = gr.FontExtents; - #region draw text cursor - if (mouseLocalPos > 0) - { - computeTextCursor(gr); - - if (SelectionInProgress) - { - selReleasePos = currentPos; - SelEndCursorPos = textCursorPos; - } - else if (selBeginPos < 0) - { - selBeginPos = currentPos; - SelStartCursorPos = textCursorPos; - selReleasePos = -1; - } - else - computeTextCursorPosition(gr); - - } - else - computeTextCursorPosition(gr); - - if (HasFocus) - { - //TODO: - gr.Color = Foreground; - gr.LineWidth = 2; - gr.MoveTo(new PointD(textCursorPos + rText.X, rText.Y )); - gr.LineTo(new PointD(textCursorPos + rText.X, rText.Y + fe.Height)); - gr.Stroke(); - } - - #endregion - - if (selReleasePos >= 0) - { - gr.Color = selColor; - Rectangle selRect = - new Rectangle((int)SelStartCursorPos + rText.X, (int)(rText.Y), (int)(SelEndCursorPos - SelStartCursorPos), (int)fe.Height); - gr.Rectangle(selRect); - gr.Fill(); - gr.Color = selFontColor; - } - else - gr.Color = Foreground; - gr.MoveTo(rText.X, rText.Y + fe.Ascent); - #if _WIN32 || _WIN64 - gr.ShowText(txt); - #elif __linux__ - gr.ShowText(Text); - #endif - gr.Fill(); +// gr.MoveTo(rText.X, rText.Y + fe.Ascent); +// #if _WIN32 || _WIN64 +// gr.ShowText(txt); +// #elif __linux__ +// gr.ShowText(Text); +// #endif +// gr.Fill(); } #endregion @@ -169,26 +97,6 @@ namespace go { TextChanged (this, e); } - - - [XmlIgnore]public int currentPos{ - get { return _currentPos; } - set { _currentPos = value; } - } - [XmlIgnore]public int selBeginPos; - [XmlIgnore]public int selReleasePos; - [XmlIgnore]public int selectionStart //ordered selection start and end positions - { - get { return selReleasePos < 0 ? selBeginPos : Math.Min(selBeginPos, selReleasePos); } - } - [XmlIgnore]public int selectionEnd - { get { return selReleasePos < 0 ? selReleasePos : Math.Max(selBeginPos, selReleasePos); } } - [XmlIgnore]public string selectedText - { get { return selectionEnd < 0 ? null : Text.Substring(selectionStart, selectionEnd - selectionStart); } } - [XmlIgnore]public bool selectionIsEmpty - { get { return string.IsNullOrEmpty(selectedText); } } - - #region Keyboard handling public override void onKeyDown (object sender, KeyboardKeyEventArgs e) @@ -202,32 +110,19 @@ namespace go case Key.Back: if (!selectionIsEmpty) { - Text = Text.Remove(selectionStart, selectionEnd - selectionStart); - selReleasePos = -1; - currentPos = selBeginPos; - } - else if (currentPos > 0) - { - currentPos--; - Text = Text.Remove(currentPos, 1); +// Text = Text.Remove(selectionStart, selectionEnd - selectionStart); +// selReleasePos = -1; +// currentCol = selBeginPos; } + else + this.DeleteChar(); break; case Key.Clear: break; case Key.Delete: - if (!selectionIsEmpty) - { - Text = Text.Remove(selectionStart, selectionEnd - selectionStart); - selReleasePos = -1; - currentPos = selBeginPos; - } - else if (currentPos < Text.Length) - Text = Text.Remove(currentPos, 1); - break; - case Key.Down: - break; - case Key.End: - currentPos = Text.Length; + if (selectionIsEmpty) + currentCol++; + this.DeleteChar (); break; case Key.Enter: case Key.KeypadEnter: @@ -235,17 +130,33 @@ namespace go break; case Key.Escape: Text = ""; - currentPos = 0; - selReleasePos = -1; + currentCol = 0; + selRelease = -1; break; case Key.Home: - currentPos = 0; + //TODO + if (e.Control) + currentLine = 0; + currentCol = 0; + break; + case Key.End: + if (e.Control) + currentLine = int.MaxValue; + currentCol = int.MaxValue; break; case Key.Insert: break; - case Key.Left: - if (currentPos > 0) - currentPos--; + case Key.Left: + currentCol--; + break; + case Key.Right: + currentCol++; + break; + case Key.Up: + currentLine--; + break; + case Key.Down: + currentLine++; break; case Key.Menu: break; @@ -257,49 +168,43 @@ namespace go break; case Key.RWin: break; - case Key.Right: - if (currentPos < Text.Length) - currentPos++; - break; case Key.Tab: - Text = Text.Insert(currentPos, "\t"); - currentPos++; - break; - case Key.Up: + this.Insert("\t"); break; case Key.KeypadDecimal: - Text = Text.Insert(currentPos, new string(new char[] - { Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) })); - currentPos++; + this.Insert ("."); break; case Key.Space: - Text = Text.Insert(currentPos, " "); - currentPos++; + this.Insert(" "); break; case Key.KeypadDivide: case Key.Slash: - Text = Text.Insert(currentPos, "/"); - currentPos++; + this.Insert("/"); break; case Key.KeypadMultiply: - Text = Text.Insert(currentPos, "*"); - currentPos++; + this.Insert("*"); break; case Key.KeypadMinus: case Key.Minus: - Text = Text.Insert(currentPos, "-"); - currentPos++; + this.Insert("-"); break; case Key.KeypadPlus: case Key.Plus: - Text = Text.Insert(currentPos, "+"); - currentPos++; + this.Insert("+"); + break; + case Key.ShiftLeft: + case Key.ShiftRight: + case Key.AltLeft: + case Key.AltRight: + break; + case Key.Semicolon: + this.Insert(";"); break; default: if (!selectionIsEmpty) { - Text = Text.Remove(selectionStart, selectionEnd - selectionStart); - currentPos = selBeginPos; +// Text = Text.Remove(selectionStart, selectionEnd - selectionStart); +// currentCol = selBeginPos; } string k = "?"; @@ -307,136 +212,20 @@ namespace go k = ((int)key - 67).ToString(); else if ((char)key >= 109 && (char)key <= 118) k = ((int)key - 109).ToString(); - else if (capitalOn) + else if (e.Shift) k = key.ToString(); else k = key.ToString().ToLower(); - Text = Text.Insert(currentPos, k); - currentPos++; + this.Insert (k); - selReleasePos = -1; - selBeginPos = currentPos; + selRelease = -1; + selBegin.X = currentCol; break; } registerForGraphicUpdate(); } #endregion - - #region Mouse handling - public override void onMouseEnter (object sender, MouseMoveEventArgs e) - { -// SelectionInProgress = true; -// mouseLocalPos = e.Position - ScreenCoordBounds.TopLeft - rText.TopLeft; -// registerForGraphicUpdate(); - - base.onMouseEnter (sender, e); - } - public override void onMouseLeave (object sender, MouseMoveEventArgs e) - { - base.onMouseLeave (sender, e); - } - public override void onMouseMove (object sender, MouseMoveEventArgs e) - { - base.onMouseMove (sender, e); - - if ((sender as OpenTKGameWindow).activeWidget != this) - return; - - SelectionInProgress = true; - mouseLocalPos = e.Position - ScreenCoordinates(rText).TopLeft; - registerForGraphicUpdate(); - - } - public override void onMouseButtonDown (object sender, MouseButtonEventArgs e) - { - if (this.HasFocus){ - mouseLocalPos = e.Position - ScreenCoordinates(rText).TopLeft - rText.TopLeft; - selBeginPos = -1; - selReleasePos = -1; - }else{ - selBeginPos = 0; - selReleasePos = Text.Length; - } - - //done at the end to set 'hasFocus' value after testing it - base.onMouseButtonDown (sender, e); - - registerForGraphicUpdate(); - } - public override void onMouseButtonUp (object sender, MouseButtonEventArgs e) - { - base.onMouseButtonUp (sender, e); - SelectionInProgress = false; - } - #endregion - - void computeTextCursor(Context gr) - { - FontExtents fe = gr.FontExtents; - TextExtents te; - - double cPos = 0f; - for (int i = 0; i < _text.Length; i++) - { -#if _WIN32 || _WIN64 - byte[] c = System.Text.UTF8Encoding.UTF8.GetBytes(Text.Substring(i, 1)); - te = gr.TextExtents(c); -#elif __linux__ - te = gr.TextExtents(Text.Substring(i, 1)); -#endif - double halfWidth = te.XAdvance / 2; - - if (mouseLocalPos.X <= cPos + halfWidth) - { - currentPos = i; - textCursorPos = cPos; - mouseLocalPos = -1; - return; - } - - cPos += te.XAdvance; - } - currentPos = _text.Length; - textCursorPos = cPos; - - //reset mouseLocalPos - mouseLocalPos = -1; - } - void computeTextCursorPosition(Context gr) - { - FontExtents fe = gr.FontExtents; - TextExtents te; - - double cPos = 0f; - - int limit = currentPos; - - if (selectionEnd > 0) - limit = Math.Max(currentPos, selectionEnd); - - for (int i = 0; i <= limit; i++) - { - if (i == currentPos) - textCursorPos = cPos; - if (i == selectionStart) - SelStartCursorPos = cPos; - if (i == selectionEnd) - SelEndCursorPos = cPos; - - if (i < Text.Length) - { -#if _WIN32 || _WIN64 - byte[] c = System.Text.UTF8Encoding.UTF8.GetBytes(Text.Substring(i, 1)); - te = gr.TextExtents(c); -#elif __linux__ - te = gr.TextExtents(Text.Substring(i,1)); -#endif - cPos += te.XAdvance; - } - } - - } } } -- 2.47.3