Mercurial
comparison third_party/raylib/include/raygui.h @ 157:2db6253f355d
[ThirdParty] Added highlight library for better readability on blog.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Tue, 13 Jan 2026 19:18:47 -0800 |
| parents | 249881ceff7b |
| children | 87d8d3eb3491 |
comparison
equal
deleted
inserted
replaced
| 156:cd35e600ae34 | 157:2db6253f355d |
|---|---|
| 477 int alignmentV; | 477 int alignmentV; |
| 478 int padding; | 478 int padding; |
| 479 } GuiTextStyle; | 479 } GuiTextStyle; |
| 480 */ | 480 */ |
| 481 | 481 |
| 482 // Result from JUNE_GuiTextBoxEx - includes selection info | |
| 483 typedef struct JUNE_TextBoxResult { | |
| 484 int result; // Original return value (0 = no change, 1 = mode changed) | |
| 485 int selectionStart; // Start index of selection (-1 if no selection) | |
| 486 int selectionEnd; // End index of selection (-1 if no selection) | |
| 487 } JUNE_TextBoxResult; | |
| 488 | |
| 482 // Gui control state | 489 // Gui control state |
| 483 typedef enum { | 490 typedef enum { |
| 484 STATE_NORMAL = 0, | 491 STATE_NORMAL = 0, |
| 485 STATE_FOCUSED, | 492 STATE_FOCUSED, |
| 486 STATE_PRESSED, | 493 STATE_PRESSED, |
| 1408 static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier | 1415 static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier |
| 1409 | 1416 |
| 1410 static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() | 1417 static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() |
| 1411 //static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking | 1418 //static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking |
| 1412 static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay) | 1419 static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay) |
| 1420 static int textBoxSelectionStart = -1; // Selection start index (-1 if no selection) | |
| 1421 static int textBoxSelectionEnd = -1; // Selection end index (-1 if no selection) | |
| 1422 static bool textBoxSelecting = false; // Currently selecting with mouse | |
| 1413 | 1423 |
| 1414 //---------------------------------------------------------------------------------- | 1424 //---------------------------------------------------------------------------------- |
| 1415 // Style data array for all gui style properties (allocated on data segment by default) | 1425 // Style data array for all gui style properties (allocated on data segment by default) |
| 1416 // | 1426 // |
| 1417 // NOTE 1: First set of BASE properties are generic to all controls but could be individually | 1427 // NOTE 1: First set of BASE properties are generic to all controls but could be individually |
| 6064 //------------------------------------------------------------------ | 6074 //------------------------------------------------------------------ |
| 6065 | 6075 |
| 6066 return result; // Button pressed: result = 1 | 6076 return result; // Button pressed: result = 1 |
| 6067 } | 6077 } |
| 6068 | 6078 |
| 6069 int JUNE_GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) | 6079 JUNE_TextBoxResult JUNE_GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool editMode) |
| 6070 { | 6080 { |
| 6071 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) | 6081 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) |
| 6072 #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement | 6082 #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement |
| 6073 #endif | 6083 #endif |
| 6074 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) | 6084 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) |
| 6075 #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement | 6085 #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement |
| 6076 #endif | 6086 #endif |
| 6077 | 6087 #if !defined(JUNE_MAX_VISUAL_LINES) |
| 6078 int result = 0; | 6088 #define JUNE_MAX_VISUAL_LINES 256 |
| 6089 #endif | |
| 6090 | |
| 6091 JUNE_TextBoxResult resultStruct = { 0, -1, -1 }; | |
| 6079 GuiState state = guiState; | 6092 GuiState state = guiState; |
| 6080 | 6093 |
| 6081 bool multiline = true; // TODO: Consider multiline text input | |
| 6082 int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); | |
| 6083 | |
| 6084 Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); | 6094 Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); |
| 6085 int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length | 6095 int textLength = (text != NULL) ? (int)strlen(text) : 0; |
| 6086 int thisCursorIndex = textBoxCursorIndex; | 6096 int thisCursorIndex = textBoxCursorIndex; |
| 6087 if (thisCursorIndex > textLength) thisCursorIndex = textLength; | 6097 if (thisCursorIndex > textLength) thisCursorIndex = textLength; |
| 6088 | 6098 |
| 6089 // Calculate cursor position for multiline | 6099 // Line height for multiline |
| 6090 int cursorLine = 0; // Current line number (0-based) | 6100 float lineHeight = GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); |
| 6091 int lineStart = 0; // Start index of current line | 6101 float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE) / (float)guiFont.baseSize; |
| 6092 | 6102 float maxLineWidth = textBounds.width - 4; // Small margin |
| 6093 if (multiline) | 6103 |
| 6094 { | 6104 // Visual line structure: stores start index of each visual line |
| 6095 for (int i = 0; i < thisCursorIndex; i++) | 6105 int visualLineStarts[JUNE_MAX_VISUAL_LINES]; |
| 6096 { | 6106 int visualLineCount = 0; |
| 6097 if (text[i] == '\n') | 6107 |
| 6098 { | 6108 // Calculate visual lines (accounting for word wrap) |
| 6099 cursorLine++; | 6109 if (textLength > 0 && maxLineWidth > 0) |
| 6100 lineStart = i + 1; | 6110 { |
| 6101 } | 6111 int lineStartIdx = 0; |
| 6102 } | 6112 visualLineStarts[visualLineCount++] = 0; |
| 6103 } | 6113 |
| 6104 | 6114 int idx = 0; |
| 6105 // Calculate horizontal position within current line | 6115 while (idx < textLength && visualLineCount < JUNE_MAX_VISUAL_LINES) |
| 6106 char lineText[1024] = { 0 }; | 6116 { |
| 6107 int lineTextLen = 0; | 6117 // Check for hard newline |
| 6108 if (multiline) | 6118 if (text[idx] == '\n') |
| 6109 { | 6119 { |
| 6110 // Extract current line text up to cursor | 6120 idx++; |
| 6111 int i = lineStart; | 6121 if (idx < textLength && visualLineCount < JUNE_MAX_VISUAL_LINES) |
| 6112 while (i < thisCursorIndex && text[i] != '\n' && lineTextLen < 1023) | 6122 { |
| 6113 { | 6123 visualLineStarts[visualLineCount++] = idx; |
| 6114 lineText[lineTextLen++] = text[i++]; | 6124 lineStartIdx = idx; |
| 6115 } | 6125 } |
| 6116 lineText[lineTextLen] = '\0'; | 6126 continue; |
| 6117 } | 6127 } |
| 6118 | 6128 |
| 6119 int textWidth = multiline ? GuiGetTextWidth(lineText) : (GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex)); | 6129 // Calculate width from lineStartIdx to current position |
| 6120 int textIndexOffset = 0; // Text index offset to start drawing in the box | 6130 float currentWidth = 0; |
| 6121 | 6131 int lastSpaceIdx = -1; |
| 6122 // Line height for multiline (matches GuiDrawText line spacing) | 6132 int charIdx = lineStartIdx; |
| 6123 int lineHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); | 6133 |
| 6134 while (charIdx < textLength && text[charIdx] != '\n') | |
| 6135 { | |
| 6136 int cpSize = 0; | |
| 6137 int cp = GetCodepointNext(&text[charIdx], &cpSize); | |
| 6138 int glyphIdx = GetGlyphIndex(guiFont, cp); | |
| 6139 | |
| 6140 float glyphWidth; | |
| 6141 if (guiFont.glyphs[glyphIdx].advanceX == 0) | |
| 6142 glyphWidth = (float)guiFont.recs[glyphIdx].width * scaleFactor; | |
| 6143 else | |
| 6144 glyphWidth = (float)guiFont.glyphs[glyphIdx].advanceX * scaleFactor; | |
| 6145 | |
| 6146 if (text[charIdx] == ' ') lastSpaceIdx = charIdx; | |
| 6147 | |
| 6148 if (currentWidth + glyphWidth > maxLineWidth && charIdx > lineStartIdx) | |
| 6149 { | |
| 6150 // Need to wrap | |
| 6151 int wrapIdx; | |
| 6152 if (lastSpaceIdx > lineStartIdx) | |
| 6153 { | |
| 6154 // Wrap at last space | |
| 6155 wrapIdx = lastSpaceIdx + 1; | |
| 6156 } | |
| 6157 else | |
| 6158 { | |
| 6159 // No space found, wrap at current char | |
| 6160 wrapIdx = charIdx; | |
| 6161 } | |
| 6162 | |
| 6163 if (visualLineCount < JUNE_MAX_VISUAL_LINES) | |
| 6164 { | |
| 6165 visualLineStarts[visualLineCount++] = wrapIdx; | |
| 6166 lineStartIdx = wrapIdx; | |
| 6167 idx = wrapIdx; | |
| 6168 lastSpaceIdx = -1; | |
| 6169 } | |
| 6170 break; | |
| 6171 } | |
| 6172 | |
| 6173 currentWidth += glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6174 charIdx += cpSize; | |
| 6175 } | |
| 6176 | |
| 6177 if (charIdx >= textLength || text[charIdx] == '\n') | |
| 6178 { | |
| 6179 idx = charIdx; | |
| 6180 } | |
| 6181 } | |
| 6182 } | |
| 6183 else | |
| 6184 { | |
| 6185 visualLineStarts[visualLineCount++] = 0; | |
| 6186 } | |
| 6187 | |
| 6188 // Helper: Find visual line for a given text index | |
| 6189 int cursorVisualLine = 0; | |
| 6190 for (int vl = visualLineCount - 1; vl >= 0; vl--) | |
| 6191 { | |
| 6192 if (thisCursorIndex >= visualLineStarts[vl]) | |
| 6193 { | |
| 6194 cursorVisualLine = vl; | |
| 6195 break; | |
| 6196 } | |
| 6197 } | |
| 6198 | |
| 6199 // Helper: Get end of visual line (exclusive) | |
| 6200 int cursorLineStart = visualLineStarts[cursorVisualLine]; | |
| 6201 int cursorLineEnd = (cursorVisualLine + 1 < visualLineCount) ? visualLineStarts[cursorVisualLine + 1] : textLength; | |
| 6202 // Adjust for newline at end | |
| 6203 if (cursorLineEnd > 0 && cursorLineEnd <= textLength && cursorLineEnd > cursorLineStart) | |
| 6204 { | |
| 6205 if (text[cursorLineEnd - 1] == '\n') cursorLineEnd--; | |
| 6206 } | |
| 6207 | |
| 6208 // Calculate cursor X position within visual line | |
| 6209 float cursorXOffset = 0; | |
| 6210 for (int k = cursorLineStart; k < thisCursorIndex && k < textLength; k++) | |
| 6211 { | |
| 6212 if (text[k] == '\n') break; | |
| 6213 int cpSize = 0; | |
| 6214 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6215 int glyphIdx = GetGlyphIndex(guiFont, cp); | |
| 6216 float glyphWidth; | |
| 6217 if (guiFont.glyphs[glyphIdx].advanceX == 0) | |
| 6218 glyphWidth = (float)guiFont.recs[glyphIdx].width * scaleFactor; | |
| 6219 else | |
| 6220 glyphWidth = (float)guiFont.glyphs[glyphIdx].advanceX * scaleFactor; | |
| 6221 cursorXOffset += glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6222 } | |
| 6124 | 6223 |
| 6125 // Cursor rectangle | 6224 // Cursor rectangle |
| 6126 // NOTE: Position X and Y values updated for multiline support | |
| 6127 Rectangle cursor = { | 6225 Rectangle cursor = { |
| 6128 textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), | 6226 textBounds.x + cursorXOffset, |
| 6129 multiline ? (textBounds.y + cursorLine * lineHeight) : (textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)), | 6227 textBounds.y + cursorVisualLine * lineHeight, |
| 6130 2, | 6228 2, |
| 6131 (float)GuiGetStyle(DEFAULT, TEXT_SIZE) | 6229 (float)GuiGetStyle(DEFAULT, TEXT_SIZE) |
| 6132 }; | 6230 }; |
| 6133 | 6231 |
| 6134 if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; | 6232 if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH) * 2; |
| 6135 if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); | 6233 if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); |
| 6136 | 6234 |
| 6137 // Mouse cursor rectangle | |
| 6138 // NOTE: Initialized outside of screen | |
| 6139 Rectangle mouseCursor = cursor; | |
| 6140 mouseCursor.x = -1; | |
| 6141 mouseCursor.width = 1; | |
| 6142 | |
| 6143 // Blink-cursor frame counter | |
| 6144 //if (!autoCursorMode) blinkCursorFrameCounter++; | |
| 6145 //else blinkCursorFrameCounter = 0; | |
| 6146 | |
| 6147 // Update control | 6235 // Update control |
| 6148 //-------------------------------------------------------------------- | 6236 if ((state != STATE_DISABLED) && |
| 6149 // WARNING: Text editing is only supported under certain conditions: | 6237 !GuiGetStyle(TEXTBOX, TEXT_READONLY) && |
| 6150 if ((state != STATE_DISABLED) && // Control not disabled | 6238 !guiLocked && |
| 6151 !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode | 6239 !guiControlExclusiveMode) |
| 6152 !guiLocked && // Gui not locked | |
| 6153 !guiControlExclusiveMode && // No gui slider on dragging | |
| 6154 (wrapMode == TEXT_WRAP_NONE)) // No wrap mode | |
| 6155 { | 6240 { |
| 6156 Vector2 mousePosition = GetMousePosition(); | 6241 Vector2 mousePosition = GetMousePosition(); |
| 6157 | 6242 |
| 6158 if (editMode) | 6243 if (editMode) |
| 6159 { | 6244 { |
| 6160 // GLOBAL: Auto-cursor movement logic | |
| 6161 // NOTE: Keystrokes are handled repeatedly when button is held down for some time | |
| 6162 if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++; | 6245 if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++; |
| 6163 else autoCursorCounter = 0; | 6246 else autoCursorCounter = 0; |
| 6164 | 6247 |
| 6165 bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); | 6248 bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); |
| 6249 bool shiftDown = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); | |
| 6166 | 6250 |
| 6167 state = STATE_PRESSED; | 6251 state = STATE_PRESSED; |
| 6168 | 6252 |
| 6169 if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; | 6253 if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; |
| 6170 | 6254 |
| 6171 // If text does not fit in the textbox and current cursor position is out of bounds, | |
| 6172 // we add an index offset to text for drawing only what requires depending on cursor | |
| 6173 while (textWidth >= textBounds.width) | |
| 6174 { | |
| 6175 int nextCodepointSize = 0; | |
| 6176 GetCodepointNext(text + textIndexOffset, &nextCodepointSize); | |
| 6177 | |
| 6178 textIndexOffset += nextCodepointSize; | |
| 6179 | |
| 6180 textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex); | |
| 6181 } | |
| 6182 | |
| 6183 int codepoint = GetCharPressed(); // Get Unicode codepoint | 6255 int codepoint = GetCharPressed(); // Get Unicode codepoint |
| 6184 if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; | 6256 if (IsKeyPressed(KEY_ENTER)) |
| 6257 codepoint = (int)'\n'; | |
| 6185 | 6258 |
| 6186 // Encode codepoint as UTF-8 | 6259 // Encode codepoint as UTF-8 |
| 6187 int codepointSize = 0; | 6260 int codepointSize = 0; |
| 6188 const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); | 6261 const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); |
| 6189 | 6262 |
| 6190 // Handle text paste action | 6263 // Helper macro to check if there's an active selection |
| 6191 if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | 6264 #define HAS_SELECTION() (textBoxSelectionStart >= 0 && textBoxSelectionEnd >= 0 && textBoxSelectionStart != textBoxSelectionEnd) |
| 6192 { | 6265 #define SELECTION_MIN() ((textBoxSelectionStart < textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd) |
| 6266 #define SELECTION_MAX() ((textBoxSelectionStart > textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd) | |
| 6267 | |
| 6268 // Ctrl+A: Select all | |
| 6269 if (IsKeyPressed(KEY_A) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER))) | |
| 6270 { | |
| 6271 textBoxSelectionStart = 0; | |
| 6272 textBoxSelectionEnd = textLength; | |
| 6273 textBoxCursorIndex = textLength; | |
| 6274 } | |
| 6275 // Ctrl+C: Copy selection to clipboard | |
| 6276 else if (IsKeyPressed(KEY_C) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER))) | |
| 6277 { | |
| 6278 if (HAS_SELECTION()) | |
| 6279 { | |
| 6280 int selMin = SELECTION_MIN(); | |
| 6281 int selMax = SELECTION_MAX(); | |
| 6282 int selLen = selMax - selMin; | |
| 6283 char *clipText = (char *)RL_MALLOC(selLen + 1); | |
| 6284 if (clipText) | |
| 6285 { | |
| 6286 memcpy(clipText, text + selMin, selLen); | |
| 6287 clipText[selLen] = '\0'; | |
| 6288 SetClipboardText(clipText); | |
| 6289 RL_FREE(clipText); | |
| 6290 } | |
| 6291 } | |
| 6292 } | |
| 6293 // Ctrl+X: Cut selection to clipboard | |
| 6294 else if (IsKeyPressed(KEY_X) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER))) | |
| 6295 { | |
| 6296 if (HAS_SELECTION()) | |
| 6297 { | |
| 6298 int selMin = SELECTION_MIN(); | |
| 6299 int selMax = SELECTION_MAX(); | |
| 6300 int selLen = selMax - selMin; | |
| 6301 | |
| 6302 // Copy to clipboard | |
| 6303 char *clipText = (char *)RL_MALLOC(selLen + 1); | |
| 6304 if (clipText) | |
| 6305 { | |
| 6306 memcpy(clipText, text + selMin, selLen); | |
| 6307 clipText[selLen] = '\0'; | |
| 6308 SetClipboardText(clipText); | |
| 6309 RL_FREE(clipText); | |
| 6310 } | |
| 6311 | |
| 6312 // Delete selection | |
| 6313 for (int j = selMax; j <= textLength; j++) text[j - selLen] = text[j]; | |
| 6314 textLength -= selLen; | |
| 6315 textBoxCursorIndex = selMin; | |
| 6316 textBoxSelectionStart = -1; | |
| 6317 textBoxSelectionEnd = -1; | |
| 6318 } | |
| 6319 } | |
| 6320 // Ctrl+V: Paste (delete selection first if any) | |
| 6321 else if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER))) | |
| 6322 { | |
| 6323 // Delete selection first if any | |
| 6324 if (HAS_SELECTION()) | |
| 6325 { | |
| 6326 int selMin = SELECTION_MIN(); | |
| 6327 int selMax = SELECTION_MAX(); | |
| 6328 int selLen = selMax - selMin; | |
| 6329 for (int j = selMax; j <= textLength; j++) text[j - selLen] = text[j]; | |
| 6330 textLength -= selLen; | |
| 6331 textBoxCursorIndex = selMin; | |
| 6332 textBoxSelectionStart = -1; | |
| 6333 textBoxSelectionEnd = -1; | |
| 6334 } | |
| 6335 | |
| 6193 const char *pasteText = GetClipboardText(); | 6336 const char *pasteText = GetClipboardText(); |
| 6194 if (pasteText != NULL) | 6337 if (pasteText != NULL) |
| 6195 { | 6338 { |
| 6196 int pasteLength = 0; | 6339 int pasteLength = 0; |
| 6197 int pasteCodepoint; | 6340 int pasteCodepoint; |
| 6198 int pasteCodepointSize; | 6341 int pasteCodepointSize; |
| 6199 | 6342 |
| 6200 // Count how many codepoints to copy, stopping at the first unwanted control character | |
| 6201 while (true) | 6343 while (true) |
| 6202 { | 6344 { |
| 6203 pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize); | 6345 pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize); |
| 6204 if (textLength + pasteLength + pasteCodepointSize >= textSize) break; | 6346 if (textLength + pasteLength + pasteCodepointSize >= textSize) break; |
| 6205 if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break; | 6347 if (!((pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break; |
| 6206 pasteLength += pasteCodepointSize; | 6348 pasteLength += pasteCodepointSize; |
| 6207 } | 6349 } |
| 6208 | 6350 |
| 6209 if (pasteLength > 0) | 6351 if (pasteLength > 0) |
| 6210 { | 6352 { |
| 6211 // Move forward data from cursor position | 6353 for (int j = textLength + pasteLength; j > textBoxCursorIndex; j--) text[j] = text[j - pasteLength]; |
| 6212 for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength]; | 6354 for (int j = 0; j < pasteLength; j++) text[textBoxCursorIndex + j] = pasteText[j]; |
| 6213 | |
| 6214 // Paste data in at cursor | |
| 6215 for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i]; | |
| 6216 | 6355 |
| 6217 textBoxCursorIndex += pasteLength; | 6356 textBoxCursorIndex += pasteLength; |
| 6218 textLength += pasteLength; | 6357 textLength += pasteLength; |
| 6219 text[textLength] = '\0'; | 6358 text[textLength] = '\0'; |
| 6220 } | 6359 } |
| 6221 } | 6360 } |
| 6222 } | 6361 } |
| 6223 else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) | 6362 else if ((((codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) |
| 6224 { | 6363 { |
| 6364 // Delete selection first if any | |
| 6365 if (HAS_SELECTION()) | |
| 6366 { | |
| 6367 int selMin = SELECTION_MIN(); | |
| 6368 int selMax = SELECTION_MAX(); | |
| 6369 int selLen = selMax - selMin; | |
| 6370 for (int j = selMax; j <= textLength; j++) text[j - selLen] = text[j]; | |
| 6371 textLength -= selLen; | |
| 6372 textBoxCursorIndex = selMin; | |
| 6373 textBoxSelectionStart = -1; | |
| 6374 textBoxSelectionEnd = -1; | |
| 6375 } | |
| 6376 | |
| 6225 // Adding codepoint to text, at current cursor position | 6377 // Adding codepoint to text, at current cursor position |
| 6226 | 6378 if ((textLength + codepointSize) < textSize) |
| 6227 // Move forward data from cursor position | 6379 { |
| 6228 for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize]; | 6380 for (int j = (textLength + codepointSize); j > textBoxCursorIndex; j--) text[j] = text[j - codepointSize]; |
| 6229 | 6381 for (int j = 0; j < codepointSize; j++) text[textBoxCursorIndex + j] = charEncoded[j]; |
| 6230 // Add new codepoint in current cursor position | 6382 |
| 6231 for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i]; | 6383 textBoxCursorIndex += codepointSize; |
| 6232 | 6384 textLength += codepointSize; |
| 6233 textBoxCursorIndex += codepointSize; | 6385 text[textLength] = '\0'; |
| 6234 textLength += codepointSize; | 6386 } |
| 6235 | 6387 } |
| 6236 // Make sure text last character is EOL | 6388 |
| 6237 text[textLength] = '\0'; | 6389 #undef HAS_SELECTION |
| 6238 } | 6390 #undef SELECTION_MIN |
| 6239 | 6391 #undef SELECTION_MAX |
| 6240 // Move cursor to start | 6392 |
| 6241 if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0; | 6393 // Move cursor to start (with Shift selection support) |
| 6242 | 6394 if ((textLength > 0) && IsKeyPressed(KEY_HOME)) |
| 6243 // Move cursor to end | 6395 { |
| 6244 if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength; | 6396 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; |
| 6245 | 6397 textBoxCursorIndex = 0; |
| 6246 // Delete related codepoints from text, after current cursor position | 6398 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; |
| 6247 if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | 6399 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } |
| 6400 } | |
| 6401 | |
| 6402 // Move cursor to end (with Shift selection support) | |
| 6403 if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) | |
| 6404 { | |
| 6405 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6406 textBoxCursorIndex = textLength; | |
| 6407 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; | |
| 6408 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } | |
| 6409 } | |
| 6410 | |
| 6411 // Delete selection if any (on Delete or Backspace) | |
| 6412 if ((textBoxSelectionStart >= 0 && textBoxSelectionEnd >= 0 && textBoxSelectionStart != textBoxSelectionEnd) && | |
| 6413 (IsKeyPressed(KEY_DELETE) || IsKeyPressed(KEY_BACKSPACE))) | |
| 6414 { | |
| 6415 int selMin = (textBoxSelectionStart < textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd; | |
| 6416 int selMax = (textBoxSelectionStart > textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd; | |
| 6417 int selLen = selMax - selMin; | |
| 6418 for (int j = selMax; j <= textLength; j++) text[j - selLen] = text[j]; | |
| 6419 textLength -= selLen; | |
| 6420 textBoxCursorIndex = selMin; | |
| 6421 textBoxSelectionStart = -1; | |
| 6422 textBoxSelectionEnd = -1; | |
| 6423 } | |
| 6424 // Ctrl+Delete: Delete word after cursor | |
| 6425 else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | |
| 6248 { | 6426 { |
| 6249 int offset = textBoxCursorIndex; | 6427 int offset = textBoxCursorIndex; |
| 6250 int accCodepointSize = 0; | 6428 int accCodepointSize = 0; |
| 6251 int nextCodepointSize; | 6429 int nextCodepointSize; |
| 6252 int nextCodepoint; | 6430 int nextCodepoint; |
| 6253 | 6431 |
| 6254 // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace) | |
| 6255 // Not using isalnum() since it only works on ASCII characters | |
| 6256 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6432 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6257 bool puctuation = ispunct(nextCodepoint & 0xff); | 6433 bool puctuation = ispunct(nextCodepoint & 0xff); |
| 6258 while (offset < textLength) | 6434 while (offset < textLength) |
| 6259 { | 6435 { |
| 6260 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) | 6436 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) |
| 6262 offset += nextCodepointSize; | 6438 offset += nextCodepointSize; |
| 6263 accCodepointSize += nextCodepointSize; | 6439 accCodepointSize += nextCodepointSize; |
| 6264 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6440 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6265 } | 6441 } |
| 6266 | 6442 |
| 6267 // Check whitespace to delete (ASCII only) | |
| 6268 while (offset < textLength) | 6443 while (offset < textLength) |
| 6269 { | 6444 { |
| 6270 if (!isspace(nextCodepoint & 0xff)) break; | 6445 if (!isspace(nextCodepoint & 0xff)) break; |
| 6271 | |
| 6272 offset += nextCodepointSize; | 6446 offset += nextCodepointSize; |
| 6273 accCodepointSize += nextCodepointSize; | 6447 accCodepointSize += nextCodepointSize; |
| 6274 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6448 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6275 } | 6449 } |
| 6276 | 6450 |
| 6277 // Move text after cursor forward (including final null terminator) | |
| 6278 for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i]; | 6451 for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i]; |
| 6279 | |
| 6280 textLength -= accCodepointSize; | 6452 textLength -= accCodepointSize; |
| 6281 } | 6453 } |
| 6282 | 6454 // Delete single character after cursor |
| 6283 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) | 6455 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) |
| 6284 { | 6456 { |
| 6285 // Delete single codepoint from text, after current cursor position | |
| 6286 | |
| 6287 int nextCodepointSize = 0; | 6457 int nextCodepointSize = 0; |
| 6288 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); | 6458 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); |
| 6289 | |
| 6290 // Move text after cursor forward (including final null terminator) | |
| 6291 for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i]; | 6459 for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i]; |
| 6292 | |
| 6293 textLength -= nextCodepointSize; | 6460 textLength -= nextCodepointSize; |
| 6294 } | 6461 } |
| 6295 | 6462 // Ctrl+Backspace: Delete word before cursor |
| 6296 // Delete related codepoints from text, before current cursor position | 6463 else if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) |
| 6297 if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | |
| 6298 { | 6464 { |
| 6299 int offset = textBoxCursorIndex; | 6465 int offset = textBoxCursorIndex; |
| 6300 int accCodepointSize = 0; | 6466 int accCodepointSize = 0; |
| 6301 int prevCodepointSize = 0; | 6467 int prevCodepointSize = 0; |
| 6302 int prevCodepoint = 0; | 6468 int prevCodepoint = 0; |
| 6303 | 6469 |
| 6304 // Check whitespace to delete (ASCII only) | |
| 6305 while (offset > 0) | 6470 while (offset > 0) |
| 6306 { | 6471 { |
| 6307 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); | 6472 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); |
| 6308 if (!isspace(prevCodepoint & 0xff)) break; | 6473 if (!isspace(prevCodepoint & 0xff)) break; |
| 6309 | |
| 6310 offset -= prevCodepointSize; | 6474 offset -= prevCodepointSize; |
| 6311 accCodepointSize += prevCodepointSize; | 6475 accCodepointSize += prevCodepointSize; |
| 6312 } | 6476 } |
| 6313 | 6477 |
| 6314 // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace) | |
| 6315 // Not using isalnum() since it only works on ASCII characters | |
| 6316 bool puctuation = ispunct(prevCodepoint & 0xff); | 6478 bool puctuation = ispunct(prevCodepoint & 0xff); |
| 6317 while (offset > 0) | 6479 while (offset > 0) |
| 6318 { | 6480 { |
| 6319 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); | 6481 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); |
| 6320 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; | 6482 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; |
| 6321 | |
| 6322 offset -= prevCodepointSize; | 6483 offset -= prevCodepointSize; |
| 6323 accCodepointSize += prevCodepointSize; | 6484 accCodepointSize += prevCodepointSize; |
| 6324 } | 6485 } |
| 6325 | 6486 |
| 6326 // Move text after cursor forward (including final null terminator) | |
| 6327 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i]; | 6487 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i]; |
| 6328 | |
| 6329 textLength -= accCodepointSize; | 6488 textLength -= accCodepointSize; |
| 6330 textBoxCursorIndex -= accCodepointSize; | 6489 textBoxCursorIndex -= accCodepointSize; |
| 6331 } | 6490 } |
| 6332 | 6491 // Backspace single character before cursor |
| 6333 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) | 6492 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) |
| 6334 { | 6493 { |
| 6335 // Delete single codepoint from text, before current cursor position | |
| 6336 | |
| 6337 int prevCodepointSize = 0; | 6494 int prevCodepointSize = 0; |
| 6338 | |
| 6339 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); | 6495 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); |
| 6340 | |
| 6341 // Move text after cursor forward (including final null terminator) | |
| 6342 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i]; | 6496 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i]; |
| 6343 | |
| 6344 textLength -= prevCodepointSize; | 6497 textLength -= prevCodepointSize; |
| 6345 textBoxCursorIndex -= prevCodepointSize; | 6498 textBoxCursorIndex -= prevCodepointSize; |
| 6346 } | 6499 } |
| 6347 | 6500 |
| 6348 // Move cursor position with keys | 6501 // Move cursor position with keys (with Shift selection support) |
| 6349 if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | 6502 if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) |
| 6350 { | 6503 { |
| 6504 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6505 | |
| 6351 int offset = textBoxCursorIndex; | 6506 int offset = textBoxCursorIndex; |
| 6352 //int accCodepointSize = 0; | |
| 6353 int prevCodepointSize = 0; | 6507 int prevCodepointSize = 0; |
| 6354 int prevCodepoint = 0; | 6508 int prevCodepoint = 0; |
| 6355 | 6509 |
| 6356 // Check whitespace to skip (ASCII only) | |
| 6357 while (offset > 0) | 6510 while (offset > 0) |
| 6358 { | 6511 { |
| 6359 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); | 6512 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); |
| 6360 if (!isspace(prevCodepoint & 0xff)) break; | 6513 if (!isspace(prevCodepoint & 0xff)) break; |
| 6361 | |
| 6362 offset -= prevCodepointSize; | 6514 offset -= prevCodepointSize; |
| 6363 //accCodepointSize += prevCodepointSize; | 6515 } |
| 6364 } | 6516 |
| 6365 | |
| 6366 // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace) | |
| 6367 // Not using isalnum() since it only works on ASCII characters | |
| 6368 bool puctuation = ispunct(prevCodepoint & 0xff); | 6517 bool puctuation = ispunct(prevCodepoint & 0xff); |
| 6369 while (offset > 0) | 6518 while (offset > 0) |
| 6370 { | 6519 { |
| 6371 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); | 6520 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); |
| 6372 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; | 6521 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; |
| 6373 | |
| 6374 offset -= prevCodepointSize; | 6522 offset -= prevCodepointSize; |
| 6375 //accCodepointSize += prevCodepointSize; | |
| 6376 } | 6523 } |
| 6377 | 6524 |
| 6378 textBoxCursorIndex = offset; | 6525 textBoxCursorIndex = offset; |
| 6526 | |
| 6527 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; | |
| 6528 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } | |
| 6379 } | 6529 } |
| 6380 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger))) | 6530 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger))) |
| 6381 { | 6531 { |
| 6532 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6533 | |
| 6382 int prevCodepointSize = 0; | 6534 int prevCodepointSize = 0; |
| 6383 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); | 6535 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); |
| 6384 | |
| 6385 textBoxCursorIndex -= prevCodepointSize; | 6536 textBoxCursorIndex -= prevCodepointSize; |
| 6537 | |
| 6538 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; | |
| 6539 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } | |
| 6386 } | 6540 } |
| 6387 else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | 6541 else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) |
| 6388 { | 6542 { |
| 6543 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6544 | |
| 6389 int offset = textBoxCursorIndex; | 6545 int offset = textBoxCursorIndex; |
| 6390 //int accCodepointSize = 0; | |
| 6391 int nextCodepointSize; | 6546 int nextCodepointSize; |
| 6392 int nextCodepoint; | 6547 int nextCodepoint; |
| 6393 | 6548 |
| 6394 // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace) | |
| 6395 // Not using isalnum() since it only works on ASCII characters | |
| 6396 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6549 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6397 bool puctuation = ispunct(nextCodepoint & 0xff); | 6550 bool puctuation = ispunct(nextCodepoint & 0xff); |
| 6398 while (offset < textLength) | 6551 while (offset < textLength) |
| 6399 { | 6552 { |
| 6400 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break; | 6553 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break; |
| 6401 | |
| 6402 offset += nextCodepointSize; | 6554 offset += nextCodepointSize; |
| 6403 //accCodepointSize += nextCodepointSize; | |
| 6404 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6555 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6405 } | 6556 } |
| 6406 | 6557 |
| 6407 // Check whitespace to skip (ASCII only) | |
| 6408 while (offset < textLength) | 6558 while (offset < textLength) |
| 6409 { | 6559 { |
| 6410 if (!isspace(nextCodepoint & 0xff)) break; | 6560 if (!isspace(nextCodepoint & 0xff)) break; |
| 6411 | |
| 6412 offset += nextCodepointSize; | 6561 offset += nextCodepointSize; |
| 6413 //accCodepointSize += nextCodepointSize; | |
| 6414 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6562 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6415 } | 6563 } |
| 6416 | 6564 |
| 6417 textBoxCursorIndex = offset; | 6565 textBoxCursorIndex = offset; |
| 6566 | |
| 6567 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; | |
| 6568 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } | |
| 6418 } | 6569 } |
| 6419 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger))) | 6570 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger))) |
| 6420 { | 6571 { |
| 6572 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6573 | |
| 6421 int nextCodepointSize = 0; | 6574 int nextCodepointSize = 0; |
| 6422 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); | 6575 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); |
| 6423 | |
| 6424 textBoxCursorIndex += nextCodepointSize; | 6576 textBoxCursorIndex += nextCodepointSize; |
| 6425 } | 6577 |
| 6426 | 6578 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; |
| 6427 // Vertical cursor movement for multiline | 6579 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } |
| 6428 if (multiline && (IsKeyPressed(KEY_UP) || (IsKeyDown(KEY_UP) && autoCursorShouldTrigger))) | 6580 } |
| 6429 { | 6581 |
| 6430 // Find start of current line | 6582 // Vertical cursor movement using visual lines (with Shift selection support) |
| 6431 int currentLineStart = textBoxCursorIndex; | 6583 if ((IsKeyPressed(KEY_UP) || (IsKeyDown(KEY_UP) && autoCursorShouldTrigger))) |
| 6432 while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--; | 6584 { |
| 6433 | 6585 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; |
| 6434 // Calculate horizontal position in current line | 6586 |
| 6435 int horizontalPos = textBoxCursorIndex - currentLineStart; | 6587 // Find current visual line |
| 6436 | 6588 int currVisLine = 0; |
| 6437 // Find start of previous line | 6589 for (int vl = visualLineCount - 1; vl >= 0; vl--) |
| 6438 if (currentLineStart > 0) | 6590 { |
| 6439 { | 6591 if (textBoxCursorIndex >= visualLineStarts[vl]) { currVisLine = vl; break; } |
| 6440 int prevLineEnd = currentLineStart - 1; // Skip the newline | 6592 } |
| 6441 int prevLineStart = prevLineEnd; | 6593 |
| 6442 while (prevLineStart > 0 && text[prevLineStart - 1] != '\n') prevLineStart--; | 6594 // Calculate X offset in current line |
| 6443 | 6595 int currLineStart = visualLineStarts[currVisLine]; |
| 6444 // Move to same horizontal position on previous line (or end of line if shorter) | 6596 float xOffset = 0; |
| 6445 int prevLineLength = prevLineEnd - prevLineStart; | 6597 for (int k = currLineStart; k < textBoxCursorIndex && k < textLength; k++) |
| 6446 int targetPos = (horizontalPos < prevLineLength) ? horizontalPos : prevLineLength; | 6598 { |
| 6447 textBoxCursorIndex = prevLineStart + targetPos; | 6599 if (text[k] == '\n') break; |
| 6600 int cpSize = 0; | |
| 6601 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6602 int gi = GetGlyphIndex(guiFont, cp); | |
| 6603 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6604 xOffset += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6605 } | |
| 6606 | |
| 6607 if (currVisLine > 0) | |
| 6608 { | |
| 6609 // Move to previous visual line at same X position | |
| 6610 int prevLineStart = visualLineStarts[currVisLine - 1]; | |
| 6611 int prevLineEnd = visualLineStarts[currVisLine]; | |
| 6612 if (prevLineEnd > 0 && text[prevLineEnd - 1] == '\n') prevLineEnd--; | |
| 6613 | |
| 6614 float accum = 0; | |
| 6615 textBoxCursorIndex = prevLineStart; | |
| 6616 for (int k = prevLineStart; k < prevLineEnd; ) | |
| 6617 { | |
| 6618 int cpSize = 0; | |
| 6619 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6620 int gi = GetGlyphIndex(guiFont, cp); | |
| 6621 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6622 if (accum + gw / 2 >= xOffset) break; | |
| 6623 accum += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6624 textBoxCursorIndex = k + cpSize; | |
| 6625 k += cpSize; | |
| 6626 } | |
| 6448 } | 6627 } |
| 6449 else | 6628 else |
| 6450 { | 6629 { |
| 6451 // Already on first line, move to start | |
| 6452 textBoxCursorIndex = 0; | 6630 textBoxCursorIndex = 0; |
| 6453 } | 6631 } |
| 6454 } | 6632 |
| 6455 else if (multiline && (IsKeyPressed(KEY_DOWN) || (IsKeyDown(KEY_DOWN) && autoCursorShouldTrigger))) | 6633 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; |
| 6456 { | 6634 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } |
| 6457 // Find start of current line | 6635 } |
| 6458 int currentLineStart = textBoxCursorIndex; | 6636 else if ((IsKeyPressed(KEY_DOWN) || (IsKeyDown(KEY_DOWN) && autoCursorShouldTrigger))) |
| 6459 while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--; | 6637 { |
| 6460 | 6638 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; |
| 6461 // Calculate horizontal position in current line | 6639 |
| 6462 int horizontalPos = textBoxCursorIndex - currentLineStart; | 6640 // Find current visual line |
| 6463 | 6641 int currVisLine = 0; |
| 6464 // Find end of current line | 6642 for (int vl = visualLineCount - 1; vl >= 0; vl--) |
| 6465 int currentLineEnd = textBoxCursorIndex; | 6643 { |
| 6466 while (currentLineEnd < textLength && text[currentLineEnd] != '\n') currentLineEnd++; | 6644 if (textBoxCursorIndex >= visualLineStarts[vl]) { currVisLine = vl; break; } |
| 6467 | 6645 } |
| 6468 // Find next line | 6646 |
| 6469 if (currentLineEnd < textLength) | 6647 // Calculate X offset in current line |
| 6470 { | 6648 int currLineStart = visualLineStarts[currVisLine]; |
| 6471 int nextLineStart = currentLineEnd + 1; // Skip the newline | 6649 float xOffset = 0; |
| 6472 int nextLineEnd = nextLineStart; | 6650 for (int k = currLineStart; k < textBoxCursorIndex && k < textLength; k++) |
| 6473 while (nextLineEnd < textLength && text[nextLineEnd] != '\n') nextLineEnd++; | 6651 { |
| 6474 | 6652 if (text[k] == '\n') break; |
| 6475 // Move to same horizontal position on next line (or end of line if shorter) | 6653 int cpSize = 0; |
| 6476 int nextLineLength = nextLineEnd - nextLineStart; | 6654 int cp = GetCodepointNext(&text[k], &cpSize); |
| 6477 int targetPos = (horizontalPos < nextLineLength) ? horizontalPos : nextLineLength; | 6655 int gi = GetGlyphIndex(guiFont, cp); |
| 6478 textBoxCursorIndex = nextLineStart + targetPos; | 6656 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; |
| 6657 xOffset += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6658 } | |
| 6659 | |
| 6660 if (currVisLine < visualLineCount - 1) | |
| 6661 { | |
| 6662 // Move to next visual line at same X position | |
| 6663 int nextLineStart = visualLineStarts[currVisLine + 1]; | |
| 6664 int nextLineEnd = (currVisLine + 2 < visualLineCount) ? visualLineStarts[currVisLine + 2] : textLength; | |
| 6665 if (nextLineEnd > nextLineStart && text[nextLineEnd - 1] == '\n') nextLineEnd--; | |
| 6666 | |
| 6667 float accum = 0; | |
| 6668 textBoxCursorIndex = nextLineStart; | |
| 6669 for (int k = nextLineStart; k < nextLineEnd; ) | |
| 6670 { | |
| 6671 int cpSize = 0; | |
| 6672 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6673 int gi = GetGlyphIndex(guiFont, cp); | |
| 6674 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6675 if (accum + gw / 2 >= xOffset) break; | |
| 6676 accum += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6677 textBoxCursorIndex = k + cpSize; | |
| 6678 k += cpSize; | |
| 6679 } | |
| 6479 } | 6680 } |
| 6480 else | 6681 else |
| 6481 { | 6682 { |
| 6482 // Already on last line, move to end | |
| 6483 textBoxCursorIndex = textLength; | 6683 textBoxCursorIndex = textLength; |
| 6484 } | 6684 } |
| 6485 } | 6685 |
| 6486 | 6686 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; |
| 6487 // Move cursor position with mouse | 6687 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } |
| 6488 if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text | 6688 } |
| 6489 { | 6689 |
| 6490 float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; | 6690 // Move cursor position with mouse using visual lines |
| 6491 int codepointIndex = 0; | 6691 { |
| 6492 float glyphWidth = 0.0f; | |
| 6493 float widthToMouseX = 0; | |
| 6494 int mouseCursorIndex = 0; | 6692 int mouseCursorIndex = 0; |
| 6495 | 6693 bool mouseInBounds = CheckCollisionPointRec(mousePosition, textBounds); |
| 6496 for (int i = textIndexOffset; i < textLength; i += codepointSize) | 6694 bool shouldCalculateMousePos = mouseInBounds || textBoxSelecting; |
| 6497 { | 6695 |
| 6498 codepoint = GetCodepointNext(&text[i], &codepointSize); | 6696 if (shouldCalculateMousePos && textLength > 0) |
| 6499 codepointIndex = GetGlyphIndex(guiFont, codepoint); | 6697 { |
| 6500 | 6698 // Determine which visual line the mouse is on |
| 6501 if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); | 6699 int mouseLine = (int)((mousePosition.y - textBounds.y) / lineHeight); |
| 6502 else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); | 6700 if (mouseLine < 0) mouseLine = 0; |
| 6503 | 6701 if (mouseLine >= visualLineCount) mouseLine = visualLineCount - 1; |
| 6504 if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2))) | 6702 |
| 6703 // Get the start and end of the target visual line | |
| 6704 int targetLineStart = visualLineStarts[mouseLine]; | |
| 6705 int targetLineEnd = (mouseLine + 1 < visualLineCount) ? visualLineStarts[mouseLine + 1] : textLength; | |
| 6706 if (targetLineEnd > targetLineStart && text[targetLineEnd - 1] == '\n') targetLineEnd--; | |
| 6707 | |
| 6708 // Find character position within the line based on mouse X | |
| 6709 float relativeMouseX = mousePosition.x - textBounds.x; | |
| 6710 if (relativeMouseX < 0) relativeMouseX = 0; | |
| 6711 | |
| 6712 float widthAccum = 0; | |
| 6713 mouseCursorIndex = targetLineStart; | |
| 6714 | |
| 6715 for (int k = targetLineStart; k < targetLineEnd; ) | |
| 6505 { | 6716 { |
| 6506 mouseCursor.x = textBounds.x + widthToMouseX; | 6717 if (text[k] == '\n') break; |
| 6507 mouseCursorIndex = i; | 6718 int cpSize = 0; |
| 6508 printf("before: %i\n", mouseCursorIndex); | 6719 int cp = GetCodepointNext(&text[k], &cpSize); |
| 6509 break; | 6720 int gi = GetGlyphIndex(guiFont, cp); |
| 6721 | |
| 6722 float gw; | |
| 6723 if (guiFont.glyphs[gi].advanceX == 0) | |
| 6724 gw = (float)guiFont.recs[gi].width * scaleFactor; | |
| 6725 else | |
| 6726 gw = (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6727 | |
| 6728 float charMidpoint = widthAccum + gw / 2.0f; | |
| 6729 | |
| 6730 if (relativeMouseX <= charMidpoint) | |
| 6731 { | |
| 6732 mouseCursorIndex = k; | |
| 6733 break; | |
| 6734 } | |
| 6735 | |
| 6736 widthAccum += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6737 mouseCursorIndex = k + cpSize; | |
| 6738 k += cpSize; | |
| 6510 } | 6739 } |
| 6511 | 6740 |
| 6512 if (mousePosition.y >= textBounds.y && mousePosition.y <= textBounds.height) | 6741 // Clamp to line end |
| 6513 { | 6742 if (mouseCursorIndex > targetLineEnd) mouseCursorIndex = targetLineEnd; |
| 6514 mouseCursor.y = mousePosition.y; | 6743 } |
| 6515 mouseCursorIndex = i; | 6744 else if (shouldCalculateMousePos) |
| 6516 int number_of_n = (int)(mousePosition.y / lineHeight); | 6745 { |
| 6517 for (int i = 0; i < textSize; i++) | 6746 mouseCursorIndex = 0; |
| 6518 { | 6747 } |
| 6519 if (text[i] == '\n') | 6748 |
| 6520 { | 6749 // Mouse selection: start selection on mouse press (only when in bounds) |
| 6521 number_of_n--; | 6750 if (mouseInBounds && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) |
| 6522 if (number_of_n == 0) | 6751 { |
| 6523 mouseCursorIndex += i; | |
| 6524 } | |
| 6525 } | |
| 6526 break; | |
| 6527 } | |
| 6528 | |
| 6529 widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); | |
| 6530 } | |
| 6531 | |
| 6532 // Check if mouse cursor is at the last position | |
| 6533 int textEndWidth = GuiGetTextWidth(text + textIndexOffset); | |
| 6534 if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) | |
| 6535 { | |
| 6536 mouseCursor.x = textBounds.x + textEndWidth; | |
| 6537 mouseCursorIndex = textLength; | |
| 6538 } | |
| 6539 | |
| 6540 // Place cursor at required index on mouse click | |
| 6541 if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) | |
| 6542 { | |
| 6543 cursor.x = mouseCursor.x; | |
| 6544 textBoxCursorIndex = mouseCursorIndex; | 6752 textBoxCursorIndex = mouseCursorIndex; |
| 6545 } | 6753 textBoxSelectionStart = mouseCursorIndex; |
| 6546 } | 6754 textBoxSelectionEnd = mouseCursorIndex; |
| 6547 else mouseCursor.x = -1; | 6755 textBoxSelecting = true; |
| 6548 | 6756 } |
| 6549 // Recalculate cursor position for multiline | 6757 // Mouse selection: update selection while dragging (even outside bounds) |
| 6550 if (multiline) | 6758 else if (textBoxSelecting && IsMouseButtonDown(MOUSE_LEFT_BUTTON)) |
| 6551 { | 6759 { |
| 6552 // Recalculate cursor line and position | 6760 textBoxSelectionEnd = mouseCursorIndex; |
| 6553 int newCursorLine = 0; | 6761 textBoxCursorIndex = mouseCursorIndex; |
| 6554 int newLineStart = 0; | 6762 } |
| 6555 | 6763 } |
| 6556 for (int i = 0; i < textBoxCursorIndex; i++) | 6764 |
| 6557 { | 6765 // End mouse selection when button released |
| 6558 if (text[i] == '\n') | 6766 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) |
| 6559 { | 6767 { |
| 6560 newCursorLine++; | 6768 textBoxSelecting = false; |
| 6561 newLineStart = i + 1; | 6769 } |
| 6562 } | 6770 |
| 6563 } | 6771 // Recalculate cursor position using visual lines |
| 6564 | 6772 // (Visual lines already calculated at the start - need to recalculate after any changes) |
| 6565 // Extract current line text up to cursor | 6773 int newCursorVisLine = 0; |
| 6566 char currentLineText[1024] = { 0 }; | 6774 for (int vl = visualLineCount - 1; vl >= 0; vl--) |
| 6567 int currentLineLen = 0; | 6775 { |
| 6568 int i = newLineStart; | 6776 if (textBoxCursorIndex >= visualLineStarts[vl]) { newCursorVisLine = vl; break; } |
| 6569 while (i < textBoxCursorIndex && text[i] != '\n' && currentLineLen < 1023) | 6777 } |
| 6570 { | 6778 |
| 6571 currentLineText[currentLineLen++] = text[i++]; | 6779 int newCursorLineStart = visualLineStarts[newCursorVisLine]; |
| 6572 } | 6780 float newCursorXOffset = 0; |
| 6573 currentLineText[currentLineLen] = '\0'; | 6781 for (int k = newCursorLineStart; k < textBoxCursorIndex && k < textLength; k++) |
| 6574 | 6782 { |
| 6575 cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(currentLineText) + GuiGetStyle(DEFAULT, TEXT_SPACING); | 6783 if (text[k] == '\n') break; |
| 6576 cursor.y = textBounds.y + (newCursorLine * lineHeight * 1.5); | 6784 int cpSize = 0; |
| 6577 } | 6785 int cp = GetCodepointNext(&text[k], &cpSize); |
| 6578 else | 6786 int gi = GetGlyphIndex(guiFont, cp); |
| 6579 { | 6787 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; |
| 6580 cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); | 6788 newCursorXOffset += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); |
| 6581 } | 6789 } |
| 6790 | |
| 6791 cursor.x = textBounds.x + newCursorXOffset; | |
| 6792 cursor.y = textBounds.y + newCursorVisLine * lineHeight; | |
| 6582 | 6793 |
| 6583 // Finish text editing on ENTER or mouse click outside bounds | 6794 // Finish text editing on ENTER or mouse click outside bounds |
| 6584 if ((!multiline && IsKeyPressed(KEY_ENTER)) || | 6795 if ((!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) |
| 6585 (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) | |
| 6586 { | 6796 { |
| 6587 textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index | 6797 textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index |
| 6588 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes | 6798 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes |
| 6589 result = 1; | 6799 textBoxSelectionStart = -1; |
| 6800 textBoxSelectionEnd = -1; | |
| 6801 textBoxSelecting = false; | |
| 6802 resultStruct.result = 1; | |
| 6590 } | 6803 } |
| 6591 } | 6804 } |
| 6592 else | 6805 else |
| 6593 { | 6806 { |
| 6594 if (CheckCollisionPointRec(mousePosition, bounds)) | 6807 if (CheckCollisionPointRec(mousePosition, bounds)) |
| 6597 | 6810 |
| 6598 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) | 6811 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) |
| 6599 { | 6812 { |
| 6600 textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text | 6813 textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text |
| 6601 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes | 6814 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes |
| 6602 result = 1; | 6815 textBoxSelectionStart = -1; |
| 6816 textBoxSelectionEnd = -1; | |
| 6817 resultStruct.result = 1; | |
| 6603 } | 6818 } |
| 6604 } | 6819 } |
| 6605 } | 6820 } |
| 6606 } | 6821 } |
| 6607 //-------------------------------------------------------------------- | 6822 //-------------------------------------------------------------------- |
| 6616 { | 6831 { |
| 6617 GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); | 6832 GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); |
| 6618 } | 6833 } |
| 6619 else GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); | 6834 else GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); |
| 6620 | 6835 |
| 6621 // Draw text considering index offset if required | 6836 // Draw selection highlight using visual lines |
| 6622 // NOTE: Text index offset depends on cursor position | 6837 if (editMode && textBoxSelectionStart >= 0 && textBoxSelectionEnd >= 0 && textBoxSelectionStart != textBoxSelectionEnd) |
| 6623 // Set vertical alignment to top for multiline | 6838 { |
| 6839 int selMin = (textBoxSelectionStart < textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd; | |
| 6840 int selMax = (textBoxSelectionStart > textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd; | |
| 6841 | |
| 6842 // Find visual line for selection start | |
| 6843 int selStartVisLine = 0; | |
| 6844 for (int vl = visualLineCount - 1; vl >= 0; vl--) | |
| 6845 { | |
| 6846 if (selMin >= visualLineStarts[vl]) { selStartVisLine = vl; break; } | |
| 6847 } | |
| 6848 | |
| 6849 // Find visual line for selection end | |
| 6850 int selEndVisLine = 0; | |
| 6851 for (int vl = visualLineCount - 1; vl >= 0; vl--) | |
| 6852 { | |
| 6853 if (selMax >= visualLineStarts[vl]) { selEndVisLine = vl; break; } | |
| 6854 } | |
| 6855 | |
| 6856 Color selectionColor = (Color){ 100, 150, 255, 100 }; | |
| 6857 | |
| 6858 // Draw selection for each visual line in range | |
| 6859 for (int vl = selStartVisLine; vl <= selEndVisLine; vl++) | |
| 6860 { | |
| 6861 int vlStart = visualLineStarts[vl]; | |
| 6862 int vlEnd = (vl + 1 < visualLineCount) ? visualLineStarts[vl + 1] : textLength; | |
| 6863 if (vlEnd > vlStart && vlEnd <= textLength && text[vlEnd - 1] == '\n') vlEnd--; | |
| 6864 | |
| 6865 // Determine selection bounds within this visual line | |
| 6866 int drawStart = (vl == selStartVisLine) ? selMin : vlStart; | |
| 6867 int drawEnd = (vl == selEndVisLine) ? selMax : vlEnd; | |
| 6868 | |
| 6869 // Calculate X positions | |
| 6870 float xStart = 0; | |
| 6871 for (int k = vlStart; k < drawStart && k < textLength; k++) | |
| 6872 { | |
| 6873 if (text[k] == '\n') break; | |
| 6874 int cpSize = 0; | |
| 6875 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6876 int gi = GetGlyphIndex(guiFont, cp); | |
| 6877 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6878 xStart += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6879 } | |
| 6880 | |
| 6881 float xEnd = 0; | |
| 6882 for (int k = vlStart; k < drawEnd && k < textLength; k++) | |
| 6883 { | |
| 6884 if (text[k] == '\n') break; | |
| 6885 int cpSize = 0; | |
| 6886 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6887 int gi = GetGlyphIndex(guiFont, cp); | |
| 6888 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6889 xEnd += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6890 } | |
| 6891 | |
| 6892 Rectangle selRect = { | |
| 6893 textBounds.x + xStart, | |
| 6894 textBounds.y + vl * lineHeight, | |
| 6895 xEnd - xStart, | |
| 6896 (float)GuiGetStyle(DEFAULT, TEXT_SIZE) | |
| 6897 }; | |
| 6898 GuiDrawRectangle(selRect, 0, BLANK, selectionColor); | |
| 6899 } | |
| 6900 } | |
| 6901 | |
| 6902 // Draw text with word wrap enabled | |
| 6624 int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); | 6903 int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); |
| 6625 if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); | 6904 int prevWrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); |
| 6626 | 6905 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); |
| 6627 GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); | 6906 GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_CHAR); |
| 6628 | 6907 |
| 6629 // Restore previous vertical alignment | 6908 GuiDrawText(text, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); |
| 6630 if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment); | 6909 |
| 6910 // Restore previous settings | |
| 6911 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment); | |
| 6912 GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, prevWrapMode); | |
| 6631 | 6913 |
| 6632 // Draw cursor | 6914 // Draw cursor |
| 6633 if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) | 6915 if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) |
| 6634 { | 6916 { |
| 6635 //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) | 6917 //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) |
| 6639 // if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); | 6921 // if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); |
| 6640 } | 6922 } |
| 6641 else if (state == STATE_FOCUSED) GuiTooltip(bounds); | 6923 else if (state == STATE_FOCUSED) GuiTooltip(bounds); |
| 6642 //-------------------------------------------------------------------- | 6924 //-------------------------------------------------------------------- |
| 6643 | 6925 |
| 6644 return result; // Mouse button pressed: result = 1 | 6926 // Return selection info |
| 6927 resultStruct.selectionStart = textBoxSelectionStart; | |
| 6928 resultStruct.selectionEnd = textBoxSelectionEnd; | |
| 6929 | |
| 6930 return resultStruct; | |
| 6645 } | 6931 } |
| 6646 | 6932 |
| 6647 void JUNE_DrawRectangleLinesNoBottom(Rectangle rect, float thickness, Color color) { | 6933 void JUNE_DrawRectangleLinesNoBottom(Rectangle rect, float thickness, Color color) { |
| 6648 Vector2 topLeft = { rect.x, rect.y }; | 6934 Vector2 topLeft = { rect.x, rect.y }; |
| 6649 Vector2 topRight = { rect.x + rect.width, rect.y }; | 6935 Vector2 topRight = { rect.x + rect.width, rect.y }; |