text: /*******************************************************************************************
  *
  *   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 = GetCharPressed();
              
              // 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;
  }