From 276bd21b6592f0b163ce23307a3dd481df276ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Sat, 15 May 2021 18:41:30 +0200 Subject: [PATCH] fix ctx.selected_op on swap --- art/demo.ecotex | Bin 92 -> 135 bytes art/gen/demo.ecotex.h | 1642 ++++++++++----------- art/gen/demo.ecotex.png | Bin 11973 -> 9273 bytes code/game/source/editors/texed.c | 9 +- code/game/source/editors/texed_ops.c | 13 +- code/game/source/editors/texed_ops_list.c | 44 +- code/game/source/editors/texed_prj.c | 4 +- code/game/source/editors/texed_widgets.c | 15 +- code/vendors/gui_textbox_extended.h | 298 ++-- 9 files changed, 1031 insertions(+), 994 deletions(-) diff --git a/art/demo.ecotex b/art/demo.ecotex index 90e825c7b7bfeb3ec7aac6bafd6593aebec2e5a1..94e8ff8a51aaf974efaaa6f8a5a41e321b6b8a9a 100644 GIT binary patch literal 135 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 literal 11973 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[0].str); } }break; + case TOP_DRAW_TEXT: { + char const *str = op->params[0].str; + int x = op->params[1].i32; + int y = op->params[2].i32; + int size = op->params[3].i32; + Color color = op->params[4].color; + ImageDrawText(&ctx.img, str, x, y, size, color); + }break; default: { zpl_printf("%s\n", "unsupported op!"); }break; @@ -74,7 +82,10 @@ void texed_process_params(void) { p->flt = (float)zpl_str_to_f64(p->str, NULL); }break; case TPARAM_INT: { - p->u32 = (int)zpl_str_to_i64(p->str, NULL, 10); + p->u32 = (uint32_t)zpl_str_to_i64(p->str, NULL, 10); + }break; + case TPARAM_COORD: { + p->i32 = (int32_t)zpl_str_to_i64(p->str, NULL, 10); }break; case TPARAM_COLOR: { uint32_t color = (uint32_t)zpl_str_to_u64(p->str, NULL, 16); diff --git a/code/game/source/editors/texed_ops_list.c b/code/game/source/editors/texed_ops_list.c index 5b24dfc..e9249b6 100644 --- a/code/game/source/editors/texed_ops_list.c +++ b/code/game/source/editors/texed_ops_list.c @@ -1,5 +1,6 @@ #define PARAM(k,n,v) { .kind = k, .name = n, .str = v } +#define PARAM_DEF_COLOR "000000ff" static td_op default_ops[] = { { @@ -13,22 +14,22 @@ static td_op default_ops[] = { OP(TOP_DRAW_RECT), .num_params = 5, .params = (td_param[]) { - PARAM(TPARAM_INT, "x", "0"), - PARAM(TPARAM_INT, "y", "0"), - PARAM(TPARAM_INT, "w", "10"), - PARAM(TPARAM_INT, "h", "10"), - PARAM(TPARAM_COLOR, "color", "0"), + PARAM(TPARAM_COORD, "x", "0"), + PARAM(TPARAM_COORD, "y", "0"), + PARAM(TPARAM_COORD, "w", "10"), + PARAM(TPARAM_COORD, "h", "10"), + PARAM(TPARAM_COLOR, "color", PARAM_DEF_COLOR), } }, { OP(TOP_DRAW_LINE), .num_params = 5, .params = (td_param[]) { - PARAM(TPARAM_INT, "x1", "0"), - PARAM(TPARAM_INT, "y1", "0"), - PARAM(TPARAM_INT, "x2", "64"), - PARAM(TPARAM_INT, "y2", "64"), - PARAM(TPARAM_COLOR, "color", "0"), + PARAM(TPARAM_COORD, "x1", "0"), + PARAM(TPARAM_COORD, "y1", "0"), + PARAM(TPARAM_COORD, "x2", "64"), + PARAM(TPARAM_COORD, "y2", "64"), + PARAM(TPARAM_COLOR, "color", PARAM_DEF_COLOR), } }, { @@ -45,17 +46,28 @@ static td_op default_ops[] = { OP(TOP_LOAD_IMAGE), .num_params = 6, .params = (td_param[]) { - PARAM(TPARAM_STRING, "src", "art/natives/test.png"), - PARAM(TPARAM_INT, "x", "0"), - PARAM(TPARAM_INT, "y", "0"), - PARAM(TPARAM_INT, "w", "-1"), - PARAM(TPARAM_INT, "h", "-1"), + PARAM(TPARAM_STRING, "src", "art/samples/test.png"), + PARAM(TPARAM_COORD, "x", "0"), + PARAM(TPARAM_COORD, "y", "0"), + PARAM(TPARAM_COORD, "w", "-1"), + PARAM(TPARAM_COORD, "h", "-1"), PARAM(TPARAM_COLOR, "tint", "ffffffff"), } + }, + { + OP(TOP_DRAW_TEXT), + .num_params = 5, + .params = (td_param[]) { + PARAM(TPARAM_STRING, "text", "hello world"), + PARAM(TPARAM_COORD, "x", "0"), + PARAM(TPARAM_COORD, "y", "0"), + PARAM(TPARAM_INT, "size", "16"), + PARAM(TPARAM_COLOR, "color", "ffffffff"), + } } }; // NOTE(zaklaus): IMPORTANT !! keep these in sync -static char const *add_op_list = "CLEAR SOLID;DRAW RECTANGLE;PLOT LINE;DITHER;LOAD IMAGE"; +static char const *add_op_list = "CLEAR SOLID;DRAW RECTANGLE;PLOT LINE;DITHER;LOAD IMAGE;DRAW TEXT"; #define DEF_OPS_LEN (int)(sizeof(default_ops) / (sizeof(default_ops[0]))) diff --git a/code/game/source/editors/texed_prj.c b/code/game/source/editors/texed_prj.c index 7502c5f..256ae55 100644 --- a/code/game/source/editors/texed_prj.c +++ b/code/game/source/editors/texed_prj.c @@ -38,13 +38,12 @@ void texed_load(void) { int parmarrsize = (int)uc.item.as.array.size; for (int j = 0; j < parmarrsize; j += 1) { td_param *p = &op->params[j]; - UNPACK(CWP_ITEM_POSITIVE_INTEGER); - p->kind = (td_param_kind)uc.item.as.u64; UNPACK(CWP_ITEM_STR); zpl_memcopy(p->str, uc.item.as.str.start, uc.item.as.str.length); // NOTE(zaklaus): fix up other metadata p->name = default_ops[kind].params[j].name; + p->kind = default_ops[kind].params[j].kind;; } } @@ -77,7 +76,6 @@ void texed_save(void) { cw_pack_array_size(&pc, op->num_params); for (int j = 0; j < op->num_params; j += 1) { td_param *p = &op->params[j]; - cw_pack_unsigned(&pc, p->kind); cw_pack_str(&pc, p->str, zpl_strlen(p->str)); } } diff --git a/code/game/source/editors/texed_widgets.c b/code/game/source/editors/texed_widgets.c index 2ef21dc..69f1d82 100644 --- a/code/game/source/editors/texed_widgets.c +++ b/code/game/source/editors/texed_widgets.c @@ -182,7 +182,7 @@ void texed_draw_props_pane(zpl_aabb2 r) { switch (p->kind) { case TPARAM_COLOR: { - if (GuiTextBox(aabb2_ray(tbox_r), p->str, 64, p->edit_mode)) { + if (GuiTextBoxEx(aabb2_ray(tbox_r), p->str, 64, p->edit_mode)) { p->edit_mode = true; } @@ -198,9 +198,18 @@ void texed_draw_props_pane(zpl_aabb2 r) { } } }break; - + case TPARAM_COORD: { + if (GuiValueBox(aabb2_ray(tbox_r), NULL, &p->i32, INT32_MIN, INT32_MAX, p->edit_mode)) { + p->edit_mode = !p->edit_mode; + + if (!p->edit_mode) { + sprintf(p->str, "%d", p->i32); + texed_repaint_preview(); + } + }; + }break; default: { - if (GuiTextBox(aabb2_ray(tbox_r), p->str, 64, p->edit_mode)) { + if (GuiTextBoxEx(aabb2_ray(tbox_r), p->str, 64, p->edit_mode)) { p->edit_mode = !p->edit_mode; if (!p->edit_mode) diff --git a/code/vendors/gui_textbox_extended.h b/code/vendors/gui_textbox_extended.h index 954a0a0..69a9929 100644 --- a/code/vendors/gui_textbox_extended.h +++ b/code/vendors/gui_textbox_extended.h @@ -52,32 +52,32 @@ typedef struct GuiTextBoxState { #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds); // Sets the active textbox -RAYGUIDEF Rectangle GuiTextBoxGetActive(void); // Get bounds of active textbox - -RAYGUIDEF void GuiTextBoxSetCursor(int cursor); // Set cursor position of active textbox -RAYGUIDEF int GuiTextBoxGetCursor(void); // Get cursor position of active textbox - -RAYGUIDEF void GuiTextBoxSetSelection(int start, int length); // Set selection of active textbox -RAYGUIDEF Vector2 GuiTextBoxGetSelection(void); // Get selection of active textbox (x - selection start y - selection length) - -RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds); // Returns true if a textbox control with specified `bounds` is the active textbox -RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void); // Get state for the active textbox -RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state); // Set state for the active textbox (state must be valid else things will break) - -RAYGUIDEF void GuiTextBoxSelectAll(const char *text); // Select all characters in the active textbox (same as pressing `CTRL` + `A`) -RAYGUIDEF void GuiTextBoxCopy(const char *text); // Copy selected text to clipboard from the active textbox (same as pressing `CTRL` + `C`) -RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize); // Paste text from clipboard into the textbox (same as pressing `CTRL` + `V`) -RAYGUIDEF void GuiTextBoxCut(char *text); // Cut selected text in the active textbox and copy it to clipboard (same as pressing `CTRL` + `X`) -RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before); // Deletes a character or selection before from the active textbox (depending on `before`). Returns bytes deleted. -RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to); // Get the byte index for a character starting at position `from` with index `start` until position `to`. - -RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool editMode); - + + //---------------------------------------------------------------------------------- + // Module Functions Declaration + //---------------------------------------------------------------------------------- + RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds); // Sets the active textbox + RAYGUIDEF Rectangle GuiTextBoxGetActive(void); // Get bounds of active textbox + + RAYGUIDEF void GuiTextBoxSetCursor(int cursor); // Set cursor position of active textbox + RAYGUIDEF int GuiTextBoxGetCursor(void); // Get cursor position of active textbox + + RAYGUIDEF void GuiTextBoxSetSelection(int start, int length); // Set selection of active textbox + RAYGUIDEF Vector2 GuiTextBoxGetSelection(void); // Get selection of active textbox (x - selection start y - selection length) + + RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds); // Returns true if a textbox control with specified `bounds` is the active textbox + RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void); // Get state for the active textbox + RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state); // Set state for the active textbox (state must be valid else things will break) + + RAYGUIDEF void GuiTextBoxSelectAll(const char *text); // Select all characters in the active textbox (same as pressing `CTRL` + `A`) + RAYGUIDEF void GuiTextBoxCopy(const char *text); // Copy selected text to clipboard from the active textbox (same as pressing `CTRL` + `C`) + RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize); // Paste text from clipboard into the textbox (same as pressing `CTRL` + `V`) + RAYGUIDEF void GuiTextBoxCut(char *text); // Cut selected text in the active textbox and copy it to clipboard (same as pressing `CTRL` + `X`) + RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before); // Deletes a character or selection before from the active textbox (depending on `before`). Returns bytes deleted. + RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to); // Get the byte index for a character starting at position `from` with index `start` until position `to`. + + RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool editMode); + #ifdef __cplusplus } #endif @@ -169,7 +169,7 @@ RAYGUIDEF void GuiTextBoxSetSelection(int start, int length) { if (start < 0) start = 0; if (length < 0) length = 0; - + GuiTextBoxSetCursor(start + length); guiTextBoxState.select = start; } @@ -179,7 +179,7 @@ RAYGUIDEF Vector2 GuiTextBoxGetSelection(void) { if (guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor) return RAYGUI_CLITERAL(Vector2){ 0 }; else if (guiTextBoxState.cursor > guiTextBoxState.select) return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.select, guiTextBoxState.cursor - guiTextBoxState.select }; - + return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.cursor, guiTextBoxState.select - guiTextBoxState.cursor }; } @@ -200,17 +200,17 @@ RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state) RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to) { int i = start, k = from; - + while ((text[i] != '\0') && (k < to)) { int j = 0; int letter = GetNextCodepoint(&text[i], &j); - + if (letter == 0x3f) j = 1; i += j; ++k; } - + return i; } @@ -224,17 +224,17 @@ RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before) // Delete selection int start = guiTextBoxState.cursor; int end = guiTextBoxState.select; - + if (guiTextBoxState.cursor > guiTextBoxState.select) { start = guiTextBoxState.select; end = guiTextBoxState.cursor; } - + // Convert to byte indexes startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start); endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end); - + // Adjust text box state guiTextBoxState.cursor = start; // Always set cursor to start of selection if (guiTextBoxState.select < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate on the next frame @@ -249,35 +249,35 @@ RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before) endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); guiTextBoxState.cursor--; startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); - + if (guiTextBoxState.cursor < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate on the next frame } } else { // Delete character after cursor - if (guiTextBoxState.cursor + 1 <= GuiCountCodepointsUntilNewline(text)) + if (guiTextBoxState.cursor + 1 <= (int)GuiCountCodepointsUntilNewline(text)) { startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor+1); } } } - + memmove(&text[startIdx], &text[endIdx], length - endIdx); text[length - (endIdx - startIdx)] = '\0'; guiTextBoxState.select = -1; // Always deselect - + return (endIdx - startIdx); } - + return 0; } RAYGUIDEF void GuiTextBoxSelectAll(const char *text) { guiTextBoxState.cursor = GuiCountCodepointsUntilNewline(text); - + if (guiTextBoxState.cursor > 0) { guiTextBoxState.select = 0; @@ -295,21 +295,21 @@ RAYGUIDEF void GuiTextBoxCopy(const char *text) { int start = guiTextBoxState.cursor; int end = guiTextBoxState.select; - + if (guiTextBoxState.cursor > guiTextBoxState.select) { start = guiTextBoxState.select; end = guiTextBoxState.cursor; } - + // Convert to byte indexes start = GuiTextBoxGetByteIndex(text, 0, 0, start); end = GuiTextBoxGetByteIndex(text, 0, 0, end); - + // FIXME: `TextSubtext()` only lets use copy TEXTSPLIT_MAX_TEXT_LENGTH (1024) bytes // maybe modify `SetClipboardText()` so we can use it only on part of a string const char *clipText = TextSubtext(text, start, end - start); - + SetClipboardText(clipText); } } @@ -320,7 +320,7 @@ RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize) { const char *clipText = GetClipboardText(); // GLFW guaratees this should be UTF8 encoded! int length = strlen(text); - + if ((text != NULL) && (clipText != NULL) && (guiTextBoxState.cursor != -1)) { if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) @@ -328,24 +328,24 @@ RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize) // If there's a selection we'll have to delete it first length -= GuiTextBoxDelete(text, length, true); } - + int clipLen = strlen(clipText); // We want the length in bytes - + // Calculate how many bytes can we copy from clipboard text before we run out of space int size = ((length + clipLen) <= textSize) ? clipLen : textSize - length; - + // Make room by shifting to right the bytes after cursor int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); int endIdx = startIdx + size; memmove(&text[endIdx], &text[startIdx], length - startIdx); text[length + size] = '\0'; // Set the NULL char - + // At long last copy the clipboard text memcpy(&text[startIdx], clipText, size); - + // Set cursor position at the end of the pasted text guiTextBoxState.cursor = 0; - + for (int i = 0; i < (startIdx + size); guiTextBoxState.cursor++) { int next = 0; @@ -353,7 +353,7 @@ RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize) if (letter != 0x3f) i += next; else i += 1; } - + guiTextBoxState.start = -1; // Force to recalculate on the next frame } } @@ -367,27 +367,27 @@ RAYGUIDEF void GuiTextBoxCut(char* text) { // First copy selection to clipboard; int start = guiTextBoxState.cursor, end = guiTextBoxState.select; - + if (guiTextBoxState.cursor > guiTextBoxState.select) { start = guiTextBoxState.select; end = guiTextBoxState.cursor; } - + // Convert to byte indexes int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start); int endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end); - + // FIXME: `TextSubtext()` only lets use copy TEXTSPLIT_MAX_TEXT_LENGTH (1024) bytes // maybe modify `SetClipboardText()` so we can use it only on parts of a string const char *clipText = TextSubtext(text, startIdx, endIdx - startIdx); SetClipboardText(clipText); - + // Now delete selection (copy data over it) int len = strlen(text); memmove(&text[startIdx], &text[endIdx], len - endIdx); text[len - (endIdx - startIdx)] = '\0'; - + // Adjust text box state guiTextBoxState.cursor = start; // Always set cursor to start of selection if (guiTextBoxState.select < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate @@ -401,13 +401,13 @@ RAYGUIDEF void GuiTextBoxCut(char* text) RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool editMode) { // Define the cursor movement/selection speed when movement keys are held/pressed - #define TEXTBOX_CURSOR_COOLDOWN 5 - +#define TEXTBOX_CURSOR_COOLDOWN 5 + static int framesCounter = 0; // Required for blinking cursor - + GuiControlState state = guiState; bool pressed = false; - + // Make sure length doesn't exceed `textSize`. `textSize` is actually the max amount of characters the textbox can handle. int length = strlen(text); if (length > textSize) @@ -415,40 +415,40 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi text[textSize] = '\0'; length = textSize; } - + // Make sure we have enough room to draw at least 1 character if ((bounds.width - 2*GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)) < GuiGetStyle(DEFAULT, TEXT_SIZE)) { bounds.width = GuiGetStyle(DEFAULT, TEXT_SIZE) + 2*GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING); } - + // Center the text vertically int verticalPadding = (bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH) - GuiGetStyle(DEFAULT, TEXT_SIZE))/2; - + if (verticalPadding < 0) { // Make sure the height is sufficient bounds.height = 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(DEFAULT, TEXT_SIZE); verticalPadding = 0; } - + // Calculate the drawing area for the text inside the control `bounds` Rectangle textRec = { bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), - bounds.y + verticalPadding + GuiGetStyle(TEXTBOX, BORDER_WIDTH), - bounds.width - 2*(GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) + GuiGetStyle(TEXTBOX, BORDER_WIDTH)), - GuiGetStyle(DEFAULT, TEXT_SIZE) }; - + bounds.y + verticalPadding + GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.width - 2*(GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) + GuiGetStyle(TEXTBOX, BORDER_WIDTH)), + GuiGetStyle(DEFAULT, TEXT_SIZE) }; + Vector2 cursorPos = { textRec.x, textRec.y }; // This holds the coordinates inside textRec of the cursor at current position and will be recalculated later bool active = GuiTextBoxIsActive(bounds); // Check if this textbox is the global active textbox - + int selStart = 0, selLength = 0, textStartIndex = 0; - + // Update control //-------------------------------------------------------------------- if ((state != GUI_STATE_DISABLED) && !guiLocked) { Vector2 mousePoint = GetMousePosition(); - + if (editMode) { // Check if we are the global active textbox @@ -469,33 +469,33 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi GuiTextBoxSetActive(RAYGUI_CLITERAL(Rectangle){ 0, 0, -1, -1 }); active = false; } - + if (active) { state = GUI_STATE_PRESSED; framesCounter++; - + // Make sure state doesn't have invalid values if (guiTextBoxState.cursor > length) guiTextBoxState.cursor = -1; if (guiTextBoxState.select > length) guiTextBoxState.select = -1; if (guiTextBoxState.start > length) guiTextBoxState.start = -1; - - + + // Check textbox state for changes and recalculate if necesary if (guiTextBoxState.cursor == -1) { // Set cursor to last visible character in textbox guiTextBoxState.cursor = GuiTextBoxMaxCharacters(text, length, textRec); } - + if (guiTextBoxState.start == -1) { // Force recalculate text start position and text start index - + // NOTE: start and index are always in sync // start will hold the starting character position from where the text will be drawn // while index will hold the byte index inside the text for that character - + if (guiTextBoxState.cursor == 0) { guiTextBoxState.start = guiTextBoxState.index = 0; // No need to recalculate @@ -508,7 +508,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi guiTextBoxState.start = guiTextBoxState.cursor - pos + 1; } } - + // ----------------- // HANDLE KEY INPUT // ----------------- @@ -529,7 +529,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi { // Selecting if (guiTextBoxState.select == -1) guiTextBoxState.select = guiTextBoxState.cursor; // Mark selection start - + MoveTextBoxCursorRight(text, length, textRec); } else @@ -548,10 +548,10 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi // Move cursor to the right MoveTextBoxCursorRight(text, length, textRec); } - + guiTextBoxState.select = -1; } - + framesCounter = 0; } else if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (framesCounter%TEXTBOX_CURSOR_COOLDOWN == 0))) @@ -560,7 +560,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi { // Selecting if (guiTextBoxState.select == -1) guiTextBoxState.select = guiTextBoxState.cursor; // Mark selection start - + MoveTextBoxCursorLeft(text); } else @@ -571,7 +571,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi if (guiTextBoxState.cursor > guiTextBoxState.select) { guiTextBoxState.cursor = guiTextBoxState.select; - + if (guiTextBoxState.start > guiTextBoxState.cursor) { guiTextBoxState.start = guiTextBoxState.cursor; @@ -584,10 +584,10 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi // Move cursor to the left MoveTextBoxCursorLeft(text); } - + guiTextBoxState.select = -1; } - + framesCounter = 0; } else if (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (framesCounter%TEXTBOX_CURSOR_COOLDOWN) == 0)) @@ -609,7 +609,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi } } else guiTextBoxState.select = -1; // Deselect everything - + // Move cursor to start of text guiTextBoxState.cursor = guiTextBoxState.start = guiTextBoxState.index = 0; framesCounter = 0; @@ -617,7 +617,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi else if (IsKeyPressed(KEY_END)) { int max = GuiCountCodepointsUntilNewline(text); - + if (IsKeyDown(KEY_LEFT_SHIFT)) { if ((guiTextBoxState.select == -1) && (guiTextBoxState.cursor != max)) @@ -626,7 +626,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi } } else guiTextBoxState.select = -1; // Deselect everything - + int pos = 0; guiTextBoxState.cursor = max; int len = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); @@ -648,16 +648,16 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi // Delete selection GuiTextBoxDelete(text, length, true); } - + // Decode codepoint char out[5] = {0}; int sz = EncodeCodepoint(key, &out[0]); - + if (sz != 0) { int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); int endIdx = startIdx + sz; - + if (endIdx <= textSize && length < textSize - 1) { guiTextBoxState.cursor++; @@ -666,18 +666,18 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi memcpy(&text[startIdx], &out[0], sz); length += sz; text[length] = '\0'; - + if (guiTextBoxState.start != -1) { const int max = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); - + if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) guiTextBoxState.start = -1; } } } } } - + // ------------- // HANDLE MOUSE // ------------- @@ -716,15 +716,15 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi cursor = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); } } - + guiTextBoxState.cursor = cursor + guiTextBoxState.start; - + if (guiTextBoxState.select == -1) { // Mark start of selection guiTextBoxState.select = guiTextBoxState.cursor; } - + // Move the text when cursor is positioned before or after the text if ((framesCounter%TEXTBOX_CURSOR_COOLDOWN) == 0 && move) { @@ -736,13 +736,13 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi } } } - + // Calculate X coordinate of the blinking cursor cursorPos.x = GuiTextBoxGetCursorCoordinates(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, guiTextBoxState.cursor - guiTextBoxState.start); - + // Update variables textStartIndex = guiTextBoxState.index; - + if (guiTextBoxState.select == -1) { selStart = guiTextBoxState.cursor; @@ -758,7 +758,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi selStart = guiTextBoxState.cursor; selLength = guiTextBoxState.select - guiTextBoxState.cursor; } - + // We aren't drawing all of the text so make sure `DrawTextRecEx()` is selecting things correctly if (guiTextBoxState.start > selStart) { @@ -776,20 +776,20 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi if (active && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) { // If active copy all text to clipboard even when disabled - + // Backup textbox state int select = guiTextBoxState.select; int cursor = guiTextBoxState.cursor; int start = guiTextBoxState.start; - + if ((guiTextBoxState.select == -1) || (guiTextBoxState.select == guiTextBoxState.cursor)) { // If no selection then mark all text to be copied to clipboard GuiTextBoxSelectAll(text); } - + GuiTextBoxCopy(text); - + // Restore textbox state guiTextBoxState.select = select; guiTextBoxState.cursor = cursor; @@ -801,20 +801,20 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi state = GUI_STATE_FOCUSED; if (IsMouseButtonPressed(0)) pressed = true; } - + } if (pressed) framesCounter = 0; } - + // Draw control //-------------------------------------------------------------------- DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); - + if (state == GUI_STATE_PRESSED) { DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); - + // Draw blinking cursor if (editMode && active && ((framesCounter/TEXTEDIT_CURSOR_BLINK_FRAMES)%2 == 0) && selLength == 0) { @@ -825,10 +825,10 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi { DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); } - + // Finally draw the text and selection DrawTextRecEx(guiFont, &text[textStartIndex], textRec, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), false, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha), selStart, selLength, GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_FG)), GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_BG))); - + return pressed; } @@ -841,7 +841,7 @@ static int GetPrevCodepoint(const char *text, const char *start, int *prev) int c = 0x3f; char *p = (char *)text; *prev = 1; - + for (int i = 0; (p >= start) && (i < 4); p--, i++) { if ((((unsigned char)*p) >> 6) != 2) @@ -850,7 +850,7 @@ static int GetPrevCodepoint(const char *text, const char *start, int *prev) break; } } - + return c; } @@ -860,17 +860,17 @@ static inline unsigned int GuiCountCodepointsUntilNewline(const char *text) { unsigned int len = 0; char *ptr = (char*)&text[0]; - + while ((*ptr != '\0') && (*ptr != '\n')) { int next = 0; int letter = GetNextCodepoint(ptr, &next); - + if (letter == 0x3f) ptr += 1; else ptr += next; ++len; } - + return len; } @@ -881,18 +881,18 @@ static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, int *p const Font font = guiFont; const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE); const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING); - + int textOffsetX = 0; // Offset between characters float scaleFactor = 0.0f; - + int letter = 0; // Current character int index = 0; // Index position in sprite font - + scaleFactor = fontSize/font.baseSize; - + int i = 0, k = 0; int glyphWidth = 0; - + for (i = 0; i < length; i++, k++) { glyphWidth = 0; @@ -901,22 +901,22 @@ static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, int *p if (letter == 0x3f) next = 1; index = GetGlyphIndex(font, letter); i += next - 1; - + if (letter != '\n') { glyphWidth = (font.chars[index].advanceX == 0)? - (int)(font.recs[index].width*scaleFactor + spacing): - (int)(font.chars[index].advanceX*scaleFactor + spacing); - + (int)(font.recs[index].width*scaleFactor + spacing): + (int)(font.chars[index].advanceX*scaleFactor + spacing); + if ((textOffsetX + glyphWidth + 1) >= rec.width) break; - + if ((mode == GUI_MEASURE_MODE_CURSOR_POS) && (*pos == k)) break; else if (mode == GUI_MEASURE_MODE_CURSOR_COORDS) { // Check if the mouse pointer is inside the glyph rect Rectangle grec = {rec.x + textOffsetX - 1, rec.y, glyphWidth, (font.baseSize + font.baseSize/2)*scaleFactor - 1 }; Vector2 mouse = GetMousePosition(); - + if (CheckCollisionPointRec(mouse, grec)) { // Smooth selection by dividing the glyph rectangle into 2 equal parts and checking where the mouse resides @@ -925,18 +925,18 @@ static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, int *p textOffsetX += glyphWidth; k++; } - + break; } } } else break; - + textOffsetX += glyphWidth; } - + *pos = k; - + return (rec.x + textOffsetX - 1); } @@ -948,41 +948,41 @@ static int GuiMeasureTextBoxRev(const char *text, int length, Rectangle rec, int const Font font = guiFont; const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE); const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING); - + int textOffsetX = 0; // Offset between characters float scaleFactor = 0.0f; - + int letter = 0; // Current character int index = 0; // Index position in sprite font - + scaleFactor = fontSize/font.baseSize; - + int i = 0, k = 0; int glyphWidth = 0, prev = 1; for (i = length; i >= 0; i--, k++) { glyphWidth = 0; letter = GetPrevCodepoint(&text[i], &text[0], &prev); - + if (letter == 0x3f) prev = 1; index = GetGlyphIndex(font, letter); i -= prev - 1; - + if (letter != '\n') { glyphWidth = (font.chars[index].advanceX == 0)? - (int)(font.recs[index].width*scaleFactor + spacing): - (int)(font.chars[index].advanceX*scaleFactor + spacing); - + (int)(font.recs[index].width*scaleFactor + spacing): + (int)(font.chars[index].advanceX*scaleFactor + spacing); + if ((textOffsetX + glyphWidth + 1) >= rec.width) break; } else break; - + textOffsetX += glyphWidth; } - + *pos = k; - + return (i + prev); } @@ -1011,9 +1011,9 @@ static inline void MoveTextBoxCursorRight(const char* text, int length, Rectangl // FIXME: Counting codepoints each time we press the key is expensive, find another way int count = GuiCountCodepointsUntilNewline(text); if (guiTextBoxState.cursor < count ) guiTextBoxState.cursor++; - + const int max = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); - + if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) { const int cidx = GuiTextBoxGetByteIndex(text, guiTextBoxState.index, guiTextBoxState.start, guiTextBoxState.cursor); @@ -1026,7 +1026,7 @@ static inline void MoveTextBoxCursorRight(const char* text, int length, Rectangl static inline void MoveTextBoxCursorLeft(const char* text) { if (guiTextBoxState.cursor > 0) guiTextBoxState.cursor--; - + if (guiTextBoxState.cursor < guiTextBoxState.start) { int prev = 0; @@ -1066,7 +1066,7 @@ static int EncodeCodepoint(unsigned int c, char out[5]) out[3] = (char)((c & 0x3f) | 0x80); len = 4; } - + out[len] = 0; return len; }