Port InputWidget to C.

This commit is contained in:
UnknownShadow200 2017-10-24 13:59:19 +11:00
parent f8dd6b0e52
commit 39eed53aba
17 changed files with 498 additions and 45 deletions

View File

@ -68,8 +68,9 @@ namespace ClassicalSharp.Gui.Widgets {
protected double caretAccumulator;
public override void Init() {
if (UsedLines > 1) {
Text.WordWrap(game.Drawer2D, lines, MaxCharsPerLine);
int numLines = UsedLines;
if (numLines > 1) {
Text.WordWrap(game.Drawer2D, lines, numLines, MaxCharsPerLine);
} else {
lines[0] = Text.ToString();
}

View File

@ -10,7 +10,7 @@ namespace ClassicalSharp {
wrap = new char[capacity];
}
public void WordWrap(IDrawer2D drawer, string[] lines, int maxPerLine) {
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++) {
@ -22,7 +22,7 @@ namespace ClassicalSharp {
char[] realText = value;
MakeWrapCopy();
int usedLines = 0, totalChars = maxPerLine * lines.Length;
int usedLines = 0, totalChars = maxPerLine * maxLines;
for (int index = 0; index < totalChars; index += maxPerLine) {
if (value[index] == '\0') break;
@ -44,7 +44,7 @@ namespace ClassicalSharp {
}
// Output the used lines
OutputLines(drawer, lines, lineLens, usedLines, maxPerLine);
OutputLines(drawer, lines, lineLens, usedLines, maxLines, maxPerLine);
value = realText;
}
@ -58,8 +58,8 @@ namespace ClassicalSharp {
value = wrap;
}
void OutputLines(IDrawer2D drawer, string[] lines, int* lineLens, int usedLines, int charsPerLine) {
int totalChars = charsPerLine * lines.Length;
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] = ' ';
}

View File

@ -10,6 +10,12 @@ void DrawTextArgs_Make(DrawTextArgs* args, STRING_REF String* text, FontDesc* fo
args->UseShadow = useShadow;
}
void DrawTextArgs_MakeEmpty(DrawTextArgs* args, FontDesc* font, bool useShadow) {
args->Text = String_MakeNull();
args->Font = *font;
args->UseShadow = useShadow;
}
Bitmap Drawer2D_FontBitmap;
Bitmap* Drawer2D_Cur;
Int32 Drawer2D_BoxSize;
@ -269,7 +275,7 @@ void Drawer2D_DrawPart(DrawTextArgs* args, Int32 x, Int32 y, bool shadowCol) {
coordsPtr[0] = (UInt8)coords; runCount = 1;
continue;
}
if (lastY == (coords >> 4) && col.Packed == lastCol.Packed) {
if (lastY == (coords >> 4) && PackedCol_Equals(col, lastCol)) {
coordsPtr[runCount] = (UInt8)coords; runCount++;
continue;
}

View File

@ -13,6 +13,7 @@ Copyright 2017 ClassicalSharp | Licensed under BSD-3
typedef struct DrawTextArgs_ { String Text; FontDesc Font; bool UseShadow; } DrawTextArgs;
void DrawTextArgs_Make(DrawTextArgs* args, STRING_REF String* text, FontDesc* font, bool useShadow);
void DrawTextArgs_MakeEmpty(DrawTextArgs* args, FontDesc* font, bool useShadow);
const float Offset = 1.3f;

View File

@ -104,9 +104,9 @@ void IModel_SetupState(IModel* model, Entity* entity) {
IModel_Cols[0] = col;
if (!entity->NoShade) {
IModel_Cols[1] = PackedCol_Scale(col, PackedCol_ShadeYBottom);
IModel_Cols[2] = PackedCol_Scale(col, PackedCol_ShadeZ);
IModel_Cols[4] = PackedCol_Scale(col, PackedCol_ShadeX);
IModel_Cols[1] = PackedCol_Scale(col, PACKEDCOL_SHADE_YMIN);
IModel_Cols[2] = PackedCol_Scale(col, PACKEDCOL_SHADE_Z);
IModel_Cols[4] = PackedCol_Scale(col, PACKEDCOL_SHADE_X);
} else {
IModel_Cols[1] = col; IModel_Cols[2] = col; IModel_Cols[4] = col;
}

View File

@ -83,6 +83,7 @@ void Key_SetPressed(Key key, bool pressed);
#define Key_IsAltPressed() (Key_GetPressed(Key_AltLeft) || Key_GetPressed(Key_AltRight))
#define Key_IsControlPressed() (Key_GetPressed(Key_ControlLeft) || Key_GetPressed(Key_ControlRight))
#define Key_IsShiftPressed() (Key_GetPressed(Key_ShiftLeft) || Key_GetPressed(Key_ShiftRight))
#define Key_IsWinPressed() (Key_GetPressed(Key_WinLeft) || Key_GetPressed(Key_WinRight))
/* Gets whether key repeating is on or not. If on (desirable for text input), multiple KeyDowns (varies by OS)
are generated for the same key when it is held down for a period of time. Should be off for game input. */

View File

@ -31,8 +31,8 @@ PackedCol PackedCol_Lerp(PackedCol a, PackedCol b, Real32 t) {
return a;
}
void PackedCol_GetShaded(PackedCol normal, PackedCol* xSide, PackedCol* zSide, PackedCol* yBottom) {
*xSide = PackedCol_Scale(normal, PackedCol_ShadeX);
*zSide = PackedCol_Scale(normal, PackedCol_ShadeZ);
*yBottom = PackedCol_Scale(normal, PackedCol_ShadeYBottom);
void PackedCol_GetShaded(PackedCol normal, PackedCol* xSide, PackedCol* zSide, PackedCol* yMin) {
*xSide = PackedCol_Scale(normal, PACKEDCOL_SHADE_X);
*zSide = PackedCol_Scale(normal, PACKEDCOL_SHADE_Z);
*yMin = PackedCol_Scale(normal, PACKEDCOL_SHADE_YMIN);
}

View File

@ -29,7 +29,7 @@ PackedCol PackedCol_Create4(UInt8 r, UInt8 g, UInt8 b, UInt8 a);
/* Constructs a new ARGB colour. */
PackedCol PackedCol_Create3(UInt8 r, UInt8 g, UInt8 b);
/* Returns whether two packed colours are equal. */
#define PackedCol_Equals(a, b) (a.Packed == b.Packed)
#define PackedCol_Equals(a, b) ((a).Packed == (b).Packed)
/* Converts a colour to ARGB form. */
#define PackedCol_ARGB(r, g, b, a) (((UInt32)(r) << 16) | ((UInt32)(g) << 8) | ((UInt32)(b)) | ((UInt32)(a) << 24))
/* Converts a colour to ARGB form. */
@ -39,15 +39,18 @@ PackedCol PackedCol_Scale(PackedCol value, Real32 t);
/* Linearly interpolates the RGB components of both colours by t, where t is in [0, 1] */
PackedCol PackedCol_Lerp(PackedCol a, PackedCol b, Real32 t);
#define PackedCol_ShadeX 0.6f
#define PackedCol_ShadeZ 0.8f
#define PackedCol_ShadeYBottom 0.5f
#define PACKEDCOL_SHADE_X 0.6f
#define PACKEDCOL_SHADE_Z 0.8f
#define PACKEDCOL_SHADE_YMIN 0.5f
/* Retrieves shaded colours for ambient block face lighting. */
void PackedCol_GetShaded(PackedCol normal, PackedCol* xSide, PackedCol* zSide, PackedCol* yBottom);
void PackedCol_GetShaded(PackedCol normal, PackedCol* xSide, PackedCol* zSide, PackedCol* yMin);
#define PACKEDCOL_WHITE PACKEDCOL_CONST(255, 255, 255, 255)
#define PACKEDCOL_BLACK PACKEDCOL_CONST( 0, 0, 0, 255)
#define PACKEDCOL_RED PACKEDCOL_CONST(255, 0, 0, 255)
#define PACKEDCOL_GREEN PACKEDCOL_CONST( 0, 255, 0, 255)
#define PACKEDCOL_BLUE PACKEDCOL_CONST( 0, 0, 255, 255)
#define PACKEDCOL_WHITE PACKEDCOL_CONST(255, 255, 255, 255)
#define PACKEDCOL_BLACK PACKEDCOL_CONST( 0, 0, 0, 255)
#define PACKEDCOL_RED PACKEDCOL_CONST(255, 0, 0, 255)
#define PACKEDCOL_GREEN PACKEDCOL_CONST( 0, 255, 0, 255)
#define PACKEDCOL_BLUE PACKEDCOL_CONST( 0, 0, 255, 255)
#define PACKEDCOL_YELLOW PACKEDCOL_CONST(255, 255, 0, 255);
#define PACKEDCOL_MAGENTA PACKEDCOL_CONST(255, 0, 255, 255);
#define PACKEDCOL_CYAN PACKEDCOL_CONST( 0, 255, 255, 255);
#endif

View File

@ -188,7 +188,7 @@ UInt8 String_CharAt(STRING_TRANSIENT String* str, Int32 offset) {
return str->buffer[offset];
}
void String_InsertAt(STRING_TRANSIENT String* str, UInt8 c, Int32 offset) {
void String_InsertAt(STRING_TRANSIENT String* str, Int32 offset, UInt8 c) {
if (offset < 0 || offset > str->length) {
ErrorHandler_Fail("Offset for InsertAt out of range");
}

View File

@ -64,7 +64,7 @@ Int32 String_LastIndexOf(STRING_TRANSIENT String* str, UInt8 c);
/* Gets the character at the given index in the string. */
UInt8 String_CharAt(STRING_TRANSIENT String* str, Int32 offset);
/* Inserts a character at the given index in the string. */
void String_InsertAt(STRING_TRANSIENT String* str, UInt8 c, Int32 offset);
void String_InsertAt(STRING_TRANSIENT String* str, Int32 offset, UInt8 c);
/* Deletes a character at the given index in the string. */
void String_DeleteAt(STRING_TRANSIENT String* str, Int32 offset);

View File

@ -16,4 +16,8 @@ UInt32 Utils_ParseEnum(STRING_TRANSIENT String* text, UInt32 defValue, const UIn
if (String_CaselessEquals(text, &name)) return i;
}
return defValue;
}
bool Utils_IsValidInputChar(UInt8 c, bool supportsCP437) {
return supportsCP437 || (Convert_CP437ToUnicode(c) == c);
}

View File

@ -7,5 +7,7 @@ Int32 Utils_AccumulateWheelDelta(Real32* accmulator, Real32 delta);
UInt32 Utils_ParseEnum(STRING_TRANSIENT String* text, UInt32 defValue, const UInt8** names, UInt32 namesCount);
bool Utils_IsValidInputChar(UInt8 c, bool supportsCP437);
#define Utils_AdjViewDist(value) ((Int32)(1.4142135f * (value)))
#endif

View File

@ -10,6 +10,8 @@
#include "Utils.h"
#include "ModelCache.h"
#include "Screens.h"
#include "Platform.h"
#include "WordWrap.h"
void Widget_SetLocation(Widget* widget, Anchor horAnchor, Anchor verAnchor, Int32 xOffset, Int32 yOffset) {
widget->HorAnchor = horAnchor; widget->VerAnchor = verAnchor;
@ -878,7 +880,7 @@ void SpecialInputWidget_IntersectsBody(SpecialInputWidget* widget, Int32 x, Int3
SpecialInputAppendFunc append = widget->AppendFunc;
if (widget->SelectedIndex == 0) {
/* TODO: need to insert characters that don't affect caret index, adjust caret colour */
/* TODO: need to insert characters that don't affect widget->CaretPos index, adjust widget->CaretPos colour */
append(widget->AppendObj, e.Contents.buffer[index * e.CharsPerItem]);
append(widget->AppendObj, e.Contents.buffer[index * e.CharsPerItem + 1]);
} else {
@ -921,8 +923,7 @@ void SpecialInputWidget_InitTabs(SpecialInputWidget* widget) {
#define SPECIAL_CONTENT_SPACING 5
Int32 SpecialInputWidget_MeasureTitles(SpecialInputWidget* widget) {
Int32 totalWidth = 0;
String str = String_MakeNull();
DrawTextArgs args; DrawTextArgs_Make(&args, &str, &widget->Font, false);
DrawTextArgs args; DrawTextArgs_MakeEmpty(&args, &widget->Font, false);
Int32 i;
for (i = 0; i < Array_NumElements(widget->Tabs); i++) {
@ -936,8 +937,7 @@ Int32 SpecialInputWidget_MeasureTitles(SpecialInputWidget* widget) {
void SpecialInputWidget_DrawTitles(SpecialInputWidget* widget) {
Int32 x = 0;
String str = String_MakeNull();
DrawTextArgs args; DrawTextArgs_Make(&args, &str, &widget->Font, false);
DrawTextArgs args; DrawTextArgs_MakeEmpty(&args, &widget->Font, false);
Int32 i;
PackedCol col_selected = PACKEDCOL_CONST(30, 30, 30, 200);
@ -1079,4 +1079,438 @@ void SpecialInputWidget_Create(SpecialInputWidget* widget, FontDesc* font, Speci
widget->Base.Base.Render = SpecialInputWidget_Render;
widget->Base.Base.Free = SpecialInputWidget_Free;
widget->Base.Base.HandlesMouseDown = SpecialInputWidget_HandlesMouseDown;
}
void InputWidget_CalculateLineSizes(InputWidget* widget) {
Int32 y;
for (y = 0; y < INPUTWIDGET_MAX_LINES; y++) {
widget->LineSizes[y] = Size2D_Empty;
}
widget->LineSizes[0].Width = widget->PrefixWidth;
DrawTextArgs args; DrawTextArgs_MakeEmpty(&args, &widget->Font, true);
for (y = 0; y < widget->GetMaxLines(); y++) {
args.Text = widget->Lines[y];
Size2D textSize = Drawer2D_MeasureText(&args);
widget->LineSizes[y].Width += textSize.Width;
widget->LineSizes[y].Height = textSize.Height;
}
if (widget->LineSizes[0].Height == 0) {
widget->LineSizes[0].Height = widget->PrefixHeight;
}
}
UInt8 InputWidget_GetLastCol(InputWidget* widget, Int32 indexX, Int32 indexY) {
Int32 x = indexX, y;
for (y = indexY; y >= 0; y--) {
UInt8 code = Drawer2D_LastCol(&widget->Lines[y], x);
if (code != NULL) return code;
if (y > 0) { x = widget->Lines[y - 1].length; }
}
return NULL;
}
void InputWidget_UpdateCaret(InputWidget* widget) {
Int32 maxChars = widget->GetMaxLines() * widget->MaxCharsPerLine;
if (widget->CaretPos >= maxChars) widget->CaretPos = -1;
WordWrap_GetCoords(widget->CaretPos, widget->Lines, widget->GetMaxLines(), &widget->CaretX, &widget->CaretY);
DrawTextArgs args; DrawTextArgs_MakeEmpty(&args, &widget->Font, false);
widget->CaretAccumulator = 0;
/* Caret is at last character on line */
Widget* elem = &widget->Base;
if (widget->CaretX == widget->MaxCharsPerLine) {
widget->CaretTex.X = elem->X + widget->Padding + widget->LineSizes[widget->CaretY].Width;
PackedCol yellow = PACKEDCOL_YELLOW; widget->CaretCol = yellow;
widget->CaretTex.Width = widget->CaretWidth;
} else {
String* line = &widget->Lines[widget->CaretY];
args.Text = String_UNSAFE_Substring(line, 0, widget->CaretX);
Size2D trimmedSize = Drawer2D_MeasureText(&args);
if (widget->CaretY == 0) { trimmedSize.Width += widget->PrefixWidth; }
widget->CaretTex.X = elem->X + widget->Padding + trimmedSize.Width;
PackedCol white = PACKEDCOL_WHITE;
widget->CaretCol = PackedCol_Scale(white, 0.8f);
if (widget->CaretX < line->length) {
args.Text = String_UNSAFE_Substring(line, widget->CaretX, 1);
args.UseShadow = true;
widget->CaretTex.Width = (UInt16)Drawer2D_MeasureText(&args).Width;
} else {
widget->CaretTex.Width = widget->CaretWidth;
}
}
widget->CaretTex.Y = widget->LineSizes[0].Height * widget->CaretY + widget->InputTex.Y + 2;
/* Update the colour of the widget->CaretPos */
UInt8 code = InputWidget_GetLastCol(widget, widget->CaretX, widget->CaretY);
if (code != NULL) widget->CaretCol = Drawer2D_Cols[code];
}
void InputWidget_RenderCaret(InputWidget* widget, Real64 delta) {
if (!widget->ShowCaret) return;
widget->CaretAccumulator += delta;
Real64 second = widget->CaretAccumulator - (Real64)Math_Floor((Real32)widget->CaretAccumulator);
if (second < 0.5) {
GfxCommon_Draw2DTexture(&widget->CaretTex, widget->CaretCol);
}
}
void InputWidget_RemakeTexture(InputWidget* widget) {
Int32 totalHeight = 0, maxWidth = 0, i;
for (i = 0; i < widget->GetMaxLines(); i++) {
totalHeight += widget->LineSizes[i].Height;
maxWidth = max(maxWidth, widget->LineSizes[i].Width);
}
Size2D size = Size2D_Make(maxWidth, totalHeight);
widget->CaretAccumulator = 0;
Int32 realHeight = 0;
Bitmap bmp; Bitmap_AllocatePow2(&bmp, size.Width, size.Height);
Drawer2D_Begin(&bmp);
DrawTextArgs args; DrawTextArgs_MakeEmpty(&args, &widget->Font, true);
if (widget->Prefix.length > 0) {
args.Text = widget->Prefix;
Drawer2D_DrawText(&args, 0, 0);
}
UInt8 tmpBuffer[String_BufferSize(STRING_SIZE + 2)];
for (i = 0; i < Array_NumElements(widget->Lines); i++) {
if (widget->Lines[i].length == 0) break;
args.Text = widget->Lines[i];
UInt8 lastCol = InputWidget_GetLastCol(widget, 0, i);
/* Colour code goes to next line */
if (!Drawer2D_IsWhiteCol(lastCol)) {
String tmp = String_FromRawBuffer(tmpBuffer, STRING_SIZE + 2);
String_Append(&tmp, '&'); String_Append(&tmp, lastCol);
String_AppendString(&tmp, &args.Text);
args.Text = tmp;
}
Int32 offset = i == 0 ? widget->PrefixWidth : 0;
Drawer2D_DrawText(&args, offset, realHeight);
realHeight += widget->LineSizes[i].Height;
}
widget->InputTex = Drawer2D_Make2DTexture(&bmp, size, 0, 0);
Drawer2D_End();
Platform_MemFree(bmp.Scan0);
Widget* elem = &widget->Base;
elem->Width = size.Width;
elem->Height = realHeight == 0 ? widget->PrefixHeight : realHeight;
elem->Reposition(elem);
widget->InputTex.X = elem->X + widget->Padding;
widget->InputTex.Y = elem->Y;
}
void InputWidget_EnterInput(InputWidget* widget) {
InputWidget_Clear(widget);
widget->Base.Height = widget->PrefixHeight;
}
void InputWidget_Clear(InputWidget* widget) {
String_Clear(&widget->Text);
Int32 i;
for (i = 0; i < Array_NumElements(widget->Lines); i++) {
String_Clear(&widget->Lines[i]);
}
widget->CaretPos = -1;
Gfx_DeleteTexture(&widget->InputTex.ID);
}
bool InputWidget_AllowedChar(InputWidget* widget, UInt8 c) {
return Utils_IsValidInputChar(c, game.Server.SupportsFullCP437);
}
void InputWidget_AppendChar(InputWidget* widget, UInt8 c) {
if (widget->CaretPos == -1) {
String_InsertAt(&widget->Text, widget->Text.length, c);
} else {
String_InsertAt(&widget->Text, widget->CaretPos, c);
widget->CaretPos++;
if (widget->CaretPos >= widget->Text.length) { widget->CaretPos = -1; }
}
}
bool InputWidget_TryAppendChar(InputWidget* widget, UInt8 c) {
Int32 maxChars = widget->GetMaxLines() * widget->MaxCharsPerLine;
if (widget->Text.length >= maxChars) return false;
if (!InputWidget_AllowedChar(widget, c)) return false;
InputWidget_AppendChar(widget, c);
return true;
}
void InputWidget_AppendString(InputWidget* widget, String text) {
Int32 appended = 0, i;
for (i = 0; i < text.length; i++) {
if (InputWidget_TryAppendChar(widget, text.buffer[i])) appended++;
}
if (appended == 0) return;
GuiElement* elem = &widget->Base.Base;
elem->Recreate(elem);
}
void InputWidget_Append(InputWidget* widget, UInt8 c) {
if (!InputWidget_TryAppendChar(widget, c)) return;
GuiElement* elem = &widget->Base.Base;
elem->Recreate(elem);
}
void InputWidget_DeleteChar(InputWidget* widget) {
if (widget->Text.length == 0) return;
if (widget->CaretPos == -1) {
String_DeleteAt(&widget->Text, widget->Text.length - 1);
} else if (widget->CaretPos > 0) {
widget->CaretPos--;
String_DeleteAt(&widget->Text, widget->CaretPos);
}
}
bool InputWidget_CheckCol(InputWidget* widget, Int32 index) {
if (index < 0) return false;
UInt8 code = widget->Text.buffer[index];
UInt8 col = widget->Text.buffer[index + 1];
return (code == '%' || code == '&') && Drawer2D_ValidColCode(col);
}
void InputWidget_BackspaceKey(InputWidget* widget, bool controlDown) {
if (controlDown) {
if (widget->CaretPos == -1) { widget->CaretPos = widget->Text.length - 1; }
Int32 len = WordWrap_GetBackLength(&widget->Text, widget->CaretPos);
if (len == 0) return;
widget->CaretPos -= len;
if (widget->CaretPos < 0) { widget->CaretPos = 0; }
Int32 i;
for (i = 0; i <= len; i++) {
String_DeleteAt(&widget->Text, widget->CaretPos);
}
if (widget->CaretPos >= widget->Text.length) { widget->CaretPos = -1; }
if (widget->CaretPos == -1 && widget->Text.length > 0) {
String_InsertAt(&widget->Text, widget->Text.length, ' ');
} else if (widget->CaretPos >= 0 && widget->Text.buffer[widget->CaretPos] != ' ') {
String_InsertAt(&widget->Text, widget->CaretPos, ' ');
}
GuiElement* elem = &widget->Base.Base;
elem->Recreate(elem);
} else if (widget->Text.length > 0 && widget->CaretPos != 0) {
Int32 index = widget->CaretPos == -1 ? widget->Text.length - 1 : widget->CaretPos;
if (InputWidget_CheckCol(widget, index - 1)) {
InputWidget_DeleteChar(widget); /* backspace XYZ%e to XYZ */
} else if (InputWidget_CheckCol(widget, index - 2)) {
InputWidget_DeleteChar(widget);
InputWidget_DeleteChar(widget); /* backspace XYZ%eH to XYZ */
}
InputWidget_DeleteChar(widget);
GuiElement* elem = &widget->Base.Base;
elem->Recreate(elem);
}
}
void InputWidget_DeleteKey(InputWidget* widget) {
if (widget->Text.length > 0 && widget->CaretPos != -1) {
String_DeleteAt(&widget->Text, widget->CaretPos);
if (widget->CaretPos >= widget->Text.length) { widget->CaretPos = -1; }
GuiElement* elem = &widget->Base.Base;
elem->Recreate(elem);
}
}
void InputWidget_LeftKey(InputWidget* widget, bool controlDown) {
if (controlDown) {
if (widget->CaretPos == -1) { widget->CaretPos = widget->Text.length - 1; }
widget->CaretPos -= WordWrap_GetBackLength(&widget->Text, widget->CaretPos);
InputWidget_UpdateCaret(widget);
return;
}
if (widget->Text.length > 0) {
if (widget->CaretPos == -1) { widget->CaretPos = widget->Text.length; }
widget->CaretPos--;
if (widget->CaretPos < 0) { widget->CaretPos = 0; }
InputWidget_UpdateCaret(widget);
}
}
void InputWidget_RightKey(InputWidget* widget, bool controlDown) {
if (controlDown) {
widget->CaretPos += WordWrap_GetForwardLength(&widget->Text, widget->CaretPos);
if (widget->CaretPos >= widget->Text.length) { widget->CaretPos = -1; }
InputWidget_UpdateCaret(widget);
return;
}
if (widget->Text.length > 0 && widget->CaretPos != -1) {
widget->CaretPos++;
if (widget->CaretPos >= widget->Text.length) { widget->CaretPos = -1; }
InputWidget_UpdateCaret(widget);
}
}
void InputWidget_HomeKey(InputWidget* widget) {
if (widget->Text.length == 0) return;
widget->CaretPos = 0;
InputWidget_UpdateCaret(widget);
}
void InputWidget_EndKey(InputWidget* widget) {
widget->CaretPos = -1;
InputWidget_UpdateCaret(widget);
}
bool InputWidget_OtherKey(InputWidget* widget, Key key) {
Int32 maxChars = widget->GetMaxLines() * widget->MaxCharsPerLine;
if (key == Key_V && widget->Text.length < maxChars) {
UInt8 textBuffer[String_BufferSize(INPUTWIDGET_MAX_LINES * STRING_SIZE)];
String text = String_FromRawBuffer(textBuffer, INPUTWIDGET_MAX_LINES * STRING_SIZE);
Window_GetClipboardText(&text);
if (text.length == 0) return true;
InputWidget_AppendString(widget, text);
return true;
} else if (key == Key_C) {
if (widget->Text.length == 0) return true;
Window_SetClipboardText(&widget->Text);
return true;
}
return false;
}
void InputWidget_Init(GuiElement* elem) {
InputWidget* widget = (InputWidget*)elem;
Int32 lines = widget->GetMaxLines();
if (lines > 1) {
WordWrap_Do(&widget->Text, widget->Lines, lines, widget->MaxCharsPerLine);
} else {
String_Clear(&widget->Lines[0]);
String_AppendString(&widget->Lines[0], &widget->Text);
}
InputWidget_CalculateLineSizes(widget);
InputWidget_RemakeTexture(widget);
InputWidget_UpdateCaret(widget);
}
void InputWidget_Free(GuiElement* elem) {
InputWidget* widget = (InputWidget*)elem;
Gfx_DeleteTexture(&widget->InputTex.ID);
Gfx_DeleteTexture(&widget->CaretTex.ID);
Gfx_DeleteTexture(&widget->PrefixTex.ID);
}
void InputWidget_Recreate(GuiElement* elem) {
InputWidget* widget = (InputWidget*)elem;
Gfx_DeleteTexture(&widget->InputTex.ID);
InputWidget_Init(elem);
}
void InputWidget_Reposition(Widget* elem) {
Int32 oldX = elem->X, oldY = elem->Y;
Widget_DoReposition(elem);
InputWidget* widget = (InputWidget*)elem;
widget->CaretTex.X += elem->X - oldX; widget->CaretTex.Y += elem->Y - oldY;
widget->InputTex.X += elem->X - oldX; widget->InputTex.Y += elem->Y - oldY;
}
bool InputWidget_HandlesKeyDown(GuiElement* elem, Key key) {
#if CC_BUILD_OSX
bool clipboardDown = Key_IsWinPressed();
#else
bool clipboardDown = Key_IsControlPressed();
#endif
InputWidget* widget = (InputWidget*)elem;
if (key == Key_Left) {
InputWidget_LeftKey(widget, clipboardDown);
} else if (key == Key_Right) {
InputWidget_RightKey(widget, clipboardDown);
} else if (key == Key_BackSpace) {
InputWidget_BackspaceKey(widget, clipboardDown);
} else if (key == Key_Delete) {
InputWidget_DeleteKey(widget);
} else if (key == Key_Home) {
InputWidget_HomeKey(widget);
} else if (key == Key_End) {
InputWidget_EndKey(widget);
} else if (clipboardDown && !InputWidget_OtherKey(widget, key)) {
return false;
}
return true;
}
bool InputWidget_HandlesKeyUp(GuiElement* elem, Key key) { return true; }
bool InputWidget_HandlesKeyPress(GuiElement* elem, UInt8 key) {
InputWidget* widget = (InputWidget*)elem;
InputWidget_AppendChar(widget, key);
return true;
}
bool InputWidget_HandlesMouseDown(GuiElement* elem, Int32 x, Int32 y, MouseButton button) {
InputWidget* widget = (InputWidget*)elem;
if (button == MouseButton_Left) {
x -= widget->InputTex.X; y -= widget->InputTex.Y;
DrawTextArgs args; DrawTextArgs_MakeEmpty(&args, &widget->Font, true);
Int32 offset = 0, charHeight = widget->CaretHeight;
Int32 charX, i;
for (i = 0; i < widget->GetMaxLines(); i++) {
String* line = &widget->Lines[i];
Int32 xOffset = i == 0 ? widget->PrefixWidth : 0;
if (line->length == 0) continue;
for (charX = 0; charX < line->length; charX++) {
args.Text = String_UNSAFE_Substring(line, 0, charX);
Int32 charOffset = Drawer2D_MeasureText(&args).Width + xOffset;
args.Text = String_UNSAFE_Substring(line, charX, 1);
Int32 charWidth = Drawer2D_MeasureText(&args).Width;
if (Gui_Contains(charOffset, i * charHeight, charWidth, charHeight, x, y)) {
widget->CaretPos = offset + charX;
InputWidget_UpdateCaret(widget);
return true;
}
}
offset += line->length;
}
widget->CaretPos = -1;
InputWidget_UpdateCaret(widget);
}
return true;
}
void InputWidget_Create(InputWidget* widget, FontDesc* font, STRING_REF String* prefix) {
Widget_Init(&widget->Base);
widget->Font = *font;
widget->Prefix = *prefix;
widget->CaretPos = -1;
widget->MaxCharsPerLine = STRING_SIZE;
String caret = String_FromConstant("_");
DrawTextArgs args; DrawTextArgs_Make(&args, &caret, font, true);
widget->CaretTex = Drawer2D_MakeTextTexture(&args, 0, 0);
widget->CaretTex.Width = (UInt16)((widget->CaretTex.Width * 3) / 4);
widget->CaretWidth = (UInt16)widget->CaretTex.Width;
widget->CaretHeight = (UInt16)widget->CaretTex.Height;
if (prefix->length == 0) return;
DrawTextArgs_Make(&args, prefix, font, true);
Size2D size = Drawer2D_MeasureText(&args);
widget->PrefixWidth = (UInt16)size.Width; widget->Base.Width = size.Width;
widget->PrefixHeight = (UInt16)size.Height; widget->Base.Height = size.Height;
}

View File

@ -124,8 +124,8 @@ struct InputWidget_;
typedef struct InputWidget_ {
Widget Base;
FontDesc Font;
Int32 (*GetMaxLines)(GuiElement* elem);
Int32 Padding;
Int32 (*GetMaxLines)(void);
Int32 Padding, MaxCharsPerLine;
void (*RemakeTexture)(GuiElement* elem); /* Remakes the raw texture containing all the chat lines. Also updates dimensions. */
void (*OnPressedEnter)(GuiElement* elem); /* Invoked when the user presses enter. */
@ -134,11 +134,11 @@ typedef struct InputWidget_ {
Size2D LineSizes[INPUTWIDGET_MAX_LINES]; /* size of each line in pixels */
Texture InputTex;
String Prefix;
Int32 PrefixWidth, PrefixHeight;
UInt16 PrefixWidth, PrefixHeight;
Texture PrefixTex;
Int32 CaretX, CaretY; /* Coordinates of caret in lines */
Int32 CaretWidth, CaretHeight;
UInt16 CaretWidth, CaretHeight;
Int32 CaretPos; /* Position of caret, -1 for at end of string. */
bool ShowCaret;
PackedCol CaretCol;

View File

@ -473,6 +473,7 @@ void Window_GetClipboardText(STRING_TRANSIENT String* value) {
if (hGlobal == NULL) { CloseClipboard(); return; }
LPVOID src = GlobalLock(hGlobal);
/* TODO: Trim space / tabs from start and end of clipboard text */
if (isUnicode) {
UInt16* text = (UInt16*)src;
while (*text != NULL) {

View File

@ -3,7 +3,7 @@
#include "Funcs.h"
#include "Platform.h"
void WordWrap_OutputLines(String* text, String** lines, Int32* lineLens, Int32 numLines, Int32 usedLines, Int32 charsPerLine) {
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] = ' ';
@ -18,7 +18,7 @@ void WordWrap_OutputLines(String* text, String** lines, Int32* lineLens, Int32 n
usedLines = max(1, usedLines);
for (i = 0; i < usedLines; i++) {
String* dst = lines[i];
String* dst = &lines[i];
UInt8* src = &text->buffer[i * charsPerLine];
for (j = 0; j < lineLens[i]; j++) { String_Append(dst, src[j]); }
}
@ -45,11 +45,11 @@ Int32 WordWrap_WrapLine(String* text, Int32 index, Int32 lineSize) {
return lineSize;
}
void WordWrap_Do(STRING_TRANSIENT String* text, STRING_TRANSIENT String** lines, Int32 numLines, Int32 maxPerLine) {
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]);
String_Clear(&lines[i]);
lineLens[i] = 0;
}
@ -83,12 +83,12 @@ void WordWrap_Do(STRING_TRANSIENT String* text, STRING_TRANSIENT String** lines,
}
/* Calculates where the given raw index is located in the wrapped lines. */
void WordWrap_GetCoords(Int32 index, STRING_TRANSIENT String** lines, Int32 numLines, Int32* coordX, Int32* coordY) {
void WordWrap_GetCoords(Int32 index, STRING_TRANSIENT 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;
Int32 lineLength = lines[y].length;
if (lineLength == 0) break;
*coordY = y;
@ -97,7 +97,7 @@ void WordWrap_GetCoords(Int32 index, STRING_TRANSIENT String** lines, Int32 numL
}
offset += lineLength;
}
if (*coordX == -1) *coordX = lines[*coordY]->length;
if (*coordX == -1) *coordX = lines[*coordY].length;
}
Int32 WordWrap_GetBackLength(STRING_TRANSIENT String* text, Int32 index) {

View File

@ -8,9 +8,9 @@
#define WORDWRAP_MAX_LINES_TO_WRAP 128
#define WORDWRAP_MAX_BUFFER_SIZE 2048
void WordWrap_Do(STRING_TRANSIENT String* text, STRING_TRANSIENT String** lines, Int32 numLines, Int32 maxPerLine);
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_TRANSIENT String** lines, Int32 numLines, Int32* coordX, Int32* coordY);
void WordWrap_GetCoords(Int32 index, STRING_TRANSIENT String* lines, Int32 numLines, Int32* coordX, Int32* coordY);
Int32 WordWrap_GetBackLength(STRING_TRANSIENT String* text, Int32 index);
Int32 WordWrap_GetForwardLength(STRING_TRANSIENT String* text, Int32 index);
#endif