REVIEWED: GuiTextBox() #257

- Added support for auto-cursor movement on key down
 - Added support for cursor blinking
 - Renamed sharedCursorIndex to textBoxCursorIndex
This commit is contained in:
Ray 2023-04-18 12:39:52 +02:00
parent f343f0057c
commit 6b2f812cc7

View File

@ -481,7 +481,8 @@ typedef enum {
typedef enum {
TEXT_INNER_PADDING = 16, // TextBox/TextBoxMulti/ValueBox/Spinner inner text padding
TEXT_LINES_SPACING, // TextBoxMulti lines separation
TEXT_ALIGNMENT_VERTICAL // TextBoxMulti vertical alignment: 0-CENTERED, 1-UP, 2-DOWN
TEXT_ALIGNMENT_VERTICAL, // TextBoxMulti vertical alignment: 0-CENTERED, 1-UP, 2-DOWN
TEXT_MULTILINE // TextBox supports multiple lines
} GuiTextBoxProperty;
// Spinner
@ -1206,7 +1207,11 @@ static unsigned int guiIconScale = 1; // Gui icon default scale (if icons
static bool guiTooltip = false; // Tooltip enabled/disabled
static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user)
static unsigned int sharedCursorIndex = 0; // Cursor index, shared by all GuiTextBox*()
static unsigned int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*()
//static unsigned int textBoxFrameCounter = 0; // Cursor blink frame counter, shared by all GuiTextBox*()
static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking
static int autoCursorFrameCounter = 0; // Frame counter for automatic cursor movement on key-down
static bool autoCursorMode = false; // Flag to note auto-cursor activation
//----------------------------------------------------------------------------------
// Style data array for all gui style properties (allocated on data segment by default)
@ -2109,11 +2114,13 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo
// NOTE: Returns true on ENTER pressed (useful for data validation)
bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
{
#define MAX_AUTO_CURSOR_FRAME_RATE 20 // Frames to wait for autocursor movement
GuiState state = guiState;
bool pressed = false;
Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
int textWidth = GetTextWidth(text) - GetTextWidth(text + sharedCursorIndex);
int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex);
int textIndexOffset = 0; // Text index offset to start drawing in the box
// Cursor rectangle
@ -2128,6 +2135,23 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH);
// Auto-cursor movement logic
// NOTE: Cursor moves automatically when key down after some time
if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE))
{
autoCursorFrameCounter++;
if (autoCursorFrameCounter > MAX_AUTO_CURSOR_FRAME_RATE) autoCursorMode = true;
}
else
{
autoCursorMode = false;
autoCursorFrameCounter = 0;
}
// Blink-cursor frame counter
if (!autoCursorMode) blinkCursorFrameCounter++;
else blinkCursorFrameCounter = 0;
// Update control
//--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked)
@ -2147,7 +2171,7 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
textIndexOffset += nextCodepointSize;
textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + sharedCursorIndex);
textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex);
}
int textLength = (int)strlen(text); // Get current text length
@ -2162,12 +2186,12 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
if ((codepoint >= 32) && ((textLength + codepointSize) < bufferSize))
{
// Move forward data from cursor position
for (int i = (textLength + codepointSize); i > sharedCursorIndex; i--) text[i] = text[i - codepointSize];
for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
// Add new codepoint in current cursor position
for (int i = 0; i < codepointSize; i++) text[sharedCursorIndex + i] = charEncoded[i];
for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i];
sharedCursorIndex += codepointSize;
textBoxCursorIndex += codepointSize;
textLength += codepointSize;
// Make sure text last character is EOL
@ -2175,15 +2199,15 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
}
// Delete codepoint from text, at current cursor position
if ((textLength > 0) && IsKeyPressed(KEY_BACKSPACE))
if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (autoCursorMode && IsKeyDown(KEY_BACKSPACE))))
{
int prevCodepointSize = 0;
GetCodepointPrevious(text + sharedCursorIndex, &prevCodepointSize);
GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
// Move backward text from cursor position
for (int i = (sharedCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
sharedCursorIndex -= codepointSize;
textBoxCursorIndex -= codepointSize;
textLength -= codepointSize;
// Make sure text last character is EOL
@ -2191,31 +2215,31 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
}
// Move cursor position with keys
if (IsKeyPressed(KEY_LEFT))
if (IsKeyPressed(KEY_LEFT) || (autoCursorMode && IsKeyDown(KEY_LEFT)))
{
int prevCodepointSize = 0;
GetCodepointPrevious(text + sharedCursorIndex, &prevCodepointSize);
GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
if (sharedCursorIndex >= prevCodepointSize) sharedCursorIndex -= prevCodepointSize;
if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize;
}
else if (IsKeyPressed(KEY_RIGHT))
else if (IsKeyPressed(KEY_RIGHT) || (autoCursorMode && IsKeyDown(KEY_RIGHT)))
{
int nextCodepointSize = 0;
GetCodepointNext(text + sharedCursorIndex, &nextCodepointSize);
GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
if ((sharedCursorIndex + nextCodepointSize) <= textLength) sharedCursorIndex += nextCodepointSize;
if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize;
}
// TODO: Move cursor position with mouse
// Recalculate cursor rectangle X position depending on sharedCursorIndex
cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + sharedCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
// Recalculate cursor rectangle X position depending on textBoxCursorIndex
cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
// Finish text editing on ENTER or mouse click outside bounds
if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
{
pressed = true; // Exiting edit mode
sharedCursorIndex = 0; // GLOBAL: Reset the shared cursor index
textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index
}
}
else
@ -2227,7 +2251,7 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
pressed = true; // Entering edit mode
sharedCursorIndex = strlen(text); // GLOBAL: Place cursor index to the end of current text
textBoxCursorIndex = strlen(text); // GLOBAL: Place cursor index to the end of current text
}
}
}
@ -2251,7 +2275,10 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha));
// Draw cursor
if (editMode) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
if (editMode)
{
if (autoCursorMode || ((blinkCursorFrameCounter/20)%2 == 0)) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
}
else if (state == STATE_FOCUSED) GuiTooltip(bounds);
//--------------------------------------------------------------------
@ -2267,7 +2294,7 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1);
Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
int textWidth = GetTextWidth(text) - GetTextWidth(text + sharedCursorIndex);
int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex);
int textIndexOffset = 0; // Text index offset to start drawing in the box
// Cursor rectangle
@ -2296,12 +2323,12 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
if (((codepoint == 10) || (codepoint >= 32)) && (textLength + codepointSize) < bufferSize)
{
// Move forward data from cursor position
for (int i = (textLength + codepointSize); i > sharedCursorIndex; i--) text[i] = text[i - codepointSize];
for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
// Add new codepoint in current cursor position
for (int i = 0; i < codepointSize; i++) text[sharedCursorIndex + i] = charEncoded[i];
for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i];
sharedCursorIndex += codepointSize;
textBoxCursorIndex += codepointSize;
textLength += codepointSize;
// Make sure text last character is EOL
@ -2312,12 +2339,12 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
if ((textLength > 0) && IsKeyPressed(KEY_BACKSPACE))
{
int prevCodepointSize = 0;
GetCodepointPrevious(text + sharedCursorIndex, &prevCodepointSize);
GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
// Move backward text from cursor position
for (int i = (sharedCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
sharedCursorIndex -= codepointSize;
textBoxCursorIndex -= codepointSize;
textLength -= codepointSize;
// Make sure text last character is EOL
@ -2328,26 +2355,26 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
if (IsKeyPressed(KEY_LEFT))
{
int prevCodepointSize = 0;
GetCodepointPrevious(text + sharedCursorIndex, &prevCodepointSize);
GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
if (sharedCursorIndex >= prevCodepointSize) sharedCursorIndex -= prevCodepointSize;
if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize;
}
else if (IsKeyPressed(KEY_RIGHT))
{
int nextCodepointSize = 0;
GetCodepointNext(text + sharedCursorIndex, &nextCodepointSize);
GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
if ((sharedCursorIndex + nextCodepointSize) <= textLength) sharedCursorIndex += nextCodepointSize;
if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize;
}
// TODO: Move cursor position with mouse
// TODO: Recalculate cursor position depending on sharedCursorIndex
// TODO: Recalculate cursor position depending on textBoxCursorIndex
char *lastTextBreak = text;
// Update cursor.y position considering line breaks
cursor.y = textBounds.y - 2; // -lineCount/2; // Move to centered text
for (int i = 0; i < sharedCursorIndex; i++)
for (int i = 0; i < textBoxCursorIndex; i++)
{
if (text[i] == '\n')
{
@ -2357,7 +2384,7 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
}
}
cursor.x = textBounds.x + GetTextWidth(lastTextBreak) - GetTextWidth(lastTextBreak + sharedCursorIndex);
cursor.x = textBounds.x + GetTextWidth(lastTextBreak) - GetTextWidth(lastTextBreak + textBoxCursorIndex);
@ -2365,7 +2392,7 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
pressed = true; // Exiting edit mode
sharedCursorIndex = 0; // GLOBAL: Reset the shared cursor index
textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index
}
}
else
@ -2377,7 +2404,7 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
pressed = true; // Entering edit mode
sharedCursorIndex = strlen(text); // GLOBAL: Place cursor index to the end of current text
textBoxCursorIndex = strlen(text); // GLOBAL: Place cursor index to the end of current text
}
}
}