mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-18 03:55:19 -04:00
Vastly simplify line wrapping code, and fix it in C port too
This commit is contained in:
parent
c252baa696
commit
1b5276bae5
@ -48,9 +48,11 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
|
|
||||||
|
|
||||||
public override void EnterInput() {
|
public override void EnterInput() {
|
||||||
if (!Text.Empty) {
|
// Don't want trailing spaces in output message
|
||||||
// Don't want trailing spaces in output message
|
int length = Text.Length;
|
||||||
string text = new String(Text.value, 0, Text.TextLength);
|
while (length > 0 && Text.value[length - 1] == ' ') { length--; }
|
||||||
|
if (length > 0) {
|
||||||
|
string text = new String(Text.value, 0, length);
|
||||||
game.Chat.Send(text);
|
game.Chat.Send(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
public abstract class InputWidget : Widget {
|
public abstract class InputWidget : Widget {
|
||||||
|
|
||||||
public InputWidget(Game game, Font font, string prefix, int maxLines) : base(game) {
|
public InputWidget(Game game, Font font, string prefix, int maxLines) : base(game) {
|
||||||
Text = new WrappableStringBuffer(Utils.StringLength * maxLines);
|
Text = new StringBuffer(Utils.StringLength * maxLines);
|
||||||
lines = new string[maxLines];
|
lines = new string[maxLines];
|
||||||
lineSizes = new Size[maxLines];
|
lineSizes = new Size[maxLines];
|
||||||
this.font = font;
|
this.font = font;
|
||||||
@ -43,7 +43,7 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
|
|
||||||
/// <summary> The raw text entered. </summary>
|
/// <summary> The raw text entered. </summary>
|
||||||
/// <remarks> You should Append() to add more text, as that also updates the caret position and texture. </remarks>
|
/// <remarks> You should Append() to add more text, as that also updates the caret position and texture. </remarks>
|
||||||
public WrappableStringBuffer Text;
|
public StringBuffer Text;
|
||||||
|
|
||||||
/// <summary> The maximum number of lines that may be entered. </summary>
|
/// <summary> The maximum number of lines that may be entered. </summary>
|
||||||
public abstract int UsedLines { get; }
|
public abstract int UsedLines { get; }
|
||||||
|
@ -295,7 +295,6 @@
|
|||||||
<Compile Include="Utils\Utils.cs" />
|
<Compile Include="Utils\Utils.cs" />
|
||||||
<Compile Include="Utils\Utils.Math.cs" />
|
<Compile Include="Utils\Utils.Math.cs" />
|
||||||
<Compile Include="Utils\Vector3I.cs" />
|
<Compile Include="Utils\Vector3I.cs" />
|
||||||
<Compile Include="Utils\WrappableStringBuffer.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\credits.txt">
|
<None Include="..\credits.txt">
|
||||||
|
@ -32,24 +32,19 @@ namespace ClassicalSharp.Model {
|
|||||||
.TexOrigin(0, 16)
|
.TexOrigin(0, 16)
|
||||||
.RotOrigin(0, 6, 2));
|
.RotOrigin(0, 6, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override float NameYOffset { get { return 1.7f; } }
|
public override float NameYOffset { get { return 1.7f; } }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override float GetEyeY(Entity entity) { return 22/16f; }
|
public override float GetEyeY(Entity entity) { return 22/16f; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override Vector3 CollisionSize {
|
public override Vector3 CollisionSize {
|
||||||
get { return new Vector3(8/16f, 26/16f, 8/16f); }
|
get { return new Vector3(8/16f, 26/16f, 8/16f); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override AABB PickingBounds {
|
public override AABB PickingBounds {
|
||||||
get { return new AABB(-4/16f, 0, -6/16f, 4/16f, 26/16f, 6/16f); }
|
get { return new AABB(-4/16f, 0, -6/16f, 4/16f, 26/16f, 6/16f); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override void DrawModel(Entity p) {
|
public override void DrawModel(Entity p) {
|
||||||
game.Graphics.BindTexture(GetTexture(p));
|
game.Graphics.BindTexture(GetTexture(p));
|
||||||
DrawRotate(-p.HeadXRadians, 0, 0, Head, true);
|
DrawRotate(-p.HeadXRadians, 0, 0, Head, true);
|
||||||
|
@ -10,7 +10,6 @@ namespace ClassicalSharp.Model {
|
|||||||
|
|
||||||
public PigModel(Game window) : base(window) { SurivalScore = 10; }
|
public PigModel(Game window) : base(window) { SurivalScore = 10; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override void CreateParts() {
|
public override void CreateParts() {
|
||||||
vertices = new ModelVertex[boxVertices * 6];
|
vertices = new ModelVertex[boxVertices * 6];
|
||||||
Head = BuildBox(MakeBoxBounds(-4, 8, -14, 4, 16, -6)
|
Head = BuildBox(MakeBoxBounds(-4, 8, -14, 4, 16, -6)
|
||||||
|
@ -65,24 +65,19 @@ namespace ClassicalSharp.Model {
|
|||||||
.RotOrigin(0, 12, 7));
|
.RotOrigin(0, 12, 7));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override float NameYOffset { get { return Fur ? 1.48125f: 1.075f; } }
|
public override float NameYOffset { get { return Fur ? 1.48125f: 1.075f; } }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override float GetEyeY(Entity entity) { return 20/16f; }
|
public override float GetEyeY(Entity entity) { return 20/16f; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override Vector3 CollisionSize {
|
public override Vector3 CollisionSize {
|
||||||
get { return new Vector3(10/16f, 20/16f, 10/16f); }
|
get { return new Vector3(10/16f, 20/16f, 10/16f); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override AABB PickingBounds {
|
public override AABB PickingBounds {
|
||||||
get { return new AABB(-6/16f, 0, -13/16f, 6/16f, 23/16f, 10/16f); }
|
get { return new AABB(-6/16f, 0, -13/16f, 6/16f, 23/16f, 10/16f); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override void DrawModel(Entity p) {
|
public override void DrawModel(Entity p) {
|
||||||
IGraphicsApi gfx = game.Graphics;
|
IGraphicsApi gfx = game.Graphics;
|
||||||
gfx.BindTexture(GetTexture(p));
|
gfx.BindTexture(GetTexture(p));
|
||||||
|
@ -28,18 +28,14 @@ namespace ClassicalSharp.Model {
|
|||||||
.RotOrigin(3, 8, 0));
|
.RotOrigin(3, 8, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override float NameYOffset { get { return 1.0125f; } }
|
public override float NameYOffset { get { return 1.0125f; } }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override float GetEyeY(Entity entity) { return 8/16f; }
|
public override float GetEyeY(Entity entity) { return 8/16f; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override Vector3 CollisionSize {
|
public override Vector3 CollisionSize {
|
||||||
get { return new Vector3(15/16f, 12/16f, 15/16f); }
|
get { return new Vector3(15/16f, 12/16f, 15/16f); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override AABB PickingBounds {
|
public override AABB PickingBounds {
|
||||||
get { return new AABB(-5/16f, 0, -11/16f, 5/16f, 12/16f, 15/16f); }
|
get { return new AABB(-5/16f, 0, -11/16f, 5/16f, 12/16f, 15/16f); }
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,6 @@ namespace ClassicalSharp.Map {
|
|||||||
if (Env.CloudHeight == -1) Env.CloudHeight = height + 2;
|
if (Env.CloudHeight == -1) Env.CloudHeight = height + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Sets the block at the given world coordinates without bounds checking. </summary>
|
|
||||||
public void SetBlock(int x, int y, int z, BlockID blockId) {
|
public void SetBlock(int x, int y, int z, BlockID blockId) {
|
||||||
int i = (y * Length + z) * Width + x;
|
int i = (y * Length + z) * Width + x;
|
||||||
blocks[i] = (BlockRaw)blockId;
|
blocks[i] = (BlockRaw)blockId;
|
||||||
@ -76,7 +75,6 @@ namespace ClassicalSharp.Map {
|
|||||||
blocks2[i] = (BlockRaw)(blockId >> 8);
|
blocks2[i] = (BlockRaw)(blockId >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Returns the block at the given world coordinates without bounds checking. </summary>
|
|
||||||
public BlockID GetBlock(int x, int y, int z) {
|
public BlockID GetBlock(int x, int y, int z) {
|
||||||
int i = (y * Length + z) * Width + x;
|
int i = (y * Length + z) * Width + x;
|
||||||
#if !ONLY_8BIT
|
#if !ONLY_8BIT
|
||||||
@ -86,7 +84,6 @@ namespace ClassicalSharp.Map {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Returns the block at the given world coordinates without bounds checking. </summary>
|
|
||||||
public BlockID GetBlock(Vector3I p) {
|
public BlockID GetBlock(Vector3I p) {
|
||||||
int i = (p.Y * Length + p.Z) * Width + p.X;
|
int i = (p.Y * Length + p.Z) * Width + p.X;
|
||||||
#if !ONLY_8BIT
|
#if !ONLY_8BIT
|
||||||
@ -96,21 +93,15 @@ namespace ClassicalSharp.Map {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Returns the block at the given world coordinates with bounds checking,
|
|
||||||
/// returning 0 is the coordinates were outside the map. </summary>
|
|
||||||
public BlockID SafeGetBlock(Vector3I p) {
|
public BlockID SafeGetBlock(Vector3I p) {
|
||||||
return IsValidPos(p.X, p.Y, p.Z) ? GetBlock(p) : Block.Air;
|
return IsValidPos(p.X, p.Y, p.Z) ? GetBlock(p) : Block.Air;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Returns whether the given world coordinates are contained
|
|
||||||
/// within the dimensions of the map. </summary>
|
|
||||||
public bool IsValidPos(int x, int y, int z) {
|
public bool IsValidPos(int x, int y, int z) {
|
||||||
return x >= 0 && y >= 0 && z >= 0 &&
|
return x >= 0 && y >= 0 && z >= 0 &&
|
||||||
x < Width && y < Height && z < Length;
|
x < Width && y < Height && z < Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Returns whether the given world coordinates are contained
|
|
||||||
/// within the dimensions of the map. </summary>
|
|
||||||
public bool IsValidPos(Vector3I p) {
|
public bool IsValidPos(Vector3I p) {
|
||||||
return p.X >= 0 && p.Y >= 0 && p.Z >= 0 &&
|
return p.X >= 0 && p.Y >= 0 && p.Z >= 0 &&
|
||||||
p.X < Width && p.Y < Height && p.Z < Length;
|
p.X < Width && p.Y < Height && p.Z < Length;
|
||||||
|
@ -7,6 +7,7 @@ namespace ClassicalSharp {
|
|||||||
|
|
||||||
public char[] value;
|
public char[] value;
|
||||||
public int Capacity, Length;
|
public int Capacity, Length;
|
||||||
|
public bool Empty { get { return Length == 0; } }
|
||||||
|
|
||||||
public StringBuffer(int capacity) {
|
public StringBuffer(int capacity) {
|
||||||
this.Capacity = capacity;
|
this.Capacity = capacity;
|
||||||
@ -39,7 +40,7 @@ namespace ClassicalSharp {
|
|||||||
internal static int MakeNum(int num) {
|
internal static int MakeNum(int num) {
|
||||||
int len = 0;
|
int len = 0;
|
||||||
do {
|
do {
|
||||||
numBuffer[len] = (char)('0' + (num % 10));
|
numBuffer[len] = (char)('0' + (num % 10));
|
||||||
num /= 10; len++;
|
num /= 10; len++;
|
||||||
} while (num > 0);
|
} while (num > 0);
|
||||||
return len;
|
return len;
|
||||||
@ -61,6 +62,31 @@ namespace ClassicalSharp {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StringBuffer DeleteAt(int index) {
|
||||||
|
for (int i = index; i < Length - 1; i++) {
|
||||||
|
value[i] = value[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
value[Length - 1] = '\0';
|
||||||
|
Length--;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer InsertAt(int index, char c) {
|
||||||
|
for (int i = Length - 1; i > index; i--) {
|
||||||
|
value[i] = value[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
value[index] = c;
|
||||||
|
Length++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer Set(string s) {
|
||||||
|
for (int i = 0; i < s.Length; i++) value[i] = s[i];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public StringBuffer Clear() {
|
public StringBuffer Clear() {
|
||||||
Length = 0;
|
Length = 0;
|
||||||
return this;
|
return this;
|
||||||
@ -69,5 +95,111 @@ namespace ClassicalSharp {
|
|||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
return new String(value, 0, Length);
|
return new String(value, 0, Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsWrapper(char c) {
|
||||||
|
return c == '\0' || c == ' ' || c == '-' || c == '>'
|
||||||
|
|| c == '<' || c == '/' || c == '\\';
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe string Substring(int offset, int len) {
|
||||||
|
if (len == 0) return "";
|
||||||
|
char* tmp = stackalloc char[len];
|
||||||
|
|
||||||
|
// convert %0-f to &0-f for colour preview.
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
tmp[i] = value[offset + i];
|
||||||
|
if (tmp[i] != '%' || (i + 1) >= len) continue;
|
||||||
|
if (IDrawer2D.ValidColCode(tmp[i + 1])) tmp[i] = '&';
|
||||||
|
}
|
||||||
|
return new String(tmp, 0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WordWrap(IDrawer2D drawer, string[] lines, int numLines, int lineLen) {
|
||||||
|
for (int i = 0; i < numLines; i++) { lines[i] = null; }
|
||||||
|
|
||||||
|
int lineStart = 0, lineEnd;
|
||||||
|
for (int i = 0; i < numLines; i++) {
|
||||||
|
int nextLineStart = lineStart + lineLen;
|
||||||
|
// No more text to wrap
|
||||||
|
if (nextLineStart >= Length) {
|
||||||
|
lines[i] = Substring(lineStart, Length - lineStart); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find beginning of last word on current line
|
||||||
|
for (lineEnd = nextLineStart; lineEnd >= lineStart; lineEnd--) {
|
||||||
|
if (IsWrapper(value[lineEnd])) break;
|
||||||
|
}
|
||||||
|
lineEnd++; // move after wrapper char (i.e. beginning of last word)
|
||||||
|
|
||||||
|
if (lineEnd <= lineStart || lineEnd >= nextLineStart) {
|
||||||
|
// Three special cases handled by this:
|
||||||
|
// - Entire line is filled with a single word
|
||||||
|
// - Last character(s) on current line are wrapper characters
|
||||||
|
// - First character on next line is a wrapper character (last word ends at current line end)
|
||||||
|
lines[i] = Substring(lineStart, lineLen);
|
||||||
|
lineStart += lineLen;
|
||||||
|
} else {
|
||||||
|
// Last word in current line does not end in current line (extends onto next line)
|
||||||
|
// Trim current line to end at beginning of last word
|
||||||
|
// Set next line to start at beginning of last word
|
||||||
|
lines[i] = Substring(lineStart, lineEnd - lineStart);
|
||||||
|
lineStart = lineEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,212 +0,0 @@
|
|||||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace ClassicalSharp {
|
|
||||||
|
|
||||||
public unsafe sealed class WrappableStringBuffer {
|
|
||||||
|
|
||||||
public int Capacity;
|
|
||||||
public char[] value;
|
|
||||||
char[] wrap;
|
|
||||||
|
|
||||||
public WrappableStringBuffer(int capacity) {
|
|
||||||
this.Capacity = capacity;
|
|
||||||
value = new char[capacity];
|
|
||||||
wrap = new char[capacity];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteAt(int index) {
|
|
||||||
for (int i = index; i < Capacity - 1; i++)
|
|
||||||
value[i] = value[i + 1];
|
|
||||||
value[Capacity - 1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InsertAt(int index, char c) {
|
|
||||||
for (int i = Capacity - 1; i > index; i--)
|
|
||||||
value[i] = value[i - 1];
|
|
||||||
value[index] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Set(string s) {
|
|
||||||
for (int i = 0; i < s.Length; i++) value[i] = s[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear() {
|
|
||||||
for (int i = 0; i < Capacity; i++) value[i] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Empty {
|
|
||||||
get {
|
|
||||||
for (int i = 0; i < Capacity; i++) {
|
|
||||||
if (value[i] != '\0') return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Length {
|
|
||||||
get {
|
|
||||||
int len = Capacity;
|
|
||||||
for (int i = Capacity - 1; i >= 0; i--) {
|
|
||||||
if (value[i] != '\0') break;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int TextLength {
|
|
||||||
get {
|
|
||||||
int len = Capacity;
|
|
||||||
for (int i = Capacity - 1; i >= 0; i--) {
|
|
||||||
if (value[i] != '\0' && value[i] != ' ') break;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() {
|
|
||||||
return new String(value, 0, Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -250,7 +250,6 @@
|
|||||||
<ClInclude Include="VertexStructs.h" />
|
<ClInclude Include="VertexStructs.h" />
|
||||||
<ClInclude Include="Widgets.h" />
|
<ClInclude Include="Widgets.h" />
|
||||||
<ClInclude Include="Window.h" />
|
<ClInclude Include="Window.h" />
|
||||||
<ClInclude Include="WordWrap.h" />
|
|
||||||
<ClInclude Include="World.h" />
|
<ClInclude Include="World.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -319,7 +318,6 @@
|
|||||||
<ClCompile Include="WinErrorHandler.c" />
|
<ClCompile Include="WinErrorHandler.c" />
|
||||||
<ClCompile Include="WinPlatform.c" />
|
<ClCompile Include="WinPlatform.c" />
|
||||||
<ClCompile Include="WinWindow.c" />
|
<ClCompile Include="WinWindow.c" />
|
||||||
<ClCompile Include="WordWrap.c" />
|
|
||||||
<ClCompile Include="World.c" />
|
<ClCompile Include="World.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
@ -315,9 +315,6 @@
|
|||||||
<ClInclude Include="Widgets.h">
|
<ClInclude Include="Widgets.h">
|
||||||
<Filter>Header Files\2D</Filter>
|
<Filter>Header Files\2D</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="WordWrap.h">
|
|
||||||
<Filter>Header Files\Utils</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Inventory.h">
|
<ClInclude Include="Inventory.h">
|
||||||
<Filter>Header Files\Game</Filter>
|
<Filter>Header Files\Game</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -521,9 +518,6 @@
|
|||||||
<ClCompile Include="Widgets.c">
|
<ClCompile Include="Widgets.c">
|
||||||
<Filter>Source Files\2D</Filter>
|
<Filter>Source Files\2D</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="WordWrap.c">
|
|
||||||
<Filter>Source Files\Utils</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Inventory.c">
|
<ClCompile Include="Inventory.c">
|
||||||
<Filter>Source Files\Game</Filter>
|
<Filter>Source Files\Game</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -11,6 +11,46 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
String text1 = String_FromConst("abcd");
|
||||||
|
String lines1[3] = { 0 };
|
||||||
|
WordWrap_Do(&text1, lines1, 3, 4);
|
||||||
|
|
||||||
|
String text2 = String_FromConst("abcde/fgh");
|
||||||
|
String lines2[3] = { 0 };
|
||||||
|
WordWrap_Do(&text2, lines2, 3, 4);
|
||||||
|
|
||||||
|
String text3 = String_FromConst("abc/defg");
|
||||||
|
String lines3[3] = { 0 };
|
||||||
|
WordWrap_Do(&text3, lines3, 3, 4);
|
||||||
|
|
||||||
|
String text4 = String_FromConst("ab/cdef");
|
||||||
|
String lines4[3] = { 0 };
|
||||||
|
WordWrap_Do(&text4, lines4, 3, 4);
|
||||||
|
|
||||||
|
String text5 = String_FromConst("abcd/efg");
|
||||||
|
String lines5[3] = { 0 };
|
||||||
|
WordWrap_Do(&text5, lines5, 3, 4);
|
||||||
|
|
||||||
|
String text6 = String_FromConst("abc/efg/hij/");
|
||||||
|
String lines6[3] = { 0 };
|
||||||
|
WordWrap_Do(&text6, lines6, 3, 4);
|
||||||
|
|
||||||
|
String text7 = String_FromConst("ab cde fgh");
|
||||||
|
String lines7[3] = { 0 };
|
||||||
|
WordWrap_Do(&text7, lines7, 3, 4);
|
||||||
|
|
||||||
|
String text8 = String_FromConst("ab//cd");
|
||||||
|
String lines8[3] = { 0 };
|
||||||
|
WordWrap_Do(&text8, lines8, 3, 4);
|
||||||
|
|
||||||
|
String text9 = String_FromConst("a///b");
|
||||||
|
String lines9[3] = { 0 };
|
||||||
|
WordWrap_Do(&text9, lines9, 3, 4);
|
||||||
|
|
||||||
|
String text10 = String_FromConst("/aaab");
|
||||||
|
String lines10[3] = { 0 };
|
||||||
|
WordWrap_Do(&text10, lines10, 3, 4);
|
||||||
|
|
||||||
ErrorHandler_Init("client.log");
|
ErrorHandler_Init("client.log");
|
||||||
Platform_Init();
|
Platform_Init();
|
||||||
|
|
||||||
|
@ -684,4 +684,99 @@ Int32 StringsBuffer_Compare(StringsBuffer* buffer, UInt32 idxA, UInt32 idxB) {
|
|||||||
String strA = StringsBuffer_UNSAFE_Get(buffer, idxA);
|
String strA = StringsBuffer_UNSAFE_Get(buffer, idxA);
|
||||||
String strB = StringsBuffer_UNSAFE_Get(buffer, idxB);
|
String strB = StringsBuffer_UNSAFE_Get(buffer, idxB);
|
||||||
return String_Compare(&strA, &strB);
|
return String_Compare(&strA, &strB);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool WordWrap_IsWrapper(UInt8 c) {
|
||||||
|
return c == NULL || c == ' ' || c == '-' || c == '>' || c == '<' || c == '/' || c == '\\';
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordWrap_Do(STRING_REF String* text, STRING_TRANSIENT String* lines, Int32 numLines, Int32 lineLen) {
|
||||||
|
Int32 i;
|
||||||
|
for (i = 0; i < numLines; i++) { lines[i] = String_MakeNull(); }
|
||||||
|
|
||||||
|
Int32 lineStart = 0, lineEnd;
|
||||||
|
for (i = 0; i < numLines; i++) {
|
||||||
|
Int32 nextLineStart = lineStart + lineLen;
|
||||||
|
/* No more text to wrap */
|
||||||
|
if (nextLineStart >= text->length) {
|
||||||
|
lines[i] = String_UNSAFE_SubstringAt(text, lineStart); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find beginning of last word on current line */
|
||||||
|
for (lineEnd = nextLineStart; lineEnd >= lineStart; lineEnd--) {
|
||||||
|
if (WordWrap_IsWrapper(text->buffer[lineEnd])) break;
|
||||||
|
}
|
||||||
|
lineEnd++; /* move after wrapper char (i.e. beginning of last word)*/
|
||||||
|
|
||||||
|
if (lineEnd <= lineStart || lineEnd >= nextLineStart) {
|
||||||
|
/* Three special cases handled by this: */
|
||||||
|
/* - Entire line is filled with a single word */
|
||||||
|
/* - Last character(s) on current line are wrapper characters */
|
||||||
|
/* - First character on next line is a wrapper character (last word ends at current line end) */
|
||||||
|
lines[i] = String_UNSAFE_Substring(text, lineStart, lineLen);
|
||||||
|
lineStart += lineLen;
|
||||||
|
} else {
|
||||||
|
/* Last word in current line does not end in current line (extends onto next line) */
|
||||||
|
/* Trim current line to end at beginning of last word */
|
||||||
|
/* Set next line to start at beginning of last word */
|
||||||
|
lines[i] = String_UNSAFE_Substring(text, lineStart, lineEnd - lineStart);
|
||||||
|
lineStart = lineEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculates where the given raw index is located in the wrapped lines. */
|
||||||
|
void WordWrap_GetCoords(Int32 index, STRING_PURE String* lines, Int32 numLines, Int32* coordX, Int32* coordY) {
|
||||||
|
if (index == -1) index = Int32_MaxValue;
|
||||||
|
Int32 offset = 0; *coordX = -1; *coordY = 0;
|
||||||
|
|
||||||
|
Int32 y;
|
||||||
|
for (y = 0; y < numLines; y++) {
|
||||||
|
Int32 lineLength = lines[y].length;
|
||||||
|
if (lineLength == 0) break;
|
||||||
|
|
||||||
|
*coordY = y;
|
||||||
|
if (index < offset + lineLength) {
|
||||||
|
*coordX = index - offset; break;
|
||||||
|
}
|
||||||
|
offset += lineLength;
|
||||||
|
}
|
||||||
|
if (*coordX == -1) *coordX = lines[*coordY].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Int32 WordWrap_GetBackLength(STRING_PURE String* text, Int32 index) {
|
||||||
|
if (index <= 0) return 0;
|
||||||
|
if (index >= text->length) {
|
||||||
|
ErrorHandler_Fail("WordWrap_GetBackLength - index past end of string");
|
||||||
|
}
|
||||||
|
|
||||||
|
Int32 start = index;
|
||||||
|
bool lookingSpace = text->buffer[index] == ' ';
|
||||||
|
/* go back to the end of the previous word */
|
||||||
|
if (lookingSpace) {
|
||||||
|
while (index > 0 && text->buffer[index] == ' ') { index--; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* go back to the start of the current word */
|
||||||
|
while (index > 0 && text->buffer[index] != ' ') { index--; }
|
||||||
|
return start - index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Int32 WordWrap_GetForwardLength(STRING_PURE String* text, Int32 index) {
|
||||||
|
if (index == -1) return 0;
|
||||||
|
if (index >= text->length) {
|
||||||
|
ErrorHandler_Fail("WordWrap_GetForwardLength - index past end of string");
|
||||||
|
}
|
||||||
|
|
||||||
|
Int32 start = index, length = text->length;
|
||||||
|
bool lookingLetter = text->buffer[index] != ' ';
|
||||||
|
/* go forward to the end of the current word */
|
||||||
|
if (lookingLetter) {
|
||||||
|
while (index < length && text->buffer[index] != ' ') { index++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* go forward to the start of the next word */
|
||||||
|
while (index < length && text->buffer[index] == ' ') { index++; }
|
||||||
|
return index - start;
|
||||||
}
|
}
|
@ -4,6 +4,7 @@
|
|||||||
/* Implements operations for a string.
|
/* Implements operations for a string.
|
||||||
Also implements conversions betweens strings and numbers.
|
Also implements conversions betweens strings and numbers.
|
||||||
Also implements converting code page 437 indices to/from unicode.
|
Also implements converting code page 437 indices to/from unicode.
|
||||||
|
Also implements wrapping a single line of text into multiple lines.
|
||||||
Copyright 2017 ClassicalSharp | Licensed under BSD-3
|
Copyright 2017 ClassicalSharp | Licensed under BSD-3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -120,4 +121,9 @@ void StringsBuffer_Resize(void** buffer, UInt32* elems, UInt32 elemSize, UInt32
|
|||||||
void StringsBuffer_Add(StringsBuffer* buffer, STRING_PURE String* text);
|
void StringsBuffer_Add(StringsBuffer* buffer, STRING_PURE String* text);
|
||||||
void StringsBuffer_Remove(StringsBuffer* buffer, UInt32 index);
|
void StringsBuffer_Remove(StringsBuffer* buffer, UInt32 index);
|
||||||
Int32 StringsBuffer_Compare(StringsBuffer* buffer, UInt32 idxA, UInt32 idxB);
|
Int32 StringsBuffer_Compare(StringsBuffer* buffer, UInt32 idxA, UInt32 idxB);
|
||||||
|
|
||||||
|
void WordWrap_Do(STRING_REF String* text, STRING_TRANSIENT String* lines, Int32 numLines, Int32 lineLen);
|
||||||
|
void WordWrap_GetCoords(Int32 index, STRING_PURE String* lines, Int32 numLines, Int32* coordX, Int32* coordY);
|
||||||
|
Int32 WordWrap_GetBackLength(STRING_PURE String* text, Int32 index);
|
||||||
|
Int32 WordWrap_GetForwardLength(STRING_PURE String* text, Int32 index);
|
||||||
#endif
|
#endif
|
@ -11,7 +11,6 @@
|
|||||||
#include "ModelCache.h"
|
#include "ModelCache.h"
|
||||||
#include "Screens.h"
|
#include "Screens.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "WordWrap.h"
|
|
||||||
#include "ServerConnection.h"
|
#include "ServerConnection.h"
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "Chat.h"
|
#include "Chat.h"
|
||||||
@ -1211,7 +1210,6 @@ void InputWidget_Init(GuiElement* elem) {
|
|||||||
InputWidget* widget = (InputWidget*)elem;
|
InputWidget* widget = (InputWidget*)elem;
|
||||||
Int32 lines = widget->GetMaxLines();
|
Int32 lines = widget->GetMaxLines();
|
||||||
if (lines > 1) {
|
if (lines > 1) {
|
||||||
/* TODO: Actually make this work */
|
|
||||||
WordWrap_Do(&widget->Text, widget->Lines, lines, INPUTWIDGET_LEN);
|
WordWrap_Do(&widget->Text, widget->Lines, lines, INPUTWIDGET_LEN);
|
||||||
} else {
|
} else {
|
||||||
widget->Lines[0] = widget->Text;
|
widget->Lines[0] = widget->Text;
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
#include "WordWrap.h"
|
|
||||||
#include "Drawer2D.h"
|
|
||||||
#include "Funcs.h"
|
|
||||||
#include "Platform.h"
|
|
||||||
#include "ErrorHandler.h"
|
|
||||||
|
|
||||||
#define WORDWRAP_MAX_LINES_TO_WRAP 128
|
|
||||||
#define WORDWRAP_MAX_BUFFER_SIZE 2048
|
|
||||||
|
|
||||||
void WordWrap_OutputLines(String* text, String* lines, Int32* lineLens, Int32 numLines, Int32 usedLines, Int32 charsPerLine) {
|
|
||||||
Int32 totalChars = charsPerLine * numLines, i, j;
|
|
||||||
for (i = 0; i < totalChars; i++) {
|
|
||||||
if (text->buffer[i] == NULL) text->buffer[i] = ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* convert %0-f to &0-f for colour preview. */
|
|
||||||
for (i = 0; i < totalChars - 1; i++) {
|
|
||||||
if (text->buffer[i] == '%' && Drawer2D_ValidColCode(text->buffer[i + 1])) {
|
|
||||||
text->buffer[i] = '&';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usedLines = max(1, usedLines);
|
|
||||||
for (i = 0; i < usedLines; i++) {
|
|
||||||
String* dst = &lines[i];
|
|
||||||
UInt8* src = &text->buffer[i * charsPerLine];
|
|
||||||
for (j = 0; j < lineLens[i]; j++) { String_Append(dst, src[j]); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WordWrap_IsWrapper(UInt8 c) {
|
|
||||||
return c == NULL || c == ' ' || c == '-' || c == '>'
|
|
||||||
|| c == '<' || c == '/' || c == '\\';
|
|
||||||
}
|
|
||||||
|
|
||||||
Int32 WordWrap_WrapLine(String* text, Int32 index, Int32 lineSize) {
|
|
||||||
Int32 lineEnd = index + (lineSize - 1), i, j;
|
|
||||||
/* wrap - but we don't want to wrap if the entire line is filled. */
|
|
||||||
for (i = lineEnd; i >= index + 1; i--) {
|
|
||||||
if (!WordWrap_IsWrapper(text->buffer[i])) continue;
|
|
||||||
|
|
||||||
for (j = lineEnd; j >= i + 1; j--) {
|
|
||||||
UInt8 c = text->buffer[j];
|
|
||||||
String_InsertAt(text, index + lineSize, c);
|
|
||||||
text->buffer[j] = ' ';
|
|
||||||
}
|
|
||||||
return (i + 1) - index;
|
|
||||||
}
|
|
||||||
return lineSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WordWrap_Do(STRING_TRANSIENT String* text, STRING_TRANSIENT String* lines, Int32 numLines, Int32 maxPerLine) {
|
|
||||||
Int32 len = text->length, i;
|
|
||||||
Int32 lineLens[WORDWRAP_MAX_LINES_TO_WRAP];
|
|
||||||
for (i = 0; i < numLines; i++) {
|
|
||||||
String_Clear(&lines[i]);
|
|
||||||
lineLens[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Need to make a copy because we mutate the characters. */
|
|
||||||
UInt8 copyBuffer[String_BufferSize(WORDWRAP_MAX_BUFFER_SIZE)];
|
|
||||||
String copy = String_InitAndClearArray(copyBuffer);
|
|
||||||
String_AppendString(©, text);
|
|
||||||
|
|
||||||
Int32 usedLines = 0, index, totalChars = maxPerLine * numLines;
|
|
||||||
for (index = 0; index < totalChars; index += maxPerLine) {
|
|
||||||
if (copy.buffer[index] == NULL) break;
|
|
||||||
usedLines++;
|
|
||||||
Int32 lineEnd = index + (maxPerLine - 1), nextStart = lineEnd + 1;
|
|
||||||
|
|
||||||
/* Do we need word wrapping? */
|
|
||||||
bool needWrap = !WordWrap_IsWrapper(copy.buffer[lineEnd])
|
|
||||||
&& nextStart < totalChars && !WordWrap_IsWrapper(copy.buffer[nextStart]);
|
|
||||||
Int32 wrappedLen = needWrap ? WordWrap_WrapLine(©, index, maxPerLine) : maxPerLine;
|
|
||||||
|
|
||||||
/* Calculate the maximum size of this line */
|
|
||||||
Int32 lineLen = maxPerLine, i;
|
|
||||||
for (i = lineEnd; i >= index; i--) {
|
|
||||||
if (copy.buffer[i] != NULL) break;
|
|
||||||
lineLen--;
|
|
||||||
}
|
|
||||||
lineLens[index / maxPerLine] = min(lineLen, wrappedLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Output the used lines */
|
|
||||||
WordWrap_OutputLines(©, lines, lineLens, numLines, usedLines, maxPerLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculates where the given raw index is located in the wrapped lines. */
|
|
||||||
void WordWrap_GetCoords(Int32 index, STRING_PURE String* lines, Int32 numLines, Int32* coordX, Int32* coordY) {
|
|
||||||
if (index == -1) index = Int32_MaxValue;
|
|
||||||
Int32 offset = 0; *coordX = -1; *coordY = 0;
|
|
||||||
|
|
||||||
for (Int32 y = 0; y < numLines; y++) {
|
|
||||||
Int32 lineLength = lines[y].length;
|
|
||||||
if (lineLength == 0) break;
|
|
||||||
|
|
||||||
*coordY = y;
|
|
||||||
if (index < offset + lineLength) {
|
|
||||||
*coordX = index - offset; break;
|
|
||||||
}
|
|
||||||
offset += lineLength;
|
|
||||||
}
|
|
||||||
if (*coordX == -1) *coordX = lines[*coordY].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
Int32 WordWrap_GetBackLength(STRING_PURE String* text, Int32 index) {
|
|
||||||
if (index <= 0) return 0;
|
|
||||||
if (index >= text->length) {
|
|
||||||
ErrorHandler_Fail("WordWrap_GetBackLength - index past end of string");
|
|
||||||
}
|
|
||||||
|
|
||||||
Int32 start = index;
|
|
||||||
bool lookingSpace = text->buffer[index] == ' ';
|
|
||||||
/* go back to the end of the previous word */
|
|
||||||
if (lookingSpace) {
|
|
||||||
while (index > 0 && text->buffer[index] == ' ') { index--; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* go back to the start of the current word */
|
|
||||||
while (index > 0 && text->buffer[index] != ' ') { index--; }
|
|
||||||
return start - index;
|
|
||||||
}
|
|
||||||
|
|
||||||
Int32 WordWrap_GetForwardLength(STRING_PURE String* text, Int32 index) {
|
|
||||||
if (index == -1) return 0;
|
|
||||||
if (index >= text->length) {
|
|
||||||
ErrorHandler_Fail("WordWrap_GetForwardLength - index past end of string");
|
|
||||||
}
|
|
||||||
|
|
||||||
Int32 start = index, length = text->length;
|
|
||||||
bool lookingLetter = text->buffer[index] != ' ';
|
|
||||||
/* go forward to the end of the current word */
|
|
||||||
if (lookingLetter) {
|
|
||||||
while (index < length && text->buffer[index] != ' ') { index++; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* go forward to the start of the next word */
|
|
||||||
while (index < length && text->buffer[index] == ' ') { index++; }
|
|
||||||
return index - start;
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#ifndef CC_WORDWRAP_H
|
|
||||||
#define CC_WORDWRAP_H
|
|
||||||
#include "String.h"
|
|
||||||
/* Allows wrapping a single line of text into multiple lines.
|
|
||||||
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
|
||||||
*/
|
|
||||||
|
|
||||||
void WordWrap_Do(STRING_TRANSIENT String* text, STRING_TRANSIENT String* lines, Int32 numLines, Int32 maxPerLine);
|
|
||||||
/* Calculates where the given raw index is located in the wrapped lines. */
|
|
||||||
void WordWrap_GetCoords(Int32 index, STRING_PURE String* lines, Int32 numLines, Int32* coordX, Int32* coordY);
|
|
||||||
Int32 WordWrap_GetBackLength(STRING_PURE String* text, Int32 index);
|
|
||||||
Int32 WordWrap_GetForwardLength(STRING_PURE String* text, Int32 index);
|
|
||||||
#endif
|
|
Loading…
x
Reference in New Issue
Block a user