raysan5 / raygui

A simple and easy-to-use immediate-mode gui library
zlib License
3.47k stars 298 forks source link

Add "read-only" GUI Elements for `GuiTextBox`/`GuiTextBoxMulti` #291

Closed furudbat closed 1 year ago

furudbat commented 1 year ago

Hello, I used the older raygui 3.6 version and wanted to upgrade to the newser 4.0 but I'm missing GuiTextBoxMulti =/

In my own raygui fork, I added GuiTextBoxReadOnly, it's basically GuiTextBox but with out the controls:

RAYGUIAPI bool GuiTextBoxReadOnly(Rectangle bounds, const char* text);
RAYGUIAPI bool GuiTextBoxMultiReadOnly(Rectangle bounds, const char* text);

It also accepts const char*, instead of char* (so I can use std::string_view and .c_str() easier).

This can be a nice compromise if I want something like GuiTextBoxMulti but without a cursor (movement).

int GuiTextBoxReadOnly(Rectangle bounds, const char* text) {
  int result = 0;
  GuiState state = (GuiState)GuiGetState();
  float guiAlpha = GuiGetFade();

  Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
  int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex);
  //int textIndexOffset = 0;        // Text index offset to start drawing in the box

  int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL);
  //int multiline = GuiGetStyle(TEXTBOX, TEXT_MULTILINE);

  // Cursor rectangle
  // NOTE: Position X value should be updated
  Rectangle cursor = {
      textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING),
      textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE),
      2,
      (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2
  };

  switch (alignmentVertical)
  {
      case 0: cursor.y = textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE); break;  // CENTERED
      case 1: cursor.y = textBounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; break;  // UP
      case 2: cursor.y = textBounds.y + textBounds.height; break;  // DOWN
      default: break;
  }

  if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
  if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH);

  //--------------------------------------------------------------------

  // Draw control
  //--------------------------------------------------------------------
  if (state == STATE_PRESSED)
  {
      GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha));
  }
  else if (state == STATE_DISABLED)
  {
      GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha));
  }
  else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK);

  // Draw text considering index offset if required
  // NOTE: Text index offset depends on cursor position
  GuiDrawText(text /*+ textIndexOffset*/, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha));

  if (state == STATE_FOCUSED) GuiTooltip(bounds);
  //--------------------------------------------------------------------

  return result;      // Mouse button pressed: result = 1
}
// Text box control with multiple lines
int GuiTextBoxMultiReadOnly(Rectangle bounds, const char *text)
{
    int result = 0;

    GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1);
    GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 1);

    // TODO: Implement methods to calculate cursor position properly
    result = GuiTextBoxReadOnly(bounds, text);

    GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 0);
    GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 0);

    return result;
}
raysan5 commented 1 year ago

@furudbat It can be probably implemented using current GuiTextBox() with some style configuration parameter, making editable the default option.

Current textbox parameters:

// TextBox/TextBoxMulti/ValueBox/Spinner
typedef enum {
    TEXT_INNER_PADDING = 16,    // TextBox/TextBoxMulti/ValueBox/Spinner inner text padding
    TEXT_LINES_SPACING,         // TextBoxMulti lines separation
    TEXT_ALIGNMENT_VERTICAL,    // TextBoxMulti vertical alignment: 0-CENTERED, 1-UP, 2-DOWN
    TEXT_MULTILINE,             // TextBox supports multiple lines
    TEXT_WRAP_MODE              // TextBox wrap mode for multiline: 0-NO_WRAP, 1-CHAR_WRAP, 2-WORD_WRAP
} GuiTextBoxProperty;

A new parameter can be added as:

TEXT_EDIT_MODE,           // TextBox edit mode: 0-No editable, 1-Selectable-only, 2-Full editable

In any case, it requires implementation...

raysan5 commented 1 year ago

implemented