From 6dad76e39792328d5bf800319aaf8f8c2e874b30 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 28 Oct 2016 20:55:23 +1100 Subject: [PATCH] Core: Don't allow multiple lines of chat in classic mode. --- .../2D/Widgets/Chat/ChatInputWidget.cs | 38 +-- ClassicalSharp/2D/Widgets/InputWidget.cs | 252 +++++++++--------- ClassicalSharp/Utils/WrappableStringBuffer.cs | 62 ++--- 3 files changed, 185 insertions(+), 167 deletions(-) diff --git a/ClassicalSharp/2D/Widgets/Chat/ChatInputWidget.cs b/ClassicalSharp/2D/Widgets/Chat/ChatInputWidget.cs index b15b0d10b..affd696ce 100644 --- a/ClassicalSharp/2D/Widgets/Chat/ChatInputWidget.cs +++ b/ClassicalSharp/2D/Widgets/Chat/ChatInputWidget.cs @@ -19,14 +19,22 @@ namespace ClassicalSharp.Gui.Widgets { typingLogPos = game.Chat.InputLog.Count; // Index of newest entry + 1. } + public override int MaxLines { get { return game.ClassicMode ? 1 : 3; } } + public override int MaxCharsPerLine { + get { + bool allChars = game.ClassicMode || game.Server.SupportsPartialMessages; + return allChars ? 64 : 62; + } + } + public override void Init() { base.Init(); bool supports = game.Server.SupportsPartialMessages; - if( buffer.Length > LineLength && !shownWarning && !supports ) { + if( buffer.Length > MaxCharsPerLine && !shownWarning && !supports ) { game.Chat.Add( "&eNote: Each line will be sent as a separate packet.", MessageType.ClientStatus6 ); shownWarning = true; - } else if( buffer.Length <= LineLength && shownWarning ) { + } else if( buffer.Length <= MaxCharsPerLine && shownWarning ) { game.Chat.Add( null, MessageType.ClientStatus6 ); shownWarning = false; } @@ -79,8 +87,8 @@ namespace ClassicalSharp.Gui.Widgets { void SendNormal() { int packetsCount = 0; - for( int i = 0; i < parts.Length; i++ ) { - if( parts[i] == null ) break; + for( int i = 0; i < lines.Length; i++ ) { + if( lines[i] == null ) break; packetsCount++; } @@ -91,7 +99,7 @@ namespace ClassicalSharp.Gui.Widgets { } void SendNormalText( int i, bool partial ) { - string text = parts[i]; + string text = lines[i]; char lastCol = GetLastColour( 0, i ); if( !IDrawer2D.IsWhiteColour( lastCol ) ) text = "&" + lastCol + text; @@ -103,10 +111,10 @@ namespace ClassicalSharp.Gui.Widgets { void UpKey( bool controlDown ) { if( controlDown ) { - int pos = caretPos == -1 ? buffer.Length : caretPos; - if( pos < LineLength ) return; + int pos = caret == -1 ? buffer.Length : caret; + if( pos < MaxCharsPerLine ) return; - caretPos = pos - LineLength; + caret = pos - MaxCharsPerLine; UpdateCaret(); return; } @@ -119,15 +127,15 @@ namespace ClassicalSharp.Gui.Widgets { buffer.Clear(); buffer.Append( 0, game.Chat.InputLog[typingLogPos] ); - caretPos = -1; + caret = -1; Recreate(); } } void DownKey( bool controlDown ) { if( controlDown ) { - if( caretPos == -1 || caretPos >= (parts.Length - 1) * LineLength ) return; - caretPos += LineLength; + if( caret == -1 || caret >= (lines.Length - 1) * MaxCharsPerLine ) return; + caret += MaxCharsPerLine; UpdateCaret(); return; } @@ -142,13 +150,13 @@ namespace ClassicalSharp.Gui.Widgets { } else { buffer.Append( 0, game.Chat.InputLog[typingLogPos] ); } - caretPos = -1; + caret = -1; Recreate(); } } void TabKey() { - int pos = caretPos == -1 ? buffer.Length - 1 : caretPos; + int pos = caret == -1 ? buffer.Length - 1 : caret; int start = pos; char[] value = buffer.value; @@ -172,11 +180,11 @@ namespace ClassicalSharp.Gui.Widgets { } if( matches.Count == 1 ) { - if( caretPos == -1 ) pos++; + if( caret == -1 ) pos++; int len = pos - start; for( int i = 0; i < len; i++ ) buffer.DeleteAt( start ); - if( caretPos != -1 ) caretPos -= len; + if( caret != -1 ) caret -= len; Append( matches[0] ); } else if( matches.Count > 1 ) { StringBuffer sb = new StringBuffer( Utils.StringLength ); diff --git a/ClassicalSharp/2D/Widgets/InputWidget.cs b/ClassicalSharp/2D/Widgets/InputWidget.cs index 6e58483dd..5ecde0000 100644 --- a/ClassicalSharp/2D/Widgets/InputWidget.cs +++ b/ClassicalSharp/2D/Widgets/InputWidget.cs @@ -12,63 +12,67 @@ namespace ClassicalSharp.Gui.Widgets { public InputWidget( Game game, Font font ) : base( game ) { HorizontalAnchor = Anchor.LeftOrTop; VerticalAnchor = Anchor.BottomOrRight; - buffer = new WrappableStringBuffer( Utils.StringLength * lines ); + + buffer = new WrappableStringBuffer( Utils.StringLength * MaxLines ); + lines = new string[MaxLines]; + lineSizes = new Size[MaxLines]; DrawTextArgs args = new DrawTextArgs( "_", font, true ); caretTex = game.Drawer2D.MakeChatTextTexture( ref args, 0, 0 ); caretTex.Width = (short)((caretTex.Width * 3) / 4); - defaultCaretWidth = caretTex.Width; + caretWidth = caretTex.Width; args = new DrawTextArgs( "> ", font, true ); - Size defSize = game.Drawer2D.MeasureChatSize( ref args ); - defaultWidth = Width = defSize.Width; - defaultHeight = Height = defSize.Height; + Size size = game.Drawer2D.MeasureChatSize( ref args ); + prefixWidth = Width = size.Width; + prefixHeight = Height = size.Height; this.font = font; - } + } - internal const int lines = 3; - internal WrappableStringBuffer buffer; - protected int caretPos = -1; + internal WrappableStringBuffer buffer; + protected int caret = -1; Texture inputTex, caretTex, prefixTex; readonly Font font; - int defaultCaretWidth, defaultWidth, defaultHeight; - FastColour caretCol; + int caretWidth, prefixWidth, prefixHeight; + FastColour caretColour; static FastColour backColour = new FastColour( 0, 0, 0, 127 ); public override void Render( double delta ) { gfx.Texturing = false; int y = Y, x = X; - for( int i = 0; i < sizes.Length; i++ ) { - if( i > 0 && sizes[i].Height == 0 ) break; - bool caretAtEnd = caretTex.Y1 == y && (indexX == LineLength || caretPos == -1); - int drawWidth = sizes[i].Width + (caretAtEnd ? caretTex.Width : 0); + for( int i = 0; i < lineSizes.Length; i++ ) { + if( i > 0 && lineSizes[i].Height == 0 ) break; + bool caretAtEnd = caretTex.Y1 == y && (caretCol == MaxCharsPerLine || caret == -1); + int drawWidth = lineSizes[i].Width + (caretAtEnd ? caretTex.Width : 0); // Cover whole window width to match original classic behaviour if( game.PureClassic ) drawWidth = Math.Max( drawWidth, game.Width - X * 4 ); - gfx.Draw2DQuad( x, y, drawWidth + 10, defaultHeight, backColour ); - y += sizes[i].Height; + gfx.Draw2DQuad( x, y, drawWidth + 10, prefixHeight, backColour ); + y += lineSizes[i].Height; } gfx.Texturing = true; inputTex.Render( gfx ); - caretTex.Render( gfx, caretCol ); + caretTex.Render( gfx, caretColour ); } - - internal string[] parts = new string[lines]; - int[] partLens = new int[lines]; - Size[] sizes = new Size[lines]; - int maxWidth = 0; - int indexX, indexY; - internal int LineLength { get { return game.Server.SupportsPartialMessages ? 64 : 62; } } - internal int TotalChars { get { return LineLength * lines; } } + /// The maximum number of lines that may be entered. + public abstract int MaxLines { get; } + + /// The maximum number of characters that can fit on one line. + public abstract int MaxCharsPerLine { get; } + + protected string[] lines; // raw text of each line + protected Size[] lineSizes; // size of each line in pixels + int caretCol, caretRow; // coordinates of caret + int maxWidth = 0; // maximum width of any line public override void Init() { X = 5; - buffer.WordWrap( game.Drawer2D, ref parts, ref partLens, LineLength, TotalChars ); + buffer.WordWrap( game.Drawer2D, lines, MaxCharsPerLine ); CalculateLineSizes(); RemakeTexture(); @@ -77,57 +81,59 @@ namespace ClassicalSharp.Gui.Widgets { /// Calculates the sizes of each line in the text buffer. public void CalculateLineSizes() { - for( int y = 0; y < sizes.Length; y++ ) - sizes[y] = Size.Empty; - sizes[0].Width = defaultWidth; - maxWidth = defaultWidth; + for( int y = 0; y < lineSizes.Length; y++ ) + lineSizes[y] = Size.Empty; + lineSizes[0].Width = prefixWidth; + maxWidth = prefixWidth; DrawTextArgs args = new DrawTextArgs( null, font, true ); - for( int y = 0; y < lines; y++ ) { - int offset = y == 0 ? defaultWidth : 0; - args.Text = parts[y]; - sizes[y] += game.Drawer2D.MeasureChatSize( ref args ); - maxWidth = Math.Max( maxWidth, sizes[y].Width ); + for( int y = 0; y < MaxLines; y++ ) { + args.Text = lines[y]; + lineSizes[y] += game.Drawer2D.MeasureChatSize( ref args ); + maxWidth = Math.Max( maxWidth, lineSizes[y].Width ); } - if( sizes[0].Height == 0 ) sizes[0].Height = defaultHeight; + if( lineSizes[0].Height == 0 ) lineSizes[0].Height = prefixHeight; } /// Calculates the location and size of the caret character public void UpdateCaret() { - if( caretPos >= buffer.Length ) caretPos = -1; - buffer.MakeCoords( caretPos, partLens, out indexX, out indexY ); + if( caret >= buffer.Length ) caret = -1; + buffer.GetCoords( caret, lines, out caretCol, out caretRow ); DrawTextArgs args = new DrawTextArgs( null, font, true ); - - if( indexX == LineLength ) { - caretTex.X1 = 10 + sizes[indexY].Width; - caretCol = FastColour.Yellow; - caretTex.Width = (short)defaultCaretWidth; - } else { - args.Text = parts[indexY].Substring( 0, indexX ); - Size trimmedSize = game.Drawer2D.MeasureChatSize( ref args ); - if( indexY == 0 ) trimmedSize.Width += defaultWidth; - caretTex.X1 = 10 + trimmedSize.Width; - caretCol = FastColour.Scale( FastColour.White, 0.8f ); - - string line = parts[indexY]; - args.Text = indexX < line.Length ? new String( line[indexX], 1 ) : ""; - int caretWidth = indexX < line.Length ? - game.Drawer2D.MeasureChatSize( ref args ).Width : defaultCaretWidth; - caretTex.Width = (short)caretWidth; - } - caretTex.Y1 = sizes[0].Height * indexY + Y; - - // Update the colour of the caret IDrawer2D drawer = game.Drawer2D; - char code = GetLastColour( indexX, indexY ); - if( code != '\0' ) caretCol = drawer.Colours[code]; + + if( caretCol == MaxCharsPerLine ) { + caretTex.X1 = 10 + lineSizes[caretRow].Width; + caretColour = FastColour.Yellow; + caretTex.Width = (short)caretWidth; + } else { + args.Text = lines[caretRow].Substring( 0, caretCol ); + Size trimmedSize = drawer.MeasureChatSize( ref args ); + if( caretRow == 0 ) trimmedSize.Width += prefixWidth; + + caretTex.X1 = 10 + trimmedSize.Width; + caretColour = FastColour.Scale( FastColour.White, 0.8f ); + + string line = lines[caretRow]; + if( caretCol < line.Length ) { + args.Text = new String( line[caretCol], 1 ); + caretTex.Width = (short)drawer.MeasureChatSize( ref args ).Width; + } else { + caretTex.Width = (short)caretWidth; + } + } + caretTex.Y1 = lineSizes[0].Height * caretRow + Y; + + // Update the colour of the caret + char code = GetLastColour( caretCol, caretRow ); + if( code != '\0' ) caretColour = drawer.Colours[code]; } /// Remakes the raw texture containg all the chat lines. public void RemakeTexture() { int totalHeight = 0; - for( int i = 0; i < lines; i++ ) - totalHeight += sizes[i].Height; + for( int i = 0; i < MaxLines; i++ ) + totalHeight += lineSizes[i].Height; Size size = new Size( maxWidth, totalHeight ); int realHeight = 0; @@ -138,21 +144,21 @@ namespace ClassicalSharp.Gui.Widgets { DrawTextArgs args = new DrawTextArgs( "> ", font, true ); drawer.DrawChatText( ref args, 0, 0 ); - for( int i = 0; i < parts.Length; i++ ) { - if( parts[i] == null ) break; - args.Text = parts[i]; + for( int i = 0; i < lines.Length; i++ ) { + if( lines[i] == null ) break; + args.Text = lines[i]; char lastCol = GetLastColour( 0, i ); if( !IDrawer2D.IsWhiteColour( lastCol ) ) args.Text = "&" + lastCol + args.Text; - int offset = i == 0 ? defaultWidth : 0; + int offset = i == 0 ? prefixWidth : 0; drawer.DrawChatText( ref args, offset, realHeight ); - realHeight += sizes[i].Height; + realHeight += lineSizes[i].Height; } inputTex = drawer.Make2DTexture( bmp, size, 10, 0 ); } - Height = realHeight == 0 ? defaultHeight : realHeight; + Height = realHeight == 0 ? prefixHeight : realHeight; Y = game.Height - Height - YOffset; inputTex.Y1 = Y; Width = size.Width; @@ -162,10 +168,10 @@ namespace ClassicalSharp.Gui.Widgets { int x = indexX; IDrawer2D drawer = game.Drawer2D; for( int y = indexY; y >= 0; y-- ) { - string part = parts[y]; + string part = lines[y]; char code = drawer.LastColour( part, x ); if( code != '\0' ) return code; - if( y > 0 ) x = parts[y - 1].Length; + if( y > 0 ) x = lines[y - 1].Length; } return '\0'; } @@ -190,7 +196,7 @@ namespace ClassicalSharp.Gui.Widgets { /// Invoked when the user presses enter. public virtual void EnterInput() { Clear(); - Height = defaultHeight; + Height = prefixHeight; } @@ -198,10 +204,10 @@ namespace ClassicalSharp.Gui.Widgets { /// Deletes the native texture. public void Clear() { buffer.Clear(); - for( int i = 0; i < parts.Length; i++ ) - parts[i] = null; + for( int i = 0; i < lines.Length; i++ ) + lines[i] = null; - caretPos = -1; + caret = -1; Dispose(); } @@ -225,14 +231,15 @@ namespace ClassicalSharp.Gui.Widgets { } bool AppendChar( char c ) { - if( buffer.Length == TotalChars ) return false; + int totalChars = MaxCharsPerLine * lines.Length; + if( buffer.Length == totalChars ) return false; - if( caretPos == -1 ) { + if( caret == -1 ) { buffer.InsertAt( buffer.Length, c ); } else { - buffer.InsertAt( caretPos, c ); - caretPos++; - if( caretPos >= buffer.Length ) caretPos = -1; + buffer.InsertAt( caret, c ); + caret++; + if( caret >= buffer.Length ) caret = -1; } return true; } @@ -276,37 +283,35 @@ namespace ClassicalSharp.Gui.Widgets { } - #region Input handling + #region Input handling void BackspaceKey( bool controlDown ) { if( controlDown ) { - if( caretPos == -1 ) caretPos = buffer.Length - 1; - int len = buffer.GetBackLength( caretPos ); + if( caret == -1 ) caret = buffer.Length - 1; + int len = buffer.GetBackLength( caret ); if( len == 0 ) return; - caretPos -= len; - if( caretPos < 0 ) caretPos = 0; + caret -= len; + if( caret < 0 ) caret = 0; for( int i = 0; i <= len; i++ ) - buffer.DeleteAt( caretPos ); + buffer.DeleteAt( caret ); - if( caretPos >= buffer.Length ) caretPos = -1; - if( caretPos == -1 && buffer.Length > 0 ) { + if( caret >= buffer.Length ) caret = -1; + if( caret == -1 && buffer.Length > 0 ) { buffer.value[buffer.Length] = ' '; - } else if( caretPos >= 0 && buffer.value[caretPos] != ' ' ) { - buffer.InsertAt( caretPos, ' ' ); + } else if( caret >= 0 && buffer.value[caret] != ' ' ) { + buffer.InsertAt( caret, ' ' ); } Recreate(); - } else if( !buffer.Empty && caretPos != 0 ) { - int index = caretPos == -1 ? buffer.Length - 1 : caretPos; + } else if( !buffer.Empty && caret != 0 ) { + int index = caret == -1 ? buffer.Length - 1 : caret; if( CheckColour( index - 1 ) ) { DeleteChar(); // backspace XYZ%e to XYZ - index -= 1; } else if( CheckColour( index - 2 ) ) { DeleteChar(); DeleteChar(); // backspace XYZ%eH to XYZ - index -= 2; } - if( index > 0 ) DeleteChar(); + DeleteChar(); Recreate(); } } @@ -318,68 +323,71 @@ namespace ClassicalSharp.Gui.Widgets { } void DeleteChar() { - if( caretPos == -1 ) { + if( buffer.Length == 0 ) return; + + if( caret == -1 ) { buffer.DeleteAt( buffer.Length - 1 ); } else { - caretPos--; - buffer.DeleteAt( caretPos ); + caret--; + buffer.DeleteAt( caret ); } } void DeleteKey() { - if( !buffer.Empty && caretPos != -1 ) { - buffer.DeleteAt( caretPos ); - if( caretPos >= buffer.Length ) caretPos = -1; + if( !buffer.Empty && caret != -1 ) { + buffer.DeleteAt( caret ); + if( caret >= buffer.Length ) caret = -1; Recreate(); } } void LeftKey( bool controlDown ) { if( controlDown ) { - if( caretPos == -1 ) - caretPos = buffer.Length - 1; - caretPos -= buffer.GetBackLength( caretPos ); + if( caret == -1 ) + caret = buffer.Length - 1; + caret -= buffer.GetBackLength( caret ); UpdateCaret(); return; } if( !buffer.Empty ) { - if( caretPos == -1 ) caretPos = buffer.Length; - caretPos--; - if( caretPos < 0 ) caretPos = 0; + if( caret == -1 ) caret = buffer.Length; + caret--; + if( caret < 0 ) caret = 0; UpdateCaret(); } } void RightKey( bool controlDown ) { if( controlDown ) { - caretPos += buffer.GetForwardLength( caretPos ); - if( caretPos >= buffer.Length ) caretPos = -1; + caret += buffer.GetForwardLength( caret ); + if( caret >= buffer.Length ) caret = -1; UpdateCaret(); return; } - if( !buffer.Empty && caretPos != -1 ) { - caretPos++; - if( caretPos >= buffer.Length ) caretPos = -1; + if( !buffer.Empty && caret != -1 ) { + caret++; + if( caret >= buffer.Length ) caret = -1; UpdateCaret(); } } void HomeKey() { if( buffer.Empty ) return; - caretPos = 0; + caret = 0; UpdateCaret(); } void EndKey() { - caretPos = -1; + caret = -1; UpdateCaret(); } static char[] trimChars = {'\r', '\n', '\v', '\f', ' ', '\t', '\0'}; bool OtherKey( Key key ) { - if( key == Key.V && buffer.Length < TotalChars ) { + int totalChars = MaxCharsPerLine * lines.Length; + if( key == Key.V && buffer.Length < totalChars ) { string text = null; try { text = game.window.ClipboardText.Trim( trimChars ); @@ -432,12 +440,12 @@ namespace ClassicalSharp.Gui.Widgets { mouseX -= inputTex.X1; mouseY -= inputTex.Y1; DrawTextArgs args = new DrawTextArgs( null, font, true ); IDrawer2D drawer = game.Drawer2D; - int offset = 0, elemHeight = defaultHeight; + int offset = 0, elemHeight = prefixHeight; string oneChar = new String( 'A', 1 ); - for( int y = 0; y < parts.Length; y++ ) { - string line = parts[y]; - int xOffset = y == 0 ? defaultWidth : 0; + for( int y = 0; y < lines.Length; y++ ) { + string line = lines[y]; + int xOffset = y == 0 ? prefixWidth : 0; if( line == null ) continue; for( int x = 0; x < line.Length; x++ ) { @@ -450,13 +458,13 @@ namespace ClassicalSharp.Gui.Widgets { args.Text = oneChar; int elemWidth = drawer.MeasureChatSize( ref args ).Width; if( GuiElement.Contains( trimmedWidth, y * elemHeight, elemWidth, elemHeight, mouseX, mouseY ) ) { - caretPos = offset + x; + caret = offset + x; UpdateCaret(); return; } } offset += line.Length; } - caretPos = -1; + caret = -1; UpdateCaret(); } diff --git a/ClassicalSharp/Utils/WrappableStringBuffer.cs b/ClassicalSharp/Utils/WrappableStringBuffer.cs index 093381c7d..a817c531e 100644 --- a/ClassicalSharp/Utils/WrappableStringBuffer.cs +++ b/ClassicalSharp/Utils/WrappableStringBuffer.cs @@ -3,17 +3,16 @@ using System; namespace ClassicalSharp { - public sealed class WrappableStringBuffer : StringBuffer { - - char[] wrap; + public unsafe sealed class WrappableStringBuffer : StringBuffer { + char[] wrap; public WrappableStringBuffer( int capacity ) : base( capacity ) { wrap = new char[capacity]; } - public void WordWrap( IDrawer2D drawer, ref string[] lines, ref int[] lineLens, - int lineSize, int totalChars ) { + public void WordWrap( IDrawer2D drawer, string[] lines, int maxPerLine ) { int len = Length; + int* lineLens = stackalloc int[lines.Length]; for( int i = 0; i < lines.Length; i++ ) { lines[i] = null; lineLens[i] = 0; @@ -23,31 +22,29 @@ namespace ClassicalSharp { char[] realText = value; MakeWrapCopy(); - int linesCount = 0; - for( int index = 0; index < totalChars; index += lineSize ) { - if( value[index] == '\0' ) - break; + int usedLines = 0, totalChars = maxPerLine * lines.Length; + for( int index = 0; index < totalChars; index += maxPerLine ) { + if( value[index] == '\0' ) break; - int lineEnd = index + (lineSize - 1), nextLine = lineEnd + 1; - linesCount++; + int lineEnd = index + (maxPerLine - 1), nextStart = lineEnd + 1; + usedLines++; // Do we need word wrapping? bool needWrap = !IsWrapper( value[lineEnd] ) - && nextLine < totalChars && !IsWrapper( value[nextLine] ); - int wrappedLen = needWrap ? WrapLine( index, lineSize ) : lineSize; + && nextStart < totalChars && !IsWrapper( value[nextStart] ); + int wrappedLen = needWrap ? WrapLine( index, maxPerLine ) : maxPerLine; // Calculate the maximum size of this line - int lineLen = lineSize; + int lineLen = maxPerLine; for( int i = lineEnd; i >= index; i-- ) { if( value[i] != '\0' ) break; lineLen--; } - lineLens[index / lineSize] = Math.Min( lineLen, wrappedLen ); + lineLens[index / maxPerLine] = Math.Min( lineLen, wrappedLen ); } // Output the used lines - OutputLines( drawer, ref lines, lineLens, - linesCount, lineSize, totalChars ); + OutputLines( drawer, lines, lineLens, usedLines, maxPerLine ); value = realText; } @@ -61,8 +58,8 @@ namespace ClassicalSharp { value = wrap; } - void OutputLines( IDrawer2D drawer, ref string[] lines, int[] lineLens, - int linesCount, int lineSize, int totalChars ) { + void OutputLines( IDrawer2D drawer, string[] lines, int* lineLens, int usedLines, int charsPerLine ) { + int totalChars = charsPerLine * lines.Length; for( int i = 0; i < totalChars; i++ ) { if( value[i] == '\0' ) value[i] = ' '; } @@ -72,8 +69,9 @@ namespace ClassicalSharp { value[i] = '&'; } - for( int i = 0; i < Math.Max( 1, linesCount ); i++ ) - lines[i] = new String( value, i * lineSize, lineLens[i] ); + usedLines = Math.Max( 1, usedLines ); + for( int i = 0; i < usedLines; i++ ) + lines[i] = new String( value, i * charsPerLine, lineLens[i] ); } int WrapLine( int index, int lineSize ) { @@ -96,22 +94,26 @@ namespace ClassicalSharp { || c == '<' || c == '/' || c == '\\'; } - public void MakeCoords( int index, int[] partLens, out int x, out int y ) { + /// Calculates where the given raw index is located in the wrapped lines. + public void GetCoords( int index, string[] lines, out int col, out int row ) { if( index == -1 ) index = Int32.MaxValue; - int total = 0; x = -1; y = 0; + int total = 0; col = -1; row = 0; - for( int yy = 0; yy < partLens.Length; yy++ ) { - if( partLens[yy] == 0 ) break; + for( int y = 0; y < lines.Length; y++ ) { + int lineLength = LineLength( lines[y] ); + if( lineLength == 0 ) break; - y = yy; - if( index < total + partLens[yy] ) { - x = index - total; break; + row = y; + if( index < total + lineLength ) { + col = index - total; break; } - total += partLens[yy]; + total += lineLength; } - if( x == -1 ) x = partLens[y]; + if( col == -1 ) col = LineLength( lines[row] ); } + static int LineLength( string line ) { return line == null ? 0 : line.Length; } + public int GetBackLength( int index ) { if( index <= 0 ) return 0; int start = index;