diff options
| author | jpbruyere <jp.bruyere@hotmail.com> | 2015-04-15 12:02:25 +0200 |
|---|---|---|
| committer | jpbruyere <jp.bruyere@hotmail.com> | 2015-04-15 12:02:25 +0200 |
| commit | 137749098ac20b4ea84ced1151dbd6d7f56766ed (patch) | |
| tree | e88f4d9e62959f74b80e51fca7b7564f82f73d89 /src/GraphicObjects/Label.cs | |
| parent | b9ef3d364896edc96f189dc899d33b90d79c6d38 (diff) | |
Multiline TextBox
Diffstat (limited to 'src/GraphicObjects/Label.cs')
| -rwxr-xr-x | src/GraphicObjects/Label.cs | 387 |
1 files changed, 359 insertions, 28 deletions
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<string> lines;
+ List<string> getLines {
+ get {
return _multiline ?
- Regex.Split (_text, "\r\n|\r|\n") :
- new string[] { _text };
+ Regex.Split (_text, "\r\n|\r|\n").ToList() :
+ new List<string>(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;
+ }
+ }
+ /// <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)
+ {
+ 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;
+ }
+ }
+
+ }
}
}
|