mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-24 05:10:42 -04:00
152 lines
4.3 KiB
C#
152 lines
4.3 KiB
C#
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
|
using System;
|
|
|
|
namespace ClassicalSharp {
|
|
|
|
public unsafe sealed class WrappableStringBuffer : StringBuffer {
|
|
|
|
char[] wrap;
|
|
public WrappableStringBuffer(int capacity) : base(capacity) {
|
|
wrap = new char[capacity];
|
|
}
|
|
|
|
public void WordWrap(IDrawer2D drawer, string[] lines, int maxLines, 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;
|
|
}
|
|
|
|
// Need to make a copy because we mutate the characters.
|
|
char[] realText = value;
|
|
MakeWrapCopy();
|
|
|
|
int usedLines = 0, totalChars = maxPerLine * maxLines;
|
|
for (int index = 0; index < totalChars; index += maxPerLine) {
|
|
if (value[index] == '\0') break;
|
|
|
|
int lineEnd = index + (maxPerLine - 1), nextStart = lineEnd + 1;
|
|
usedLines++;
|
|
|
|
// Do we need word wrapping?
|
|
bool needWrap = !IsWrapper(value[lineEnd])
|
|
&& nextStart < totalChars && !IsWrapper(value[nextStart]);
|
|
int wrappedLen = needWrap ? WrapLine(index, maxPerLine) : maxPerLine;
|
|
|
|
// Calculate the maximum size of this line
|
|
int lineLen = maxPerLine;
|
|
for (int i = lineEnd; i >= index; i--) {
|
|
if (value[i] != '\0') break;
|
|
lineLen--;
|
|
}
|
|
lineLens[index / maxPerLine] = Math.Min(lineLen, wrappedLen);
|
|
}
|
|
|
|
// Output the used lines
|
|
OutputLines(drawer, lines, lineLens, usedLines, maxLines, maxPerLine);
|
|
value = realText;
|
|
}
|
|
|
|
void MakeWrapCopy() {
|
|
int len = Length;
|
|
for (int i = 0; i < len; i++)
|
|
wrap[i] = value[i];
|
|
|
|
for (int i = len; i < Capacity; i++)
|
|
wrap[i] = '\0';
|
|
value = wrap;
|
|
}
|
|
|
|
void OutputLines(IDrawer2D drawer, string[] lines, int* lineLens, int usedLines, int maxLines, int charsPerLine) {
|
|
int totalChars = charsPerLine * maxLines;
|
|
for (int i = 0; i < totalChars; i++) {
|
|
if (value[i] == '\0') value[i] = ' ';
|
|
}
|
|
// convert %0-f to &0-f for colour preview.
|
|
for (int i = 0; i < totalChars - 1; i++) {
|
|
if (value[i] == '%' && IDrawer2D.ValidColCode(value[i + 1]))
|
|
value[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) {
|
|
int lineEnd = index + (lineSize - 1);
|
|
// wrap - but we don't want to wrap if the entire line is filled.
|
|
for (int i = lineEnd; i >= index + 1; i--) {
|
|
if (IsWrapper(value[i])) {
|
|
for (int j = lineEnd; j >= i + 1; j--) {
|
|
InsertAt(index + lineSize, value[j]);
|
|
value[j] = ' ';
|
|
}
|
|
return (i + 1) - index;
|
|
}
|
|
}
|
|
return lineSize;
|
|
}
|
|
|
|
bool IsWrapper(char c) {
|
|
return c == '\0' || c == ' ' || c == '-' || c == '>'
|
|
|| c == '<' || c == '/' || c == '\\';
|
|
}
|
|
|
|
/// <summary> Calculates where the given raw index is located in the wrapped lines. </summary>
|
|
public void GetCoords(int index, string[] lines, out int coordX, out int coordY) {
|
|
if (index == -1) index = Int32.MaxValue;
|
|
int total = 0; coordX = -1; coordY = 0;
|
|
|
|
for (int y = 0; y < lines.Length; y++) {
|
|
int lineLength = LineLength(lines[y]);
|
|
if (lineLength == 0) break;
|
|
|
|
coordY = y;
|
|
if (index < total + lineLength) {
|
|
coordX = index - total; break;
|
|
}
|
|
total += lineLength;
|
|
}
|
|
if (coordX == -1) coordX = LineLength(lines[coordY]);
|
|
}
|
|
|
|
static int LineLength(string line) { return line == null ? 0 : line.Length; }
|
|
|
|
public int GetBackLength(int index) {
|
|
if (index <= 0) return 0;
|
|
int start = index;
|
|
|
|
bool lookingSpace = value[index] == ' ';
|
|
// go back to the end of the previous word
|
|
if (lookingSpace) {
|
|
while (index > 0 && value[index] == ' ')
|
|
index--;
|
|
}
|
|
|
|
// go back to the start of the current word
|
|
while (index > 0 && value[index] != ' ')
|
|
index--;
|
|
return (start - index);
|
|
}
|
|
|
|
public int GetForwardLength(int index) {
|
|
if (index == -1) return 0;
|
|
int start = index;
|
|
|
|
bool lookingLetter = value[index] != ' ';
|
|
// go forward to the end of the current word
|
|
if (lookingLetter) {
|
|
while (index < Length && value[index] != ' ')
|
|
index++;
|
|
}
|
|
|
|
// go forward to the start of the next word
|
|
while (index < Length && value[index] == ' ')
|
|
index++;
|
|
return index - start;
|
|
}
|
|
}
|
|
}
|