mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-18 12:05:14 -04:00
Port InputWidget to C.
This commit is contained in:
parent
f8dd6b0e52
commit
39eed53aba
@ -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();
|
||||
}
|
||||
|
@ -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] = ' ';
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user