瀏覽代碼

GuiTextEditor() control example -WIP-

Ray 5 年之前
父節點
當前提交
d297b5d7d9
共有 1 個文件被更改,包括 297 次插入0 次删除
  1. 297 0
      examples/text_editor/text_editor.c

+ 297 - 0
examples/text_editor/text_editor.c

@@ -0,0 +1,297 @@
+/*******************************************************************************************
+*
+*   raygui - Controls test
+*
+*   TEST CONTROLS:
+*       - GuiTextEditor()
+*
+*   DEPENDENCIES:
+*       raylib 3.0  - Windowing/input management and drawing.
+*       raygui 2.7  - Immediate-mode GUI controls.
+*
+*   COMPILATION (Windows - MinGW):
+*       gcc -o $(NAME_PART).exe $(FILE_NAME) -I../../src -lraylib -lopengl32 -lgdi32 -std=c99
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2020 Ramon Santamaria (@raysan5)
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#define RAYGUI_SUPPORT_ICONS
+#include "../../src/raygui.h"
+
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static char text01[128] = "Lorem ipsum dolor sit amet, \xE7\x8C\xBF\xE3\x82\x82\xE6\x9C\xA8\xE3\x81\x8B\xE3\x82\x89\xE8\x90\xBD\xE3\x81\xA1\xE3\x82\x8B consectetur adipiscing elit...\0"; // including some hiragana/kanji
+static char text02[128] = "Here's another, much bigger textbox extended.\xf4\xa1\xa1\xff TIP: try COPY/PASTE ;)\0"; // Including some invalid UTF8 
+
+bool GuiTextEditor(Rectangle bounds, char *text, int textLen, bool editMode);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(int argc, char **argv)
+{
+    // Initialization
+    //---------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raygui - gui text editor test");
+
+    Font font = { 0 };
+
+    bool textEditor01EditMode = false;
+    bool textEditor02EditMode = false;
+    
+    SetTargetFPS(60);
+    //---------------------------------------------------------------------------------------
+    
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        Vector2 mouse = GetMousePosition();
+
+        // Fonts drag & drop logic
+        if (IsFileDropped()) 
+        {
+            int count = 0;
+            char **files = GetDroppedFiles(&count);
+            
+            if (IsFileExtension(files[0], ".ttf") || 
+                IsFileExtension(files[0], ".otf") || 
+                IsFileExtension(files[0], ".fnt"))
+            {
+                Font fnt = LoadFont(files[0]);
+                
+                if (fnt.texture.id != 0)
+                {
+                    // Font was loaded, only change font on success
+                    GuiSetFont(fnt);
+
+                    // Remove old font
+                    if (font.texture.id != 0) UnloadFont(font);
+                    font = fnt;
+                }
+            }
+            
+            ClearDroppedFiles();
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+		BeginDrawing();
+		
+            ClearBackground(RAYWHITE);
+            
+            // Draw textboxes extended
+            //---------------------------------------------------------------------------------------
+            if (GuiTextEditor((Rectangle){ 20, 20, 380, 410 }, text01, strlen(text01), textEditor01EditMode)) textEditor01EditMode = !textEditor01EditMode;
+            if (GuiTextEditor((Rectangle){ 420, 20, 360, 410 }, text02, strlen(text02), textEditor02EditMode)) textEditor02EditMode = !textEditor02EditMode;
+            //---------------------------------------------------------------------------------------
+            
+		EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+// Text editor control (Advanced text box)
+bool GuiTextEditor(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+    static Rectangle cursor = { 0 };    // Cursor position and size
+    static int framesCounter = 0;       // Blinking cursor frames counter
+    static int cursorCodepoint = -1;
+    static int selectStartCp = -1;
+    static int selectLengthCp = 0;
+    
+    GuiControlState state = guiState;
+    bool pressed = false;
+    
+    bool textWrap = true;           // TODO: Word-Wrap vs Char-Wrap -> textWrapMode { NO_WRAP_LOCK, NO_WRAP_OVERFLOW, CHAR_WRAP, WORD_WRAP }
+
+    // WARNING: First string full traversal
+    int codepointCount = GetCodepointsCount(text);
+    
+    int textLen = strlen(text);     // Text length in bytes
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != GUI_STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (editMode)
+        {
+            state = GUI_STATE_PRESSED;
+            framesCounter++;
+            
+            // TODO: Cursor position logic (mouse and keys)
+            
+            // Characters selection logic
+            if (selectStartCp != -1)
+            {
+                if (IsKeyDown(KEY_LEFT_SHIFT) && IsKeyPressed(KEY_RIGHT))
+                {
+                    selectLengthCp++;
+                    if (selectLengthCp >= (codepointCount - selectStartCp)) selectLengthCp = codepointCount - selectStartCp;
+                }
+                
+                if (IsKeyDown(KEY_LEFT_SHIFT) && IsKeyPressed(KEY_LEFT))
+                {
+                    selectLengthCp--;
+                    if (selectLengthCp < 0) selectLengthCp = 0;
+                }
+            }
+            
+            int key = GetKeyPressed();
+            
+            // TODO: On key pressed, place new character in cursor position
+
+            // Exit edit mode logic
+            if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0))) pressed = true;
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                state = GUI_STATE_FOCUSED;
+                if (IsMouseButtonPressed(0)) pressed = true;
+            }
+        }
+
+        if (pressed) 
+        {
+            // Exiting edit mode, reset temp variables
+            framesCounter = 0;
+            cursor = (Rectangle){ 0 };
+            
+            cursorCodepoint = -1;
+            selectStartCp = -1;
+            selectLengthCp = 0;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha));
+
+    if (state == GUI_STATE_PRESSED) DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha));
+    else if (state == GUI_STATE_DISABLED) DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha));
+
+    Font font = GetFontDefault();
+    
+    int textOffsetY = 0;            // Offset between lines (on line break '\n')
+    float textOffsetX = 0.0f;       // Offset X to next character to draw
+    
+    float scaleFactor = GuiGetStyle(DEFAULT, TEXT_SIZE)*2/font.baseSize;     // Character quad scaling factor
+    
+    for (int i = 0, cp = 0; i < textLen; i++)
+    {
+        // Get next codepoint from byte string and glyph index in font
+        int codepointByteCount = 0;
+        int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
+        int index = GetGlyphIndex(font, codepoint);
+        
+        Rectangle rec = { bounds.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
+                          bounds.y + textOffsetY + font.chars[index].offsetY*scaleFactor, 
+                          font.recs[index].width*scaleFactor, font.recs[index].height*scaleFactor };
+                          
+        // Automatic line break to wrap text inside box
+        if (textWrap && ((rec.x + rec.width) >= (bounds.x + bounds.width)))
+        {
+            textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
+            textOffsetX = 0.0f;
+            
+            // Recalculate drawing rectangle position
+            rec = (Rectangle){ bounds.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
+                               bounds.y + textOffsetY + font.chars[index].offsetY*scaleFactor, 
+                               font.recs[index].width*scaleFactor, font.recs[index].height*scaleFactor };
+        }
+        
+        // Check selected codepoint
+        if (editMode)
+        {
+            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) && CheckCollisionPointRec(GetMousePosition(), rec))
+            {
+                cursor = rec;
+                cursorCodepoint = cp;
+                selectStartCp = cursorCodepoint;
+                selectLengthCp = 0;
+                
+                // TODO: Place cursor at the end if pressed out of text
+            }
+            
+            // On mouse left button down allow text selection
+            if ((selectStartCp != -1) && IsMouseButtonDown(MOUSE_LEFT_BUTTON) && CheckCollisionPointRec(GetMousePosition(), rec))
+            {
+                if (cp >= selectStartCp) selectLengthCp = cp - selectStartCp;
+                else if (cp < selectStartCp) 
+                { 
+                    //int temp = selectStartCp;
+                    //selectStartCp = cp;
+                    //selectLengthCp = temp - selectStartCp;
+                }
+            }
+        }
+        
+
+        
+        if (codepoint == '\n')    // Line break character
+        {
+            // NOTE: Fixed line spacing of 1.5 line-height
+            // TODO: Support custom line spacing defined by user
+            textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
+            textOffsetX = 0.0f;
+        }
+        else
+        {
+            // Draw codepoint glyph
+            if ((codepoint != ' ') && (codepoint != '\t') && ((rec.x + rec.width) < (bounds.x + bounds.width)))
+            {
+                DrawTexturePro(font.texture, font.recs[index], rec, (Vector2){ 0, 0 }, 0.0f, GetColor(GuiGetStyle(DEFAULT, TEXT_COLOR_NORMAL)));
+            }
+            
+            // TODO: On text overflow do something... move text to the left?
+        }
+        
+        // Draw codepoints selection from selectStartCp to selectLengthCp
+        // TODO: Consider spacing when drawing selected characters background
+        if (editMode && (selectStartCp != -1) && ((cp >= selectStartCp) && (cp <= (selectStartCp + selectLengthCp)))) DrawRectangleRec(rec, MAROON);
+        
+        if (font.chars[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING));
+        else textOffsetX += ((float)font.chars[index].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING));
+        
+        i += (codepointByteCount - 1);   // Move text bytes counter to next codepoint
+        cp++;
+    }
+    
+    // Draw blinking cursor
+    if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangleRec(cursor, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
+
+    //GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha));
+    //--------------------------------------------------------------------
+
+    return pressed;
+}