comparison third_party/raylib/include/raygui.h @ 111:48f260576059

[PostDog] Rewriting it from scratch as it is unreadable for me.
author June Park <parkjune1995@gmail.com>
date Sun, 04 Jan 2026 06:39:16 -0800
parents b3e82d22f961
children d6d578b49a19
comparison
equal deleted inserted replaced
110:99c4530e4629 111:48f260576059
314 * Daniel Nicolas: Initial implementation of basic components (2014) 314 * Daniel Nicolas: Initial implementation of basic components (2014)
315 * 315 *
316 * 316 *
317 * LICENSE: zlib/libpng 317 * LICENSE: zlib/libpng
318 * 318 *
319 * Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) 319 * Copyright (c) 2014-2026 Ramon Santamaria (@raysan5)
320 * 320 *
321 * This software is provided "as-is", without any express or implied warranty. In no event 321 * This software is provided "as-is", without any express or implied warranty. In no event
322 * will the authors be held liable for any damages arising from the use of this software. 322 * will the authors be held liable for any damages arising from the use of this software.
323 * 323 *
324 * Permission is granted to anyone to use this software for any purpose, including commercial 324 * Permission is granted to anyone to use this software for any purpose, including commercial
2504 #endif 2504 #endif
2505 2505
2506 int result = 0; 2506 int result = 0;
2507 GuiState state = guiState; 2507 GuiState state = guiState;
2508 2508
2509 bool multiline = true; // TODO: Consider multiline text input 2509 bool multiline = false; // TODO: Consider multiline text input
2510 int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); 2510 int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
2511 2511
2512 Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); 2512 Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
2513 int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length 2513 int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
2514 int thisCursorIndex = textBoxCursorIndex; 2514 int thisCursorIndex = textBoxCursorIndex;
2515 if (thisCursorIndex > textLength) thisCursorIndex = textLength; 2515 if (thisCursorIndex > textLength) thisCursorIndex = textLength;
2516 2516 int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
2517 // Calculate cursor position for multiline
2518 int cursorLine = 0; // Current line number (0-based)
2519 int lineStart = 0; // Start index of current line
2520
2521 if (multiline)
2522 {
2523 // Count newlines before cursor to determine line number
2524 for (int i = 0; i < thisCursorIndex; i++)
2525 {
2526 if (text[i] == '\n')
2527 {
2528 cursorLine++;
2529 lineStart = i + 1;
2530 }
2531 }
2532 }
2533
2534 // Calculate horizontal position within current line
2535 char lineText[1024] = { 0 };
2536 int lineTextLen = 0;
2537 if (multiline)
2538 {
2539 // Extract current line text up to cursor
2540 int i = lineStart;
2541 while (i < thisCursorIndex && text[i] != '\n' && lineTextLen < 1023)
2542 {
2543 lineText[lineTextLen++] = text[i++];
2544 }
2545 lineText[lineTextLen] = '\0';
2546 }
2547
2548 int textWidth = multiline ? GuiGetTextWidth(lineText) : (GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex));
2549 int textIndexOffset = 0; // Text index offset to start drawing in the box 2517 int textIndexOffset = 0; // Text index offset to start drawing in the box
2550 2518
2551 // Line height for multiline (matches GuiDrawText line spacing)
2552 int lineHeight = GuiGetStyle(DEFAULT, TEXT_SIZE);
2553
2554 // Cursor rectangle 2519 // Cursor rectangle
2555 // NOTE: Position X and Y values updated for multiline support 2520 // NOTE: Position X value should be updated
2556 Rectangle cursor = { 2521 Rectangle cursor = {
2557 textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), 2522 textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING),
2558 multiline ? (textBounds.y + cursorLine * lineHeight) : (textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)), 2523 textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE),
2559 2, 2524 2,
2560 (float)GuiGetStyle(DEFAULT, TEXT_SIZE) 2525 (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2
2561 }; 2526 };
2562 2527
2563 if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; 2528 if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
2564 if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); 2529 if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH);
2565 2530
2851 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); 2816 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
2852 2817
2853 textBoxCursorIndex += nextCodepointSize; 2818 textBoxCursorIndex += nextCodepointSize;
2854 } 2819 }
2855 2820
2856 // Vertical cursor movement for multiline
2857 if (multiline && (IsKeyPressed(KEY_UP) || (IsKeyDown(KEY_UP) && autoCursorShouldTrigger)))
2858 {
2859 // Find start of current line
2860 int currentLineStart = textBoxCursorIndex;
2861 while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--;
2862
2863 // Calculate horizontal position in current line
2864 int horizontalPos = textBoxCursorIndex - currentLineStart;
2865
2866 // Find start of previous line
2867 if (currentLineStart > 0)
2868 {
2869 int prevLineEnd = currentLineStart - 1; // Skip the newline
2870 int prevLineStart = prevLineEnd;
2871 while (prevLineStart > 0 && text[prevLineStart - 1] != '\n') prevLineStart--;
2872
2873 // Move to same horizontal position on previous line (or end of line if shorter)
2874 int prevLineLength = prevLineEnd - prevLineStart;
2875 int targetPos = (horizontalPos < prevLineLength) ? horizontalPos : prevLineLength;
2876 textBoxCursorIndex = prevLineStart + targetPos;
2877 }
2878 else
2879 {
2880 // Already on first line, move to start
2881 textBoxCursorIndex = 0;
2882 }
2883 }
2884 else if (multiline && (IsKeyPressed(KEY_DOWN) || (IsKeyDown(KEY_DOWN) && autoCursorShouldTrigger)))
2885 {
2886 // Find start of current line
2887 int currentLineStart = textBoxCursorIndex;
2888 while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--;
2889
2890 // Calculate horizontal position in current line
2891 int horizontalPos = textBoxCursorIndex - currentLineStart;
2892
2893 // Find end of current line
2894 int currentLineEnd = textBoxCursorIndex;
2895 while (currentLineEnd < textLength && text[currentLineEnd] != '\n') currentLineEnd++;
2896
2897 // Find next line
2898 if (currentLineEnd < textLength)
2899 {
2900 int nextLineStart = currentLineEnd + 1; // Skip the newline
2901 int nextLineEnd = nextLineStart;
2902 while (nextLineEnd < textLength && text[nextLineEnd] != '\n') nextLineEnd++;
2903
2904 // Move to same horizontal position on next line (or end of line if shorter)
2905 int nextLineLength = nextLineEnd - nextLineStart;
2906 int targetPos = (horizontalPos < nextLineLength) ? horizontalPos : nextLineLength;
2907 textBoxCursorIndex = nextLineStart + targetPos;
2908 }
2909 else
2910 {
2911 // Already on last line, move to end
2912 textBoxCursorIndex = textLength;
2913 }
2914 }
2915
2916 // Move cursor position with mouse 2821 // Move cursor position with mouse
2917 if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text 2822 if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text
2918 { 2823 {
2919 float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; 2824 float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize;
2920 int codepointIndex = 0; 2825 int codepointIndex = 0;
2955 textBoxCursorIndex = mouseCursorIndex; 2860 textBoxCursorIndex = mouseCursorIndex;
2956 } 2861 }
2957 } 2862 }
2958 else mouseCursor.x = -1; 2863 else mouseCursor.x = -1;
2959 2864
2960 // Recalculate cursor position for multiline 2865 // Recalculate cursor position.y depending on textBoxCursorIndex
2961 if (multiline) 2866 cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
2962 { 2867 // if (multiline) GetTextLines(text, &cursor.y);
2963 // Recalculate cursor line and position
2964 int newCursorLine = 0;
2965 int newLineStart = 0;
2966
2967 for (int i = 0; i < textBoxCursorIndex; i++)
2968 {
2969 if (text[i] == '\n')
2970 {
2971 newCursorLine++;
2972 newLineStart = i + 1;
2973 }
2974 }
2975
2976 // Extract current line text up to cursor
2977 char currentLineText[1024] = { 0 };
2978 int currentLineLen = 0;
2979 int i = newLineStart;
2980 while (i < textBoxCursorIndex && text[i] != '\n' && currentLineLen < 1023)
2981 {
2982 currentLineText[currentLineLen++] = text[i++];
2983 }
2984 currentLineText[currentLineLen] = '\0';
2985
2986 cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(currentLineText) + GuiGetStyle(DEFAULT, TEXT_SPACING);
2987 cursor.y = textBounds.y + (newCursorLine * lineHeight * 1.5);
2988 }
2989 else
2990 {
2991 cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
2992 }
2993 2868
2994 // Finish text editing on ENTER or mouse click outside bounds 2869 // Finish text editing on ENTER or mouse click outside bounds
2995 if ((!multiline && IsKeyPressed(KEY_ENTER)) || 2870 if ((!multiline && IsKeyPressed(KEY_ENTER)) ||
2996 (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) 2871 (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
2997 { 2872 {
3029 } 2904 }
3030 else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); 2905 else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK);
3031 2906
3032 // Draw text considering index offset if required 2907 // Draw text considering index offset if required
3033 // NOTE: Text index offset depends on cursor position 2908 // NOTE: Text index offset depends on cursor position
3034 // Set vertical alignment to top for multiline
3035 int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL);
3036 if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP);
3037
3038 GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); 2909 GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))));
3039
3040 // Restore previous vertical alignment
3041 if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment);
3042 2910
3043 // Draw cursor 2911 // Draw cursor
3044 if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) 2912 if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY))
3045 { 2913 {
3046 //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) 2914 //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0))
4328 GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); 4196 GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
4329 GuiLabel(textBounds, message); 4197 GuiLabel(textBounds, message);
4330 GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); 4198 GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
4331 } 4199 }
4332 4200
4333 int prevTextBoxAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT);
4334 GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
4335
4336 if (secretViewActive != NULL) 4201 if (secretViewActive != NULL)
4337 { 4202 {
4338 static char stars[] = "****************"; 4203 static char stars[] = "****************";
4339 if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height }, 4204 if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height },
4340 ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode; 4205 ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode;
4343 } 4208 }
4344 else 4209 else
4345 { 4210 {
4346 if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode; 4211 if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode;
4347 } 4212 }
4348 GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, prevTextBoxAlignment);
4349 4213
4350 int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); 4214 int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
4351 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); 4215 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
4352 4216
4353 for (int i = 0; i < buttonCount; i++) 4217 for (int i = 0; i < buttonCount; i++)
4365 } 4229 }
4366 4230
4367 // Grid control 4231 // Grid control
4368 // NOTE: Returns grid mouse-hover selected cell 4232 // NOTE: Returns grid mouse-hover selected cell
4369 // About drawing lines at subpixel spacing, simple put, not easy solution: 4233 // About drawing lines at subpixel spacing, simple put, not easy solution:
4370 // Ref: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster 4234 // REF: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
4371 int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell) 4235 int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell)
4372 { 4236 {
4373 // Grid lines alpha amount 4237 // Grid lines alpha amount
4374 #if !defined(RAYGUI_GRID_ALPHA) 4238 #if !defined(RAYGUI_GRID_ALPHA)
4375 #define RAYGUI_GRID_ALPHA 0.15f 4239 #define RAYGUI_GRID_ALPHA 0.15f
6118 5982
6119 return codepoint; 5983 return codepoint;
6120 } 5984 }
6121 #endif // RAYGUI_STANDALONE 5985 #endif // RAYGUI_STANDALONE
6122 5986
6123 #endif // RAYGUI_IMPLEMENTATION 5987
5988 // --- Custom --- //
5989 int JUNE_GuiButton(Rectangle bounds, const char *text, int borderWidth, Color borderColor)
5990 {
5991 int result = 0;
5992 GuiState state = guiState;
5993
5994 // Update control
5995 //--------------------------------------------------------------------
5996 if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
5997 {
5998 Vector2 mousePoint = GetMousePosition();
5999
6000 // Check button state
6001 if (CheckCollisionPointRec(mousePoint, bounds))
6002 {
6003 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
6004 else state = STATE_FOCUSED;
6005
6006 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1;
6007 }
6008 }
6009 //--------------------------------------------------------------------
6010
6011 // Draw control
6012 //--------------------------------------------------------------------
6013 GuiDrawRectangle(bounds, borderWidth, borderColor, GetColor(GuiGetStyle(BUTTON, BASE + (state*3))));
6014 GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))));
6015
6016 if (state == STATE_FOCUSED) GuiTooltip(bounds);
6017 //------------------------------------------------------------------
6018
6019 return result; // Button pressed: result = 1
6020 }
6021
6022 int JUNE_GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
6023 {
6024 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
6025 #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement
6026 #endif
6027 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
6028 #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement
6029 #endif
6030
6031 int result = 0;
6032 GuiState state = guiState;
6033
6034 bool multiline = true;
6035 int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
6036
6037 Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
6038 int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
6039 int thisCursorIndex = textBoxCursorIndex;
6040 if (thisCursorIndex > textLength) thisCursorIndex = textLength;
6041 int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
6042 int textIndexOffset = 0; // Text index offset to start drawing in the box
6043
6044 // Cursor rectangle
6045 // NOTE: Position X value should be updated
6046 Rectangle cursor = {
6047 textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING),
6048 textBounds.y + GuiGetStyle(DEFAULT, TEXT_SIZE),
6049 2,
6050 (float)GuiGetStyle(DEFAULT, TEXT_SIZE)
6051 };
6052
6053 if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
6054 if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH);
6055
6056 // Mouse cursor rectangle
6057 // NOTE: Initialized outside of screen
6058 Rectangle mouseCursor = cursor;
6059 mouseCursor.x = -1;
6060 mouseCursor.width = 1;
6061
6062 // Blink-cursor frame counter
6063 //if (!autoCursorMode) blinkCursorFrameCounter++;
6064 //else blinkCursorFrameCounter = 0;
6065
6066 // Update control
6067 //--------------------------------------------------------------------
6068 // WARNING: Text editing is only supported under certain conditions:
6069 if ((state != STATE_DISABLED) && // Control not disabled
6070 !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode
6071 !guiLocked && // Gui not locked
6072 !guiControlExclusiveMode && // No gui slider on dragging
6073 (wrapMode == TEXT_WRAP_NONE)) // No wrap mode
6074 {
6075 Vector2 mousePosition = GetMousePosition();
6076
6077 if (editMode)
6078 {
6079 // GLOBAL: Auto-cursor movement logic
6080 // NOTE: Keystrokes are handled repeatedly when button is held down for some time
6081 if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
6082 else autoCursorCounter = 0;
6083
6084 bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
6085
6086 state = STATE_PRESSED;
6087
6088 if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
6089
6090 // If text does not fit in the textbox and current cursor position is out of bounds,
6091 // we add an index offset to text for drawing only what requires depending on cursor
6092 while (textWidth >= textBounds.width)
6093 {
6094 int nextCodepointSize = 0;
6095 GetCodepointNext(text + textIndexOffset, &nextCodepointSize);
6096
6097 textIndexOffset += nextCodepointSize;
6098
6099 textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
6100 }
6101
6102 int codepoint = GetCharPressed(); // Get Unicode codepoint
6103 if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n';
6104
6105 // Encode codepoint as UTF-8
6106 int codepointSize = 0;
6107 const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
6108
6109 // Handle text paste action
6110 if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
6111 {
6112 const char *pasteText = GetClipboardText();
6113 if (pasteText != NULL)
6114 {
6115 int pasteLength = 0;
6116 int pasteCodepoint;
6117 int pasteCodepointSize;
6118
6119 // Count how many codepoints to copy, stopping at the first unwanted control character
6120 while (true)
6121 {
6122 pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
6123 if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
6124 if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
6125 pasteLength += pasteCodepointSize;
6126 }
6127
6128 if (pasteLength > 0)
6129 {
6130 // Move forward data from cursor position
6131 for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
6132
6133 // Paste data in at cursor
6134 for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
6135
6136 textBoxCursorIndex += pasteLength;
6137 textLength += pasteLength;
6138 text[textLength] = '\0';
6139 }
6140 }
6141 }
6142 else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
6143 {
6144 // Adding codepoint to text, at current cursor position
6145
6146 // Move forward data from cursor position
6147 for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
6148
6149 // Add new codepoint in current cursor position
6150 for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i];
6151
6152 textBoxCursorIndex += codepointSize;
6153 textLength += codepointSize;
6154
6155 // Make sure text last character is EOL
6156 text[textLength] = '\0';
6157 }
6158
6159 // Move cursor to start
6160 if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0;
6161
6162 // Move cursor to end
6163 if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
6164
6165 // Delete related codepoints from text, after current cursor position
6166 if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
6167 {
6168 int offset = textBoxCursorIndex;
6169 int accCodepointSize = 0;
6170 int nextCodepointSize;
6171 int nextCodepoint;
6172
6173 // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
6174 // Not using isalnum() since it only works on ASCII characters
6175 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
6176 bool puctuation = ispunct(nextCodepoint & 0xff);
6177 while (offset < textLength)
6178 {
6179 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
6180 break;
6181 offset += nextCodepointSize;
6182 accCodepointSize += nextCodepointSize;
6183 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
6184 }
6185
6186 // Check whitespace to delete (ASCII only)
6187 while (offset < textLength)
6188 {
6189 if (!isspace(nextCodepoint & 0xff)) break;
6190
6191 offset += nextCodepointSize;
6192 accCodepointSize += nextCodepointSize;
6193 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
6194 }
6195
6196 // Move text after cursor forward (including final null terminator)
6197 for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
6198
6199 textLength -= accCodepointSize;
6200 }
6201
6202 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
6203 {
6204 // Delete single codepoint from text, after current cursor position
6205
6206 int nextCodepointSize = 0;
6207 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
6208
6209 // Move text after cursor forward (including final null terminator)
6210 for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
6211
6212 textLength -= nextCodepointSize;
6213 }
6214
6215 // Delete related codepoints from text, before current cursor position
6216 if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
6217 {
6218 int offset = textBoxCursorIndex;
6219 int accCodepointSize = 0;
6220 int prevCodepointSize = 0;
6221 int prevCodepoint = 0;
6222
6223 // Check whitespace to delete (ASCII only)
6224 while (offset > 0)
6225 {
6226 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
6227 if (!isspace(prevCodepoint & 0xff)) break;
6228
6229 offset -= prevCodepointSize;
6230 accCodepointSize += prevCodepointSize;
6231 }
6232
6233 // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
6234 // Not using isalnum() since it only works on ASCII characters
6235 bool puctuation = ispunct(prevCodepoint & 0xff);
6236 while (offset > 0)
6237 {
6238 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
6239 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
6240
6241 offset -= prevCodepointSize;
6242 accCodepointSize += prevCodepointSize;
6243 }
6244
6245 // Move text after cursor forward (including final null terminator)
6246 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
6247
6248 textLength -= accCodepointSize;
6249 textBoxCursorIndex -= accCodepointSize;
6250 }
6251
6252 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
6253 {
6254 // Delete single codepoint from text, before current cursor position
6255
6256 int prevCodepointSize = 0;
6257
6258 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
6259
6260 // Move text after cursor forward (including final null terminator)
6261 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
6262
6263 textLength -= prevCodepointSize;
6264 textBoxCursorIndex -= prevCodepointSize;
6265 }
6266
6267 // Move cursor position with keys
6268 if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
6269 {
6270 int offset = textBoxCursorIndex;
6271 //int accCodepointSize = 0;
6272 int prevCodepointSize = 0;
6273 int prevCodepoint = 0;
6274
6275 // Check whitespace to skip (ASCII only)
6276 while (offset > 0)
6277 {
6278 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
6279 if (!isspace(prevCodepoint & 0xff)) break;
6280
6281 offset -= prevCodepointSize;
6282 //accCodepointSize += prevCodepointSize;
6283 }
6284
6285 // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
6286 // Not using isalnum() since it only works on ASCII characters
6287 bool puctuation = ispunct(prevCodepoint & 0xff);
6288 while (offset > 0)
6289 {
6290 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
6291 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
6292
6293 offset -= prevCodepointSize;
6294 //accCodepointSize += prevCodepointSize;
6295 }
6296
6297 textBoxCursorIndex = offset;
6298 }
6299 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
6300 {
6301 int prevCodepointSize = 0;
6302 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
6303
6304 textBoxCursorIndex -= prevCodepointSize;
6305 }
6306 else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
6307 {
6308 int offset = textBoxCursorIndex;
6309 //int accCodepointSize = 0;
6310 int nextCodepointSize;
6311 int nextCodepoint;
6312
6313 // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
6314 // Not using isalnum() since it only works on ASCII characters
6315 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
6316 bool puctuation = ispunct(nextCodepoint & 0xff);
6317 while (offset < textLength)
6318 {
6319 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
6320
6321 offset += nextCodepointSize;
6322 //accCodepointSize += nextCodepointSize;
6323 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
6324 }
6325
6326 // Check whitespace to skip (ASCII only)
6327 while (offset < textLength)
6328 {
6329 if (!isspace(nextCodepoint & 0xff)) break;
6330
6331 offset += nextCodepointSize;
6332 //accCodepointSize += nextCodepointSize;
6333 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
6334 }
6335
6336 textBoxCursorIndex = offset;
6337 }
6338 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
6339 {
6340 int nextCodepointSize = 0;
6341 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
6342
6343 textBoxCursorIndex += nextCodepointSize;
6344 }
6345
6346 // Move cursor position with mouse
6347 if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text
6348 {
6349 float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize;
6350 int codepointIndex = 0;
6351 float glyphWidth = 0.0f;
6352 float widthToMouseX = 0;
6353 int mouseCursorIndex = 0;
6354
6355 for (int i = textIndexOffset; i < textLength; i += codepointSize)
6356 {
6357 codepoint = GetCodepointNext(&text[i], &codepointSize);
6358 codepointIndex = GetGlyphIndex(guiFont, codepoint);
6359
6360 if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
6361 else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
6362
6363 if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2)))
6364 {
6365 mouseCursor.x = textBounds.x + widthToMouseX;
6366 mouseCursorIndex = i;
6367 break;
6368 }
6369
6370 widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
6371 }
6372
6373 // Check if mouse cursor is at the last position
6374 int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
6375 if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
6376 {
6377 mouseCursor.x = textBounds.x + textEndWidth;
6378 mouseCursorIndex = textLength;
6379 }
6380
6381 // Place cursor at required index on mouse click
6382 if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
6383 {
6384 cursor.x = mouseCursor.x;
6385 textBoxCursorIndex = mouseCursorIndex;
6386 }
6387 }
6388 else mouseCursor.x = -1;
6389
6390 // Recalculate cursor position.y depending on textBoxCursorIndex
6391 cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
6392 if (multiline) {
6393 const char **lines = GetTextLines(text, &cursor.y);
6394 printf("Cursor: %f", cursor.y);
6395 }
6396
6397 // Finish text editing on ENTER or mouse click outside bounds
6398 if ((!multiline && IsKeyPressed(KEY_ENTER)) ||
6399 (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
6400 {
6401 textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index
6402 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes
6403 result = 1;
6404 }
6405 }
6406 else
6407 {
6408 if (CheckCollisionPointRec(mousePosition, bounds))
6409 {
6410 state = STATE_FOCUSED;
6411
6412 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
6413 {
6414 textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text
6415 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes
6416 result = 1;
6417 }
6418 }
6419 }
6420 }
6421 //--------------------------------------------------------------------
6422
6423 // Draw control
6424 //--------------------------------------------------------------------
6425 if (state == STATE_PRESSED)
6426 {
6427 GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)));
6428 }
6429 else if (state == STATE_DISABLED)
6430 {
6431 GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)));
6432 }
6433 else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK);
6434
6435
6436
6437 int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL);
6438 if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP);
6439
6440 // Draw text considering index offset if required
6441 // NOTE: Text index offset depends on cursor position
6442 GuiDrawText(
6443 text + textIndexOffset,
6444 textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))));
6445
6446 // RESET
6447 if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment);
6448
6449 // Draw cursor
6450 if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY))
6451 {
6452 //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0))
6453 GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)));
6454
6455 // Draw mouse position cursor (if required)
6456 if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)));
6457 }
6458 else if (state == STATE_FOCUSED) GuiTooltip(bounds);
6459 //--------------------------------------------------------------------
6460
6461 return result; // Mouse button pressed: result = 1
6462 }
6463
6464
6465 #endif // RAYGUI_IMPLEMENTnk_command_bufferATION