Mercurial
comparison third_party/raylib/include/raygui.h @ 173:827c6ac504cd hg-web
Merged in default here.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 19 Jan 2026 18:59:10 -0800 |
| parents | 058de208e640 |
| children |
comparison
equal
deleted
inserted
replaced
| 151:c033667da5f9 | 173:827c6ac504cd |
|---|---|
| 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, |
| 734 RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control | 741 RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control |
| 735 | 742 |
| 736 // Basic controls set | 743 // Basic controls set |
| 737 RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control | 744 RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control |
| 738 RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked | 745 RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked |
| 746 RAYGUIAPI int GuiButtonRounded(Rectangle bounds, const char *text, float roundness, int segments); | |
| 739 RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, returns true when clicked | 747 RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, returns true when clicked |
| 740 RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control | 748 RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control |
| 741 RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control | 749 RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control |
| 742 RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control | 750 RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control |
| 743 RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active | 751 RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active |
| 744 RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control | 752 RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control |
| 745 | 753 |
| 746 RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control | 754 RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control |
| 755 RAYGUIAPI int GuiDropdownBoxRounded(Rectangle bounds, const char *text, int *active, bool editMode, float roundness, int segments); | |
| 747 RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control | 756 RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control |
| 748 RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers | 757 RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers |
| 749 RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values | 758 RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values |
| 750 RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text | 759 RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text |
| 751 | 760 |
| 1408 static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier | 1417 static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier |
| 1409 | 1418 |
| 1410 static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() | 1419 static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() |
| 1411 //static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking | 1420 //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) | 1421 static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay) |
| 1422 static int textBoxSelectionStart = -1; // Selection start index (-1 if no selection) | |
| 1423 static int textBoxSelectionEnd = -1; // Selection end index (-1 if no selection) | |
| 1424 static bool textBoxSelecting = false; // Currently selecting with mouse | |
| 1413 | 1425 |
| 1414 //---------------------------------------------------------------------------------- | 1426 //---------------------------------------------------------------------------------- |
| 1415 // Style data array for all gui style properties (allocated on data segment by default) | 1427 // Style data array for all gui style properties (allocated on data segment by default) |
| 1416 // | 1428 // |
| 1417 // NOTE 1: First set of BASE properties are generic to all controls but could be individually | 1429 // NOTE 1: First set of BASE properties are generic to all controls but could be individually |
| 2027 //------------------------------------------------------------------ | 2039 //------------------------------------------------------------------ |
| 2028 | 2040 |
| 2029 return result; // Button pressed: result = 1 | 2041 return result; // Button pressed: result = 1 |
| 2030 } | 2042 } |
| 2031 | 2043 |
| 2044 // JUNE | |
| 2045 int GuiButtonRounded(Rectangle bounds, const char *text, float roundness, int segments) | |
| 2046 { | |
| 2047 int result = 0; | |
| 2048 GuiState state = guiState; | |
| 2049 | |
| 2050 // Update control | |
| 2051 //-------------------------------------------------------------------- | |
| 2052 if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) | |
| 2053 { | |
| 2054 Vector2 mousePoint = GetMousePosition(); | |
| 2055 | |
| 2056 // Check button state | |
| 2057 if (CheckCollisionPointRec(mousePoint, bounds)) | |
| 2058 { | |
| 2059 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; | |
| 2060 else state = STATE_FOCUSED; | |
| 2061 | |
| 2062 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; | |
| 2063 } | |
| 2064 } | |
| 2065 //-------------------------------------------------------------------- | |
| 2066 | |
| 2067 // Draw control | |
| 2068 //-------------------------------------------------------------------- | |
| 2069 DrawRectangleRounded(bounds, roundness, segments, GetColor(GuiGetStyle(BUTTON, BASE + (state*3)))); | |
| 2070 GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3)))); | |
| 2071 | |
| 2072 if (state == STATE_FOCUSED) GuiTooltip(bounds); | |
| 2073 //------------------------------------------------------------------ | |
| 2074 | |
| 2075 return result; // Button pressed: result = 1 | |
| 2076 } | |
| 2077 | |
| 2032 // Label button control | 2078 // Label button control |
| 2033 int GuiLabelButton(Rectangle bounds, const char *text) | 2079 int GuiLabelButton(Rectangle bounds, const char *text) |
| 2034 { | 2080 { |
| 2035 GuiState state = guiState; | 2081 GuiState state = guiState; |
| 2036 bool pressed = false; | 2082 bool pressed = false; |
| 2539 | 2585 |
| 2540 // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item... | 2586 // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item... |
| 2541 return result; // Mouse click: result = 1 | 2587 return result; // Mouse click: result = 1 |
| 2542 } | 2588 } |
| 2543 | 2589 |
| 2590 // JUNE | |
| 2591 int GuiDropdownBoxRounded(Rectangle bounds, const char *text, int *active, bool editMode, float roundness, int segments) | |
| 2592 { | |
| 2593 int result = 0; | |
| 2594 GuiState state = guiState; | |
| 2595 | |
| 2596 int temp = 0; | |
| 2597 if (active == NULL) active = &temp; | |
| 2598 | |
| 2599 int itemSelected = *active; | |
| 2600 int itemFocused = -1; | |
| 2601 | |
| 2602 int direction = 0; // Dropdown box open direction: down (default) | |
| 2603 if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up | |
| 2604 | |
| 2605 // Get substrings items from text (items pointers, lengths and count) | |
| 2606 int itemCount = 0; | |
| 2607 const char **items = GuiTextSplit(text, ';', &itemCount, NULL); | |
| 2608 | |
| 2609 Rectangle boundsOpen = bounds; | |
| 2610 boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); | |
| 2611 if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING); | |
| 2612 | |
| 2613 Rectangle itemBounds = bounds; | |
| 2614 | |
| 2615 // Update control | |
| 2616 //-------------------------------------------------------------------- | |
| 2617 if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode) | |
| 2618 { | |
| 2619 Vector2 mousePoint = GetMousePosition(); | |
| 2620 | |
| 2621 if (editMode) | |
| 2622 { | |
| 2623 state = STATE_PRESSED; | |
| 2624 | |
| 2625 // Check if mouse has been pressed or released outside limits | |
| 2626 if (!CheckCollisionPointRec(mousePoint, boundsOpen)) | |
| 2627 { | |
| 2628 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; | |
| 2629 } | |
| 2630 | |
| 2631 // Check if already selected item has been pressed again | |
| 2632 if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; | |
| 2633 | |
| 2634 // Check focused and selected item | |
| 2635 for (int i = 0; i < itemCount; i++) | |
| 2636 { | |
| 2637 // Update item rectangle y position for next item | |
| 2638 if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); | |
| 2639 else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); | |
| 2640 | |
| 2641 if (CheckCollisionPointRec(mousePoint, itemBounds)) | |
| 2642 { | |
| 2643 itemFocused = i; | |
| 2644 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) | |
| 2645 { | |
| 2646 itemSelected = i; | |
| 2647 result = 1; // Item selected | |
| 2648 } | |
| 2649 break; | |
| 2650 } | |
| 2651 } | |
| 2652 | |
| 2653 itemBounds = bounds; | |
| 2654 } | |
| 2655 else | |
| 2656 { | |
| 2657 if (CheckCollisionPointRec(mousePoint, bounds)) | |
| 2658 { | |
| 2659 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) | |
| 2660 { | |
| 2661 result = 1; | |
| 2662 state = STATE_PRESSED; | |
| 2663 } | |
| 2664 else state = STATE_FOCUSED; | |
| 2665 } | |
| 2666 } | |
| 2667 } | |
| 2668 //-------------------------------------------------------------------- | |
| 2669 | |
| 2670 // Draw control | |
| 2671 //-------------------------------------------------------------------- | |
| 2672 if (editMode) GuiPanel(boundsOpen, NULL); | |
| 2673 | |
| 2674 // Main (closed) control: draw rounded background + rounded border | |
| 2675 { | |
| 2676 int borderWidth = GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH); | |
| 2677 Color borderColor = GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)); | |
| 2678 Color baseColor = GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)); | |
| 2679 | |
| 2680 // Filled rounded rect | |
| 2681 DrawRectangleRounded(bounds, roundness, segments, baseColor); | |
| 2682 } | |
| 2683 | |
| 2684 GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3))); | |
| 2685 | |
| 2686 if (editMode) | |
| 2687 { | |
| 2688 // Draw visible items | |
| 2689 for (int i = 0; i < itemCount; i++) | |
| 2690 { | |
| 2691 // Update item rectangle y position for next item | |
| 2692 if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); | |
| 2693 else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); | |
| 2694 | |
| 2695 bool isLastVisible = (i == itemCount - 1); | |
| 2696 | |
| 2697 if (i == itemSelected) | |
| 2698 { | |
| 2699 Color borderColor = GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)); | |
| 2700 Color baseColor = GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)); | |
| 2701 int borderWidth = GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH); | |
| 2702 | |
| 2703 if (isLastVisible) | |
| 2704 { | |
| 2705 DrawRectangleRounded(itemBounds, roundness, segments, baseColor); | |
| 2706 } | |
| 2707 else | |
| 2708 { | |
| 2709 DrawRectangle(itemBounds.x, itemBounds.y, itemBounds.width, itemBounds.height, baseColor); | |
| 2710 if (borderWidth > 0) DrawRectangleLinesEx(itemBounds, borderWidth, borderColor); | |
| 2711 } | |
| 2712 | |
| 2713 GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED))); | |
| 2714 } | |
| 2715 else if (i == itemFocused) | |
| 2716 { | |
| 2717 Color borderColor = GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)); | |
| 2718 Color baseColor = GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)); | |
| 2719 int borderWidth = GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH); | |
| 2720 | |
| 2721 if (isLastVisible) | |
| 2722 { | |
| 2723 DrawRectangleRounded(itemBounds, roundness, segments, baseColor); | |
| 2724 } | |
| 2725 else | |
| 2726 { | |
| 2727 DrawRectangle(itemBounds.x, itemBounds.y, itemBounds.width, itemBounds.height, baseColor); | |
| 2728 if (borderWidth > 0) DrawRectangleLinesEx(itemBounds, borderWidth, borderColor); | |
| 2729 } | |
| 2730 | |
| 2731 GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED))); | |
| 2732 } | |
| 2733 else | |
| 2734 { | |
| 2735 // Normal item: draw only text (background left as panel background), | |
| 2736 // but if you want a visible background for normal items, uncomment the following: | |
| 2737 // DrawRectangle(itemBounds.x, itemBounds.y, itemBounds.width, itemBounds.height, GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_NORMAL))); | |
| 2738 GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL))); | |
| 2739 } | |
| 2740 } | |
| 2741 } | |
| 2742 | |
| 2743 if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN)) | |
| 2744 { | |
| 2745 // Draw arrows (using icon if available) | |
| 2746 #if defined(RAYGUI_NO_ICONS) | |
| 2747 GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, | |
| 2748 TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); | |
| 2749 #else | |
| 2750 GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, | |
| 2751 TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL | |
| 2752 #endif | |
| 2753 } | |
| 2754 //-------------------------------------------------------------------- | |
| 2755 | |
| 2756 *active = itemSelected; | |
| 2757 | |
| 2758 // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item... | |
| 2759 return result; // Mouse click: result = 1 | |
| 2760 } | |
| 2761 | |
| 2544 // Text Box control | 2762 // Text Box control |
| 2545 // NOTE: Returns true on ENTER pressed (useful for data validation) | 2763 // NOTE: Returns true on ENTER pressed (useful for data validation) |
| 2546 int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) | 2764 int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) |
| 2547 { | 2765 { |
| 2548 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) | 2766 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) |
| 2942 //-------------------------------------------------------------------- | 3160 //-------------------------------------------------------------------- |
| 2943 | 3161 |
| 2944 // Draw control | 3162 // Draw control |
| 2945 //-------------------------------------------------------------------- | 3163 //-------------------------------------------------------------------- |
| 2946 if (state == STATE_PRESSED) | 3164 if (state == STATE_PRESSED) |
| 2947 { | 3165 GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); |
| 2948 GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); | |
| 2949 } | |
| 2950 else if (state == STATE_DISABLED) | 3166 else if (state == STATE_DISABLED) |
| 2951 { | 3167 GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); |
| 2952 GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); | 3168 else GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); |
| 2953 } | |
| 2954 else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); | |
| 2955 | 3169 |
| 2956 // Draw text considering index offset if required | 3170 // Draw text considering index offset if required |
| 2957 // NOTE: Text index offset depends on cursor position | 3171 // NOTE: Text index offset depends on cursor position |
| 2958 GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); | 3172 GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); |
| 2959 | 3173 |
| 6064 //------------------------------------------------------------------ | 6278 //------------------------------------------------------------------ |
| 6065 | 6279 |
| 6066 return result; // Button pressed: result = 1 | 6280 return result; // Button pressed: result = 1 |
| 6067 } | 6281 } |
| 6068 | 6282 |
| 6069 int JUNE_GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) | 6283 JUNE_TextBoxResult JUNE_GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool editMode) |
| 6070 { | 6284 { |
| 6071 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) | 6285 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) |
| 6072 #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement | 6286 #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement |
| 6073 #endif | 6287 #endif |
| 6074 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) | 6288 #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) |
| 6075 #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement | 6289 #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement |
| 6076 #endif | 6290 #endif |
| 6077 | 6291 #if !defined(JUNE_MAX_VISUAL_LINES) |
| 6078 int result = 0; | 6292 #define JUNE_MAX_VISUAL_LINES 256 |
| 6293 #endif | |
| 6294 | |
| 6295 JUNE_TextBoxResult resultStruct = { 0, -1, -1 }; | |
| 6079 GuiState state = guiState; | 6296 GuiState state = guiState; |
| 6080 | 6297 |
| 6081 bool multiline = true; // TODO: Consider multiline text input | |
| 6082 int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); | |
| 6083 | |
| 6084 Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); | 6298 Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); |
| 6085 int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length | 6299 int textLength = (text != NULL) ? (int)strlen(text) : 0; |
| 6086 int thisCursorIndex = textBoxCursorIndex; | 6300 int thisCursorIndex = textBoxCursorIndex; |
| 6087 if (thisCursorIndex > textLength) thisCursorIndex = textLength; | 6301 if (thisCursorIndex > textLength) thisCursorIndex = textLength; |
| 6088 | 6302 |
| 6089 // Calculate cursor position for multiline | 6303 // Line height for multiline |
| 6090 int cursorLine = 0; // Current line number (0-based) | 6304 float lineHeight = GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); |
| 6091 int lineStart = 0; // Start index of current line | 6305 float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE) / (float)guiFont.baseSize; |
| 6092 | 6306 float maxLineWidth = textBounds.width - 4; // Small margin |
| 6093 if (multiline) | 6307 |
| 6094 { | 6308 // Visual line structure: stores start index of each visual line |
| 6095 for (int i = 0; i < thisCursorIndex; i++) | 6309 int visualLineStarts[JUNE_MAX_VISUAL_LINES]; |
| 6096 { | 6310 int visualLineCount = 0; |
| 6097 if (text[i] == '\n') | 6311 |
| 6098 { | 6312 // Calculate visual lines (accounting for word wrap) |
| 6099 cursorLine++; | 6313 if (textLength > 0 && maxLineWidth > 0) |
| 6100 lineStart = i + 1; | 6314 { |
| 6101 } | 6315 int lineStartIdx = 0; |
| 6102 } | 6316 visualLineStarts[visualLineCount++] = 0; |
| 6103 } | 6317 |
| 6104 | 6318 int idx = 0; |
| 6105 // Calculate horizontal position within current line | 6319 while (idx < textLength && visualLineCount < JUNE_MAX_VISUAL_LINES) |
| 6106 char lineText[1024] = { 0 }; | 6320 { |
| 6107 int lineTextLen = 0; | 6321 // Check for hard newline |
| 6108 if (multiline) | 6322 if (text[idx] == '\n') |
| 6109 { | 6323 { |
| 6110 // Extract current line text up to cursor | 6324 idx++; |
| 6111 int i = lineStart; | 6325 if (idx < textLength && visualLineCount < JUNE_MAX_VISUAL_LINES) |
| 6112 while (i < thisCursorIndex && text[i] != '\n' && lineTextLen < 1023) | 6326 { |
| 6113 { | 6327 visualLineStarts[visualLineCount++] = idx; |
| 6114 lineText[lineTextLen++] = text[i++]; | 6328 lineStartIdx = idx; |
| 6115 } | 6329 } |
| 6116 lineText[lineTextLen] = '\0'; | 6330 continue; |
| 6117 } | 6331 } |
| 6118 | 6332 |
| 6119 int textWidth = multiline ? GuiGetTextWidth(lineText) : (GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex)); | 6333 // Calculate width from lineStartIdx to current position |
| 6120 int textIndexOffset = 0; // Text index offset to start drawing in the box | 6334 float currentWidth = 0; |
| 6121 | 6335 int lastSpaceIdx = -1; |
| 6122 // Line height for multiline (matches GuiDrawText line spacing) | 6336 int charIdx = lineStartIdx; |
| 6123 int lineHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); | 6337 |
| 6338 while (charIdx < textLength && text[charIdx] != '\n') | |
| 6339 { | |
| 6340 int cpSize = 0; | |
| 6341 int cp = GetCodepointNext(&text[charIdx], &cpSize); | |
| 6342 int glyphIdx = GetGlyphIndex(guiFont, cp); | |
| 6343 | |
| 6344 float glyphWidth; | |
| 6345 if (guiFont.glyphs[glyphIdx].advanceX == 0) | |
| 6346 glyphWidth = (float)guiFont.recs[glyphIdx].width * scaleFactor; | |
| 6347 else | |
| 6348 glyphWidth = (float)guiFont.glyphs[glyphIdx].advanceX * scaleFactor; | |
| 6349 | |
| 6350 if (text[charIdx] == ' ') lastSpaceIdx = charIdx; | |
| 6351 | |
| 6352 if (currentWidth + glyphWidth > maxLineWidth && charIdx > lineStartIdx) | |
| 6353 { | |
| 6354 // Need to wrap | |
| 6355 int wrapIdx; | |
| 6356 if (lastSpaceIdx > lineStartIdx) | |
| 6357 { | |
| 6358 // Wrap at last space | |
| 6359 wrapIdx = lastSpaceIdx + 1; | |
| 6360 } | |
| 6361 else | |
| 6362 { | |
| 6363 // No space found, wrap at current char | |
| 6364 wrapIdx = charIdx; | |
| 6365 } | |
| 6366 | |
| 6367 if (visualLineCount < JUNE_MAX_VISUAL_LINES) | |
| 6368 { | |
| 6369 visualLineStarts[visualLineCount++] = wrapIdx; | |
| 6370 lineStartIdx = wrapIdx; | |
| 6371 idx = wrapIdx; | |
| 6372 lastSpaceIdx = -1; | |
| 6373 } | |
| 6374 break; | |
| 6375 } | |
| 6376 | |
| 6377 currentWidth += glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6378 charIdx += cpSize; | |
| 6379 } | |
| 6380 | |
| 6381 if (charIdx >= textLength || text[charIdx] == '\n') | |
| 6382 { | |
| 6383 idx = charIdx; | |
| 6384 } | |
| 6385 } | |
| 6386 } | |
| 6387 else | |
| 6388 { | |
| 6389 visualLineStarts[visualLineCount++] = 0; | |
| 6390 } | |
| 6391 | |
| 6392 // Helper: Find visual line for a given text index | |
| 6393 int cursorVisualLine = 0; | |
| 6394 for (int vl = visualLineCount - 1; vl >= 0; vl--) | |
| 6395 { | |
| 6396 if (thisCursorIndex >= visualLineStarts[vl]) | |
| 6397 { | |
| 6398 cursorVisualLine = vl; | |
| 6399 break; | |
| 6400 } | |
| 6401 } | |
| 6402 | |
| 6403 // Helper: Get end of visual line (exclusive) | |
| 6404 int cursorLineStart = visualLineStarts[cursorVisualLine]; | |
| 6405 int cursorLineEnd = (cursorVisualLine + 1 < visualLineCount) ? visualLineStarts[cursorVisualLine + 1] : textLength; | |
| 6406 // Adjust for newline at end | |
| 6407 if (cursorLineEnd > 0 && cursorLineEnd <= textLength && cursorLineEnd > cursorLineStart) | |
| 6408 { | |
| 6409 if (text[cursorLineEnd - 1] == '\n') cursorLineEnd--; | |
| 6410 } | |
| 6411 | |
| 6412 // Calculate cursor X position within visual line | |
| 6413 float cursorXOffset = 0; | |
| 6414 for (int k = cursorLineStart; k < thisCursorIndex && k < textLength; k++) | |
| 6415 { | |
| 6416 if (text[k] == '\n') break; | |
| 6417 int cpSize = 0; | |
| 6418 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6419 int glyphIdx = GetGlyphIndex(guiFont, cp); | |
| 6420 float glyphWidth; | |
| 6421 if (guiFont.glyphs[glyphIdx].advanceX == 0) | |
| 6422 glyphWidth = (float)guiFont.recs[glyphIdx].width * scaleFactor; | |
| 6423 else | |
| 6424 glyphWidth = (float)guiFont.glyphs[glyphIdx].advanceX * scaleFactor; | |
| 6425 cursorXOffset += glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6426 } | |
| 6124 | 6427 |
| 6125 // Cursor rectangle | 6428 // Cursor rectangle |
| 6126 // NOTE: Position X and Y values updated for multiline support | |
| 6127 Rectangle cursor = { | 6429 Rectangle cursor = { |
| 6128 textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), | 6430 textBounds.x + cursorXOffset, |
| 6129 multiline ? (textBounds.y + cursorLine * lineHeight) : (textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)), | 6431 textBounds.y + cursorVisualLine * lineHeight, |
| 6130 2, | 6432 2, |
| 6131 (float)GuiGetStyle(DEFAULT, TEXT_SIZE) | 6433 (float)GuiGetStyle(DEFAULT, TEXT_SIZE) |
| 6132 }; | 6434 }; |
| 6133 | 6435 |
| 6134 if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; | 6436 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); | 6437 if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); |
| 6136 | 6438 |
| 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 | 6439 // Update control |
| 6148 //-------------------------------------------------------------------- | 6440 if ((state != STATE_DISABLED) && |
| 6149 // WARNING: Text editing is only supported under certain conditions: | 6441 !GuiGetStyle(TEXTBOX, TEXT_READONLY) && |
| 6150 if ((state != STATE_DISABLED) && // Control not disabled | 6442 !guiLocked && |
| 6151 !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode | 6443 !guiControlExclusiveMode) |
| 6152 !guiLocked && // Gui not locked | |
| 6153 !guiControlExclusiveMode && // No gui slider on dragging | |
| 6154 (wrapMode == TEXT_WRAP_NONE)) // No wrap mode | |
| 6155 { | 6444 { |
| 6156 Vector2 mousePosition = GetMousePosition(); | 6445 Vector2 mousePosition = GetMousePosition(); |
| 6157 | 6446 |
| 6158 if (editMode) | 6447 if (editMode) |
| 6159 { | 6448 { |
| 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++; | 6449 if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++; |
| 6163 else autoCursorCounter = 0; | 6450 else autoCursorCounter = 0; |
| 6164 | 6451 |
| 6165 bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); | 6452 bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); |
| 6453 bool shiftDown = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); | |
| 6166 | 6454 |
| 6167 state = STATE_PRESSED; | 6455 state = STATE_PRESSED; |
| 6168 | 6456 |
| 6169 if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; | 6457 if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; |
| 6170 | 6458 |
| 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 | 6459 int codepoint = GetCharPressed(); // Get Unicode codepoint |
| 6184 if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; | 6460 if (IsKeyPressed(KEY_ENTER)) |
| 6461 codepoint = (int)'\n'; | |
| 6185 | 6462 |
| 6186 // Encode codepoint as UTF-8 | 6463 // Encode codepoint as UTF-8 |
| 6187 int codepointSize = 0; | 6464 int codepointSize = 0; |
| 6188 const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); | 6465 const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); |
| 6189 | 6466 |
| 6190 // Handle text paste action | 6467 // Helper macro to check if there's an active selection |
| 6191 if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | 6468 #define HAS_SELECTION() (textBoxSelectionStart >= 0 && textBoxSelectionEnd >= 0 && textBoxSelectionStart != textBoxSelectionEnd) |
| 6192 { | 6469 #define SELECTION_MIN() ((textBoxSelectionStart < textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd) |
| 6470 #define SELECTION_MAX() ((textBoxSelectionStart > textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd) | |
| 6471 | |
| 6472 // Ctrl+A: Select all | |
| 6473 if (IsKeyPressed(KEY_A) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER))) | |
| 6474 { | |
| 6475 textBoxSelectionStart = 0; | |
| 6476 textBoxSelectionEnd = textLength; | |
| 6477 textBoxCursorIndex = textLength; | |
| 6478 } | |
| 6479 // Ctrl+C: Copy selection to clipboard | |
| 6480 else if (IsKeyPressed(KEY_C) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER))) | |
| 6481 { | |
| 6482 if (HAS_SELECTION()) | |
| 6483 { | |
| 6484 int selMin = SELECTION_MIN(); | |
| 6485 int selMax = SELECTION_MAX(); | |
| 6486 int selLen = selMax - selMin; | |
| 6487 char *clipText = (char *)RL_MALLOC(selLen + 1); | |
| 6488 if (clipText) | |
| 6489 { | |
| 6490 memcpy(clipText, text + selMin, selLen); | |
| 6491 clipText[selLen] = '\0'; | |
| 6492 SetClipboardText(clipText); | |
| 6493 RL_FREE(clipText); | |
| 6494 } | |
| 6495 } | |
| 6496 } | |
| 6497 // Ctrl+X: Cut selection to clipboard | |
| 6498 else if (IsKeyPressed(KEY_X) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER))) | |
| 6499 { | |
| 6500 if (HAS_SELECTION()) | |
| 6501 { | |
| 6502 int selMin = SELECTION_MIN(); | |
| 6503 int selMax = SELECTION_MAX(); | |
| 6504 int selLen = selMax - selMin; | |
| 6505 | |
| 6506 // Copy to clipboard | |
| 6507 char *clipText = (char *)RL_MALLOC(selLen + 1); | |
| 6508 if (clipText) | |
| 6509 { | |
| 6510 memcpy(clipText, text + selMin, selLen); | |
| 6511 clipText[selLen] = '\0'; | |
| 6512 SetClipboardText(clipText); | |
| 6513 RL_FREE(clipText); | |
| 6514 } | |
| 6515 | |
| 6516 // Delete selection | |
| 6517 for (int j = selMax; j <= textLength; j++) text[j - selLen] = text[j]; | |
| 6518 textLength -= selLen; | |
| 6519 textBoxCursorIndex = selMin; | |
| 6520 textBoxSelectionStart = -1; | |
| 6521 textBoxSelectionEnd = -1; | |
| 6522 } | |
| 6523 } | |
| 6524 // Ctrl+V: Paste (delete selection first if any) | |
| 6525 else if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER))) | |
| 6526 { | |
| 6527 // Delete selection first if any | |
| 6528 if (HAS_SELECTION()) | |
| 6529 { | |
| 6530 int selMin = SELECTION_MIN(); | |
| 6531 int selMax = SELECTION_MAX(); | |
| 6532 int selLen = selMax - selMin; | |
| 6533 for (int j = selMax; j <= textLength; j++) text[j - selLen] = text[j]; | |
| 6534 textLength -= selLen; | |
| 6535 textBoxCursorIndex = selMin; | |
| 6536 textBoxSelectionStart = -1; | |
| 6537 textBoxSelectionEnd = -1; | |
| 6538 } | |
| 6539 | |
| 6193 const char *pasteText = GetClipboardText(); | 6540 const char *pasteText = GetClipboardText(); |
| 6194 if (pasteText != NULL) | 6541 if (pasteText != NULL) |
| 6195 { | 6542 { |
| 6196 int pasteLength = 0; | 6543 int pasteLength = 0; |
| 6197 int pasteCodepoint; | 6544 int pasteCodepoint; |
| 6198 int pasteCodepointSize; | 6545 int pasteCodepointSize; |
| 6199 | 6546 |
| 6200 // Count how many codepoints to copy, stopping at the first unwanted control character | |
| 6201 while (true) | 6547 while (true) |
| 6202 { | 6548 { |
| 6203 pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize); | 6549 pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize); |
| 6204 if (textLength + pasteLength + pasteCodepointSize >= textSize) break; | 6550 if (textLength + pasteLength + pasteCodepointSize >= textSize) break; |
| 6205 if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break; | 6551 if (!((pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break; |
| 6206 pasteLength += pasteCodepointSize; | 6552 pasteLength += pasteCodepointSize; |
| 6207 } | 6553 } |
| 6208 | 6554 |
| 6209 if (pasteLength > 0) | 6555 if (pasteLength > 0) |
| 6210 { | 6556 { |
| 6211 // Move forward data from cursor position | 6557 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]; | 6558 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 | 6559 |
| 6217 textBoxCursorIndex += pasteLength; | 6560 textBoxCursorIndex += pasteLength; |
| 6218 textLength += pasteLength; | 6561 textLength += pasteLength; |
| 6219 text[textLength] = '\0'; | 6562 text[textLength] = '\0'; |
| 6220 } | 6563 } |
| 6221 } | 6564 } |
| 6222 } | 6565 } |
| 6223 else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) | 6566 else if ((((codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) |
| 6224 { | 6567 { |
| 6568 // Delete selection first if any | |
| 6569 if (HAS_SELECTION()) | |
| 6570 { | |
| 6571 int selMin = SELECTION_MIN(); | |
| 6572 int selMax = SELECTION_MAX(); | |
| 6573 int selLen = selMax - selMin; | |
| 6574 for (int j = selMax; j <= textLength; j++) text[j - selLen] = text[j]; | |
| 6575 textLength -= selLen; | |
| 6576 textBoxCursorIndex = selMin; | |
| 6577 textBoxSelectionStart = -1; | |
| 6578 textBoxSelectionEnd = -1; | |
| 6579 } | |
| 6580 | |
| 6225 // Adding codepoint to text, at current cursor position | 6581 // Adding codepoint to text, at current cursor position |
| 6226 | 6582 if ((textLength + codepointSize) < textSize) |
| 6227 // Move forward data from cursor position | 6583 { |
| 6228 for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize]; | 6584 for (int j = (textLength + codepointSize); j > textBoxCursorIndex; j--) text[j] = text[j - codepointSize]; |
| 6229 | 6585 for (int j = 0; j < codepointSize; j++) text[textBoxCursorIndex + j] = charEncoded[j]; |
| 6230 // Add new codepoint in current cursor position | 6586 |
| 6231 for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i]; | 6587 textBoxCursorIndex += codepointSize; |
| 6232 | 6588 textLength += codepointSize; |
| 6233 textBoxCursorIndex += codepointSize; | 6589 text[textLength] = '\0'; |
| 6234 textLength += codepointSize; | 6590 } |
| 6235 | 6591 } |
| 6236 // Make sure text last character is EOL | 6592 |
| 6237 text[textLength] = '\0'; | 6593 #undef HAS_SELECTION |
| 6238 } | 6594 #undef SELECTION_MIN |
| 6239 | 6595 #undef SELECTION_MAX |
| 6240 // Move cursor to start | 6596 |
| 6241 if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0; | 6597 // Move cursor to start (with Shift selection support) |
| 6242 | 6598 if ((textLength > 0) && IsKeyPressed(KEY_HOME)) |
| 6243 // Move cursor to end | 6599 { |
| 6244 if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength; | 6600 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; |
| 6245 | 6601 textBoxCursorIndex = 0; |
| 6246 // Delete related codepoints from text, after current cursor position | 6602 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; |
| 6247 if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | 6603 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } |
| 6604 } | |
| 6605 | |
| 6606 // Move cursor to end (with Shift selection support) | |
| 6607 if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) | |
| 6608 { | |
| 6609 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6610 textBoxCursorIndex = textLength; | |
| 6611 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; | |
| 6612 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } | |
| 6613 } | |
| 6614 | |
| 6615 // Delete selection if any (on Delete or Backspace) | |
| 6616 if ((textBoxSelectionStart >= 0 && textBoxSelectionEnd >= 0 && textBoxSelectionStart != textBoxSelectionEnd) && | |
| 6617 (IsKeyPressed(KEY_DELETE) || IsKeyPressed(KEY_BACKSPACE))) | |
| 6618 { | |
| 6619 int selMin = (textBoxSelectionStart < textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd; | |
| 6620 int selMax = (textBoxSelectionStart > textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd; | |
| 6621 int selLen = selMax - selMin; | |
| 6622 for (int j = selMax; j <= textLength; j++) text[j - selLen] = text[j]; | |
| 6623 textLength -= selLen; | |
| 6624 textBoxCursorIndex = selMin; | |
| 6625 textBoxSelectionStart = -1; | |
| 6626 textBoxSelectionEnd = -1; | |
| 6627 } | |
| 6628 // Ctrl+Delete: Delete word after cursor | |
| 6629 else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | |
| 6248 { | 6630 { |
| 6249 int offset = textBoxCursorIndex; | 6631 int offset = textBoxCursorIndex; |
| 6250 int accCodepointSize = 0; | 6632 int accCodepointSize = 0; |
| 6251 int nextCodepointSize; | 6633 int nextCodepointSize; |
| 6252 int nextCodepoint; | 6634 int nextCodepoint; |
| 6253 | 6635 |
| 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); | 6636 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6257 bool puctuation = ispunct(nextCodepoint & 0xff); | 6637 bool puctuation = ispunct(nextCodepoint & 0xff); |
| 6258 while (offset < textLength) | 6638 while (offset < textLength) |
| 6259 { | 6639 { |
| 6260 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) | 6640 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) |
| 6262 offset += nextCodepointSize; | 6642 offset += nextCodepointSize; |
| 6263 accCodepointSize += nextCodepointSize; | 6643 accCodepointSize += nextCodepointSize; |
| 6264 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6644 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6265 } | 6645 } |
| 6266 | 6646 |
| 6267 // Check whitespace to delete (ASCII only) | |
| 6268 while (offset < textLength) | 6647 while (offset < textLength) |
| 6269 { | 6648 { |
| 6270 if (!isspace(nextCodepoint & 0xff)) break; | 6649 if (!isspace(nextCodepoint & 0xff)) break; |
| 6271 | |
| 6272 offset += nextCodepointSize; | 6650 offset += nextCodepointSize; |
| 6273 accCodepointSize += nextCodepointSize; | 6651 accCodepointSize += nextCodepointSize; |
| 6274 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6652 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6275 } | 6653 } |
| 6276 | 6654 |
| 6277 // Move text after cursor forward (including final null terminator) | |
| 6278 for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i]; | 6655 for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i]; |
| 6279 | |
| 6280 textLength -= accCodepointSize; | 6656 textLength -= accCodepointSize; |
| 6281 } | 6657 } |
| 6282 | 6658 // Delete single character after cursor |
| 6283 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) | 6659 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) |
| 6284 { | 6660 { |
| 6285 // Delete single codepoint from text, after current cursor position | |
| 6286 | |
| 6287 int nextCodepointSize = 0; | 6661 int nextCodepointSize = 0; |
| 6288 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); | 6662 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]; | 6663 for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i]; |
| 6292 | |
| 6293 textLength -= nextCodepointSize; | 6664 textLength -= nextCodepointSize; |
| 6294 } | 6665 } |
| 6295 | 6666 // Ctrl+Backspace: Delete word before cursor |
| 6296 // Delete related codepoints from text, before current cursor position | 6667 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 { | 6668 { |
| 6299 int offset = textBoxCursorIndex; | 6669 int offset = textBoxCursorIndex; |
| 6300 int accCodepointSize = 0; | 6670 int accCodepointSize = 0; |
| 6301 int prevCodepointSize = 0; | 6671 int prevCodepointSize = 0; |
| 6302 int prevCodepoint = 0; | 6672 int prevCodepoint = 0; |
| 6303 | 6673 |
| 6304 // Check whitespace to delete (ASCII only) | |
| 6305 while (offset > 0) | 6674 while (offset > 0) |
| 6306 { | 6675 { |
| 6307 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); | 6676 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); |
| 6308 if (!isspace(prevCodepoint & 0xff)) break; | 6677 if (!isspace(prevCodepoint & 0xff)) break; |
| 6309 | |
| 6310 offset -= prevCodepointSize; | 6678 offset -= prevCodepointSize; |
| 6311 accCodepointSize += prevCodepointSize; | 6679 accCodepointSize += prevCodepointSize; |
| 6312 } | 6680 } |
| 6313 | 6681 |
| 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); | 6682 bool puctuation = ispunct(prevCodepoint & 0xff); |
| 6317 while (offset > 0) | 6683 while (offset > 0) |
| 6318 { | 6684 { |
| 6319 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); | 6685 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); |
| 6320 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; | 6686 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; |
| 6321 | |
| 6322 offset -= prevCodepointSize; | 6687 offset -= prevCodepointSize; |
| 6323 accCodepointSize += prevCodepointSize; | 6688 accCodepointSize += prevCodepointSize; |
| 6324 } | 6689 } |
| 6325 | 6690 |
| 6326 // Move text after cursor forward (including final null terminator) | |
| 6327 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i]; | 6691 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i]; |
| 6328 | |
| 6329 textLength -= accCodepointSize; | 6692 textLength -= accCodepointSize; |
| 6330 textBoxCursorIndex -= accCodepointSize; | 6693 textBoxCursorIndex -= accCodepointSize; |
| 6331 } | 6694 } |
| 6332 | 6695 // Backspace single character before cursor |
| 6333 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) | 6696 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) |
| 6334 { | 6697 { |
| 6335 // Delete single codepoint from text, before current cursor position | |
| 6336 | |
| 6337 int prevCodepointSize = 0; | 6698 int prevCodepointSize = 0; |
| 6338 | |
| 6339 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); | 6699 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]; | 6700 for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i]; |
| 6343 | |
| 6344 textLength -= prevCodepointSize; | 6701 textLength -= prevCodepointSize; |
| 6345 textBoxCursorIndex -= prevCodepointSize; | 6702 textBoxCursorIndex -= prevCodepointSize; |
| 6346 } | 6703 } |
| 6347 | 6704 |
| 6348 // Move cursor position with keys | 6705 // Move cursor position with keys (with Shift selection support) |
| 6349 if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | 6706 if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) |
| 6350 { | 6707 { |
| 6708 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6709 | |
| 6351 int offset = textBoxCursorIndex; | 6710 int offset = textBoxCursorIndex; |
| 6352 //int accCodepointSize = 0; | |
| 6353 int prevCodepointSize = 0; | 6711 int prevCodepointSize = 0; |
| 6354 int prevCodepoint = 0; | 6712 int prevCodepoint = 0; |
| 6355 | 6713 |
| 6356 // Check whitespace to skip (ASCII only) | |
| 6357 while (offset > 0) | 6714 while (offset > 0) |
| 6358 { | 6715 { |
| 6359 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); | 6716 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); |
| 6360 if (!isspace(prevCodepoint & 0xff)) break; | 6717 if (!isspace(prevCodepoint & 0xff)) break; |
| 6361 | |
| 6362 offset -= prevCodepointSize; | 6718 offset -= prevCodepointSize; |
| 6363 //accCodepointSize += prevCodepointSize; | 6719 } |
| 6364 } | 6720 |
| 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); | 6721 bool puctuation = ispunct(prevCodepoint & 0xff); |
| 6369 while (offset > 0) | 6722 while (offset > 0) |
| 6370 { | 6723 { |
| 6371 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); | 6724 prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); |
| 6372 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; | 6725 if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; |
| 6373 | |
| 6374 offset -= prevCodepointSize; | 6726 offset -= prevCodepointSize; |
| 6375 //accCodepointSize += prevCodepointSize; | |
| 6376 } | 6727 } |
| 6377 | 6728 |
| 6378 textBoxCursorIndex = offset; | 6729 textBoxCursorIndex = offset; |
| 6730 | |
| 6731 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; | |
| 6732 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } | |
| 6379 } | 6733 } |
| 6380 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger))) | 6734 else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger))) |
| 6381 { | 6735 { |
| 6736 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6737 | |
| 6382 int prevCodepointSize = 0; | 6738 int prevCodepointSize = 0; |
| 6383 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); | 6739 GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); |
| 6384 | |
| 6385 textBoxCursorIndex -= prevCodepointSize; | 6740 textBoxCursorIndex -= prevCodepointSize; |
| 6741 | |
| 6742 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; | |
| 6743 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } | |
| 6386 } | 6744 } |
| 6387 else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) | 6745 else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) |
| 6388 { | 6746 { |
| 6747 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6748 | |
| 6389 int offset = textBoxCursorIndex; | 6749 int offset = textBoxCursorIndex; |
| 6390 //int accCodepointSize = 0; | |
| 6391 int nextCodepointSize; | 6750 int nextCodepointSize; |
| 6392 int nextCodepoint; | 6751 int nextCodepoint; |
| 6393 | 6752 |
| 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); | 6753 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6397 bool puctuation = ispunct(nextCodepoint & 0xff); | 6754 bool puctuation = ispunct(nextCodepoint & 0xff); |
| 6398 while (offset < textLength) | 6755 while (offset < textLength) |
| 6399 { | 6756 { |
| 6400 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break; | 6757 if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break; |
| 6401 | |
| 6402 offset += nextCodepointSize; | 6758 offset += nextCodepointSize; |
| 6403 //accCodepointSize += nextCodepointSize; | |
| 6404 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6759 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6405 } | 6760 } |
| 6406 | 6761 |
| 6407 // Check whitespace to skip (ASCII only) | |
| 6408 while (offset < textLength) | 6762 while (offset < textLength) |
| 6409 { | 6763 { |
| 6410 if (!isspace(nextCodepoint & 0xff)) break; | 6764 if (!isspace(nextCodepoint & 0xff)) break; |
| 6411 | |
| 6412 offset += nextCodepointSize; | 6765 offset += nextCodepointSize; |
| 6413 //accCodepointSize += nextCodepointSize; | |
| 6414 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); | 6766 nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); |
| 6415 } | 6767 } |
| 6416 | 6768 |
| 6417 textBoxCursorIndex = offset; | 6769 textBoxCursorIndex = offset; |
| 6770 | |
| 6771 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; | |
| 6772 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } | |
| 6418 } | 6773 } |
| 6419 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger))) | 6774 else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger))) |
| 6420 { | 6775 { |
| 6776 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; | |
| 6777 | |
| 6421 int nextCodepointSize = 0; | 6778 int nextCodepointSize = 0; |
| 6422 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); | 6779 GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); |
| 6423 | |
| 6424 textBoxCursorIndex += nextCodepointSize; | 6780 textBoxCursorIndex += nextCodepointSize; |
| 6425 } | 6781 |
| 6426 | 6782 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; |
| 6427 // Vertical cursor movement for multiline | 6783 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } |
| 6428 if (multiline && (IsKeyPressed(KEY_UP) || (IsKeyDown(KEY_UP) && autoCursorShouldTrigger))) | 6784 } |
| 6429 { | 6785 |
| 6430 // Find start of current line | 6786 // Vertical cursor movement using visual lines (with Shift selection support) |
| 6431 int currentLineStart = textBoxCursorIndex; | 6787 if ((IsKeyPressed(KEY_UP) || (IsKeyDown(KEY_UP) && autoCursorShouldTrigger))) |
| 6432 while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--; | 6788 { |
| 6433 | 6789 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; |
| 6434 // Calculate horizontal position in current line | 6790 |
| 6435 int horizontalPos = textBoxCursorIndex - currentLineStart; | 6791 // Find current visual line |
| 6436 | 6792 int currVisLine = 0; |
| 6437 // Find start of previous line | 6793 for (int vl = visualLineCount - 1; vl >= 0; vl--) |
| 6438 if (currentLineStart > 0) | 6794 { |
| 6439 { | 6795 if (textBoxCursorIndex >= visualLineStarts[vl]) { currVisLine = vl; break; } |
| 6440 int prevLineEnd = currentLineStart - 1; // Skip the newline | 6796 } |
| 6441 int prevLineStart = prevLineEnd; | 6797 |
| 6442 while (prevLineStart > 0 && text[prevLineStart - 1] != '\n') prevLineStart--; | 6798 // Calculate X offset in current line |
| 6443 | 6799 int currLineStart = visualLineStarts[currVisLine]; |
| 6444 // Move to same horizontal position on previous line (or end of line if shorter) | 6800 float xOffset = 0; |
| 6445 int prevLineLength = prevLineEnd - prevLineStart; | 6801 for (int k = currLineStart; k < textBoxCursorIndex && k < textLength; k++) |
| 6446 int targetPos = (horizontalPos < prevLineLength) ? horizontalPos : prevLineLength; | 6802 { |
| 6447 textBoxCursorIndex = prevLineStart + targetPos; | 6803 if (text[k] == '\n') break; |
| 6804 int cpSize = 0; | |
| 6805 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6806 int gi = GetGlyphIndex(guiFont, cp); | |
| 6807 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6808 xOffset += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6809 } | |
| 6810 | |
| 6811 if (currVisLine > 0) | |
| 6812 { | |
| 6813 // Move to previous visual line at same X position | |
| 6814 int prevLineStart = visualLineStarts[currVisLine - 1]; | |
| 6815 int prevLineEnd = visualLineStarts[currVisLine]; | |
| 6816 if (prevLineEnd > 0 && text[prevLineEnd - 1] == '\n') prevLineEnd--; | |
| 6817 | |
| 6818 float accum = 0; | |
| 6819 textBoxCursorIndex = prevLineStart; | |
| 6820 for (int k = prevLineStart; k < prevLineEnd; ) | |
| 6821 { | |
| 6822 int cpSize = 0; | |
| 6823 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6824 int gi = GetGlyphIndex(guiFont, cp); | |
| 6825 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6826 if (accum + gw / 2 >= xOffset) break; | |
| 6827 accum += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6828 textBoxCursorIndex = k + cpSize; | |
| 6829 k += cpSize; | |
| 6830 } | |
| 6448 } | 6831 } |
| 6449 else | 6832 else |
| 6450 { | 6833 { |
| 6451 // Already on first line, move to start | |
| 6452 textBoxCursorIndex = 0; | 6834 textBoxCursorIndex = 0; |
| 6453 } | 6835 } |
| 6454 } | 6836 |
| 6455 else if (multiline && (IsKeyPressed(KEY_DOWN) || (IsKeyDown(KEY_DOWN) && autoCursorShouldTrigger))) | 6837 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; |
| 6456 { | 6838 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } |
| 6457 // Find start of current line | 6839 } |
| 6458 int currentLineStart = textBoxCursorIndex; | 6840 else if ((IsKeyPressed(KEY_DOWN) || (IsKeyDown(KEY_DOWN) && autoCursorShouldTrigger))) |
| 6459 while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--; | 6841 { |
| 6460 | 6842 if (shiftDown && textBoxSelectionStart < 0) textBoxSelectionStart = textBoxCursorIndex; |
| 6461 // Calculate horizontal position in current line | 6843 |
| 6462 int horizontalPos = textBoxCursorIndex - currentLineStart; | 6844 // Find current visual line |
| 6463 | 6845 int currVisLine = 0; |
| 6464 // Find end of current line | 6846 for (int vl = visualLineCount - 1; vl >= 0; vl--) |
| 6465 int currentLineEnd = textBoxCursorIndex; | 6847 { |
| 6466 while (currentLineEnd < textLength && text[currentLineEnd] != '\n') currentLineEnd++; | 6848 if (textBoxCursorIndex >= visualLineStarts[vl]) { currVisLine = vl; break; } |
| 6467 | 6849 } |
| 6468 // Find next line | 6850 |
| 6469 if (currentLineEnd < textLength) | 6851 // Calculate X offset in current line |
| 6470 { | 6852 int currLineStart = visualLineStarts[currVisLine]; |
| 6471 int nextLineStart = currentLineEnd + 1; // Skip the newline | 6853 float xOffset = 0; |
| 6472 int nextLineEnd = nextLineStart; | 6854 for (int k = currLineStart; k < textBoxCursorIndex && k < textLength; k++) |
| 6473 while (nextLineEnd < textLength && text[nextLineEnd] != '\n') nextLineEnd++; | 6855 { |
| 6474 | 6856 if (text[k] == '\n') break; |
| 6475 // Move to same horizontal position on next line (or end of line if shorter) | 6857 int cpSize = 0; |
| 6476 int nextLineLength = nextLineEnd - nextLineStart; | 6858 int cp = GetCodepointNext(&text[k], &cpSize); |
| 6477 int targetPos = (horizontalPos < nextLineLength) ? horizontalPos : nextLineLength; | 6859 int gi = GetGlyphIndex(guiFont, cp); |
| 6478 textBoxCursorIndex = nextLineStart + targetPos; | 6860 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; |
| 6861 xOffset += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6862 } | |
| 6863 | |
| 6864 if (currVisLine < visualLineCount - 1) | |
| 6865 { | |
| 6866 // Move to next visual line at same X position | |
| 6867 int nextLineStart = visualLineStarts[currVisLine + 1]; | |
| 6868 int nextLineEnd = (currVisLine + 2 < visualLineCount) ? visualLineStarts[currVisLine + 2] : textLength; | |
| 6869 if (nextLineEnd > nextLineStart && text[nextLineEnd - 1] == '\n') nextLineEnd--; | |
| 6870 | |
| 6871 float accum = 0; | |
| 6872 textBoxCursorIndex = nextLineStart; | |
| 6873 for (int k = nextLineStart; k < nextLineEnd; ) | |
| 6874 { | |
| 6875 int cpSize = 0; | |
| 6876 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 6877 int gi = GetGlyphIndex(guiFont, cp); | |
| 6878 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6879 if (accum + gw / 2 >= xOffset) break; | |
| 6880 accum += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6881 textBoxCursorIndex = k + cpSize; | |
| 6882 k += cpSize; | |
| 6883 } | |
| 6479 } | 6884 } |
| 6480 else | 6885 else |
| 6481 { | 6886 { |
| 6482 // Already on last line, move to end | |
| 6483 textBoxCursorIndex = textLength; | 6887 textBoxCursorIndex = textLength; |
| 6484 } | 6888 } |
| 6485 } | 6889 |
| 6486 | 6890 if (shiftDown) textBoxSelectionEnd = textBoxCursorIndex; |
| 6487 // Move cursor position with mouse | 6891 else { textBoxSelectionStart = -1; textBoxSelectionEnd = -1; } |
| 6488 if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text | 6892 } |
| 6489 { | 6893 |
| 6490 float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; | 6894 // Move cursor position with mouse using visual lines |
| 6491 int codepointIndex = 0; | 6895 { |
| 6492 float glyphWidth = 0.0f; | |
| 6493 float widthToMouseX = 0; | |
| 6494 int mouseCursorIndex = 0; | 6896 int mouseCursorIndex = 0; |
| 6495 | 6897 bool mouseInBounds = CheckCollisionPointRec(mousePosition, textBounds); |
| 6496 for (int i = textIndexOffset; i < textLength; i += codepointSize) | 6898 bool shouldCalculateMousePos = mouseInBounds || textBoxSelecting; |
| 6497 { | 6899 |
| 6498 codepoint = GetCodepointNext(&text[i], &codepointSize); | 6900 if (shouldCalculateMousePos && textLength > 0) |
| 6499 codepointIndex = GetGlyphIndex(guiFont, codepoint); | 6901 { |
| 6500 | 6902 // Determine which visual line the mouse is on |
| 6501 if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); | 6903 int mouseLine = (int)((mousePosition.y - textBounds.y) / lineHeight); |
| 6502 else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); | 6904 if (mouseLine < 0) mouseLine = 0; |
| 6503 | 6905 if (mouseLine >= visualLineCount) mouseLine = visualLineCount - 1; |
| 6504 if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2))) | 6906 |
| 6907 // Get the start and end of the target visual line | |
| 6908 int targetLineStart = visualLineStarts[mouseLine]; | |
| 6909 int targetLineEnd = (mouseLine + 1 < visualLineCount) ? visualLineStarts[mouseLine + 1] : textLength; | |
| 6910 if (targetLineEnd > targetLineStart && text[targetLineEnd - 1] == '\n') targetLineEnd--; | |
| 6911 | |
| 6912 // Find character position within the line based on mouse X | |
| 6913 float relativeMouseX = mousePosition.x - textBounds.x; | |
| 6914 if (relativeMouseX < 0) relativeMouseX = 0; | |
| 6915 | |
| 6916 float widthAccum = 0; | |
| 6917 mouseCursorIndex = targetLineStart; | |
| 6918 | |
| 6919 for (int k = targetLineStart; k < targetLineEnd; ) | |
| 6505 { | 6920 { |
| 6506 mouseCursor.x = textBounds.x + widthToMouseX; | 6921 if (text[k] == '\n') break; |
| 6507 mouseCursorIndex = i; | 6922 int cpSize = 0; |
| 6508 printf("before: %i\n", mouseCursorIndex); | 6923 int cp = GetCodepointNext(&text[k], &cpSize); |
| 6509 break; | 6924 int gi = GetGlyphIndex(guiFont, cp); |
| 6925 | |
| 6926 float gw; | |
| 6927 if (guiFont.glyphs[gi].advanceX == 0) | |
| 6928 gw = (float)guiFont.recs[gi].width * scaleFactor; | |
| 6929 else | |
| 6930 gw = (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 6931 | |
| 6932 float charMidpoint = widthAccum + gw / 2.0f; | |
| 6933 | |
| 6934 if (relativeMouseX <= charMidpoint) | |
| 6935 { | |
| 6936 mouseCursorIndex = k; | |
| 6937 break; | |
| 6938 } | |
| 6939 | |
| 6940 widthAccum += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 6941 mouseCursorIndex = k + cpSize; | |
| 6942 k += cpSize; | |
| 6510 } | 6943 } |
| 6511 | 6944 |
| 6512 if (mousePosition.y >= textBounds.y && mousePosition.y <= textBounds.height) | 6945 // Clamp to line end |
| 6513 { | 6946 if (mouseCursorIndex > targetLineEnd) mouseCursorIndex = targetLineEnd; |
| 6514 mouseCursor.y = mousePosition.y; | 6947 } |
| 6515 mouseCursorIndex = i; | 6948 else if (shouldCalculateMousePos) |
| 6516 int number_of_n = (int)(mousePosition.y / lineHeight); | 6949 { |
| 6517 for (int i = 0; i < textSize; i++) | 6950 mouseCursorIndex = 0; |
| 6518 { | 6951 } |
| 6519 if (text[i] == '\n') | 6952 |
| 6520 { | 6953 // Mouse selection: start selection on mouse press (only when in bounds) |
| 6521 number_of_n--; | 6954 if (mouseInBounds && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) |
| 6522 if (number_of_n == 0) | 6955 { |
| 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; | 6956 textBoxCursorIndex = mouseCursorIndex; |
| 6545 } | 6957 textBoxSelectionStart = mouseCursorIndex; |
| 6546 } | 6958 textBoxSelectionEnd = mouseCursorIndex; |
| 6547 else mouseCursor.x = -1; | 6959 textBoxSelecting = true; |
| 6548 | 6960 } |
| 6549 // Recalculate cursor position for multiline | 6961 // Mouse selection: update selection while dragging (even outside bounds) |
| 6550 if (multiline) | 6962 else if (textBoxSelecting && IsMouseButtonDown(MOUSE_LEFT_BUTTON)) |
| 6551 { | 6963 { |
| 6552 // Recalculate cursor line and position | 6964 textBoxSelectionEnd = mouseCursorIndex; |
| 6553 int newCursorLine = 0; | 6965 textBoxCursorIndex = mouseCursorIndex; |
| 6554 int newLineStart = 0; | 6966 } |
| 6555 | 6967 } |
| 6556 for (int i = 0; i < textBoxCursorIndex; i++) | 6968 |
| 6557 { | 6969 // End mouse selection when button released |
| 6558 if (text[i] == '\n') | 6970 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) |
| 6559 { | 6971 { |
| 6560 newCursorLine++; | 6972 textBoxSelecting = false; |
| 6561 newLineStart = i + 1; | 6973 } |
| 6562 } | 6974 |
| 6563 } | 6975 // Recalculate cursor position using visual lines |
| 6564 | 6976 // (Visual lines already calculated at the start - need to recalculate after any changes) |
| 6565 // Extract current line text up to cursor | 6977 int newCursorVisLine = 0; |
| 6566 char currentLineText[1024] = { 0 }; | 6978 for (int vl = visualLineCount - 1; vl >= 0; vl--) |
| 6567 int currentLineLen = 0; | 6979 { |
| 6568 int i = newLineStart; | 6980 if (textBoxCursorIndex >= visualLineStarts[vl]) { newCursorVisLine = vl; break; } |
| 6569 while (i < textBoxCursorIndex && text[i] != '\n' && currentLineLen < 1023) | 6981 } |
| 6570 { | 6982 |
| 6571 currentLineText[currentLineLen++] = text[i++]; | 6983 int newCursorLineStart = visualLineStarts[newCursorVisLine]; |
| 6572 } | 6984 float newCursorXOffset = 0; |
| 6573 currentLineText[currentLineLen] = '\0'; | 6985 for (int k = newCursorLineStart; k < textBoxCursorIndex && k < textLength; k++) |
| 6574 | 6986 { |
| 6575 cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(currentLineText) + GuiGetStyle(DEFAULT, TEXT_SPACING); | 6987 if (text[k] == '\n') break; |
| 6576 cursor.y = textBounds.y + (newCursorLine * lineHeight * 1.5); | 6988 int cpSize = 0; |
| 6577 } | 6989 int cp = GetCodepointNext(&text[k], &cpSize); |
| 6578 else | 6990 int gi = GetGlyphIndex(guiFont, cp); |
| 6579 { | 6991 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); | 6992 newCursorXOffset += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); |
| 6581 } | 6993 } |
| 6994 | |
| 6995 cursor.x = textBounds.x + newCursorXOffset; | |
| 6996 cursor.y = textBounds.y + newCursorVisLine * lineHeight; | |
| 6582 | 6997 |
| 6583 // Finish text editing on ENTER or mouse click outside bounds | 6998 // Finish text editing on ENTER or mouse click outside bounds |
| 6584 if ((!multiline && IsKeyPressed(KEY_ENTER)) || | 6999 if ((!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) |
| 6585 (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) | |
| 6586 { | 7000 { |
| 6587 textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index | 7001 textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index |
| 6588 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes | 7002 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes |
| 6589 result = 1; | 7003 textBoxSelectionStart = -1; |
| 7004 textBoxSelectionEnd = -1; | |
| 7005 textBoxSelecting = false; | |
| 7006 resultStruct.result = 1; | |
| 6590 } | 7007 } |
| 6591 } | 7008 } |
| 6592 else | 7009 else |
| 6593 { | 7010 { |
| 6594 if (CheckCollisionPointRec(mousePosition, bounds)) | 7011 if (CheckCollisionPointRec(mousePosition, bounds)) |
| 6597 | 7014 |
| 6598 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) | 7015 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) |
| 6599 { | 7016 { |
| 6600 textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text | 7017 textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text |
| 6601 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes | 7018 autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes |
| 6602 result = 1; | 7019 textBoxSelectionStart = -1; |
| 7020 textBoxSelectionEnd = -1; | |
| 7021 resultStruct.result = 1; | |
| 6603 } | 7022 } |
| 6604 } | 7023 } |
| 6605 } | 7024 } |
| 6606 } | 7025 } |
| 6607 //-------------------------------------------------------------------- | 7026 //-------------------------------------------------------------------- |
| 6616 { | 7035 { |
| 6617 GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); | 7036 GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); |
| 6618 } | 7037 } |
| 6619 else GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); | 7038 else GuiDrawRectangle(bounds, 0, GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); |
| 6620 | 7039 |
| 6621 // Draw text considering index offset if required | 7040 // Draw selection highlight using visual lines |
| 6622 // NOTE: Text index offset depends on cursor position | 7041 if (editMode && textBoxSelectionStart >= 0 && textBoxSelectionEnd >= 0 && textBoxSelectionStart != textBoxSelectionEnd) |
| 6623 // Set vertical alignment to top for multiline | 7042 { |
| 7043 int selMin = (textBoxSelectionStart < textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd; | |
| 7044 int selMax = (textBoxSelectionStart > textBoxSelectionEnd) ? textBoxSelectionStart : textBoxSelectionEnd; | |
| 7045 | |
| 7046 // Find visual line for selection start | |
| 7047 int selStartVisLine = 0; | |
| 7048 for (int vl = visualLineCount - 1; vl >= 0; vl--) | |
| 7049 { | |
| 7050 if (selMin >= visualLineStarts[vl]) { selStartVisLine = vl; break; } | |
| 7051 } | |
| 7052 | |
| 7053 // Find visual line for selection end | |
| 7054 int selEndVisLine = 0; | |
| 7055 for (int vl = visualLineCount - 1; vl >= 0; vl--) | |
| 7056 { | |
| 7057 if (selMax >= visualLineStarts[vl]) { selEndVisLine = vl; break; } | |
| 7058 } | |
| 7059 | |
| 7060 Color selectionColor = (Color){ 100, 150, 255, 100 }; | |
| 7061 | |
| 7062 // Draw selection for each visual line in range | |
| 7063 for (int vl = selStartVisLine; vl <= selEndVisLine; vl++) | |
| 7064 { | |
| 7065 int vlStart = visualLineStarts[vl]; | |
| 7066 int vlEnd = (vl + 1 < visualLineCount) ? visualLineStarts[vl + 1] : textLength; | |
| 7067 if (vlEnd > vlStart && vlEnd <= textLength && text[vlEnd - 1] == '\n') vlEnd--; | |
| 7068 | |
| 7069 // Determine selection bounds within this visual line | |
| 7070 int drawStart = (vl == selStartVisLine) ? selMin : vlStart; | |
| 7071 int drawEnd = (vl == selEndVisLine) ? selMax : vlEnd; | |
| 7072 | |
| 7073 // Calculate X positions | |
| 7074 float xStart = 0; | |
| 7075 for (int k = vlStart; k < drawStart && k < textLength; k++) | |
| 7076 { | |
| 7077 if (text[k] == '\n') break; | |
| 7078 int cpSize = 0; | |
| 7079 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 7080 int gi = GetGlyphIndex(guiFont, cp); | |
| 7081 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 7082 xStart += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 7083 } | |
| 7084 | |
| 7085 float xEnd = 0; | |
| 7086 for (int k = vlStart; k < drawEnd && k < textLength; k++) | |
| 7087 { | |
| 7088 if (text[k] == '\n') break; | |
| 7089 int cpSize = 0; | |
| 7090 int cp = GetCodepointNext(&text[k], &cpSize); | |
| 7091 int gi = GetGlyphIndex(guiFont, cp); | |
| 7092 float gw = (guiFont.glyphs[gi].advanceX == 0) ? (float)guiFont.recs[gi].width * scaleFactor : (float)guiFont.glyphs[gi].advanceX * scaleFactor; | |
| 7093 xEnd += gw + (float)GuiGetStyle(DEFAULT, TEXT_SPACING); | |
| 7094 } | |
| 7095 | |
| 7096 Rectangle selRect = { | |
| 7097 textBounds.x + xStart, | |
| 7098 textBounds.y + vl * lineHeight, | |
| 7099 xEnd - xStart, | |
| 7100 (float)GuiGetStyle(DEFAULT, TEXT_SIZE) | |
| 7101 }; | |
| 7102 GuiDrawRectangle(selRect, 0, BLANK, selectionColor); | |
| 7103 } | |
| 7104 } | |
| 7105 | |
| 7106 // Draw text with word wrap enabled | |
| 6624 int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); | 7107 int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); |
| 6625 if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); | 7108 int prevWrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); |
| 6626 | 7109 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); |
| 6627 GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); | 7110 GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_CHAR); |
| 6628 | 7111 |
| 6629 // Restore previous vertical alignment | 7112 GuiDrawText(text, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); |
| 6630 if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment); | 7113 |
| 7114 // Restore previous settings | |
| 7115 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment); | |
| 7116 GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, prevWrapMode); | |
| 6631 | 7117 |
| 6632 // Draw cursor | 7118 // Draw cursor |
| 6633 if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) | 7119 if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) |
| 6634 { | 7120 { |
| 6635 //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) | 7121 //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) |
| 6639 // if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); | 7125 // if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); |
| 6640 } | 7126 } |
| 6641 else if (state == STATE_FOCUSED) GuiTooltip(bounds); | 7127 else if (state == STATE_FOCUSED) GuiTooltip(bounds); |
| 6642 //-------------------------------------------------------------------- | 7128 //-------------------------------------------------------------------- |
| 6643 | 7129 |
| 6644 return result; // Mouse button pressed: result = 1 | 7130 // Return selection info |
| 7131 resultStruct.selectionStart = textBoxSelectionStart; | |
| 7132 resultStruct.selectionEnd = textBoxSelectionEnd; | |
| 7133 | |
| 7134 return resultStruct; | |
| 6645 } | 7135 } |
| 6646 | 7136 |
| 6647 void JUNE_DrawRectangleLinesNoBottom(Rectangle rect, float thickness, Color color) { | 7137 void JUNE_DrawRectangleLinesNoBottom(Rectangle rect, float thickness, Color color) { |
| 6648 Vector2 topLeft = { rect.x, rect.y }; | 7138 Vector2 topLeft = { rect.x, rect.y }; |
| 6649 Vector2 topRight = { rect.x + rect.width, rect.y }; | 7139 Vector2 topRight = { rect.x + rect.width, rect.y }; |